okstra 0.111.0 → 0.113.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.
- package/README.kr.md +2 -2
- package/README.md +2 -2
- package/bin/okstra +7 -1
- package/docs/for-ai/README.md +2 -2
- package/docs/for-ai/skills/okstra-brief.md +2 -3
- package/docs/for-ai/skills/okstra-container-build.md +2 -3
- package/docs/for-ai/skills/okstra-inspect.md +5 -12
- package/docs/for-ai/skills/okstra-rollup.md +3 -4
- package/docs/for-ai/skills/okstra-run.md +3 -5
- package/docs/for-ai/skills/okstra-schedule.md +2 -7
- package/docs/for-ai/skills/okstra-setup.md +1 -1
- package/docs/kr/architecture/storage-model.md +1 -2
- package/docs/kr/architecture.md +2 -2
- package/docs/kr/cli.md +4 -2
- package/docs/project-structure-overview.md +5 -3
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +1 -1
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/agents/workers/codex-worker.md +1 -1
- package/runtime/prompts/coding-preflight/overview.md +3 -1
- package/runtime/prompts/launch.template.md +1 -5
- package/runtime/prompts/lead/context-loader.md +2 -4
- package/runtime/prompts/lead/convergence.md +11 -240
- package/runtime/prompts/lead/okstra-lead-contract.md +70 -34
- package/runtime/prompts/lead/plan-body-verification.md +240 -0
- package/runtime/prompts/lead/report-writer.md +7 -7
- package/runtime/prompts/lead/team-contract.md +15 -17
- package/runtime/prompts/profiles/_common-contract.md +1 -37
- package/runtime/prompts/profiles/_implementation-executor.md +2 -2
- package/runtime/prompts/profiles/_implementation-self-check.md +1 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- package/runtime/prompts/profiles/final-verification.md +6 -5
- package/runtime/prompts/profiles/implementation-planning.md +6 -4
- package/runtime/prompts/profiles/implementation.md +1 -1
- package/runtime/prompts/profiles/improvement-discovery.md +1 -0
- package/runtime/prompts/profiles/requirements-discovery.md +1 -0
- package/runtime/python/okstra_ctl/path_hints.py +1 -0
- package/runtime/python/okstra_ctl/paths.py +3 -0
- package/runtime/python/okstra_ctl/render.py +8 -0
- package/runtime/python/okstra_ctl/set_work_status.py +147 -0
- package/runtime/python/okstra_ctl/team_reconcile.py +1 -1
- package/runtime/schemas/final-report-v1.0.schema.json +6 -4
- package/runtime/skills/okstra-brief/SKILL.md +32 -176
- package/runtime/skills/okstra-brief/references/reporter-confirmations.md +71 -0
- package/runtime/skills/okstra-brief/references/tracker-recursion.md +90 -0
- package/runtime/skills/okstra-container-build/SKILL.md +7 -20
- package/runtime/skills/okstra-graphify/SKILL.md +8 -8
- package/runtime/skills/okstra-inspect/SKILL.md +27 -43
- package/runtime/skills/okstra-rollup/SKILL.md +6 -6
- package/runtime/skills/okstra-run/SKILL.md +7 -16
- package/runtime/skills/okstra-schedule/SKILL.md +64 -419
- package/runtime/skills/okstra-setup/SKILL.md +25 -223
- package/runtime/skills/okstra-setup/references/project-config.md +188 -0
- package/runtime/templates/reports/final-report.template.md +24 -1
- package/runtime/templates/reports/improvement-discovery-input.template.md +3 -2
- package/runtime/templates/reports/schedule.template.md +2 -2
- package/runtime/templates/worker-prompt-preamble.md +5 -1
- package/runtime/validators/validate-run.py +8 -7
- package/src/cli-registry.mjs +14 -0
- package/src/commands/inspect/set-work-status.mjs +31 -0
- package/src/commands/lifecycle/check-project.mjs +68 -56
- package/src/commands/lifecycle/install.mjs +1 -0
- package/src/commands/lifecycle/preflight.mjs +82 -0
|
@@ -53,13 +53,9 @@ Show the final summary line back to the user (`version stamp: x.y.z`).
|
|
|
53
53
|
If install fails, surface the stderr verbatim. Do NOT try to "fix" it by
|
|
54
54
|
running the legacy `okstra-install.sh` — that path is dev-only.
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
**Bash invocation rule (permission-friendly)**: after Step 1, every Bash command in this skill MUST begin with the literal token `okstra` and pass literal argument values. Do not introduce shell variables (`$PROJECT_ROOT`, `$PROJECT_ID`, ...), `$(...)` command substitution, leading `VAR=...` assignments, or wrap commands in `if`/`eval`/`||`/`&&` — any of those make the leading token non-literal, defeat the `Bash(okstra:*)` permission match, and force a confirmation prompt on every call. When a prior tool call emitted a path or value, paste the literal string into the next command. Every `okstra <subcmd>` call self-bootstraps its Python path — never run `okstra paths --shell` / `export PYTHONPATH=...`.
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
**Bash invocation rule (permission-friendly)**: from this step on, every Bash command in this skill MUST begin with the literal token `okstra` and pass literal argument values. Do not introduce shell variables (`$PROJECT_ROOT`, `$PROJECT_ID`, ...), `$(...)` command substitution, leading `VAR=...` assignments, or wrap commands in `if`/`eval`/`||`/`&&` — any of those make the leading token non-literal, defeat the `Bash(okstra:*)` permission match, and force a confirmation prompt on every call. When a prior tool call emitted a path or value, read it from the tool output and paste the literal string into the next command.
|
|
61
|
-
|
|
62
|
-
## Step 3: Resolve PROJECT_ROOT
|
|
58
|
+
## Step 2: Resolve PROJECT_ROOT
|
|
63
59
|
|
|
64
60
|
```bash
|
|
65
61
|
okstra check-project --json
|
|
@@ -74,12 +70,12 @@ Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, pro
|
|
|
74
70
|
okstra check-project --cwd /abs/path/from/user --json
|
|
75
71
|
```
|
|
76
72
|
|
|
77
|
-
- `ok: false`, `stage: "project_json_missing"` → proceed to Step
|
|
73
|
+
- `ok: false`, `stage: "project_json_missing"` → proceed to Step 3 (this is the normal create path).
|
|
78
74
|
- `ok: false`, any other `stage` (`python`, `parse`, `project_json_invalid`) → show the JSON `reason` field to the user verbatim and follow the recovery it names (typically `okstra doctor` to diagnose, then `okstra ensure-installed` or re-running the Step 1 install). `reason` is the SSOT — do not hand-enumerate stage causes.
|
|
79
75
|
|
|
80
|
-
## Step
|
|
76
|
+
## Step 3: Inspect or create `project.json`
|
|
81
77
|
|
|
82
|
-
Use the `Read` tool on the literal absolute path `<projectRoot>/.okstra/project.json` (substitute the literal `projectRoot` value parsed in Step
|
|
78
|
+
Use the `Read` tool on the literal absolute path `<projectRoot>/.okstra/project.json` (substitute the literal `projectRoot` value parsed in Step 2). If `Read` errors with "file does not exist", treat that as the "create" branch below; otherwise the file exists and you can inspect its contents inline.
|
|
83
79
|
|
|
84
80
|
If the file exists, surface its `projectId`/`projectRoot` and ask whether to
|
|
85
81
|
keep or overwrite. Default is to keep — okstra refuses to change `projectId`
|
|
@@ -95,226 +91,32 @@ If the file does NOT exist, ask via `AskUserQuestion`:
|
|
|
95
91
|
`error: --project-id is required (no existing project.json, not a TTY)`,
|
|
96
92
|
so passing the user's empty answer through is a silent failure path.
|
|
97
93
|
|
|
98
|
-
Then create the file — paste the literal `projectRoot` from Step
|
|
94
|
+
Then create the file — paste the literal `projectRoot` from Step 2 and the literal `projectId` from the user's answer (no shell variables):
|
|
99
95
|
|
|
100
96
|
```bash
|
|
101
97
|
okstra setup --yes --project-root /abs/path/to/projectRoot --project-id my-project-id
|
|
102
98
|
```
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
> work for most projects — skip straight to Step 5 (`doctor`) unless the
|
|
106
|
-
> user explicitly asks to customise worktree sync, declare QA commands,
|
|
107
|
-
> or register a PR-body template. Step 4.7 (settings.local.json symlink)
|
|
108
|
-
> is automatic; it is documented for diagnostic purposes only.
|
|
109
|
-
|
|
110
|
-
## Step 4.5 (optional): customise worktree sync dirs
|
|
111
|
-
|
|
112
|
-
Each okstra run provisions a task-scoped git worktree under
|
|
113
|
-
`~/.okstra/worktrees/.../`. A small set of project-root-relative
|
|
114
|
-
directories is symlinked from the main checkout into that worktree so
|
|
115
|
-
every task sees the shared state. The built-in default is
|
|
116
|
-
`.project-docs`, `.scratch`, `graphify-out`, `.claude`.
|
|
117
|
-
Syncing a directory does not make it okstra memory; okstra-owned context
|
|
118
|
-
and writes still stay under `<PROJECT_ROOT>/.okstra/**`
|
|
119
|
-
unless the task brief explicitly authorizes a non-okstra path.
|
|
120
|
-
|
|
121
|
-
To override per-project, add a `worktreeSyncDirs` array to
|
|
122
|
-
`project.json`. Empty array disables the feature; the field is
|
|
123
|
-
preserved across the runtime's auto-upserts (see Step 4.6 for the
|
|
124
|
-
canonical list of runtime-owned fields).
|
|
125
|
-
|
|
126
|
-
```json
|
|
127
|
-
{
|
|
128
|
-
"projectId": "...",
|
|
129
|
-
"projectRoot": "...",
|
|
130
|
-
"worktreeSyncDirs": [".project-docs", ".scratch", "graphify-out", ".claude", "my-custom-dir"]
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
Resolution precedence: `OKSTRA_WORKTREE_SYNC_DIRS` env var → this
|
|
135
|
-
field → built-in default. Only edit when defaults don't cover the
|
|
136
|
-
project's working files (e.g. additional cache or local-config dirs
|
|
137
|
-
that must follow the executor into the worktree).
|
|
138
|
-
|
|
139
|
-
## Step 4.6 (optional but recommended): declare project QA commands
|
|
140
|
-
|
|
141
|
-
`implementation`-phase verifiers run an independent QA gate over the
|
|
142
|
-
executor's diff and need a project-wide baseline of check-only
|
|
143
|
-
lint / format / typecheck / test commands. okstra does NOT auto-detect
|
|
144
|
-
tooling from manifest files — declare the commands explicitly in
|
|
145
|
-
`project.json` under `qaCommands`. Skipping this declaration is
|
|
146
|
-
allowed but the verifier will then only run the plan's per-task
|
|
147
|
-
`validation` set, with `qa-command not configured: <category>`
|
|
148
|
-
recorded per missing category in the final report.
|
|
149
|
-
|
|
150
|
-
Each category is an array of `{ "label", "cmd", "language"? }`
|
|
151
|
-
objects. `language` is optional; when present the verifier MAY skip
|
|
152
|
-
commands whose language is not represented in this run's diff.
|
|
153
|
-
|
|
154
|
-
```json
|
|
155
|
-
{
|
|
156
|
-
"projectId": "...",
|
|
157
|
-
"projectRoot": "...",
|
|
158
|
-
"qaCommands": {
|
|
159
|
-
"lint": [{ "label": "cargo clippy", "cmd": "cargo clippy --all-targets -- -D warnings", "language": "rust" }],
|
|
160
|
-
"format": [{ "label": "cargo fmt", "cmd": "cargo fmt --check", "language": "rust" }],
|
|
161
|
-
"typecheck": [{ "label": "tsc", "cmd": "pnpm exec tsc --noEmit", "language": "ts" }],
|
|
162
|
-
"test": [{ "label": "cargo test", "cmd": "cargo test --workspace --locked", "language": "rust" }]
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
**`cmd` deny-list (mutation guard):** the verifier rejects any `cmd`
|
|
168
|
-
containing tokens that imply mutation — declare commands in their
|
|
169
|
-
check-only form only. Representative denied tokens: `--fix` / `--write`
|
|
170
|
-
(formatters), `--updateSnapshot`, `npm install` (use `npm ci`),
|
|
171
|
-
`pip install -U`, `cargo add`. The authoritative deny list is
|
|
172
|
-
`scripts/okstra_ctl/qa_commands.py::find_denied_tokens`
|
|
173
|
-
(`_DENIED_LITERAL_TOKENS` / `_DENIED_SUBSTRINGS` plus the dynamic
|
|
174
|
-
`npm install` and `INSTA_UPDATE=` checks) — do not re-enumerate it here.
|
|
175
|
-
|
|
176
|
-
Encountering a denied token aborts the verifier with status
|
|
177
|
-
`contract-violated`; re-declare the command in check-only form to
|
|
178
|
-
recover (e.g. swap `prettier --write` → `prettier --check`).
|
|
179
|
-
|
|
180
|
-
The field is preserved across the runtime's auto-upserts of
|
|
181
|
-
`project.json` — only `projectId`, `projectRoot`, `createdAt`,
|
|
182
|
-
`updatedAt` are runtime-owned, so manual edits to `qaCommands`
|
|
183
|
-
survive every subsequent `okstra setup` / `okstra run` invocation.
|
|
184
|
-
|
|
185
|
-
### Step 4.6.1 (optional): `qaEnv` — Tier 3 conformance environment
|
|
186
|
-
|
|
187
|
-
`implementation` / `final-verification` verifiers run **stage
|
|
188
|
-
conformance scripts** (Tier 3) that may need to reach a database or an
|
|
189
|
-
HTTP endpoint to prove the diff satisfies upstream requirements. Declare
|
|
190
|
-
the environment those scripts are allowed to touch under `qaEnv`. Every
|
|
191
|
-
field is optional; declare only what your conformance scripts use.
|
|
192
|
-
|
|
193
|
-
```json
|
|
194
|
-
"qaEnv": {
|
|
195
|
-
"replicaDbDsn": "<replica/test DB DSN — never shared/staging/prod>",
|
|
196
|
-
"appBaseUrl": "http://localhost:3000",
|
|
197
|
-
"envFile": ".okstra/qa.env",
|
|
198
|
-
"surfacePatterns": { "db": ["*.sql", "*repository*"], "http": ["*controller*"] }
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
- `replicaDbDsn` — DSN the conformance script connects to. MUST be a
|
|
203
|
-
replica / disposable test DB, **never** a shared, staging, or
|
|
204
|
-
production database (conformance scripts may write).
|
|
205
|
-
- `appBaseUrl` — base URL for endpoint-level conformance checks
|
|
206
|
-
(local app only).
|
|
207
|
-
- `envFile` — path (under `.okstra/`) to an env file the verifier
|
|
208
|
-
sources before running conformance scripts.
|
|
209
|
-
- `surfacePatterns` — per-project **override** of the diff-surface
|
|
210
|
-
cross-check map (`capability → glob list`). The validator maps each
|
|
211
|
-
changed file to a capability surface (`db` / `http` / `io`) and fails
|
|
212
|
-
the run when the diff touches a surface no stage `requires`. The
|
|
213
|
-
built-in patterns (e.g. `*router*` for `http`, `*storage*` for `io`)
|
|
214
|
-
are broad and match many front-end files, so front-end-heavy repos
|
|
215
|
-
should override with narrower globs to avoid false BLOCKING verdicts
|
|
216
|
-
(Phase 4b review note). An over-broad pattern over-blocks; an
|
|
217
|
-
over-narrow one lets an undeclared surface through — tune to the
|
|
218
|
-
repo's real db/http/io file naming.
|
|
219
|
-
|
|
220
|
-
## Step 4.7 (automatic): project-local Claude settings symlink
|
|
221
|
-
|
|
222
|
-
`okstra setup` (and `okstra run` on its first invocation per project)
|
|
223
|
-
provisions `<PROJECT_ROOT>/.claude/settings.local.json` as a symlink to
|
|
224
|
-
`~/.okstra/templates/settings.local.json`. The template is installed
|
|
225
|
-
by `okstra install` 0.14.0+ and contains the Bash permission rules
|
|
226
|
-
required for the codex/antigravity worker wrappers:
|
|
227
|
-
|
|
228
|
-
- `Bash($HOME/.okstra/bin/okstra-codex-exec.sh:*)`
|
|
229
|
-
- `Bash($HOME/.okstra/bin/okstra-antigravity-exec.sh:*)`
|
|
230
|
-
|
|
231
|
-
Claude Code automatically loads `.claude/settings.local.json` whenever
|
|
232
|
-
it operates inside that project, so okstra workers dispatched from
|
|
233
|
-
**any** Claude Code session (host or okstra.sh-spawned) are allowed to
|
|
234
|
-
run their wrapper scripts without further configuration.
|
|
235
|
-
|
|
236
|
-
This replaces the previous per-run `--settings` injection model
|
|
237
|
-
(`<run-dir>/okstra-runtime-settings.json`) and the earlier guidance to
|
|
238
|
-
modify the user's global `~/.claude/settings.json`.
|
|
239
|
-
|
|
240
|
-
If a non-symlink `.claude/settings.local.json` already exists, the
|
|
241
|
-
setup step backs it up to `.claude/settings.local.json.bak.<timestamp>`
|
|
242
|
-
before installing the symlink — surface that to the user so they can
|
|
243
|
-
merge any project-specific rules back into a downstream file (the
|
|
244
|
-
symlinked template is okstra-owned and gets refreshed when okstra
|
|
245
|
-
updates).
|
|
246
|
-
|
|
247
|
-
To opt out (advanced): replace the symlink with a regular file. okstra
|
|
248
|
-
will detect that it is no longer a symlink on its next setup call and
|
|
249
|
-
back it up as `.bak.<timestamp>` rather than overwriting silently.
|
|
250
|
-
|
|
251
|
-
## Step 4.8 (optional, opt-in): register a project PR body template
|
|
252
|
-
|
|
253
|
-
`release-handoff` fills the PR body from a template. By default it uses the
|
|
254
|
-
installed template at
|
|
255
|
-
`~/.okstra/templates/prd/pr-body.template.md`. Most projects
|
|
256
|
-
want their own — e.g. the repo's `.github/PULL_REQUEST_TEMPLATE.md` — to
|
|
257
|
-
keep PRs consistent with what the team already merges manually.
|
|
258
|
-
|
|
259
|
-
**Pre-registration during setup is opt-in.** The same prompt is also
|
|
260
|
-
offered on the first `release-handoff` run in this project, so it is
|
|
261
|
-
safe to defer this step (`나중에`) — most users should skip unless they
|
|
262
|
-
explicitly want to lock the template in now.
|
|
263
|
-
|
|
264
|
-
Ask the user with `AskUserQuestion` (fixed options, NOT free text — file
|
|
265
|
-
path entry happens in the follow-up plain text prompt per the
|
|
266
|
-
okstra-run prompt convention):
|
|
267
|
-
|
|
268
|
-
- **Question**: `"이 프로젝트에서 release-handoff 가 사용할 PR 본문 템플릿을 등록할까요?"`
|
|
269
|
-
- **Options**:
|
|
270
|
-
1. `이번 프로젝트만 (project scope)` — write to `<PROJECT_ROOT>/.okstra/project.json` `prTemplatePath`.
|
|
271
|
-
2. `전역 (global scope)` — write to `~/.okstra/config.json` `prTemplatePath`.
|
|
272
|
-
3. `나중에` — skip.
|
|
273
|
-
|
|
274
|
-
If the user picks scope 1 or 2, follow up with a **plain text prompt**:
|
|
275
|
-
`"PR 본문 템플릿 파일 경로를 알려주세요. project 스코프는 project-root 기준 상대경로 또는 절대경로, global 스코프는 절대경로 또는 ~/ 시작 경로만 허용됩니다."` Consume the next user message, then run:
|
|
276
|
-
|
|
277
|
-
```bash
|
|
278
|
-
okstra config set pr-template-path "<typed-path>" --scope <project|global>
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
The command validates the value (global rejects relative paths) and
|
|
282
|
-
writes atomically. Surface its stdout JSON so the user sees which file
|
|
283
|
-
was updated.
|
|
284
|
-
|
|
285
|
-
If the user chose `나중에`, tell them they can register later with one of:
|
|
286
|
-
|
|
287
|
-
- `okstra config set pr-template-path <path> --scope project|global` from a
|
|
288
|
-
terminal, or
|
|
289
|
-
- the per-run 1회성 override prompt during the next release-handoff run
|
|
290
|
-
(the run can also persist that override to project/global scope on the
|
|
291
|
-
spot — see the okstra-run skill).
|
|
292
|
-
|
|
293
|
-
## Step 4.9 (optional): choose final report language
|
|
294
|
-
|
|
295
|
-
기본은 영어. 이 step 을 skip 하면 `reportLanguage` 필드를 두지 않고
|
|
296
|
-
runtime 이 `auto` 로 처리한다 — 즉 task brief 의 주 서술 언어를 따라
|
|
297
|
-
간다 (한국어 brief → 한국어 report, 영어 brief → 영어 report).
|
|
298
|
-
|
|
299
|
-
사용자가 명시적으로 선택하면 `okstra config set` 으로 `project.json`
|
|
300
|
-
에 기록한다.
|
|
301
|
-
|
|
302
|
-
AskUserQuestion (fixed options):
|
|
303
|
-
- Question: `"Final report 를 어느 언어로 작성할까요?"`
|
|
304
|
-
- Options:
|
|
305
|
-
1. `English (recommended)` → reportLanguage: "en"
|
|
306
|
-
2. `한국어` → reportLanguage: "ko"
|
|
307
|
-
3. `Auto (task brief 언어로 추론, 불분명하면 영어)` → reportLanguage: "auto"
|
|
308
|
-
4. `나중에` → skip (필드 미설정 → runtime 이 auto 로 처리)
|
|
309
|
-
|
|
310
|
-
If the user picks 1/2/3, run:
|
|
100
|
+
## Step 3.5 (optional): project customisation
|
|
311
101
|
|
|
312
|
-
|
|
102
|
+
The built-in defaults work for most projects — skip straight to Step 4
|
|
103
|
+
(`doctor`) unless the user explicitly asks for one of these. When they do,
|
|
104
|
+
**read `references/project-config.md` next to this SKILL.md** and follow the
|
|
105
|
+
matching section:
|
|
313
106
|
|
|
314
|
-
|
|
315
|
-
|
|
107
|
+
- **A. worktree sync dirs** (`worktreeSyncDirs`) — extra directories symlinked
|
|
108
|
+
into task worktrees.
|
|
109
|
+
- **B. project QA commands** (`qaCommands`, recommended) + **B.1 `qaEnv`** —
|
|
110
|
+
check-only lint/format/typecheck/test baseline and the Tier 3 conformance
|
|
111
|
+
environment the `implementation` verifiers use.
|
|
112
|
+
- **C. `.claude/settings.local.json` symlink** — automatic; read only to
|
|
113
|
+
diagnose a symlink/backup warning.
|
|
114
|
+
- **D. PR body template** (release-handoff) — opt-in pre-registration; also
|
|
115
|
+
offered on the first release-handoff run.
|
|
116
|
+
- **E. final report language** (`reportLanguage`) — default is auto (follows
|
|
117
|
+
the brief's language).
|
|
316
118
|
|
|
317
|
-
## Step
|
|
119
|
+
## Step 4: Verify
|
|
318
120
|
|
|
319
121
|
```bash
|
|
320
122
|
okstra doctor --runtime claude-code
|
|
@@ -323,7 +125,7 @@ okstra doctor --runtime claude-code
|
|
|
323
125
|
If all checks return `OK`, the setup is complete. If any check fails, surface
|
|
324
126
|
the output and let the user decide whether to re-run install or skip.
|
|
325
127
|
|
|
326
|
-
## Step
|
|
128
|
+
## Step 5: Hand-off
|
|
327
129
|
|
|
328
130
|
Inform the user with a short summary:
|
|
329
131
|
|
|
@@ -337,7 +139,7 @@ Inform the user with a short summary:
|
|
|
337
139
|
|---|---|---|
|
|
338
140
|
| `command not found: npx` | Node missing | Install node 18+. |
|
|
339
141
|
| `okstra ensure-installed` keeps reinstalling | `~/.okstra/version` write fails (permissions) | Check `~/.okstra` ownership and writability. |
|
|
340
|
-
| `error: --project-id is required (no existing project.json, not a TTY)` | `okstra setup --yes` invoked without `--project-id`, or with empty answer to Step
|
|
142
|
+
| `error: --project-id is required (no existing project.json, not a TTY)` | `okstra setup --yes` invoked without `--project-id`, or with empty answer to Step 3 prompt | Re-ask Step 3 and pass a non-empty id via `--project-id`. |
|
|
341
143
|
| `projectId mismatch` / `projectId 불일치` | `project.json` already exists with a different id | Decide which id is canonical; manually delete `<PROJECT_ROOT>/.okstra/project.json` to re-register, or re-run with the existing id. |
|
|
342
144
|
| `EACCES` writing under `.okstra/` | directory owned by another user (e.g. created by a previous root-shell run) | `chown -R "$USER" <PROJECT_ROOT>/.okstra` or delete and let setup recreate. |
|
|
343
145
|
| `warning: failed to provision .claude/settings.local.json symlink` | a non-symlink `.claude/settings.local.json` already exists and the backup-and-replace step failed | Inspect `<PROJECT_ROOT>/.claude/settings.local.json{,.bak.*}`; manually merge project-specific rules, then re-run setup. |
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Optional project configuration (okstra-setup)
|
|
2
|
+
|
|
3
|
+
Read this only when the user asks to customise one of the areas below after
|
|
4
|
+
`project.json` exists. The built-in defaults work for most projects — none of
|
|
5
|
+
these sections is required for a working setup.
|
|
6
|
+
|
|
7
|
+
The Bash invocation rule from SKILL.md applies to every command here.
|
|
8
|
+
|
|
9
|
+
## A. Worktree sync dirs (`worktreeSyncDirs`)
|
|
10
|
+
|
|
11
|
+
Each okstra run provisions a task-scoped git worktree under
|
|
12
|
+
`~/.okstra/worktrees/.../`. A small set of project-root-relative directories is
|
|
13
|
+
symlinked from the main checkout into that worktree so every task sees the
|
|
14
|
+
shared state. The built-in default is `.project-docs`, `.scratch`,
|
|
15
|
+
`graphify-out`, `.claude`. Syncing a directory does not make it okstra memory;
|
|
16
|
+
okstra-owned context and writes still stay under `<PROJECT_ROOT>/.okstra/**`
|
|
17
|
+
unless the task brief explicitly authorizes a non-okstra path.
|
|
18
|
+
|
|
19
|
+
To override per-project, add a `worktreeSyncDirs` array to `project.json`.
|
|
20
|
+
Empty array disables the feature; the field is preserved across the runtime's
|
|
21
|
+
auto-upserts (only `projectId`, `projectRoot`, `createdAt`, `updatedAt` are
|
|
22
|
+
runtime-owned).
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"projectId": "...",
|
|
27
|
+
"projectRoot": "...",
|
|
28
|
+
"worktreeSyncDirs": [".project-docs", ".scratch", "graphify-out", ".claude", "my-custom-dir"]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Resolution precedence: `OKSTRA_WORKTREE_SYNC_DIRS` env var → this field →
|
|
33
|
+
built-in default. Only edit when defaults don't cover the project's working
|
|
34
|
+
files (e.g. additional cache or local-config dirs that must follow the
|
|
35
|
+
executor into the worktree).
|
|
36
|
+
|
|
37
|
+
## B. Project QA commands (`qaCommands`) — recommended
|
|
38
|
+
|
|
39
|
+
`implementation`-phase verifiers run an independent QA gate over the
|
|
40
|
+
executor's diff and need a project-wide baseline of check-only lint / format /
|
|
41
|
+
typecheck / test commands. okstra does NOT auto-detect tooling — declare the
|
|
42
|
+
commands explicitly in `project.json` under `qaCommands`. Skipping this is
|
|
43
|
+
allowed but the verifier will then only run the plan's per-task `validation`
|
|
44
|
+
set, with `qa-command not configured: <category>` recorded per missing
|
|
45
|
+
category in the final report.
|
|
46
|
+
|
|
47
|
+
Each category is an array of `{ "label", "cmd", "language"? }` objects.
|
|
48
|
+
`language` is optional; when present the verifier MAY skip commands whose
|
|
49
|
+
language is not represented in this run's diff.
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"projectId": "...",
|
|
54
|
+
"projectRoot": "...",
|
|
55
|
+
"qaCommands": {
|
|
56
|
+
"lint": [{ "label": "cargo clippy", "cmd": "cargo clippy --all-targets -- -D warnings", "language": "rust" }],
|
|
57
|
+
"format": [{ "label": "cargo fmt", "cmd": "cargo fmt --check", "language": "rust" }],
|
|
58
|
+
"typecheck": [{ "label": "tsc", "cmd": "pnpm exec tsc --noEmit", "language": "ts" }],
|
|
59
|
+
"test": [{ "label": "cargo test", "cmd": "cargo test --workspace --locked", "language": "rust" }]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**`cmd` deny-list (mutation guard):** the verifier rejects any `cmd`
|
|
65
|
+
containing tokens that imply mutation — declare commands in their check-only
|
|
66
|
+
form only. Representative denied tokens: `--fix` / `--write` (formatters),
|
|
67
|
+
`--updateSnapshot`, `npm install` (use `npm ci`), `pip install -U`,
|
|
68
|
+
`cargo add`. The authoritative deny list is
|
|
69
|
+
`scripts/okstra_ctl/qa_commands.py::find_denied_tokens`
|
|
70
|
+
(`_DENIED_LITERAL_TOKENS` / `_DENIED_SUBSTRINGS` plus the dynamic
|
|
71
|
+
`npm install` and `INSTA_UPDATE=` checks) — do not re-enumerate it here.
|
|
72
|
+
|
|
73
|
+
Encountering a denied token aborts the verifier with status
|
|
74
|
+
`contract-violated`; re-declare the command in check-only form to recover
|
|
75
|
+
(e.g. swap `prettier --write` → `prettier --check`).
|
|
76
|
+
|
|
77
|
+
The field is preserved across the runtime's auto-upserts of `project.json`, so
|
|
78
|
+
manual edits to `qaCommands` survive every subsequent `okstra setup` /
|
|
79
|
+
`okstra run` invocation.
|
|
80
|
+
|
|
81
|
+
### B.1 `qaEnv` — Tier 3 conformance environment
|
|
82
|
+
|
|
83
|
+
`implementation` / `final-verification` verifiers run **stage conformance
|
|
84
|
+
scripts** (Tier 3) that may need to reach a database or an HTTP endpoint to
|
|
85
|
+
prove the diff satisfies upstream requirements. Declare the environment those
|
|
86
|
+
scripts are allowed to touch under `qaEnv`. Every field is optional; declare
|
|
87
|
+
only what your conformance scripts use.
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
"qaEnv": {
|
|
91
|
+
"replicaDbDsn": "<replica/test DB DSN — never shared/staging/prod>",
|
|
92
|
+
"appBaseUrl": "http://localhost:3000",
|
|
93
|
+
"envFile": ".okstra/qa.env",
|
|
94
|
+
"surfacePatterns": { "db": ["*.sql", "*repository*"], "http": ["*controller*"] }
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- `replicaDbDsn` — DSN the conformance script connects to. MUST be a replica /
|
|
99
|
+
disposable test DB, **never** a shared, staging, or production database
|
|
100
|
+
(conformance scripts may write).
|
|
101
|
+
- `appBaseUrl` — base URL for endpoint-level conformance checks (local app
|
|
102
|
+
only).
|
|
103
|
+
- `envFile` — path (under `.okstra/`) to an env file the verifier sources
|
|
104
|
+
before running conformance scripts.
|
|
105
|
+
- `surfacePatterns` — per-project **override** of the diff-surface cross-check
|
|
106
|
+
map (`capability → glob list`). The validator maps each changed file to a
|
|
107
|
+
capability surface (`db` / `http` / `io`) and fails the run when the diff
|
|
108
|
+
touches a surface no stage `requires`. The built-in patterns (e.g. `*router*`
|
|
109
|
+
for `http`, `*storage*` for `io`) are broad and match many front-end files,
|
|
110
|
+
so front-end-heavy repos should override with narrower globs to avoid false
|
|
111
|
+
BLOCKING verdicts. An over-broad pattern over-blocks; an over-narrow one
|
|
112
|
+
lets an undeclared surface through — tune to the repo's real db/http/io file
|
|
113
|
+
naming.
|
|
114
|
+
|
|
115
|
+
## C. Project-local Claude settings symlink (automatic — diagnostics only)
|
|
116
|
+
|
|
117
|
+
`okstra setup` (and `okstra run` on its first invocation per project)
|
|
118
|
+
provisions `<PROJECT_ROOT>/.claude/settings.local.json` as a symlink to
|
|
119
|
+
`~/.okstra/templates/settings.local.json`. The template contains the Bash
|
|
120
|
+
permission rules required for the codex/antigravity worker wrappers:
|
|
121
|
+
|
|
122
|
+
- `Bash($HOME/.okstra/bin/okstra-codex-exec.sh:*)`
|
|
123
|
+
- `Bash($HOME/.okstra/bin/okstra-antigravity-exec.sh:*)`
|
|
124
|
+
|
|
125
|
+
Claude Code automatically loads `.claude/settings.local.json` inside that
|
|
126
|
+
project, so okstra workers dispatched from **any** Claude Code session are
|
|
127
|
+
allowed to run their wrapper scripts without further configuration.
|
|
128
|
+
|
|
129
|
+
If a non-symlink `.claude/settings.local.json` already exists, setup backs it
|
|
130
|
+
up to `.claude/settings.local.json.bak.<timestamp>` before installing the
|
|
131
|
+
symlink — surface that to the user so they can merge project-specific rules
|
|
132
|
+
back (the symlinked template is okstra-owned and refreshed on okstra updates).
|
|
133
|
+
To opt out (advanced): replace the symlink with a regular file; okstra will
|
|
134
|
+
back it up as `.bak.<timestamp>` on its next setup call rather than
|
|
135
|
+
overwriting silently.
|
|
136
|
+
|
|
137
|
+
## D. Project PR body template (release-handoff)
|
|
138
|
+
|
|
139
|
+
`release-handoff` fills the PR body from a template — by default
|
|
140
|
+
`~/.okstra/templates/prd/pr-body.template.md`. Most projects want their own
|
|
141
|
+
(e.g. `.github/PULL_REQUEST_TEMPLATE.md`). Pre-registration during setup is
|
|
142
|
+
opt-in; the same prompt is offered again on the first `release-handoff` run,
|
|
143
|
+
so deferring (`나중에`) is safe.
|
|
144
|
+
|
|
145
|
+
Ask with `AskUserQuestion` (fixed options — file path entry happens in the
|
|
146
|
+
follow-up plain text prompt):
|
|
147
|
+
|
|
148
|
+
- **Question**: `"이 프로젝트에서 release-handoff 가 사용할 PR 본문 템플릿을 등록할까요?"`
|
|
149
|
+
- **Options**:
|
|
150
|
+
1. `이번 프로젝트만 (project scope)` — writes `prTemplatePath` to `<PROJECT_ROOT>/.okstra/project.json`.
|
|
151
|
+
2. `전역 (global scope)` — writes `prTemplatePath` to `~/.okstra/config.json`.
|
|
152
|
+
3. `나중에` — skip.
|
|
153
|
+
|
|
154
|
+
If scope 1 or 2, follow up with a plain text prompt:
|
|
155
|
+
`"PR 본문 템플릿 파일 경로를 알려주세요. project 스코프는 project-root 기준 상대경로 또는 절대경로, global 스코프는 절대경로 또는 ~/ 시작 경로만 허용됩니다."`
|
|
156
|
+
Consume the next user message, then run:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
okstra config set pr-template-path "<typed-path>" --scope <project|global>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The command validates the value (global rejects relative paths) and writes
|
|
163
|
+
atomically. Surface its stdout JSON. If the user chose `나중에`, tell them
|
|
164
|
+
they can register later via the same `okstra config set` command or the
|
|
165
|
+
per-run override prompt during the next release-handoff run.
|
|
166
|
+
|
|
167
|
+
## E. Final report language (`reportLanguage`)
|
|
168
|
+
|
|
169
|
+
기본은 영어. Skip 하면 `reportLanguage` 필드를 두지 않고 runtime 이 `auto` 로
|
|
170
|
+
처리한다 — task brief 의 주 서술 언어를 따라간다 (한국어 brief → 한국어
|
|
171
|
+
report, 영어 brief → 영어 report).
|
|
172
|
+
|
|
173
|
+
AskUserQuestion (fixed options):
|
|
174
|
+
- Question: `"Final report 를 어느 언어로 작성할까요?"`
|
|
175
|
+
- Options:
|
|
176
|
+
1. `English (recommended)` → `en`
|
|
177
|
+
2. `한국어` → `ko`
|
|
178
|
+
3. `Auto (task brief 언어로 추론, 불분명하면 영어)` → `auto`
|
|
179
|
+
4. `나중에` → skip (필드 미설정 → runtime 이 auto 로 처리)
|
|
180
|
+
|
|
181
|
+
If the user picks 1/2/3:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
okstra config set report-language <en|ko|auto> --scope project
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
전역 기본값은 README 의 "global config" 안내대로 `--scope global` 로 수동
|
|
188
|
+
설정한다 — 이 flow 에서는 project 스코프만 제공한다.
|
|
@@ -609,7 +609,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
609
609
|
| # | Tier | Command (verbatim) | Status | Exit code | Output tail |
|
|
610
610
|
|---|------|---------------------|--------|-----------|-------------|
|
|
611
611
|
{% for row in finalVerification.readonlyCommandLog -%}
|
|
612
|
-
| {{ row.number | mdcell }} | {{ row.tier | mdcell }} | `{{ row.command | mdcell }}` | `{{ row.status | mdcell }}` | {{ (row.exitCode if row.exitCode is not none else '—') | mdcell }} | {{ (row.
|
|
612
|
+
| {{ row.number | mdcell }} | {{ row.tier | mdcell }} | `{{ row.command | mdcell }}` | `{{ row.status | mdcell }}` | {{ (row.exitCode if row.exitCode is not none else '—') | mdcell }} | {{ (row.statusReason if row.status in ['rejected', 'env-unavailable'] else row.outputTail) | mdcell }} |
|
|
613
613
|
{% endfor %}
|
|
614
614
|
|
|
615
615
|
### 5.8.6 Conditional Acceptance Conditions
|
|
@@ -640,6 +640,29 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
640
640
|
|
|
641
641
|
{{ finalVerification.routingRecommendation }}
|
|
642
642
|
|
|
643
|
+
### 5.8.9 Could-Not-Verify Roll-up
|
|
644
|
+
|
|
645
|
+
Single scan of every agreed check that this run did NOT confirm — requirement gaps, commands not run (rejected / not-configured / env-unavailable), and blocked manual tests — aggregated from the sections above.
|
|
646
|
+
|
|
647
|
+
{% set nv = namespace(any=false) -%}
|
|
648
|
+
{% for row in finalVerification.validationEvidence if row.status == 'gap' -%}
|
|
649
|
+
{% set nv.any = true -%}
|
|
650
|
+
- `{{ row.id | mdcell }}` requirement gap — {{ row.requirement | mdcell }}
|
|
651
|
+
{% endfor -%}
|
|
652
|
+
{% for row in finalVerification.readonlyCommandLog if row.status in ['rejected', 'not-configured', 'env-unavailable'] -%}
|
|
653
|
+
{% set nv.any = true -%}
|
|
654
|
+
- `{{ row.command | mdcell }}` — {{ row.status | mdcell }}: {{ row.statusReason | mdcell }}
|
|
655
|
+
{% endfor -%}
|
|
656
|
+
{% if finalVerification.manualUserTest.applicable -%}
|
|
657
|
+
{% for row in finalVerification.manualUserTest.results if row.result == 'blocked' -%}
|
|
658
|
+
{% set nv.any = true -%}
|
|
659
|
+
- manual: {{ row.target | mdcell }} — {{ row.performed | mdcell }} (blocked): {{ row.observed | mdcell }}
|
|
660
|
+
{% endfor -%}
|
|
661
|
+
{% endif -%}
|
|
662
|
+
{% if not nv.any -%}
|
|
663
|
+
- All agreed checks were executed or covered — nothing left unverified.
|
|
664
|
+
{%- endif %}
|
|
665
|
+
|
|
643
666
|
{% endif %}
|
|
644
667
|
{% if header.taskType == 'improvement-discovery' %}
|
|
645
668
|
## 5.9 Improvement Candidates
|
|
@@ -51,8 +51,9 @@ taskType: "{{FM_TASK_TYPE}}"
|
|
|
51
51
|
## Phase 1.5 — Lead Reflect-Back Grilling
|
|
52
52
|
|
|
53
53
|
This section is filled in by the lead during Phase 1.5 before worker dispatch.
|
|
54
|
-
Workers MUST read the resolved values from
|
|
55
|
-
|
|
54
|
+
Workers MUST read the resolved values from the log the lead points to via the
|
|
55
|
+
`**Phase 1.5 Grilling Log:** <abs-path>` dispatch-prompt anchor
|
|
56
|
+
(`runs/improvement-discovery/<seq>/state/phase-1.5-grilling.md`) rather than the unresolved brief.
|
|
56
57
|
|
|
57
58
|
- Reflect-back summary:
|
|
58
59
|
- Open questions (Q1..QN):
|
|
@@ -81,9 +81,9 @@ _의존 정보 없음_
|
|
|
81
81
|
<!-- OPTIONAL: include only when per-task day estimates are available.
|
|
82
82
|
If included, MUST use this exact heading literal `## Gantt Chart`.
|
|
83
83
|
Render as ASCII inside a fenced ``` block with NO language tag.
|
|
84
|
-
Mermaid / PlantUML / Graphviz are forbidden
|
|
84
|
+
Mermaid / PlantUML / Graphviz are forbidden.
|
|
85
85
|
The axis is RELATIVE DAY-COUNTS (Day 1 / J1, …) — never calendar dates.
|
|
86
|
-
If day estimates are missing, omit this whole section and replace it with one blockquote: `> _Gantt Chart 생략: <reason>_
|
|
86
|
+
If day estimates are missing, omit this whole section and replace it with one blockquote: `> _Gantt Chart 생략: <reason>_`. -->
|
|
87
87
|
|
|
88
88
|
```
|
|
89
89
|
Day: 1 5 10 15 20 25 30
|
|
@@ -20,7 +20,7 @@ Work like a senior engineer who owns this result, not a commentator on it.
|
|
|
20
20
|
|
|
21
21
|
You are required to read every primary input file enumerated by the dispatcher (the lead's prompt lists them under `[Required reading]`) from the very first character to the very last character before you produce any analysis output. Skimming, partial reads, jumping to a single section, or relying on prior knowledge of a similar file's structure is not acceptable. Source files listed as fallback/evidence paths are read on demand when you need to verify a citation, resolve ambiguity, or inspect material the packet says it omitted.
|
|
22
22
|
|
|
23
|
-
### Audience-scoped enumeration (
|
|
23
|
+
### Audience-scoped enumeration (performance optimization — mandatory)
|
|
24
24
|
|
|
25
25
|
Different recipients need different files. Do NOT include `final-report-template.md` in analysis worker prompts: analysis workers produce findings (not the final report), and forcing them to read the template inflates token usage without changing finding quality.
|
|
26
26
|
|
|
@@ -103,6 +103,10 @@ For the **final-verification phase** specifically, the dispatched prompt MUST al
|
|
|
103
103
|
- `**Verification base ref:** <base-ref>` — the implementation base for the diff.
|
|
104
104
|
- `**Verification diff stat:** <git diff --stat output>` — the captured diff-stat that bounds the verification scope.
|
|
105
105
|
|
|
106
|
+
For the **improvement-discovery phase** specifically, the dispatched prompt MUST also include the resolved-scope log the lead wrote during Phase 1.5, so workers key off an explicit absolute path instead of re-deriving the run directory:
|
|
107
|
+
|
|
108
|
+
- `**Phase 1.5 Grilling Log:** <absolute-path>` — the lead's Phase 1.5 reflect-back log (`<RUN_DIR>/state/phase-1.5-grilling.md`, written before dispatch). Its `Resolved scope` / `Resolved lenses` blocks are the authoritative scope and lens definition for this run. Read the file at this absolute path; do NOT re-derive it from `<RUN_DIR>`, and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses`. The presence of this anchor is itself the signal that this is an improvement-discovery run — no separate task-type detection is required.
|
|
109
|
+
|
|
106
110
|
When a worker reads any project-relative path from the prompt, it MUST resolve it against `Project Root` (e.g. `<Project Root>/<Result Path>`) — never use bare relative paths that depend on cwd.
|
|
107
111
|
|
|
108
112
|
---
|
|
@@ -1220,6 +1220,7 @@ FINAL_VERIFICATION_REQUIRED_SECTIONS = (
|
|
|
1220
1220
|
"Conditional Acceptance Conditions",
|
|
1221
1221
|
"Manual User Test Results",
|
|
1222
1222
|
"Routing Recommendation",
|
|
1223
|
+
"Could-Not-Verify Roll-up",
|
|
1223
1224
|
)
|
|
1224
1225
|
|
|
1225
1226
|
# Allowed Verdict Token vocabulary, by task-type. `release-handoff` is
|
|
@@ -1715,7 +1716,7 @@ _SINGLE_VOTE_BLOCKING_KINDS = {"a", "d"}
|
|
|
1715
1716
|
|
|
1716
1717
|
def _classify_plan_item_gate(item: dict) -> str:
|
|
1717
1718
|
"""Recompute one plan item's gate class from its per-worker verdicts,
|
|
1718
|
-
per `prompts/lead/
|
|
1719
|
+
per `prompts/lead/plan-body-verification.md` "Round protocol". Returns one of
|
|
1719
1720
|
``majority-disagree`` / ``has-dissent`` / ``full-consensus`` /
|
|
1720
1721
|
``all-non-result``. Collapses ``partial-consensus`` and
|
|
1721
1722
|
``dissent-isolated`` into ``has-dissent`` because they resolve to the
|
|
@@ -1797,12 +1798,12 @@ def _validate_plan_body_gate_recompute(data: dict, failures: list[str]) -> None:
|
|
|
1797
1798
|
f"only support `{recomputed}` (a majority DISAGREE, a DISAGREE(f) on "
|
|
1798
1799
|
"a P-Req item, or all-non-result dispatches were recorded). The gate "
|
|
1799
1800
|
"value must honestly aggregate the worker votes — do not upgrade it "
|
|
1800
|
-
"to unblock the run (
|
|
1801
|
+
"to unblock the run (plan-body-verification.md Round protocol)."
|
|
1801
1802
|
)
|
|
1802
1803
|
|
|
1803
1804
|
|
|
1804
1805
|
# Each plan-body deliverable array and the P-* verdict-item prefix it must be
|
|
1805
|
-
# extracted into for §5.5.9 cross-verification (
|
|
1806
|
+
# extracted into for §5.5.9 cross-verification (plan-body-verification.md Plan-item
|
|
1806
1807
|
# extraction). A weak item dropped from planItems escapes the gate entirely.
|
|
1807
1808
|
_PLAN_ITEM_SOURCES = (
|
|
1808
1809
|
("optionCandidates", "P-Opt", "Option Candidates (§4.5.1)"),
|
|
@@ -1848,7 +1849,7 @@ def _validate_plan_item_extraction_completeness(data: dict, failures: list[str])
|
|
|
1848
1849
|
f"planBodyVerification.planItems carries only {extracted} "
|
|
1849
1850
|
f"`{prefix}-*` verdict item(s). Every plan-body item MUST be "
|
|
1850
1851
|
"extracted for cross-verification — a dropped item escapes the "
|
|
1851
|
-
"gate (
|
|
1852
|
+
"gate (plan-body-verification.md Plan-item extraction)."
|
|
1852
1853
|
)
|
|
1853
1854
|
|
|
1854
1855
|
|
|
@@ -1885,7 +1886,7 @@ def _validate_plan_item_subject_substance(data: dict, failures: list[str]) -> No
|
|
|
1885
1886
|
f"placeholder subject `{subject}`. Give a plain-language label of "
|
|
1886
1887
|
"what the item is (e.g. 'Option A: upload v2 를 신규 모듈로 분리') so "
|
|
1887
1888
|
"the §5.5.9 reader knows what each verdict is about "
|
|
1888
|
-
"(
|
|
1889
|
+
"(plan-body-verification.md Plan-item extraction)."
|
|
1889
1890
|
)
|
|
1890
1891
|
|
|
1891
1892
|
|
|
@@ -1894,7 +1895,7 @@ def _validate_plan_body_clarification_matching(data: dict, failures: list[str])
|
|
|
1894
1895
|
must point (via `clarificationId`) at an existing `blocks: approval`
|
|
1895
1896
|
clarification row. Closes the hole where a majority-disagree item blocks the
|
|
1896
1897
|
gate but no §1 Clarification row is raised, so the user never sees why
|
|
1897
|
-
approval is withheld (
|
|
1898
|
+
approval is withheld (implementation-planning.md self-review step 7).
|
|
1898
1899
|
"""
|
|
1899
1900
|
ip = data.get("implementationPlanning")
|
|
1900
1901
|
if not isinstance(ip, dict):
|
|
@@ -1920,7 +1921,7 @@ def _validate_plan_body_clarification_matching(data: dict, failures: list[str])
|
|
|
1920
1921
|
f"final-report data.json: plan item `{item_id}` is majority-disagree "
|
|
1921
1922
|
"but carries no `clarificationId`. A blocking disagreement MUST "
|
|
1922
1923
|
"surface as a `## 1. Clarification Items` row (blocks=approval) so "
|
|
1923
|
-
"the user sees the blocker (
|
|
1924
|
+
"the user sees the blocker (implementation-planning.md self-review step 7)."
|
|
1924
1925
|
)
|
|
1925
1926
|
elif cid not in approval_ids:
|
|
1926
1927
|
reason = (
|