@kaydash9999/banker-plugins 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +30 -0
  4. package/README.md +10 -7
  5. package/bin/lib/codex-hooks.mjs +7 -1
  6. package/codex/command-skills/front-qa/SKILL.md +3 -1
  7. package/codex/hooks.json +15 -5
  8. package/codex/manifest.json +2 -1
  9. package/codex/skills/ralph/SKILL.md +272 -0
  10. package/codex/skills/ralph/scripts/gate-evidence.mjs +457 -0
  11. package/codex/skills/ralph/scripts/ralph-state.mjs +622 -0
  12. package/codex/skills/ralph/scripts/run-folder.mjs +299 -0
  13. package/codex/transform-matrix.md +6 -5
  14. package/commands/front-qa.md +3 -1
  15. package/hooks/ralph-continue-codex.mjs +59 -0
  16. package/package.json +1 -1
  17. package/skills/all-in-one/SKILL.md +22 -8
  18. package/skills/make-evidence/SKILL.md +207 -131
  19. package/skills/make-evidence/scripts/evidence.mjs +162 -0
  20. package/skills/make-evidence/scripts/me-check.mjs +125 -0
  21. package/skills/make-evidence/scripts/me-commit.mjs +48 -0
  22. package/skills/make-evidence/scripts/me-git.mjs +63 -0
  23. package/skills/make-evidence/scripts/me-oracle.mjs +244 -0
  24. package/skills/make-evidence/scripts/me-precheck.mjs +122 -0
  25. package/skills/make-evidence/scripts/me-run.mjs +214 -0
  26. package/skills/ralph-qa/references/gate-evidence.mjs +3 -3
  27. package/skills/ralph-qa/references/run-folder.mjs +3 -3
  28. package/skills/ultra-chain/SKILL.md +11 -4
  29. package/skills/ultra-init/SKILL.md +9 -7
  30. package/skills/ultraqa/references/gate-evidence.mjs +3 -3
  31. package/skills/ultraqa/references/run-folder.mjs +3 -3
  32. package/skills/visual-ralph/SKILL.md +9 -4
@@ -1,182 +1,258 @@
1
1
  ---
2
2
  name: make-evidence
3
- description: "구현한 서비스의 증거 문서 여섯(optimize, verify, flow, scale, decisions, trace)을 실제 실행으로 만들어 docs/evidence/ 에 커밋. 수치는 실행 로그에서만 옮기고, 문서마다 실행 명령과 로그 경로를 적음. 판정기는 구현을 보지 않은 새 맥락 하위 에이전트가 씀. 미구현은 숨기지 않음. 'make-evidence'/'증거 문서'/'evidence 만들어'/'검증 근거 문서' 시 사용."
4
- argument-hint: "[--host <host>] [--port <port>] [--only <문서,...>] [--no-commit]"
3
+ description: "구현한 서비스의 증거 문서 여섯(optimize, verify, flow, scale, decisions, trace)을 실제 실행으로 만들어 docs/evidence/ 에 커밋. 수치는 실행 로그의 METRIC 줄에서만 옮기고 도우미가 대조함. 판정기는 저장소 밖 방에서 README 와 담당자 사실만 받은 새 맥락 하위 에이전트가 씀. 버그 셋은 별도 작업 트리에서만 넣고 지움. 미구현은 숨기지 않음. 새 프로젝트는 구현 전에 --oracle-only 로 먼저 불러야 판정기가 구현보다 앞섬. 'make-evidence'/'증거 문서'/'evidence 만들어'/'검증 근거 문서' 시 사용."
4
+ argument-hint: "[--host <host>] [--port <port>] [--only <문서,...>] [--oracle-only] [--no-commit]"
5
5
  ---
6
6
 
7
7
  # make-evidence: 실행으로 만드는 증거 문서 여섯
8
8
 
9
9
  구현한 서비스가 README 의 요구를 지키는지, 실제로 실행한 결과로 보이는 문서 여섯을 만든다.
10
- 문서는 `docs/evidence/` 에 쓰고 커밋한다. 답변은 한글.
10
+ 문서는 `docs/evidence/` 에, 판정기는 `tests/oracle/` 에 쓰고 커밋한다. 답변은 한글.
11
11
 
12
12
  ## 원칙
13
13
 
14
- - **수치는 손으로 쓰지 않는다.** 시간, 목적값, 차이(%), 통과 수, 건수는 실행 로그에서만 옮긴다. 로그에 없는 수는 문서에 쓰지 않는다.
15
- - **문서마다 실행 명령과 로그 경로를 적는다.** 읽는 사람이 같은 명령을 다시 돌려 같은 로그를 얻을 수 있어야 한다.
16
- - **실행하지 못한 것은 실행하지 못했다고 쓴다.** 서버가 안 뜨거나 도구가 없으면 그 칸은 `미검증` 과 까닭이다. 추정값이나 예상값으로 채우지 않는다.
14
+ - **수치는 손으로 쓰지 않는다.** 시간, 목적값, 차이, 통과 수, 건수는 스크립트가 로그에 `METRIC <이름>=<값>` 줄로 찍고, 문서는 그 값을 `` `<이름>=<값>` `` 으로 자릿수 그대로 옮긴다.
15
+ - 이름에는 점이 하나 이상 있다(예: `scale.x5.median_s`). 점 없는 `exit=0` 같은 코드 표기는 수치로 보지 않는다.
16
+ - 반올림이나 단위 바꾸기는 스크립트가 해서 찍는다. 측정 스크립트는 상수를 찍지 않는다. 로그를 손으로 쓰거나 고치지 않는다.
17
+ - 시험 통과 수와 종료 코드는 `run` 이 로그 끝에 `METRIC run.exit=`, `METRIC run.tests.pass=` 처럼 찍는다(node --test, pytest, jest).
18
+ - METRIC 을 찍는 코드는 `docs/evidence/scripts/` 의 파일에 둔다. 명령 줄에 `METRIC` 글이 든 로그(`node -e "console.log('METRIC ...')"` 등)는 `check` 가 거부한다.
19
+ - **문서마다 실행 명령과 로그 경로를 적는다.** 수를 적은 줄에는 그 로그 경로를 함께 적는다. 도우미 `check` 가 그 로그에서 같은 줄을 찾는다.
20
+ - **실행하지 못한 것은 `미검증` 과 까닭이다.** 추정값, 예상값, 다른 시험의 결과로 채우지 않는다.
17
21
  - **미구현을 숨기지 않는다.** README 요구 가운데 구현하지 않은 것은 `trace.md` 에 `미구현` 과 이유로 남긴다.
18
- - 문서에 비밀값을 넣지 않는다. 로그에 토큰, 비밀번호, 키가 찍혔으면 커밋 전에 값을 `***` 로 가리고, 가렸다는 사실을 그 문서에 적는다.
22
+ - **구현을 고치지 않는다.** 결함을 찾으면 그대로 기록하고 사용자에게 알린다. 고칠지는 사용자가 정한다. 고친 뒤 다시 부르면 고치기 전 로그도 남긴다.
23
+ - **설치하거나 내려받지 않는다.** 없는 도구(브라우저, DB 클라이언트, 시험 도구)가 필요한 칸은 `미검증(<도구> 없음)` 이다. `npm install`, `pip install`, `npx playwright install`, 이미지 받기를 하지 않는다.
24
+ - **운영 서버, 운영 DB, 남의 서비스에 보내거나 쓰지 않는다.** 로컬 서버라도 설정이나 환경 변수가 그런 곳을 가리키면 먼저 사용자에게 묻는다. 구현을 띄우는 모든 실행(판정기 기준선과 버그 셋, 종단 시험, 규모 측정)에 같다.
25
+ - **사용자의 작업 트리를 건드리지 않는다.** 버그는 별도 작업 트리에서만 넣는다. `git checkout --`, `git restore`, `git stash`, `git clean`, `git reset --hard` 를 쓰지 않는다.
26
+ - 로그와 문서에 비밀값을 넣지 않는다. 도우미가 명령 줄과 출력의 흔한 비밀값 꼴(`DB_PASSWORD=`, `--token`, URL 의 비밀번호, 개인 키 등)을 `***` 로 가리고 그 수를 `# redacted=<n>` 로 남긴다. `commit` 은 비밀값 꼴이 남은 파일을 거부한다. METRIC 이름에 password, secret, token, key 를 쓰지 않는다(가림에 걸림).
27
+
28
+ ## 런타임
29
+
30
+ | 항목 | Claude Code | Codex |
31
+ |---|---|---|
32
+ | 스킬 디렉터리 `<S>` | 스킬 맨 위 "Base directory for this skill:" 경로 | 이 SKILL.md 의 폴더. npm 설치는 `~/.codex/skills/banker-make-evidence/`, 플러그인 설치는 `~/.codex/plugins/cache/<마켓플레이스>/banker/<판>/skills/make-evidence/`. 절대경로로 씀 |
33
+ | 부르기 | `/banker:make-evidence` | `$banker-make-evidence`, 플러그인 `$banker:make-evidence` |
34
+ | 확인 질문 | `AskUserQuestion` | `request_user_input`. 없으면 번호 붙인 평문 선택지 |
35
+ | 판정기 하위 에이전트 | Agent 도구, `general-purpose`, `model` 없이 | `spawn_agent` 를 `agent_type` 과 `model` 없이. 대화를 넘기지 않음(multi-agent v2 `fork_turns="none"`, v1 은 `fork_context` 를 주지 않음). 결과를 받으면 `close_agent` |
36
+ | 샌드박스 | 없음 | 아래 명령은 `sandbox_permissions: "require_escalated"` 로 승인을 청함: `worktree`, `bug`, `commit`(`.git` 쓰기), `serve`, `stop`, 로컬 서버와 DB 에 닿는 `run`(기본 샌드박스는 127.0.0.1 연결과 포트 열기도 막을 수 있음), 브라우저 실행. 권한 오류로 닿지 못하면 `미검증` 으로 적기 전에 승인을 청하고 다시 돈다. 승인할 수 없는 실행이면 `미검증(sandbox)` |
37
+ | 셸 | bash | Windows 는 PowerShell. 도우미는 Node 라 두 셸에서 같다. `--shell` 은 Windows 에서 PowerShell, 그 밖에서 `/bin/sh` 로 한 줄을 돌림. 로그 이름의 `{ts}` 는 예시처럼 작은따옴표로 감싼다(PowerShell 은 따옴표 없는 `{...}` 를 지움). `@TS@` 도 같은 뜻. `wt=$(node "$E" worktree)` 는 PowerShell 에서 `$wt = node $E worktree` |
38
+ | 명령 시간 | Bash 도구의 `timeout` 을 도우미 `--timeout` 보다 30초 길게 준다(최대 600000). 더 길면 `run_in_background` 로 돌린다 | `timeout_ms` 를 같은 값으로 준다 |
39
+
40
+ 물을 수 없는 실행(`codex exec`, 다른 스킬의 연쇄)에서 질문이 필요한 칸은 `미검증(질문 불가)` 로 두고 진행한다.
19
41
 
20
42
  ## 인자
21
43
 
22
- - `--host <host>`, `--port <port>`: `flow.md` 의 종단 시험이 요청을 보낼 서버. 없으면 README, `.env.example`, 서비스 시작 스크립트에서 찾고, 그래도 없으면 사용자에게 묻는다. 찾은 값과 그 출처를 `flow.md` 에 적는다.
23
- - `--only <문서,...>`: 일부만 만든다(예: `--only scale,trace`). 이름은 `optimize`, `verify`, `flow`, `scale`, `decisions`, `trace`.
24
- - `--no-commit`: 파일만 쓰고 커밋하지 않는다.
44
+ | 인자 | 뜻 |
45
+ |---|---|
46
+ | `--host <host>`, `--port <port>` | `flow.md` 의 종단 시험이 요청을 보낼 서버. 없으면 직접 띄운 서버의 시작 로그, README, `.env.example`, 시작 스크립트에서 찾고, 그래도 없으면 묻는다. 찾은 값과 출처를 `flow.md` 에 적는다. 화면과 API 의 포트가 다르면 둘 다 적는다 |
47
+ | `--only <문서,...>` | 일부만 만든다. 이름은 `optimize`, `verify`, `flow`, `scale`, `decisions`, `trace`. 빠진 문서에 기대는 칸(판정기 결과, 시험 로그)은 `미검증(선행 문서 없음)` |
48
+ | `--oracle-only` | 구현을 시작하기 전에 부른다. 담당자 사실, 판정기, 판정기 커밋까지만 한다. "판정기를 구현 전에 씀" 을 지키는 길은 이것뿐이다 |
49
+ | `--no-commit` | 파일은 쓰고 아무것도 커밋하지 않는다(판정기 커밋도). `verify.md` 첫 줄에 `판정기 미커밋: 구현 전에 썼다는 순서를 보일 수 없음` |
25
50
 
26
- ## 폴더
51
+ ## 도우미
27
52
 
28
- ```
29
- docs/evidence/
30
- optimize.md verify.md flow.md scale.md decisions.md trace.md
31
- logs/ 실행 로그. 문서는 이 파일들을 경로로 가리킨다
32
- scripts/ 데이터 생성, 측정, 완전 탐색처럼 증거를 만드는 데 쓴 스크립트
33
- tests/oracle/ 판정기와 그 시험 (verify.md)
34
- ```
53
+ `E="<S>/scripts/evidence.mjs"`. Node 만 쓰고 네트워크를 쓰지 않는다. `node "$E" --help` 가 전체 사용법이다.
54
+
55
+ | 명령 | 하는 일 |
56
+ |---|---|
57
+ | `run --log <파일> [--timeout <초>] [--cwd <폴더>] [--shell] -- <명령...>` | 명령을 돌려 로그를 남긴다. 첫 줄은 다시 돌릴 수 있게 인용한 명령, 둘째 줄은 `# head= dirty= at= node= os= cwd=`, 끝은 `exit=<코드>` 와 `METRIC run.exit=`. 명령의 종료 코드를 그대로 돌려준다. 입력은 닫혀 있고, 시간 제한(기본 300초)에 걸리면 프로세스 트리를 끝내고 `exit=timeout(<초>s)`, 종료 124. 출력을 쥔 채 떨어져 나간 자손은 기다리지 않는다(`# orphan-output=yes`) |
58
+ | `serve --log <파일> --pid <파일> --ready <URL> -- <명령...>` / `stop --pid <파일>` | 서버를 띄워 URL 이 답할 때까지 기다리고, 끝에 트리째 내린다. URL 이 이미 답하면 띄우지 않고, 기다리는 동안 서버가 끝나면 `not-ready(exited <코드>)`. 먼저 죽은 서버는 `exit=gone-before-stop` |
59
+ | `precheck` | 스테이징된 변경(`STOP`), 증거 밖의 바뀐 파일(`ASK dirty`), 이 컴퓨터 밖 주소(`ASK host`, 값은 안 보임)를 보인다. 주소는 네 단계 아래까지의 설정 파일과 환경 변수에서 찾는다. 환경 변수는 저장소가 이름을 부르는 것과, 프레임워크와 DB, 메시지 라이브러리가 스스로 읽는 이름(`SPRING_*`, `QUARKUS_*`, `ConnectionStrings__*`, `PG*`, `MYSQL_*`, `DATABASE_*`, `REDIS*`, `MONGO*`, `KAFKA_*` 등)을 묻고, 그 밖은 `NOTE host` 로만 보인다. 비밀번호와 토큰 이름의 변수는 값을 읽지 않는다. compose 서비스 이름 밖의 점 없는 이름도 묻는다. 결정할 것이 있으면 종료 3 |
60
+ | `room`, `collect --room <폴더> [--replace]` | 판정기 작성용 방(저장소 밖 임시 폴더)에 요구 문서와 담당자 사실만 넣고(방의 기록 파일은 방 밖), 끝나면 방의 `tests/oracle/` 만 가져온다. 방에 다른 파일이 생겼거나 입력이 바뀌었으면 거부(종료 1). 이미 판정기가 있으면 `--replace` 때만 바꾸고 이전 판정기 커밋을 찍는다 |
61
+ | `worktree [--link <폴더>]...`, `worktree --remove <폴더>`, `bug --worktree <폴더> --name <이름> [--kind <종류> --baseline <로그>] -- <판정기 명령...>` | HEAD 의 별도 작업 트리를 만들고(추적되지 않는 의존 폴더는 `--link` 로 이음, 빌드 결과와 `.env` 는 거부), 거기서 판정기를 돌리고, 패치와 로그를 남기고, 작업 트리를 지운다(거부돼도 지움). 사용자 작업 트리(추적 파일의 내용과 추적되지 않는 파일)와 링크한 폴더가 그대로인지 확인한다. 무시된 파일과 DB 는 확인 범위 밖이다 |
62
+ | `check [--tracked]`, `count --doc <파일>` | 문서의 `` `이름=값` `` 을 가리킨 로그의 `METRIC 이름=값` 줄과 정확히 대조한다. `run` 이 만든 로그만 받는다. `` `이름=값` `` 밖의 숫자는 로그 경로가 있든 없든 `WARN` 이고, 오류나 경고가 하나라도 있으면 종료 1. `count` 는 trace 표의 `상태` 칸(머리 줄로 찾음)을 세어 METRIC 줄로 찍고, 넷 가운데 하나가 아닌 행이 있으면 종료 1 |
63
+ | `commit --message <글> [--paths <경로>]... [--accept <파일:줄>]...` | 기본 `docs/evidence`, `tests/oracle` 만 커밋한다. 다른 스테이징된 변경이 있으면 거부. `.gitignore` 에 걸린 파일은 `docs/evidence/logs/` 의 `.log`, `.patch`, `.md` 만 넣는다. `.env`, 5MB 넘는 파일, 비밀값 꼴이 남은 줄(`--accept` 로 사용자가 확인한 자리는 빼고)이 있으면 거부. 커밋 훅은 우회하지 않는다 |
64
+
65
+ 로그 이름은 `docs/evidence/logs/<문서>-<무엇>-{ts}.log` 다. `{ts}` 는 밀리초까지의 UTC 시각이고, 같은 이름은 덮어쓰지 않는다. 문서가 가리키는 로그가 결과를 내는 로그이고, 다시 돌린 로그도 지우지 않는다.
66
+
67
+ 판정기는 판정마다 `ORACLE <constraint|objective|permission> <PASS|FAIL|ERROR> <사례 이름> [설명]` 한 줄을 찍는다. 사례 이름은 공백 없는 한 낱말이다. `bug` 는 이 줄로 판정한다.
68
+
69
+ ## 진행
70
+
71
+ 순서가 중요하다. 판정기 작성자에게 구현 정보가 가지 않도록, 구현을 읽기 전에 0~3 을 한다.
72
+
73
+ `git ls-files tests/oracle` 이 비어 있지 않으면(앞서 `--oracle-only` 로 판정기를 커밋함) 1~3 의 작성은 건너뛴다. 3 의 순서 로그만 남기고, 판정기 커밋 뒤 요구 문서나 담당자 사실이 바뀌었는지 `git diff <판정기 커밋> -- README.md docs/evidence/owner-facts.md` 를 `run` 으로 남긴다. 바뀌었으면 그 사실을 `verify.md` 에 적는다.
35
74
 
36
- - 로그 이름은 `<문서>-<무엇>-<YYYYMMDD-HHMMSS>.log` 다. 같은 시험을 다시 돌리면 새 파일을 만들고, 문서는 마지막 로그를 가리킨다.
37
- - 로그는 명령의 표준 출력과 표준 오류를 함께 담고, 첫 줄에 실행한 명령, 마지막 줄에 종료 코드를 남긴다.
75
+ ### 0. 사전 확인
38
76
 
39
77
  ```bash
40
- run_log() { # run_log <로그 경로> <명령...>
41
- mkdir -p "$(dirname "$1")"; f=$1; shift
42
- { echo "\$ $*"; "$@"; echo "exit=$?"; } > "$f" 2>&1
43
- }
78
+ node "$E" run --log 'docs/evidence/logs/precheck-{ts}.log' -- node "$E" precheck
44
79
  ```
45
80
 
46
- ## 진행
81
+ - `STOP staged`: 사용자가 스테이징한 변경이 있다. 이 스킬은 스테이징을 풀지 않는다. 사용자에게 알리고 멈춘다.
82
+ - `ASK dirty`: 커밋하지 않은 변경이 있다. 묻는다: 사용자가 커밋한 뒤 다시 부름(권장), 또는 그대로 진행. 그대로 진행하면 문서마다 "커밋하지 않은 변경 위에서 실행함(precheck 로그)" 을 적는다. 판정기와 버그 시험은 늘 HEAD 의 별도 작업 트리에서 돈다.
83
+ - `ASK host`: 설정이나 환경 변수가 이 컴퓨터 밖의 DB 나 서비스를 가리킨다. 묻는다. 허락이 없으면 구현을 띄우지 않는다. 판정기 기준선과 버그 셋, `flow.md`, `scale.md` 의 측정이 모두 `미검증(비로컬 설정)` 이다.
84
+ - `NOTE host`: 저장소가 이름을 부르지 않는 환경 변수의 주소다. 서비스나 그 라이브러리가 그 변수를 읽는다면 `ASK host` 와 같이 묻는다.
85
+ - **구현 전인지 본다.** `--oracle-only` 없이 불렀는데 README 의 요구를 처리하는 코드(최적화, API 라우트, CLI 나 배치의 진입점)를 하나도 찾지 못하면 구현 전으로 보고 묻는다: 판정기만 쓰고 멈춤(`--oracle-only` 와 같음, 권장), 또는 그대로 진행. 물을 수 없으면 판정기만 쓰고 멈춘다. 구현 전에 판정기를 커밋해야 "판정기를 구현 전에 씀" 이 지켜지기 때문이다.
86
+ - `precheck` 는 흔한 설정 꼴만 본다. 시작 스크립트와 설정 로더가 읽는 곳(코드 안의 기본값, 다른 이름의 설정 파일, 프레임워크가 환경에서 덮어쓰는 이름)도 직접 읽어 확인한다.
87
+
88
+ ### 1. 담당자 사실
89
+
90
+ - 담당자(사용자)가 준 업무 규칙, 수치 기준, 권한 규칙을 `docs/evidence/owner-facts.md` 에 옮긴다. 항목마다 출처(사용자 메시지 인용과 날짜, 또는 담당자가 준 문서의 경로)를 붙인다.
91
+ - 구현에서 읽은 것, 스스로 짐작한 것은 넣지 않는다. 담당자 사실이 없으면 묻는다. 그래도 없으면 `담당자 사실 없음` 한 줄로 두고 그 사실을 `verify.md` 에 적는다.
92
+ - 요구가 README 가 아닌 문서(사용자가 준 명세)에 있으면 그 경로를 `room` 의 `--req` 로 준다.
93
+
94
+ ### 2. 판정기(구현 보기 전)
95
+
96
+ 1. 방을 만든다: `node "$E" room [--req <요구 문서>]`. 출력의 `room: <폴더>` 가 방 `R` 이다.
97
+ 2. 아래 지시문의 `{R}` 만 채워 `docs/evidence/logs/verify-oracle-prompt-{ts}.md` 에 그대로 저장하고, 그 글 그대로 하위 에이전트를 띄운다. 구현에서 알게 된 이름, 형식, 식, 경로를 더하지 않는다.
98
+
99
+ ```text
100
+ 당신은 판정기(oracle) 작성자입니다. 작업 폴더는 {R} 입니다.
101
+ - 읽어도 되는 파일은 {R} 안의 파일뿐입니다(요구 문서와 docs/evidence/owner-facts.md). {R} 밖의 경로는 읽지도, 목록을 보지도, 실행하지도 않습니다.
102
+ - 쓸 곳은 {R}/tests/oracle/ 뿐입니다.
103
+ - 만들 것:
104
+ 1. 판정기: 입력과 출력만 보고 세 가지를 판정합니다. 제약을 모두 지키는가(constraint), 보고된 목적값이 입력과 출력으로 다시 계산한 값과 같은가(objective), 권한 규칙을 지키는가(permission). 판정마다 한 줄 `ORACLE <constraint|objective|permission> <PASS|FAIL|ERROR> <사례 이름> [설명]` 을 표준 출력에 찍습니다. 사례 이름은 공백 없는 한 낱말입니다. 구현이 뜨지 않았거나, 응답이 없거나, 출력을 읽지 못해 판정할 수 없으면 FAIL 이 아니라 ERROR 를 찍습니다. 요구 문서와 담당자 사실에 권한 규칙이 없으면 permission 은 만들지 않고 그 사실을 RUN.md 에 적습니다.
105
+ 2. 판정기 자체 시험: 손으로 만든 입력과 출력(맞는 것과 틀린 것)으로 판정기만 시험합니다. 구현을 부르지 않습니다. RUN.md 에 자체 시험 명령도 한 줄 적습니다. 목적값 계산과 제약 판정 함수는 다른 스크립트가 불러 쓸 수 있게 내보내고, 그 이름을 RUN.md 에 적습니다.
106
+ 3. RUN.md: 판정기로 구현을 판정하는 명령 한 줄. 명령은 프로젝트 맨 위 폴더(요구 문서가 있는 폴더)에서 돕니다. 구현을 부르는 방법이 요구 문서에 있으면(실행 방법, API) 판정기가 그 폴더에서 구현을 직접 띄워 부르고 끝에 내립니다. 빈 포트를 쓰고, 이미 떠 있는 서버는 쓰지 않습니다. 방법이 없으면 IO.md 에 입력 파일과 출력 파일의 형식을 정해 그 파일을 읽게 합니다. 명령은 파일을 하나하나 이름으로 줍니다(폴더 인자는 판에 따라 시험을 하나도 돌리지 않습니다).
107
+ 4. REQUIREMENTS.md: 요구 문서의 요구를 빠짐없이 번호(R1, R2...), 요구 문서:줄, 원문 문장으로 적습니다.
108
+ - 시험은 돌리지 않습니다. 끝에 읽은 파일 목록(명령으로 본 것 포함)과 만든 파일 목록을 보고합니다.
109
+ ```
110
+
111
+ 3. 띄울 수 없으면(이미 하위 에이전트 안, 다중 에이전트 기능이 꺼짐, 비대화 실행) 같은 맥락이 판정기를 쓰지 않는다. `verify.md` 는 `작성 안 함(하위 에이전트를 띄울 수 없음: <까닭>)` 으로 두고 끝 보고에 올린다.
112
+ 4. 돌아오면 `node "$E" run --log 'docs/evidence/logs/verify-collect-{ts}.log' -- node "$E" collect --room <R>`. 거부되면(종료 1) 그 판정기를 버리고 새 방으로 다시 띄운다. 두 번 더 실패하면 멈추고 알린다. 보고된 읽은 파일 목록에 방 밖의 경로가 있어도 같다.
113
+ 5. 판정기 자체 시험을 RUN.md 의 명령으로 `run` 해 로그를 남긴다(구현은 부르지 않으므로 HEAD 에서 돌려도 된다). 실패하면 판정기를 고치지 않고, 그 사실을 적은 뒤 새 방으로 다시 띄운다.
114
+ 6. `verify.md` 의 "격리" 절에 적는다. 방은 읽을 파일을 줄일 뿐, 하위 에이전트가 방 밖을 읽지 못하게 막지는 않는다. 그 사실도 적는다.
115
+ - 지시문 로그 경로
116
+ - 하위 에이전트가 보고한 읽은 파일 목록. 자기 보고라는 사실도 적는다
117
+ - 자동으로 들어갔을 수 있는 맥락: 세션 폴더(저장소)의 `CLAUDE.md`, `AGENTS.md`, 사용자 전역 지시 파일(`~/.claude/CLAUDE.md`, `~/.claude/rules/`, `~/.codex/AGENTS.md`), 자동 메모리, Claude Code 가 붙이는 git 상태(최근 커밋 제목). 그 가운데 구현 구조나 식을 설명하는 것이 있으면 그 사실을 한계로 적는다
118
+
119
+ ### 3. 판정기 커밋
120
+
121
+ ```bash
122
+ node "$E" commit --paths tests/oracle --paths docs/evidence/owner-facts.md --paths docs/evidence/logs --message "test(oracle): README 와 담당자 사실만으로 쓴 판정기"
123
+ ```
47
124
 
48
- 먼저 저장소를 읽는다: README, 시작 스크립트, 시험 실행 방법, 최적화 코드, DB 스키마, 내보내기 코드.
49
- 문서 여섯은 아래 순서로 만든다. `verify.md` 의 판정기는 다른 문서보다 먼저 쓴다.
125
+ - 구현 커밋보다 앞서는지 `git log --format='%h %ad %s' --date=iso -- tests/oracle <구현 경로>` 를 `run` 으로 남긴다.
126
+ - 담당자 사실, 지시문 로그, collect 로그가 판정기와 같은 커밋에 들어가, 판정기가 무엇을 보고 언제 쓰였는지 git 으로 보인다.
127
+ - 구현이 이미 있었으면 `verify.md` 첫 줄에 `요구 미충족: 판정기가 구현 뒤에 쓰임(<순서 로그>)` 을 쓰고 끝 보고에도 올린다. 순서를 꾸미지 않는다.
128
+ - `--oracle-only` 면 여기서 멈추고, 구현한 뒤 다시 부르라고 알린다.
50
129
 
51
- ### 1. optimize.md
130
+ ### 4. optimize.md
52
131
 
53
132
  | 항목 | 내용 |
54
133
  |---|---|
55
134
  | 목적 함수 | 최소화 또는 최대화하는 값과 식 |
56
135
  | 제약 | 제약마다 한 줄. 강한 제약과 약한 제약(벌점)을 나눔 |
57
- | 알고리즘 이름 | 쓴 알고리즘의 이름(예: 정수 계획법, 담금질, 탐욕 + 지역 탐색). 라이브러리를 쓰면 그 이름과 판 |
58
- | 핵심 함수 | `파일:줄` 로. 목적값 계산, 제약 검사, 탐색 본체를 각각 |
59
- | 의사코드 | 10줄 |
60
-
61
- - `파일:줄` 은 지금 커밋에서 실제로 그 줄을 열어 확인한 것만 쓴다. 문서 끝에 확인한 커밋 해시를 적는다.
62
- - 이 문서에는 실행 수치가 없다. 실행 명령 칸에는 줄 번호를 확인한 명령(예: `grep -n 'def objective' src/opt.py`)과 그 출력 로그를 적는다.
63
-
64
- ### 2. verify.md
65
-
66
- 판정기(oracle)는 구현을 보지 않은 하위 에이전트가 쓴다. 구현을 본 맥락이 판정기를 쓰면 구현의 실수를 판정기도 따라 한다.
67
-
68
- 1. **담당자 사실을 모은다.** 담당자(사용자)가 준 업무 규칙, 수치 기준, 권한 규칙을 `docs/evidence/owner-facts.md` 에 그대로 옮긴다. 담당자 사실이 없으면 사용자에게 묻는다. 그래도 없으면 README 만으로 진행하고 그 사실을 `verify.md` 에 적는다.
69
- 2. **새 맥락 하위 에이전트를 띄운다.** 이 대화를 넘기지 않는다.
70
- - Claude Code: Agent 도구를 `general-purpose` 로, `model` 없이 부른다.
71
- - Codex: `spawn_agent` 를 `agent_type` 과 `model` 없이 부르고 대화를 넘기지 않는다(multi-agent v2 는 `fork_turns="none"`, v1 은 `fork_context` 를 주지 않음).
72
- - 지시에 들어갈 것:
73
- - 읽어도 되는 것은 `README.md` 와 `docs/evidence/owner-facts.md` 둘뿐이다.
74
- - 구현 폴더와 기존 시험은 열지 않는다.
75
- - 쓸 곳은 `tests/oracle/` 다.
76
- - 판정기는 입력과 출력만 보고 세 가지를 판정한다.
77
- - 제약을 모두 지키는가
78
- - 보고된 목적값이 입력과 출력으로 다시 계산한 값과 같은가
79
- - 권한 규칙을 지키는가
80
- - 시험은 판정기를 쓴다.
81
- - 끝에 읽은 파일 목록을 보고한다.
82
- 3. **읽은 파일 목록을 확인한다.** 목록에 두 파일 밖의 경로가 있으면 그 판정기는 버리고 다시 띄운다. 목록은 `verify.md` 에 옮긴다.
83
- 4. **판정기를 구현보다 먼저 커밋한다.** 커밋 메시지는 `test(oracle): ...` 다. 구현이 아직 없으면 이 커밋이 구현 커밋보다 앞선다. 구현이 이미 있으면 그 사실과 두 커밋의 순서(`git log --format='%h %ad %s' --date=iso -- tests/oracle <구현 경로>`)를 그대로 적는다. 순서를 꾸미지 않는다.
84
- 5. **판정기를 지금 구현에 돌린다.** 명령과 로그 경로, 통과와 실패 수를 로그에서 옮긴다.
85
- 6. **일부러 넣은 버그 셋을 판정기가 잡는지 본다.**
86
-
87
- | 버그 | 넣는 방법의 예 |
136
+ | 알고리즘 이름 | 쓴 알고리즘의 이름. 라이브러리면 이름과 판(잠금 파일이나 매니페스트에서 읽음, 그 경로를 적음) |
137
+ | 핵심 함수 | `파일:줄`. 목적값 계산, 제약 검사, 탐색 본체를 각각 |
138
+ | 의사코드 | 10줄. 코드 펜스(```) 안에 씀 |
139
+ | 실행 명령, 로그 | 줄 번호를 확인한 명령(예: `git grep -n "def objective"`)을 `run` 으로 남긴 로그 |
140
+
141
+ `파일:줄` 은 그 로그가 보인 줄만 쓴다. 로그 둘째 줄의 `head=` 가 확인한 커밋이다.
142
+
143
+ ### 5. verify.md (판정 실행과 버그 셋)
144
+
145
+ 1. **잇기**: RUN.md 가 IO.md 형식을 요구하면, 구현을 그 형식으로 잇는 어댑터 `docs/evidence/scripts/oracle-adapter.*` 를 쓴다. 어댑터는 구현을 부르고 형식만 바꾼다. 목적값이나 제약을 스스로 계산하지 않는다. 판정기 파일은 고치지 않는다.
146
+ 2. **DB 격리**: 판정기는 구현을 띄워 요청을 보낸다. 버그를 넣은 구현이 쓰는 DB 는 일회용이어야 한다(작업 트리 안의 파일 DB, 임시 SQLite, 시험 전용 스키마나 컨테이너). 그렇게 돌리는 환경 변수를 RUN.md 명령 앞에 주고 `verify.md` 에 적는다. 일회용으로 돌릴 수 없으면 묻고, 허락이 없으면 `미검증(DB 격리 불가)` 이다.
147
+ 3. **기준선**: 판정기는 HEAD 의 별도 작업 트리에서, 그 작업 트리의 구현을 직접 띄워 돈다. 이미 떠 있는 서버를 쓰면 넣은 버그가 반영되지 않는다. 빈 포트를 쓴다.
148
+ ```bash
149
+ wt=$(node "$E" worktree [--link <의존 폴더>])
150
+ node "$E" bug --worktree "$wt" --name baseline -- <RUN.md 의 명령>
151
+ ```
152
+ - 구현이 추적되지 않는 의존 폴더(`node_modules`, `.venv`)를 쓰면 `--link` 로 이어 준다. 빌드 결과(`dist`, `build`, `target` 등)는 잇지 않는다. 버그를 넣은 빌드가 사용자 폴더에 써지기 때문이다. 빌드가 필요하면 RUN.md 명령 앞에서 작업 트리 안에 새로 빌드한다. 별도 작업 트리에 없는 것을 받으려고 설치하지 않는다.
153
+ - 링크한 폴더가 원래 트리의 코드를 가리키면(작업 공간 패키지 링크, editable 설치) `bug` 가 `WARN` 과 `links_to_repo=` 를 찍고 결과를 `undecidable(link-to-main)` 으로 둔다. 넣은 버그 대신 사용자 코드가 돌기 때문이다. Python 은 링크 대신 `PYTHONPATH=<작업 트리>/src` 를 명령 앞에 줄 수 있다. 그래도 안 되면 버그 셋은 `미검증(의존 폴더가 원래 트리를 가리킴)` 이다.
154
+ - 로그 끝의 `METRIC baseline.<종류>.pass=`, `fail=`, `error=` 이 지금 구현의 판정 결과다. 실패가 있으면 그것이 찾은 결함이다. 고치지 않고 적는다.
155
+ - 기준선이 판정 줄을 하나도 내지 않았거나 ERROR 가 있으면 판정기나 RUN.md 명령이 구현을 부르지 못한 것이다. 버그 셋으로 가지 않고 그 로그를 적은 뒤, 명령(환경 변수, 포트, 빌드)을 고쳐 기준선을 다시 돈다. 판정기 파일은 고치지 않는다.
156
+ 4. **버그 셋**: 종류마다 새 작업 트리를 만들고, 그 작업 트리의 구현 파일만 고친 뒤 돌린다. 사용자 작업 트리의 파일은 고치지 않는다.
157
+ ```bash
158
+ wt=$(node "$E" worktree [--link <의존 폴더>])
159
+ # "$wt" 안의 구현 파일을 고침(예: 제약 검사를 건너뜀)
160
+ node "$E" bug --worktree "$wt" --name constraint --kind constraint --baseline <기준선 로그> -- <RUN.md 의 명령>
161
+ ```
162
+
163
+ | 종류 `--kind` | 넣는 방법의 예 |
88
164
  |---|---|
89
- | 제약 위반 | 제약 검사 한 줄을 건너뛰게 바꿈 |
90
- | 목적값 오계산 | 목적값 합산에서 항 하나를 빼거나 부호를 바꿈 |
91
- | 권한 우회 | 권한 확인을 늘 통과하게 바꿈 |
92
-
93
- - 버그마다 차례로 진행한다.
94
- 1. 바꾼 것을 패치 파일로 남긴다: `docs/evidence/logs/verify-bug-<이름>.patch`
95
- 2. `git apply` 로 넣는다.
96
- 3. 판정기를 돌려 로그를 남긴다.
97
- 4. `git apply -R` 로 되돌린다.
98
- 5. `git status --short` 와 `git diff --stat` 이 빈 것을 로그로 남긴다.
99
- - 표에는 버그마다 다섯 가지를 적는다: 패치 경로, 넣은 뒤 결과(잡음 또는 놓침), 로그 경로, 되돌린 명령, 되돌린 뒤 판정기 결과.
100
- - 놓친 버그는 놓쳤다고 쓰고, 판정기의 빈틈을 한 줄로 적는다. 놓친 것을 잡게 하려고 판정기를 고치면 그 커밋과 재실행 로그를 더한다.
101
- - 되돌리기가 실패하면 멈추고 사용자에게 알린다. 작업 트리를 지우거나 덮어쓰지 않는다.
102
-
103
- ### 3. flow.md
104
-
105
- - 순서는 화면, API, 최적화, DB, 내보내기다.
106
- - 단계마다 그 단계를 처리하는 `파일:줄` 을 적는다. 화면은 요청을 보내는 코드, API 는 라우트와 처리 함수, 최적화는 호출 지점, DB 는 쓰기와 읽기 쿼리, 내보내기는 파일을 만드는 함수다.
107
- - **종단 시험**: `{host}:{port}` 에 떠 있는 서버에 실제 요청을 보내 다섯 단계를 끝까지 지난다.
108
- - 스크립트는 `docs/evidence/scripts/e2e-flow.*` 다.
109
- - 서버가 떠 있지 않으면 저장소의 시작 방법으로 띄운다. 띄운 명령과 그 로그를 적는다.
110
- - 화면 단계는 브라우저 도구(Playwright 등)가 있으면 실제로 누르고, 없으면 화면 URL 의 응답과 화면이 부르는 API 요청을 그대로 보낸다. 어느 쪽인지 적는다.
111
- - 단계마다 다섯 가지를 로그에 남긴다: 요청, 응답 코드, 걸린 시간, DB 에 생긴 행의 확인 쿼리 결과, 내보낸 파일의 이름과 크기.
165
+ | `constraint` | 해를 고르는 단계나 검사에서 제약 하나를 빼서, 실제로 위반한 해가 나오게 함 |
166
+ | `objective` | 목적값 합산에서 항 하나를 빼거나 부호를 바꿈 |
167
+ | `permission` | 권한 확인을 늘 통과하게 바꿈 |
168
+
169
+ - `bug` 가 남기는 것: 패치(`verify-bug-<이름>-*.patch`), 로그, 그리고 로그 끝의 `METRIC bug.<이름>.result=`, `reason=`, `collateral=`, `tree_unchanged=`, `links_unchanged=`.
170
+ - `bug` 는 기준선이 지금 HEAD 와 지금 판정기의 것인지 본다. 아니면 거부하므로 기준선을 다시 돈다.
171
+ - `caught`: 기준선과 같은 사례를 모두 판정했고 ERROR 가 없으며, 기준선에 없던 그 종류의 FAIL 이 나옴.
172
+ - `missed`: 같은 조건에서 그 종류의 새 FAIL 이 없음.
173
+ - `undecidable`: 시간 초과(`timeout`), ERROR 가 있음(`oracle-error`), 판정한 사례가 기준선과 다름(`cases-differ`, 충돌로 중간에 끝남), 기준선에 그 종류의 PASS 가 없음(`baseline-no-pass`, 잡을 수 없음), 링크가 원래 트리를 가리킴(`link-to-main`). 잡음으로 세지 않는다. 버그를 고쳐 넣어 다시 돌리고 두 로그를 다 적는다.
174
+ - `collateral` 이 0 이 아니면(다른 종류에도 새 FAIL) 그 사실을 표에 적는다.
175
+ - `missed` 면 그 버그가 판정기의 입력에서 실제로 출력을 바꾸는지 확인한다(같은 패치를 새 작업 트리에 `git apply` 해 구현이나 어댑터를 돌린 로그). 출력이 같으면 `버그가 출력을 바꾸지 않음` 으로 적고 다른 자리에 넣어 다시 돈다. 출력이 바뀌는데 놓쳤으면 판정기의 빈틈이다.
176
+ - 판정기의 빈틈은 구현을 본 이 맥락이 고치지 않는다. 고치려면 새 방과 새 하위 에이전트에 요구 수준의 한 문장(넣은 버그나 구현 내용 없이)만 더해 다시 쓰게 하고, `collect --replace` 로 가져와 새 커밋으로 남긴다. 처음 결과 `missed` 는 지우지 않고, 고친 뒤 결과는 다른 열에 적는다. `verify.md` 에 처음 판정기 커밋과 바꾼 판정기 커밋, 각각이 구현보다 앞서는지를 따로 적는다.
177
+ - 출력 비교 같은 확인용 작업 트리는 `bug --name probe-<이름>` 으로 돌리거나 `worktree --remove` 로 지운다.
178
+ - `tree_unchanged=no` 나 `links_unchanged=no` 면 멈추고 사용자에게 알린다. 이 확인은 추적 파일의 내용, 추적되지 않는 파일, 링크한 폴더를 본다. 무시된 다른 파일과 DB 는 보지 못한다는 사실을 `verify.md` 에 적는다.
179
+ - 권한 규칙이 요구 문서와 담당자 사실에 없으면 permission 행은 `해당 없음(권한 규칙 없음)` 이다.
180
+ 5. 표에는 버그마다 패치 경로, 결과 `` `bug.<이름>.result=...` `` 와 `reason`, 로그 경로를 적는다.
181
+
182
+ ### 6. flow.md
183
+
184
+ - 순서는 화면, API, 최적화, DB, 내보내기다. 단계마다 처리하는 `파일:줄` 을 적는다.
185
+ - **서버**: 떠 있지 않으면 `serve` 로 띄운다. 시작 스크립트가 마이그레이션, 시드, 초기화를 함께 하면 그 사실을 보이고 허락을 받은 뒤에만 띄운다. 이미 떠 있던 서버는 내리지 않고, 띄운 서버는 끝에 `stop` 으로 내린다.
186
+ ```bash
187
+ node "$E" serve --log 'docs/evidence/logs/flow-server-{ts}.log' --pid <임시폴더>/server.pid --ready http://{host}:{port}/ -- <시작 명령>
188
+ ```
189
+ - **종단 시험**: 스크립트 `docs/evidence/scripts/e2e-flow.*` 가 다섯 단계를 실제 요청으로 지나며 단계마다 METRIC 줄을 찍는다(예: `flow.api.status=200`, `flow.api.ms=`, `flow.db.rows=`, `flow.export.bytes=`). `run --timeout 300` 으로 돌린다.
190
+ - 화면 단계: 이미 설치된 브라우저 도구가 있으면 실제로 누르고, 없으면 화면 URL 의 응답과 화면이 부르는 API 요청을 보낸다. 어느 쪽인지 적고, HTTP 로 대신했으면 `trace.md` 의 화면 요구는 `부분(화면 조작 미검증)` 이다.
191
+ - DB 확인은 읽기(SELECT)만 하고 로컬이나 일회용 DB 에서만 한다. 시험이 만든 행은 표식을 달고, 끝에 지우거나 남긴 사실을 적는다. 서버가 사용자 폴더에 파일 DB 를 새로 만들었으면 그 경로와 남긴 사실도 적는다.
192
+ - 로그에는 시험이 만든 행만 찍는다.
112
193
  - 서버에 닿지 못하면 종단 시험 칸은 `미검증` 과 오류 문구다. 단위 시험으로 대신하지 않는다.
113
- - 운영 서버나 남의 서버에는 보내지 않는다. 대상이 로컬이나 개발 서버가 아니면 사용자에게 먼저 확인한다.
114
194
 
115
- ### 4. scale.md
195
+ ### 7. scale.md
116
196
 
117
- - **데이터 1배, 5배, 10배**: 기준 데이터(README 의 예시나 시험 고정물)를 늘리는 스크립트 `docs/evidence/scripts/gen-scale.*` 로 만든다. 같은 시드로 다시 만들 수 있게 시드를 고정하고 적는다.
118
- - 배수마다 최적화를 돌려 걸린 시간과 목적값을 잰다. 한 배수를 3회 돌려 각 값과 중앙값을 로그에 남긴다.
119
- - **작은 사례 다섯**: 완전 탐색이 끝나는 크기로 다섯 개를 만들고, 완전 탐색 스크립트 `docs/evidence/scripts/brute-force.*` 로 정해를 구한다.
120
- - 사례마다 구현의 목적값과 정해를 적는다.
121
- - 차이(%)는 `|구현 - 정해| / |정해| x 100` 이다. 정해가 0 이면 차이 대신 두 값을 그대로 적는다.
122
- - 차이도 스크립트가 계산해 로그에 찍는다.
123
- - 표의 칸마다 로그 경로를 단다. 시간 제한에 걸린 배수는 `시간 초과(제한 N초)` 로 적는다.
197
+ - **배수 데이터**: `docs/evidence/scripts/gen-scale.*` 가 기준 데이터(README 의 예시나 시험 고정물)를 1배, 5배, 10배로 늘린다. 시드를 고정해 적는다. 기준 데이터가 작아 10배도 요구의 상한에 못 미치면 상한 근처 사례를 하나 더 만들고 그 사실을 적는다. 기준 데이터가 없으면 `미검증(기준 데이터 없음)`.
198
+ - **측정**: `docs/evidence/scripts/measure.*` 가 배수마다 최적화를 3회 돌려 각 시간과 중앙값, 목적값을 METRIC 줄로 찍는다(예: `scale.x5.median_s=`, `scale.x5.objective=`). 무엇을 쟀는지(함수 호출만, HTTP 포함) 적는다. 시간 제한은 실행 전에 정해 문서와 `run --timeout` 에 같게 준다. 걸리면 `시간 초과(제한 N초)` 다. 배수마다 판정기의 제약 판정을 돌려 `scale.x5.feasible=` 을 찍는다.
199
+ - **완전 탐색**: 작은 사례 다섯을 `docs/evidence/scripts/brute-force.*` 로 푼다. 목적값과 실행 가능성은 `tests/oracle/` 의 판정기가 내보낸 함수로 계산하고 구현 코드를 부르지 않는다. 판정기가 그런 함수를 내보내지 않으면 완전 탐색 스크립트가 요구 문서와 담당자 사실만으로 직접 계산하고, 그 사실을 적는다. 크기는 완전 탐색이 제한 안에 끝나는 가장 큰 쪽으로 한다.
200
+ - 사례마다 구현의 목적값, 정해, 차이를 스크립트가 찍는다. 차이(%)는 부호를 지킨다: `(구현 - 정해) / |정해| x 100`. 정해가 0 이면 두 값만 찍는다.
201
+ - 구현이 정해보다 좋으면(최대화에서 크거나 최소화에서 작으면) `gap.c<n>.flag=better-than-optimal` 을 찍고, 제약 위반이나 완전 탐색 오류로 보고 조사한다.
124
202
 
125
- ### 5. decisions.md
203
+ ### 8. decisions.md
126
204
 
127
- 결정마다 한 행이다.
205
+ | 결정 | 고른 것 | 버린 것 | 이유 | 출처 | 확인하는 시험 | 결과 로그 |
206
+ |---|---|---|---|---|---|---|
128
207
 
129
- | 결정 | 고른 것 | 버린 것 | 이유 | 확인하는 시험 |
130
- |---|---|---|---|---|
208
+ - 출처는 코드, 커밋 메시지, README, ADR, 이 세션의 대화(날짜) 가운데 실제로 그렇게 말한 곳이다.
209
+ - 출처가 말하지 않는 "버린 것" 과 "이유" 는 `기록 없음` 이다. 지어내지 않는다.
210
+ - 확인하는 시험은 실제로 있는 시험 이름이다. 결과는 통과와 실패 그대로다. 없으면 `없음`.
131
211
 
132
- - 결정은 코드와 커밋 기록, README, ADR 이 있으면 ADR 에서 찾는다. 찾은 곳을 적는다.
133
- - 확인하는 시험은 실제로 있는 시험의 이름이다. 이름마다 통과 로그 경로를 단다. 그 결정을 확인하는 시험이 없으면 `없음` 으로 적는다. 이름을 지어내지 않는다.
212
+ ### 9. trace.md
134
213
 
135
- ### 6. trace.md
214
+ | 번호 | 요구 | 출처 | 상태 | 시험 이름 | 로그 경로 |
215
+ |---|---|---|---|---|---|
136
216
 
137
- README 의 요구마다 한 행이다.
138
-
139
- | 번호 | 요구 | 상태 | 시험 이름 | 로그 경로 |
140
- |---|---|---|---|---|
141
-
142
- - 번호는 README 에 나온 순서로 `R1`, `R2` 다. 요구 칸에는 README 문장을 줄이지 않고 옮긴다.
143
- - 상태는 셋 가운데 하나다.
144
- - `완료`: 그 요구를 확인하는 시험이 있고 통과함
145
- - `부분`: 일부만 구현했거나 시험이 일부만 확인함. 빠진 부분을 적음
217
+ - 요구 뽑기: 요구 문서의 요구 절의 목록 항목 전부와 "해야", "한다", "must", "should" 가 든 문장 전부다. 한 문장의 나열은 항목마다 한 행이다. 출처는 `README.md:<줄>` 이다. 요구 칸은 원문을 줄이지 않고 큰따옴표 안에 옮긴다(`|` 는 `\|`). 나열을 나눈 행은 원문 전체를 옮기고 그 행이 맡은 항목을 뒤에 적는다.
218
+ - 판정기 작성자의 `tests/oracle/REQUIREMENTS.md` 와 대조해 한쪽에만 있는 요구를 표 아래에 적는다.
219
+ - 담당자 사실은 요구 문서의 요구가 아니므로 행으로 넣지 않는다. 판정기가 담당자 사실을 판정한 결과는 `verify.md` 에 있다.
220
+ - 상태는 넷 가운데 하나다.
221
+ - `완료`: 그 요구를 확인하는 시험이 있고 통과함. 건너뜀이나 0개 실행은 통과가 아니다
222
+ - `부분`: 일부만 구현, 시험이 일부만 확인, 시험이 실패, 시험이 없음. 빠진 것을 적음
146
223
  - `미구현`: 이유를 적음
147
- - 시험이 실패하면 `완료` 로 쓰지 않는다. 시험이 없으면 `완료` 가 아니라 `부분` 이고, 시험이 없다고 적는다.
148
- - 마지막에 상태별 개수를 적는다. 개수도 표에서 센 것이다.
224
+ - `미검증`: 시험을 돌릴 수 없었음(서버, 도구, 샌드박스). 까닭을 적음
225
+ - 개수는 손으로 세지 않는다: `node "$E" run --log 'docs/evidence/logs/trace-count-{ts}.log' -- node "$E" count --doc docs/evidence/trace.md`. 그 METRIC 값을 표 아래에 옮긴다. 종료 1(상태를 알 수 없는 행)이면 표를 고치고 다시 센다.
149
226
 
150
- ## 수치 대조
227
+ ### 10. 수치 대조
151
228
 
152
- 문서를 다 쓴 뒤, 문서에 적은 수마다 그 칸이 가리키는 로그에 같은 수가 있는지 확인한다.
153
-
154
- - 문서의 숫자(시간, 목적값, 차이, 개수)를 하나씩 뽑아 가리킨 로그에서 찾는다(`grep -F`).
155
- - 로그에서 찾지 못한 수는 지우거나, 그 수를 찍는 명령을 다시 돌려 로그를 만든다.
156
- - 대조 결과(확인한 수의 개수, 못 찾은 수)를 각 문서 끝의 `수치 대조` 줄에 적는다.
229
+ ```bash
230
+ node "$E" run --log 'docs/evidence/logs/check-{ts}.log' -- node "$E" check
231
+ ```
157
232
 
158
- ## 커밋
233
+ - `ERROR` 는 모두 고친다. 로그에 없는 수는 지우거나 그 수를 찍는 명령을 다시 돌린다.
234
+ - `WARN`(`이름=값` 밖의 숫자)도 0 이 될 때까지 고친다. 측정값이면 METRIC 으로 바꾼다. 측정값이 아니면 도우미가 빼는 꼴로 쓴다: 코드 펜스나 백틱 안, URL, `/`, `./`, `~/` 로 시작하는 경로와 확장자로 끝나는 경로, 확장자가 있는 `파일:줄`, 글자가 든 해시, 날짜, 판, `R1`, `c3` 같은 번호, 배수 데이터 이름(`5배 데이터`처럼 뒤에 데이터나 입력이 오거나 표 행의 첫 칸에 홀로 있을 때, 또는 `x5`), `10줄`, `3단계`, 끝에 점이 있는 절 번호(`## 5. `)와 목록 번호, 세 자리 이하의 표 행 번호. 큰따옴표 안의 인용은 `trace.md` 의 요구 칸에서만 빠진다. 제목 줄의 다른 수, `시간도 10배` 나 표의 `약 5배` 같은 비율, `120ms`, `p50/p95=120/340ms`, `1200req/s` 같은 수는 측정값이므로 METRIC 으로 바꾼다.
235
+ - 문서마다 마지막 줄은 `수치 대조: <check 로그 경로>` 한 줄이다(이 꼴의 줄만 대조에서 빠짐). `{ts}` 같은 이름 꼴이 아니라 실제 로그를 가리켜야 한다. 마지막 대조의 로그 이름을 먼저 정해(예: `docs/evidence/logs/check-final-20261105-0930.log`) 문서에 쓰고, 그 이름 그대로 `run --log` 로 check 를 돌린다. `run` 이 로그 머리를 먼저 쓰므로 check 가 그 로그를 찾는다. 그 대조가 실패하면 고친 뒤 새 이름으로 다시 한다.
159
236
 
160
- - 커밋 대상은 `docs/evidence/` 와 `tests/oracle/` 뿐이다. 다른 경로는 넣지 않는다.
161
- - 증거를 만들다 구현을 고쳤다면 그 수정은 따로 커밋하고 사용자에게 알린다.
162
- - 판정기 커밋은 2번의 4단계에서 이미 했다.
163
- - 커밋 전에 비밀값을 찾는다. 걸린 값은 가린다. 커밋 메시지는 `docs(evidence): ...` 다.
164
- - push 하지 않는다. 사용자가 요청할 때만 한다.
165
- - `--no-commit` 이면 쓸 파일 목록만 보이고 커밋하지 않는다.
237
+ ### 11. 커밋
166
238
 
167
- ## 런타임
239
+ ```bash
240
+ node "$E" commit --message "docs(evidence): 증거 문서 여섯"
241
+ node "$E" check --tracked
242
+ ```
168
243
 
169
- - Claude Code: `/banker:make-evidence`. 하위 에이전트는 Agent 도구다.
170
- - Codex: npm 길 `$banker-make-evidence`, 플러그인 길 `$banker:make-evidence`. 하위 에이전트는 `spawn_agent` 다.
171
- - 기본 샌드박스가 `.git` 쓰기를 막으면 커밋 명령에 `sandbox_permissions: "require_escalated"` 를 청한다.
172
- - 로컬이 아닌 서버에 보내는 종단 시험은 네트워크 승인이 필요하다.
244
+ - `ERROR 비밀값 꼴이 남음` 이면 값을 가린다. 비밀값이 아니라고 사용자가 확인한 자리만 `--accept <파일:줄>` 로 넘긴다.
245
+ - 커밋 훅이 막으면 우회하지 않고 알린다.
246
+ - 커밋 뒤 `check --tracked` 로 문서가 가리킨 로그가 모두 커밋에 들었는지 본다.
247
+ - push 하지 않는다. 사용자가 요청할 때만 한다.
173
248
 
174
249
  ## 보고
175
250
 
176
- 끝나면 표 하나로 알린다.
177
-
178
- | 문서 | 상태 | 로그 수 | 미검증이나 미구현 |
251
+ | 문서 | 상태 | 미검증이나 미구현 | 핵심 결과 |
179
252
  |---|---|---|---|
180
253
 
181
254
  - 상태는 `작성`, `일부 미검증`, `작성 안 함(까닭)` 가운데 하나다.
182
- - 커밋 해시를 적는다. push 는 하지 않았다고 적는다.
255
+ - 핵심 결과에는 판정기 기준선의 실패 수, 버그 셋의 결과와 까닭, trace 의 상태별 개수, 수치 대조의 오류와 경고 수를 METRIC 값 그대로 적는다.
256
+ - `요구 미충족`(판정기가 구현 뒤에 쓰임), 찾은 결함, 띄운 서버를 내렸는지, 커밋 해시를 적는다. push 는 하지 않았다고 적는다.
257
+ - `요구 미충족` 이면 마지막 줄에 다음 행동을 적는다: 다음 프로젝트는 구현을 시작하기 전에 `/banker:make-evidence --oracle-only`(Codex 는 `$banker-make-evidence --oracle-only`, 플러그인 설치면 `$banker:make-evidence --oracle-only`)로 판정기를 먼저 커밋한다.
258
+ - `--oracle-only` 로 멈췄으면 마지막 줄에 다음 행동을 적는다: 구현을 마친 뒤 같은 스킬을 인자 없이 다시 부른다.
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * evidence.mjs: the helper the make-evidence skill runs. It records commands into logs, checks the numbers written
4
+ * in the evidence documents against those logs, gives the oracle writer a room outside the repository, runs the
5
+ * seeded bugs in a separate worktree, and commits only the evidence paths. Pure Node, no dependencies, no network.
6
+ *
7
+ * node evidence.mjs precheck [--repo <dir>]
8
+ * node evidence.mjs run --log <file> [--timeout <s>] [--cwd <dir>] [--shell] -- <command...>
9
+ * node evidence.mjs serve --log <file> --pid <file> --ready <url> [--wait <s>] [--cwd <dir>] [--shell] -- <command...>
10
+ * node evidence.mjs stop --pid <file>
11
+ * node evidence.mjs check [--repo <dir>] [--tracked]
12
+ * node evidence.mjs count --doc <trace.md>
13
+ * node evidence.mjs room [--repo <dir>] [--req <file>]...
14
+ * node evidence.mjs collect --room <dir> [--repo <dir>] [--replace]
15
+ * node evidence.mjs worktree [--repo <dir>] [--link <untracked dependency folder>]... | worktree --remove <dir>
16
+ * node evidence.mjs bug --worktree <dir> --name <name> [--kind <kind> --baseline <log>] [--timeout <s>] [--shell] -- <oracle command...>
17
+ * node evidence.mjs commit --message <text> [--paths <p>]... [--accept <file:line>]... [--repo <dir>]
18
+ *
19
+ * - A log name may hold {ts} or @TS@; it becomes a UTC time with milliseconds. An existing log is never overwritten.
20
+ * - A log starts with the command line (shell-quoted, secrets masked) and `# head= dirty= at= node= os= cwd=`, holds
21
+ * stdout and stderr as they come (secrets masked), and ends with `# redacted=<n>`, `exit=<code>` (or
22
+ * `exit=timeout(<s>s)`, `exit=interrupted(<signal>)`) and `METRIC run.exit=` plus the test counts it recognised.
23
+ * - Measured numbers go into a log as `METRIC <name.with.dots>=<value>`; a document cites them in backticks on a
24
+ * line that names the log, and check looks for that exact line there.
25
+ * - Oracle output lines are `ORACLE <constraint|objective|permission> <PASS|FAIL|ERROR> <case> [note]`.
26
+ *
27
+ * Exit status: 0 done, 1 a check failed (numbers, room, tree changed, commit refused, unknown trace status), 2
28
+ * usage error or a step that could not run, 3 precheck found something the user must decide, 124 run hit its
29
+ * time limit; run otherwise passes the command's own exit code through.
30
+ */
31
+ import { readFileSync } from "node:fs";
32
+ import { resolve } from "node:path";
33
+ import { pathToFileURL } from "node:url";
34
+ import { OWN, ORACLE, Usage } from "./me-git.mjs";
35
+ import { runLogged, serve, stop } from "./me-run.mjs";
36
+ import { precheck } from "./me-precheck.mjs";
37
+ import { checkDocs, countTrace } from "./me-check.mjs";
38
+ import { addWorktree, collectRoom, makeRoom, removeWorktree, runBug } from "./me-oracle.mjs";
39
+ import { commitEvidence } from "./me-commit.mjs";
40
+
41
+ const USAGE = `make-evidence: 증거 문서용 도우미(기록 실행, 수치 대조, 판정기 방, 버그 작업 트리, 증거만 커밋)
42
+ 사용법: node evidence.mjs <precheck|run|serve|stop|check|count|room|collect|worktree|bug|commit> [인자]
43
+ precheck [--repo <dir>] 스테이징, 미커밋 변경, 이 컴퓨터 밖 주소(설정, 환경 변수). 결정할 것이 있으면 종료 3
44
+ run --log <file> [--timeout <s>] [--cwd <dir>] [--shell] -- <명령...> 기본 제한 300초
45
+ serve --log <file> --pid <file> --ready <url> [--wait <s>] [--cwd <dir>] [--shell] -- <명령...>
46
+ stop --pid <file>
47
+ check [--repo <dir>] [--tracked] 문서의 \`이름.점=값\` 을 가리킨 로그의 METRIC 줄과 대조, 손 숫자 경고
48
+ count --doc <trace.md> trace 표의 상태 칸을 세어 METRIC 줄로 출력
49
+ room [--repo <dir>] [--req <file>]... 판정기 작성용 방(요구 문서와 owner-facts 만)
50
+ collect --room <dir> [--repo <dir>] [--replace] 방의 tests/oracle 을 저장소로 가져옴
51
+ worktree [--repo <dir>] [--link <폴더>]... | --remove <dir> HEAD 의 별도 작업 트리
52
+ bug --worktree <dir> --name <이름> [--kind <종류> --baseline <log>] [--timeout <s>] [--shell] -- <판정기 명령...>
53
+ commit --message <text> [--paths <p>]... [--accept <file:line>]... [--repo <dir>]
54
+ 로그 이름의 {ts} 는 PowerShell 에서 따옴표로 감싸거나 @TS@ 로 씀`;
55
+
56
+ const FLAGS = new Set(["--shell", "--tracked", "--replace"]);
57
+ const MULTI = new Set(["--req", "--paths", "--link", "--accept"]);
58
+
59
+ export function parseArgs(argv) {
60
+ const a = { _: [], rest: [], multi: {} };
61
+ for (let i = 0; i < argv.length; i++) {
62
+ const t = argv[i];
63
+ if (t === "--") { a.rest = argv.slice(i + 1); break; }
64
+ if (FLAGS.has(t)) a[t.slice(2)] = true;
65
+ else if (t.startsWith("--")) {
66
+ if (i + 1 >= argv.length) throw new Usage(`${t} 에 값이 없음`);
67
+ const v = argv[++i];
68
+ if (MULTI.has(t)) (a.multi[t.slice(2)] ||= []).push(v);
69
+ else a[t.slice(2)] = v;
70
+ } else a._.push(t);
71
+ }
72
+ return a;
73
+ }
74
+
75
+ const need = (a, k) => { if (!a[k]) throw new Usage(`--${k} 가 필요함`); return a[k]; };
76
+ const seconds = (v, d) => { const n = v === undefined ? d : Number(v); if (!(n > 0)) throw new Usage(`시간 값이 잘못됨: ${v}`); return n; };
77
+ const say = (xs) => { for (const x of xs) console.log(x); };
78
+
79
+ async function cmdRun(a) {
80
+ const r = await runLogged({ log: need(a, "log"), argv: a.rest, cwd: resolve(a.cwd || "."), timeoutS: seconds(a.timeout, 300), shell: !!a.shell });
81
+ console.log(`run: exit=${r.exit} log=${r.file}`);
82
+ return r.code;
83
+ }
84
+
85
+ async function cmdServe(a) {
86
+ const r = await serve({ log: need(a, "log"), pidFile: resolve(need(a, "pid")), ready: need(a, "ready"), waitS: seconds(a.wait, 60), argv: a.rest, cwd: resolve(a.cwd || "."), shell: !!a.shell });
87
+ console.log(`serve: ${r.ready ? "ready" : `${r.why}, stopped`} pid=${r.pid} log=${r.file}`);
88
+ return r.ready ? 0 : 2;
89
+ }
90
+
91
+ function cmdCheck(a) {
92
+ const c = checkDocs(a.repo || ".", { tracked: !!a.tracked });
93
+ say(c.errors.map((e) => `ERROR ${e}`));
94
+ say(c.warnings.map((w) => `WARN ${w}`));
95
+ say([`METRIC check.docs=${c.docs}`, `METRIC check.values=${c.tokens}`, `METRIC check.errors=${c.errors.length}`, `METRIC check.warnings=${c.warnings.length}`]);
96
+ return c.errors.length || c.warnings.length ? 1 : 0;
97
+ }
98
+
99
+ function cmdCount(a) {
100
+ const c = countTrace(readFileSync(need(a, "doc"), "utf8"));
101
+ say(c.unknownRows.map((n) => `ERROR 상태를 알 수 없는 행: ${a.doc}:${n}`));
102
+ say(["rows", "done", "partial", "notdone", "unverified", "unknown"].map((k) => `METRIC trace.${k}=${c[k]}`));
103
+ return c.unknown ? 1 : 0;
104
+ }
105
+
106
+ function cmdCollect(a) {
107
+ const r = collectRoom(resolve(need(a, "room")), a.repo, { replace: !!a.replace });
108
+ say(r.problems.map((p) => `ERROR ${p}`));
109
+ if (r.previous && !r.problems.length) console.log(`replaced ${ORACLE} 의 이전 커밋: ${r.previous}`);
110
+ say(r.made.map((f) => `oracle ${ORACLE}/${f}`));
111
+ return r.problems.length ? 1 : 0;
112
+ }
113
+
114
+ function cmdWorktree(a) {
115
+ if (a.remove) { removeWorktree(resolve(a.remove)); console.log(`removed ${resolve(a.remove)}`); return 0; }
116
+ console.log(addWorktree(a.repo || ".", a.multi.link || []));
117
+ return 0;
118
+ }
119
+
120
+ async function cmdBug(a) {
121
+ const r = await runBug({ wt: resolve(need(a, "worktree")), name: need(a, "name"), kind: a.kind, baseline: a.baseline && resolve(a.baseline), timeoutS: seconds(a.timeout, 300), argv: a.rest, shell: !!a.shell });
122
+ say([`bug: log=${r.log}${r.patch ? ` patch=${r.patch}` : ""}`, ...r.toRepo.map((p) => `WARN 링크한 폴더가 원래 트리를 가리킴: ${p}`), ...r.metrics.map((m) => `METRIC ${m}`)]);
123
+ return r.unchanged ? 0 : 1;
124
+ }
125
+
126
+ function cmdCommit(a) {
127
+ const r = commitEvidence(a.repo || ".", need(a, "message"), a.multi.paths || OWN, a.multi.accept || []);
128
+ if (r.refused) { say(r.refused.map((x) => `ERROR ${x}`)); return 1; }
129
+ say([`commit: ${r.sha}`, ...r.files.map((f) => `file ${f}`), ...r.outside.map((f) => `ERROR 증거 밖의 파일이 커밋됨: ${f}`)]);
130
+ return r.outside.length ? 1 : 0;
131
+ }
132
+
133
+ const COMMANDS = {
134
+ precheck: (a) => { const r = precheck(a.repo || "."); say(r.out); return r.decide ? 3 : 0; },
135
+ run: cmdRun,
136
+ serve: cmdServe,
137
+ stop: (a) => { const r = stop({ pidFile: resolve(need(a, "pid")) }); console.log(`stop: pid=${r.pid} log=${r.file}`); return 0; },
138
+ check: cmdCheck,
139
+ count: cmdCount,
140
+ room: (a) => { const r = makeRoom(a.repo || ".", a.multi.req); say([`room: ${r.dir}`, ...r.files.map((f) => `file ${f}`)]); return 0; },
141
+ collect: cmdCollect,
142
+ worktree: cmdWorktree,
143
+ bug: cmdBug,
144
+ commit: cmdCommit,
145
+ };
146
+
147
+ export async function main(argv) {
148
+ const [sub, ...rest] = argv;
149
+ if (!sub || sub === "--help" || sub === "-h") { console.log(USAGE); return sub ? 0 : 2; }
150
+ const fn = COMMANDS[sub];
151
+ if (!fn) { console.error(`make-evidence: 모르는 명령: ${sub}\n${USAGE}`); return 2; }
152
+ try {
153
+ return await fn(parseArgs(rest));
154
+ } catch (e) {
155
+ console.error(`make-evidence: ${e.message}`);
156
+ return 2;
157
+ }
158
+ }
159
+
160
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
161
+ main(process.argv.slice(2)).then((code) => { process.exitCode = code; });
162
+ }