okstra 0.122.0 → 0.124.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 (108) hide show
  1. package/README.md +5 -2
  2. package/docs/architecture/storage-model.md +15 -1
  3. package/docs/architecture.md +45 -7
  4. package/docs/cli.md +47 -5
  5. package/docs/for-ai/README.md +42 -36
  6. package/docs/for-ai/skills/okstra-brief-gen.md +105 -105
  7. package/docs/for-ai/skills/okstra-container-build.md +61 -61
  8. package/docs/for-ai/skills/okstra-graphify.md +64 -0
  9. package/docs/for-ai/skills/okstra-inspect.md +86 -86
  10. package/docs/for-ai/skills/okstra-manager.md +32 -32
  11. package/docs/for-ai/skills/okstra-memory.md +49 -50
  12. package/docs/for-ai/skills/okstra-pr-gen.md +48 -0
  13. package/docs/for-ai/skills/okstra-rollup.md +58 -58
  14. package/docs/for-ai/skills/okstra-run.md +95 -95
  15. package/docs/for-ai/skills/okstra-schedule-gen.md +320 -0
  16. package/docs/for-ai/skills/okstra-setup.md +63 -64
  17. package/docs/for-ai/skills/okstra-user-response.md +48 -0
  18. package/docs/performance-improvement-plan-v2.md +4 -4
  19. package/docs/pr-template-usage.md +34 -34
  20. package/docs/project-structure-overview.md +92 -70
  21. package/docs/task-process/README.md +33 -33
  22. package/docs/task-process/common-flow.md +26 -26
  23. package/docs/task-process/error-analysis.md +20 -21
  24. package/docs/task-process/final-verification.md +41 -41
  25. package/docs/task-process/implementation-planning.md +52 -28
  26. package/docs/task-process/implementation.md +51 -32
  27. package/docs/task-process/release-handoff.md +46 -46
  28. package/docs/task-process/requirements-discovery.md +22 -23
  29. package/package.json +1 -1
  30. package/runtime/BUILD.json +2 -2
  31. package/runtime/agents/workers/antigravity-worker.md +4 -4
  32. package/runtime/agents/workers/claude-worker.md +2 -2
  33. package/runtime/agents/workers/codex-worker.md +4 -4
  34. package/runtime/agents/workers/report-writer-worker.md +4 -4
  35. package/runtime/bin/lib/okstra/usage.sh +3 -3
  36. package/runtime/prompts/coding-preflight/frameworks/node-server.md +1 -1
  37. package/runtime/prompts/launch.template.md +6 -3
  38. package/runtime/prompts/lead/convergence.md +11 -21
  39. package/runtime/prompts/lead/okstra-lead-contract.md +16 -18
  40. package/runtime/prompts/lead/plan-body-verification.md +47 -18
  41. package/runtime/prompts/lead/report-writer.md +50 -45
  42. package/runtime/prompts/lead/team-contract.md +11 -122
  43. package/runtime/prompts/profiles/_common-contract.md +15 -22
  44. package/runtime/prompts/profiles/_implementation-deliverable.md +4 -2
  45. package/runtime/prompts/profiles/_implementation-executor.md +6 -1
  46. package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
  47. package/runtime/prompts/profiles/error-analysis.md +2 -2
  48. package/runtime/prompts/profiles/final-verification.md +3 -1
  49. package/runtime/prompts/profiles/implementation-planning.md +24 -14
  50. package/runtime/prompts/profiles/implementation.md +1 -1
  51. package/runtime/prompts/profiles/improvement-discovery.md +1 -1
  52. package/runtime/prompts/profiles/release-handoff.md +3 -3
  53. package/runtime/prompts/profiles/requirements-discovery.md +18 -18
  54. package/runtime/prompts/wizard/prompts.ko.json +44 -0
  55. package/runtime/python/okstra_ctl/codex_dispatch.py +23 -1
  56. package/runtime/python/okstra_ctl/design_prep.py +1462 -0
  57. package/runtime/python/okstra_ctl/design_surfaces.py +243 -0
  58. package/runtime/python/okstra_ctl/final_report_schema.py +33 -1
  59. package/runtime/python/okstra_ctl/implementation_stage.py +35 -0
  60. package/runtime/python/okstra_ctl/incremental_carry.py +294 -21
  61. package/runtime/python/okstra_ctl/incremental_scope.py +51 -5
  62. package/runtime/python/okstra_ctl/material.py +1 -1
  63. package/runtime/python/okstra_ctl/model_discovery.py +98 -0
  64. package/runtime/python/okstra_ctl/models.py +8 -3
  65. package/runtime/python/okstra_ctl/render.py +5 -0
  66. package/runtime/python/okstra_ctl/run.py +53 -5
  67. package/runtime/python/okstra_ctl/user_response.py +67 -2
  68. package/runtime/python/okstra_ctl/wizard.py +283 -3
  69. package/runtime/python/okstra_token_usage/report.py +11 -0
  70. package/runtime/schemas/final-report-v1.0.schema.json +336 -0
  71. package/runtime/skills/_fragments/bash-invocation-rule.md +1 -0
  72. package/runtime/skills/_fragments/preflight-outdated-cli.md +1 -0
  73. package/runtime/skills/_fragments/python-bootstrap-note.md +1 -0
  74. package/runtime/skills/okstra-brief-gen/SKILL.md +117 -122
  75. package/runtime/skills/okstra-container-build/SKILL.md +24 -14
  76. package/runtime/skills/okstra-graphify/SKILL.md +12 -4
  77. package/runtime/skills/okstra-inspect/SKILL.md +105 -99
  78. package/runtime/skills/okstra-manager/SKILL.md +1 -1
  79. package/runtime/skills/okstra-memory/SKILL.md +3 -3
  80. package/runtime/skills/okstra-rollup/SKILL.md +12 -6
  81. package/runtime/skills/okstra-run/SKILL.md +49 -88
  82. package/runtime/skills/{okstra-schedule → okstra-schedule-gen}/SKILL.md +38 -32
  83. package/runtime/skills/okstra-setup/SKILL.md +1 -1
  84. package/runtime/skills/okstra-setup/references/project-config.md +17 -16
  85. package/runtime/skills/okstra-usage/SKILL.md +5 -2
  86. package/runtime/skills/okstra-user-response/SKILL.md +23 -9
  87. package/runtime/templates/prd/brief.template.md +92 -92
  88. package/runtime/templates/reports/error-analysis-input.template.md +1 -1
  89. package/runtime/templates/reports/fan-out-unit.template.md +6 -6
  90. package/runtime/templates/reports/final-report.template.md +67 -0
  91. package/runtime/templates/reports/final-verification-input.template.md +6 -6
  92. package/runtime/templates/reports/i18n/en.json +31 -0
  93. package/runtime/templates/reports/i18n/ko.json +31 -0
  94. package/runtime/templates/reports/implementation-input.template.md +1 -1
  95. package/runtime/templates/reports/implementation-planning-input.template.md +1 -1
  96. package/runtime/templates/reports/improvement-discovery-input.template.md +1 -1
  97. package/runtime/templates/reports/quick-input.template.md +1 -1
  98. package/runtime/templates/reports/release-handoff-input.template.md +1 -1
  99. package/runtime/templates/reports/schedule.template.md +22 -22
  100. package/runtime/templates/reports/task-brief.template.md +3 -3
  101. package/runtime/templates/reports/user-response.template.md +20 -20
  102. package/runtime/templates/worker-prompt-preamble.md +111 -13
  103. package/runtime/validators/validate-run.py +426 -5
  104. package/runtime/validators/validate-schedule.py +5 -5
  105. package/src/cli-registry.mjs +7 -0
  106. package/src/commands/inspect/design-prep.mjs +23 -0
  107. package/src/lib/skill-catalog.mjs +2 -1
  108. package/docs/for-ai/skills/okstra-schedule.md +0 -320
@@ -30,9 +30,10 @@ are collected and convergence finished. Phase 1-5 do not need it.
30
30
  - **Pure code changes** (no persisted state, no infra mutation): a reachable revert SHA is sufficient. Record the exact `git revert <SHA>` command that would undo the change, and confirm `git rev-parse <SHA>` resolves.
31
31
  - **Feature-flag-gated changes**: confirm the off-switch path was exercised in this run's validation evidence (i.e. one of the validation commands ran with the flag off and succeeded). A plan that ships a flag without exercising the off-path does NOT satisfy this requirement.
32
32
  - **Schema migrations, config-format changes, or any change with persisted state**: a **dry-run of the rollback step is mandatory**, not preferred. Record the exact rollback command and its captured exit code / stdout. If the migration tool offers no dry-run mode (`--dry-run`, `--plan`, equivalent), the executor MUST refuse to claim rollback verification and instead end the run with a routing recommendation back to `implementation-planning` for a safer rollback strategy. Skipping this step on a stateful change is treated as a `contract-violated` outcome by `final-verification`.
33
- - **Manual user test draft**: when this run produces a user-observable change (UI / API / CLI / artifact), write `target / environment / steps / expected result` per change into §5.7.9 (data field `implementation.manualUserTest`, `applicable=true` with `items`). Environment line: if the project has a `docker-compose.yml`, use `/okstra-container-build` (which runs `okstra container up <task-id>`) then connect to the published port; otherwise the project's run command (e.g. `npm start`). When there is no user-observable change, set `applicable=false` and give a one-line `exemptionReason` instead of items. Base the steps on the approved plan's `Acceptance:` and this run's actual diff — these are the steps a human (or `final-verification`) re-runs by hand, NOT the automated validation commands in `Validation evidence`.
33
+ - **Manual user test draft**: when this run produces a user-observable change (UI / API / CLI / artifact), write `target / environment / steps / expected result` per change into §5.7.9 (data field `implementation.manualUserTest`, `applicable=true` with `items`). Treat effective `manual-user-test` PREP content only as a seed: reconcile it with the approved plan's `Acceptance:` and this run's actual diff before writing the final steps. Environment line: if the project has a `docker-compose.yml`, use `/okstra-container-build` (which runs `okstra container up <task-id>`) then connect to the published port; otherwise the project's run command (e.g. `npm start`). When there is no user-observable change, set `applicable=false` and give a one-line `exemptionReason` instead of items. These are the steps a human (or `final-verification`) re-runs by hand, NOT the automated validation commands in `Validation evidence`; planning PREP is never a second final-verification manual-test source.
34
+ - **Design preparation handoff**: `implementation.manualUserTest` remains the only manual-test handoff to final-verification. For each effective non-`manual-user-test` item whose `reviewAt.phase` is `final-verification` and whose question remains unresolved after inspecting the actual diff, add one common `missingInformation` row. Set `source` to `design-prep:<PREP-ID>:<assessment-fingerprint>`, put `ifStillOpen` and the unresolved question in `item`, and put the guardrails plus risk in `risk`. Do not introduce an implementation-only PREP schema.
34
35
  - **Routing recommendation for `final-verification`**: brief note on whether the changes are ready for final-verification phase or need a new error-analysis / planning loop first.
35
- - **Follow-up tasks (Section 4 of the final report)**: every item discovered during this run that was *not* delivered MUST appear in the final report's `## 4. Follow-up Tasks (후속 작업)` table with a concrete `Origin`, `New Task ID`, `Suggested task-type`, `Scope`, and `Reason / Why deferred`. Sources include: out-of-scope discoveries that the executor consciously chose not to fold into this run, verifier concerns the executor declined to fix in-place, scope-boundary items from the approved plan that turned out to need their own ticket, and any unresolved `## 1. Clarification Items` row carried over from the approved plan (`Status` ∈ `{open, answered}` at approval time). An empty section is acceptable but only when expressed as the single line `- 후속 작업 없음.` — silence is treated as a contract violation. Rows with `Auto-spawn? = yes` will be materialised by `scripts/okstra-spawn-followups.py` in Phase 7; rows with `Auto-spawn? = no` MUST also appear in `Section 3. Recommended Next Steps` so the user knows to act manually.
36
+ - **Follow-up tasks (Section 4 of the final report)**: every item discovered during this run that was *not* delivered MUST appear in the final report's `## 4. Follow-up Tasks` table with a concrete `Origin`, `New Task ID`, `Suggested task-type`, `Scope`, and `Reason / Why deferred`. Sources include: out-of-scope discoveries that the executor consciously chose not to fold into this run, verifier concerns the executor declined to fix in-place, scope-boundary items from the approved plan that turned out to need their own ticket, and any unresolved `## 1. Clarification Items` row carried over from the approved plan (`Status` ∈ `{open, answered}` at approval time). An empty section is acceptable but only when expressed as the single line `- No follow-up tasks.` — silence is treated as a contract violation. Rows with `Auto-spawn? = yes` will be materialised by `scripts/okstra-spawn-followups.py` in Phase 7; rows with `Auto-spawn? = no` MUST also appear in `Section 3. Recommended Next Steps` so the user knows to act manually.
36
37
 
37
38
  ## Self-review pass before finalising the report (`Claude lead` runs this; do not delegate to a generic subagent)
38
39
 
@@ -52,6 +53,7 @@ are collected and convergence finished. Phase 1-5 do not need it.
52
53
  ## Lead post-stage persistence (BLOCKING — runs after the Executor emits `### Stage Carry Evidence`)
53
54
 
54
55
  - Parse the executor's `### Stage Carry Evidence` JSON block. If absent or unparsable, end with status `contract-violated` and route to a follow-up `error-analysis`.
56
+ - The `### Stage Carry Evidence` JSON may include `designPrepEvidence[]`. Emit a row only when this stage produced concrete evidence that refines an effective PREP item: `itemId`, the injected `assessmentFingerprint`, `resolution`, and non-empty `evidence[]` are required; `overrides` is optional and only records observed, non-authoritative refinements. Carry evidence never represents user approval. Downstream resolution accepts it only from transitive dependency stages with the matching fingerprint.
55
57
  - For this run's single stage: write its JSON verbatim to `runs/<impl-task-key>/carry/stage-<N>.json`. Refuse to overwrite an existing file (one stage = one sidecar; re-runs are out of scope for this version).
56
58
  - For this run's single stage: append a `status:"done"` row to `runs/<plan-task-key>/consumers.jsonl` with `completed_at`, `carry_path`, `report_path` (this run's final-report path relative to the run root), and the SHA of HEAD. Append it with `okstra_ctl.consumers.append_consumer` (NOT a raw filesystem write) — that call honours the consumers lock AND releases this stage's worktree-registry occupancy, so later runs stop seeing a finished stage as a concurrent run. `report_path` lets `final-verification` cite each stage's originating report when assembling its Source Implementation Report list.
57
59
  - The verifier round, Phase 5.5 convergence, and this Phase 6 report run **once per run** over this stage's diff — NOT per step.
@@ -34,12 +34,17 @@ until Phase 5 ends, then drop from active context for Phase 6/7.
34
34
  - Order of operations per plan step: (1) write/extend the test that captures the step's acceptance criterion and confirm it fails for the right reason, (2) implement the minimum change to make it pass, (3) commit the test and its implementation together in a single commit (`feat|fix(<scope>): ...`) — do NOT commit the failing test separately, (4) refactor without changing behaviour and commit separately if any cleanup is made (`refactor(<scope>): ...`). The failing-then-passing transition is preserved as `TDD evidence` in the final report (failing output captured before the merged commit, passing output after), not as two separate commits.
35
35
  - Doc-only / config-only / pure-rename steps that have no observable runtime behaviour are exempt from the failing-test requirement, but the executor MUST cite the exemption per step in the final report (`TDD exemption: <reason>`).
36
36
  - When the touched area has no existing test harness, the executor MUST stand up the minimum harness needed to host one regression test for this run rather than skipping TDD entirely. Record the harness-bootstrap step as an `Out-of-plan edit` if it is not in the plan.
37
- - **DB / IO / SQL changes require real execution — mock-only is NOT validation evidence:** when this run's diff touches DB/IO/SQL (ORM / query-builder code — sequelize / typeorm / prisma / knex / raw SQL — `*.repository.*`, model/entity files, `migrations/**`, `*.sql`, or any changed query string), a mocked unit test cannot observe the SQL the query builder actually emits (observed failure class: `_implementation-verifier.md` §"DB / IO / SQL change — real-execution gate"). The executor MUST run the change against a real (or faithful-replica) datastore — the `db-test` validation step (plan `validation` db step, else `project.json.qaCommands.db-test`), targeting a **local / replica** DB — and cite its exact command + exit code in the final report's `Validation evidence`. If no real DB / `db-test` command is reachable, do NOT claim the change verified: label the DB portion `정적 분석상 …, 미검증(실행 안 함)` in the report, surface it in the routing recommendation, and never downplay the real run as "too heavy". `git push` stays forbidden (universal list); the unverified DB state is carried forward so `final-verification` cannot accept it and `release-handoff` cannot push.
37
+ - **DB / IO / SQL changes require real execution — mock-only is NOT validation evidence:** when this run's diff touches DB/IO/SQL (ORM / query-builder code — sequelize / typeorm / prisma / knex / raw SQL — `*.repository.*`, model/entity files, `migrations/**`, `*.sql`, or any changed query string), a mocked unit test cannot observe the SQL the query builder actually emits (observed failure class: `_implementation-verifier.md` §"DB / IO / SQL change — real-execution gate"). The executor MUST run the change against a real (or faithful-replica) datastore — the `db-test` validation step (plan `validation` db step, else `project.json.qaCommands.db-test`), targeting a **local / replica** DB — and cite its exact command + exit code in the final report's `Validation evidence`. If no real DB / `db-test` command is reachable, do NOT claim the change verified: label the DB portion `static-analysis only …, unverified (not executed)` in the report, surface it in the routing recommendation, and never downplay the real run as "too heavy". `git push` stays forbidden (universal list); the unverified DB state is carried forward so `final-verification` cannot accept it and `release-handoff` cannot push.
38
38
  - **Real-IO test isolation (BLOCKING).** A test that exercises a **real** datastore, HTTP endpoint, external service, message queue, or filesystem — a live DB connection / DSN, a real `fetch` / `axios` / `http` request, an actual S3 / queue client, anything the project's normal CI test suite cannot run because that backend is absent — MUST be written under the task's qa scripts directory `<task_root>/qa/scripts/` (`<TASK_QA_PATH>/scripts`; the `qa/` root itself holds only data sidecars — the Tier 3 conformance manifest and `result-*.json`). It MUST NOT be written into the project source test tree — `src/**`, `test/**`, `tests/**`, `**/__test__/**`, `**/__tests__/**`, `*.spec.*`, `*.test.*`, or anywhere the project's lint/test globs collect. Two reasons: (a) the project's CI / normal suite has no real DB or network, so a real-IO test placed in source silently breaks the pipeline; (b) it is an okstra verification artifact, and the artifact-home rule confines okstra outputs to `.okstra/`. **The dividing line is the IO, not the intent:** a unit test that stubs/spies only *injected collaborators* (mock — no real socket, no real DB handle) is a TDD red-green artifact and stays in source; the moment a test opens a real connection or makes a real network call it belongs in qa. A stage's real-IO requirement check is a Tier 3 conformance script under `<task_root>/qa/scripts/` (declared via the implementation-planning conformance entry) — never smuggle real IO into a `*.spec.*` in source to make it run "as a unit test". The `db-test` real-execution gate above is satisfied by the conformance/db-test path against the replica, NOT by adding a live-DB `*.spec.*` to the project suite. **Author qa specs with the project's own test framework — never hand-roll `describe`/`it`/`expect`.** When the project ships a test runner as a devDependency (jest / vitest / pytest …), the qa spec uses it, invoked with the project config plus a discovery override pointing at the qa scripts dir (jest: `npx jest --config <project jest config> --roots <task_root>/qa/scripts --runInBand <spec-name>`) — the project config keeps module aliases resolving while the default sweep never collects the file; never widen the project's own test config to include qa paths. For TypeScript qa specs also write `<task_root>/qa/scripts/tsconfig.json` (`extends` the project tsconfig, adds the runner's `types` entry, `"include": ["**/*.ts"]`) so editors resolve path aliases and test globals — it is a qa artifact like the rest (untracked). **These qa artifacts stay untracked — never commit them.** `.okstra/**` is gitignored (the artifact-home rule); conformance scripts and their results are *executed* and recorded in the carry sidecar / verifier result, never written into git history. A committed `.okstra/qa` file is a stage-branch defect that leaks okstra internals into the eventual PR (see the `git add` rules below).
39
39
  - re-read the approved plan end-to-end and parse the `## 5.5 Stage Map`. Read the **Stage** injected in the launch prompt (`Stage for this implementation run`): the single stage number this run owns. The runtime already selected and reserved this stage (one run = one stage) — do NOT recompute the start stage from `consumers.jsonl`.
40
40
  - load every `runs/<plan-key>/carry/stage-<i>.json` for `i ∈ depends-on(this stage)` and inject them into the executor's working context as "runtime carry-in". For a `depends-on (none)` stage, no sidecar load — task-brief only.
41
41
  - this stage's `depends-on` are all already `status:done`. Its file list, step order, Stage Validation commands, Stage Exit Contract, and rollback path are the authoritative scope.
42
42
  - **Clarification answers carried in (read before the first edit):** when `instruction-set/clarification-response.md` exists, it carries the user's answers to the approved plan's `## 1. Clarification Items` rows (the planning HTML form's `# Attached User Responses`). Treat each answer as an authoritative refinement of the plan's scope for the matching row; an answer that contradicts or expands the approved scope beyond the plan is a re-plan trigger (route to a new `implementation-planning` run), not a silent in-run change. **CLI executor (codex/antigravity):** that file sits outside the CLI sandbox, so the lead MUST transcribe its body into the dispatched executor prompt at dispatch time (same rule as the preflight / stage-discipline transcription above) — a path reference never reaches the CLI process.
43
+ - **Effective design preparation (runtime-resolved after stage selection):**
44
+
45
+ {{DESIGN_PREP_CONTEXT}}
46
+
47
+ Within the approved stage, effective user overrides are authoritative. AI proposals remain working assumptions, not user decisions. Obey every injected guardrail and `reviewAt` point. A conflict with approved scope, the selected option, or Stage Map structure stops execution and routes to a new `implementation-planning` run. Do not reread planning PREP sidecars or independently merge stale or duplicate inputs; the injected context is the stage-scoped decision.
43
48
  - inspect the current state of every file the plan names; if any file has changed materially since the plan was written, stop and route to a new `implementation-planning` run instead of editing speculatively
44
49
  - "materially changed" means: the function, class, section, or behaviour the plan targets has been edited, renamed, moved, removed, or otherwise altered in a way that invalidates the plan's reasoning. Cosmetic edits (whitespace, comment-only changes, unrelated function modifications elsewhere in the same file) do NOT trigger a re-plan; cite the diff (`git log --oneline <plan-created-at>..HEAD -- <file>`) in the final report and proceed.
45
50
  - distinguish the two file-scope rules (they are not in conflict):
@@ -12,7 +12,7 @@ at Phase 5, BEFORE constructing the verifier worker dispatch prompts.
12
12
  - **Verifier dispatch labelling.** The core functional role label is `<provider>-verifier` (here, and identically in `final-verification`). Provider, role, and model identity are owned by `prompts/lead/okstra-lead-contract.md` "Model assignments"; the selected runtime adapter owns provider-native dispatch-label mapping (including any `name` / `**Pane role:**` fields) and token-attribution wiring under its "Semantic operation mapping".
13
13
  - The verifier slots are `Claude verifier` and `Codex verifier`, plus `Antigravity verifier` **only when `antigravity` is in the resolved `--workers` roster**. Every verifier in the resolved roster is dispatched regardless of which provider holds the executor role; the executor's own provider is run *separately* as a verifier (a fresh CLI session with no shared context) so that no verdict is produced from the same session that wrote the diff. Verifiers MUST NOT call Edit, Write, or any Bash command that mutates files outside the run's artifact directories. If a verifier wants a fix, it records the recommendation in its worker result; it does not apply the fix itself.
14
14
  - Session isolation — not model-variant divergence — is the primary self-review safeguard: each verifier is a separate CLI invocation with its own context window, so reusing the same model variant for executor and same-provider verifier is acceptable. Different model variants (e.g. executor=opus / Claude verifier=sonnet) remain recommended when available.
15
- - Phase-specific model defaults override the shared defaults: `Claude verifier`=`opus`, `Codex verifier`=`gpt-5.6`, `Antigravity verifier`=`auto` (only when present in the roster). The `Executor`'s model is taken from the provider-specific worker model corresponding to `--executor`: claude→`--claude-model` (default `opus`), codex→`--codex-model` (default `gpt-5.6`), antigravity→`--antigravity-model` (default `auto`).
15
+ - Phase-specific model defaults override the shared defaults: `Claude verifier`=`opus`, `Codex verifier`=`gpt-5.6-sol`, `Antigravity verifier`=`auto` (only when present in the roster). The `Executor`'s model is taken from the provider-specific worker model corresponding to `--executor`: claude→`--claude-model` (default `opus`), codex→`--codex-model` (default `gpt-5.6-sol`), antigravity→`--antigravity-model` (default `auto`).
16
16
  - Verifiers read from the SAME working tree path the Executor used so they observe the exact diff the Executor produced. Verifiers remain strictly read-only there.
17
17
 
18
18
  ## Verifier QA duties (independent re-run mandate)
@@ -42,7 +42,7 @@ Verifier obtains the QA command set from exactly two declared sources, in order
42
42
 
43
43
  Tier 1 commands run verbatim first. Then every Tier 2 entry runs once. Then the Tier 3 stage conformance script (below) runs once. Each command runs in the worktree cwd, and is recorded in the worker result with its exact command line, exit code, and the tail of stdout/stderr. Substituting or paraphrasing a Tier 1 command is forbidden (see Verifier-specific forbidden actions below).
44
44
 
45
- ### Tier 3 — stage conformance scripts (요구사항 부합 검증)
45
+ ### Tier 3 — stage conformance scripts (requirement-conformance verification)
46
46
 
47
47
  Tier 3 proves the stage actually *meets the upper-level requirement* it was scoped to, by running a declared conformance script against the running state — Tiers 1·2 only prove the diff *builds and passes*. This is a real gate: its result sidecar is the input the `validate-run.py` Tier 3 gate reads, so a missing or non-PASS result BLOCKS acceptance.
48
48
 
@@ -109,7 +109,7 @@ A mocked unit test cannot observe the SQL a query builder actually emits — `co
109
109
  - **Trigger.** Fires when `git diff <base>...HEAD` touches DB/IO/SQL: ORM / query-builder code (sequelize / typeorm / prisma / knex / raw SQL), `*.repository.*`, model/entity files, `migrations/**`, `*.sql`, or any changed query string.
110
110
  - **Requirement when fired.** The verifier MUST reproduce a real-DB execution: run the `db-test` tier (Tier 1 = plan `validation` db step; else Tier 2 = `project.json.qaCommands.db-test`) against a **local / replica** datastore (same engine + schema — never shared / staging / prod, consistent with the verifier forbidden-actions list) and record its exact command + exit code. A mock, an in-memory shim that does not parse real SQL, or static reasoning does NOT satisfy this.
111
111
  - **No `db-test` command available → blocking, not a passive skip.** If neither tier declares a `db-test` command, the verifier records the blocking finding `db-test not configured — DB change unverified (mock-only)` and sets the verdict to `FAIL`; it MUST NOT emit only the passive `qa-command not configured` note and pass. Recommended fix: declare a `db-test` command in `project.json.qaCommands` or the plan's validation set.
112
- - **Mock-only evidence → unverified.** If the diff's only DB coverage is mocked, the verifier labels the DB portion `정적 분석상 …, 미검증(실행 안 함)` (never `검증됨`), records it as a blocking finding, and sets `FAIL`. Never downplay the real run as "too heavy / static proof suffices".
112
+ - **Mock-only evidence → unverified.** If the diff's only DB coverage is mocked, the verifier labels the DB portion `static-analysis only …, unverified (not executed)` (never `verified`), records it as a blocking finding, and sets `FAIL`. Never downplay the real run as "too heavy / static proof suffices".
113
113
  - **Surface it at every layer.** The finding is copied verbatim into the verifier result and MUST survive into the final report's `## 6.` and Verdict Card, so the user sees the DB-unverified state continuously — it is the load-bearing reason a downstream `final-verification` cannot reach `accepted` and `release-handoff` cannot push.
114
114
 
115
115
  ## All-verifier-failure policy
@@ -12,7 +12,7 @@
12
12
  - Apply the shared reporter-confirmation precondition exactly as written. In this phase, unresolved `intent-check:` / `conversion-block:` rows use `Blocks=next-phase`; any unconfirmed inference may be used as a labelled hypothesis only.
13
13
  - the reporter's symptom description in `Source Material` is the ground truth for what to reproduce. Do not paraphrase it when stating the symptom in the report; quote it.
14
14
  - read `Related Task Graph` before forming root-cause candidates. `depends-on`, `blocks` / `blocked-by`, parent/child, follow-up, and split edges define upstream/downstream boundaries for the symptom: identify whether this task's failure is caused by, blocks, or merely relates to another task before merging causes.
15
- - any `intent-inference` augmentation that re-characterises the symptom (e.g. classifying "가끔 안 됨" as "intermittent failure on a specific code path") is a **hypothesis**, not a confirmed symptom. If `[CONFIRMED …]` appears on the matching `intent-check:` row, treat that confirmation as the symptom. Otherwise follow the precondition's `skipped` branch above and keep the inference labelled as a hypothesis in the root-cause analysis.
15
+ - any `intent-inference` augmentation that re-characterises the symptom (e.g. classifying a vague reporter phrase like "it sometimes doesn't work" as "intermittent failure on a specific code path") is a **hypothesis**, not a confirmed symptom. If `[CONFIRMED …]` appears on the matching `intent-check:` row, treat that confirmation as the symptom. Otherwise follow the precondition's `skipped` branch above and keep the inference labelled as a hypothesis in the root-cause analysis.
16
16
  - `conversion-block:` rows mean the brief could not map a reporter statement to project vocabulary; never invent the missing mapping in this phase.
17
17
  - Diagnosis loop:
18
18
  - **Symptom lock:** state the reporter's symptom verbatim, then translate it into one observable failure condition. If no observable condition can be derived from the brief, record that gap as the first blocker instead of guessing.
@@ -34,7 +34,7 @@
34
34
  - practical next diagnostic steps
35
35
  - Clarification request policy (phase-specific addenda — shared policy is in `_common-contract.md`):
36
36
  - if any blocking uncertainty remains at the time of writing the final report, populate `## 1. Clarification Items` in `final-report-template.md` (a single unified table; `Blocks=next-phase` for items the next run cannot start without)
37
- - prefer plain Korean over abbreviations (e.g. write "초당 평균 요청 수" instead of "QPS", "재현 절차" instead of "repro")
37
+ - prefer plain Korean over abbreviations (e.g. write out the full Korean phrase for "requests per second" instead of "QPS", and for "reproduction steps" instead of "repro")
38
38
  {{INCLUDE:_clarification-recommendation.md}}
39
39
  - **Codebase-first ambiguity resolution (defect rule)**: any ambiguity about repro, file behavior, or symbol semantics that can be answered by `Read` / `Grep` / log inspection MUST be resolved that way and recorded with file:line (or log-line) evidence. Writing a clarification row for something the codebase or shipped logs already answer is a defect of this phase.
40
40
  - **Evidence note required inside `Statement`**: every clarification row includes `Evidence checked: <path:line>` or `Evidence checked: none — <reporter-only reason>` in the `Statement` cell. `none` is allowed ONLY when the row's nature is "only the reporter can answer this" (reporter-side data, business priority, environment they observed). A row with `none` that *could* have been answered by code or logs is a defect.
@@ -26,7 +26,7 @@
26
26
  - Pre-verification entry gate (resolved & enforced by `okstra render-bundle` prep — the lead does NOT recompute it):
27
27
  - the verification target (scope / worktree / base / stages / source reports / diff stat) is injected as the `VERIFICATION_TARGET` block. The lead MUST treat it as authoritative and MUST NOT re-pick a target from the brief.
28
28
  - **whole-task scope** (`--stage auto`, default): prep has already verified every Stage Map stage is `status:done` in `consumers.jsonl`, every done stage's `head_commit` is an ancestor of the task worktree HEAD (all stage branches merged), and the worktree is clean outside `.okstra/`. If any check failed the run never started (PrepareError); a started whole-task run is therefore a fully-merged, clean target.
29
- - **whole-task 은 read-only 가 아니라 mutating phase 다.** whole-task 모드는 진입 시 아직 task 브랜치에 머지되지 않은 done stage 들을 `--no-ff` 로 자동 머지해 통합 커밋을 생성하고, 이어서 정리 가능한 stage worktree·registry stage-key·stage 브랜치를 제거한다. 머지 충돌이 발생하면 충돌 파일을 보고하고 중단한다(사용자가 수동 해소 후 재시도). 미커밋 변경이 남은 stage worktree 는 보존한다. 따라서 위 entry gate 가 말하는 "fully-merged, clean target" 은 이 자동 통합 단계가 끝난 뒤의 상태이며, whole-task final-verification 은 통합 커밋을 만드는 mutating phase 로 취급해야 한다.
29
+ - **whole-task is a mutating phase, not a read-only one.** On entry, whole-task mode auto-merges (with `--no-ff`) the done stages not yet merged into the task branch to create an integration commit, then removes the cleanupable stage worktrees, registry stage-keys, and stage branches. If a merge conflict occurs it reports the conflicting files and aborts (the user resolves them manually and retries). A stage worktree with uncommitted changes remaining is preserved. Therefore the "fully-merged, clean target" the entry gate above refers to is the state after this auto-integration step completes, and whole-task final-verification must be treated as a mutating phase that creates the integration commit.
30
30
  - **single-stage scope** (`--stage N`): prep verified stage N is `status:done` and its isolated stage worktree exists and is clean. Other stages' state is irrelevant. A single-stage run is a partial verification: it MUST NOT recommend plain `release-handoff`, but MAY recommend `release-handoff(stage-group)` when the verdict is `accepted` — the stage becomes PR-eligible for a stage-group handoff.
31
31
  - the lead still captures `git status --short` from the injected worktree to confirm the analysis ran against the delivered work-tree state; an unexpected divergence (dirty tree outside `.okstra/`, missing worktree) is a `tool-failure`, not a silent proceed.
32
32
  - Required deliverable shape (final report, in addition to the standard sections):
@@ -39,6 +39,8 @@
39
39
  - **Two-tier command lookup (shared with `implementation`):** when this phase performs its own independent re-validation, the command source is exactly the same two tiers `implementation` verifiers use — Tier 1 is the originating task brief / approved plan's `validation` set, Tier 2 is `<PROJECT_ROOT>/.okstra/project.json` under `qaCommands`. Auto-detecting tools from manifest files is forbidden; missing tiers are recorded as `qa-command not configured: <category>` rather than guessed. The `cmd` deny-list (`--fix`, `--write`, ` -w`, ` -u`, `--snapshot-update`, `INSTA_UPDATE=<not-no>`, `cargo update`, `npm install` without `ci`, etc.) is enforced identically. NOTE: runtime fail-fast validation (`okstra_ctl.qa_commands.validate_qa_commands`) only fires at `--task-type implementation` run-prep, so this phase MUST self-check each `qaCommands` entry against the deny-list before executing it — if a denied token is present, skip the command and record it as a `Read-only command log` line `qa-command rejected (denied token: <token>): <label>`.
40
40
  - **Tier 3 — stage conformance scripts (whole-task union):** because this phase verifies the **integrated, merged** state, it re-runs conformance against that state rather than per-stage. Read the task-level manifest `<task_root>/qa/conformance-manifest.json` (the directory is the `TASK_QA_PATH` token) and, in **whole-task scope**, run the `runCommand` of **every** `entries[]` item against the merged worktree, refreshing each `<task_root>/qa/result-<stageKey>.json` (`{ "stageKey", "overall": "PASS"|"FAIL"|"MISSING", "ranAt", "requirements" }`). In **single-stage scope**, run only the entry whose `stageKey` matches the verified stage. An entry carrying an `exemption` or user `waiver` is NOT executed — record the skip and reason; a `waiver` becomes a `conditional-accept` condition surfaced in the section 7 Verdict (conformance left unverified by user acknowledgement). Each `runCommand` runs in the worktree cwd with `qaEnv` env (replica DB DSN / app base URL / env file) — **replica / test environment only**, never shared / staging / prod, and the same source/lockfile mutation deny-list applies (a conformance script MAY mutate only its `qaEnv` replica datastore). Interpret each result from the exit code + stdout `QA-RESULT: PASS|FAIL` (last wins) and `REQ <id>: PASS|FAIL: <reason>` lines; no `QA-RESULT` marker → `MISSING`. Any entry whose result is not `PASS` (including `MISSING` or a never-run/missing sidecar) is an **Acceptance Blocker** (`major`+). This is the same gate the `validate-run.py` Tier 3 check enforces on the result sidecars.
41
41
  - **Manual user test results**: take each item from the source implementation report's §5.7.9 Manual User Test (Draft), execute the ones reproducible in this environment (e.g. `/okstra-container-build`, which runs `okstra container up`, then the documented steps), and record `result` (`pass` / `fail` / `blocked`) + observed value in §5.8.7 (data field `finalVerification.manualUserTest`). Steps that need human-only interaction this run cannot perform are recorded as `blocked` with the reason (handed to the user), never silently skipped. A failed manual test is an Acceptance Blocker. If the draft was an exemption (`applicable=false`), reaffirm the reason in one line (`applicable=false` + `exemptionReaffirm`).
42
+ For manual testing, read only the source implementation report's `implementation.manualUserTest`. Do not execute planning `designPreparation` / `manual-user-test` PREP items directly. The implementation report has already reconciled the approved seed with the actual diff. Record reproducible results; human-only or unavailable environments become `result: blocked` with the exact reason.
43
+ - **Non-manual design-preparation handoff**: read only source implementation report `missingInformation` rows whose `source` starts with `design-prep:`. Record `ifStillOpen: block` as an acceptance blocker and `ifStillOpen: follow-up` as residual risk. This phase does not read planning PREP sidecars directly, merge their inputs, or mutate the planning snapshot.
42
44
  - **Could-not-verify roll-up (§5.8.9)**: the template mechanically aggregates every not-confirmed check into one scannable list — `gap` requirement-coverage rows, `not-configured` / `env-unavailable` / `rejected` command rows, and `blocked` manual tests. You do not hand-author it, but you MUST give those rows their honest status so nothing unverified hides across sections: a check silently recorded as `executed`/`covered` will not surface in the roll-up. This is okstra's answer to "say what could not be verified this run."
43
45
  - **Routing recommendation**: the next safe phase — one of `release-handoff`, `done`, `error-analysis`, `implementation-planning` — tied to the verdict and blocker list. `release-handoff` is allowed ONLY when the Verdict Token is `accepted`. `release-handoff` is additionally allowed ONLY when the verification scope (the `Verification scope:` line of the injected `VERIFICATION_TARGET` block, recorded as the report's `verificationScope` field) is `whole-task`; a `single-stage` accepted run routes to `release-handoff(stage-group)` (or `implementation` / `done`); plain `release-handoff` remains whole-task-only. Enforcement: `validators/validate-run.py` rejects a `single-stage` report whose routing cites plain `release-handoff`.
44
46
  - **Verified-row recording** (single-stage scope only): when the Verdict Token is `accepted`, the lead MUST run `okstra handoff record-verified --plan-run-root <plan-run-root> --stage <N> --report-path <final-report.md path> --data-json <final-report data.json path>` and quote the command + exit code in the report. The helper re-validates taskType/scope/verdict from data.json, so a non-accepted or whole-task report is rejected at the tool layer.
@@ -38,12 +38,22 @@
38
38
  - feasible plan options
39
39
  - dependency and risk visibility
40
40
  - recommended execution order
41
+ - Implementation Design Preparation (`implementation-design-prep-v1`, BLOCKING):
42
+ - **Detector SSOT:** the planner MUST run the V1 detector defined by `scripts/okstra_ctl/design_surfaces.py` (`detect_design_surfaces()` over the detector's `RULES`) and MUST NOT invent or copy a second keyword list into the plan or prompt. **Enforced:** `validators/validate-run.py` `_validate_detector_coverage` reruns that detector and compares every `(stage, kind)` plus its trigger evidence.
43
+ - **Exactly-once coverage:** for every detector-produced `(stage, kind)`, the planner MUST write exactly one `designSurfaceCoverage` row on that stage. **Enforced:** `validators/validate-run.py` `_validate_detector_coverage` rejects missing, duplicate, extra-detector-kind, or evidence-mismatched rows; `schemas/final-report-v1.0.schema.json` `$defs.DesignSurfaceCoverage` enforces the row shape.
44
+ - **Disposition:** a row MUST use `inline-contract` only when the stage already states the kind-specific minimum implementation contract; otherwise it MUST use `prep-item` and reference one or more `designPreparation.items`. `not-applicable` is legal only with a concrete rationale consistent with the stage action. **Enforced:** `schemas/final-report-v1.0.schema.json` `$defs.DesignSurfaceCoverage` enforces the disposition-specific fields, and `prompts/lead/plan-body-verification.md` `P-Prep-S<stage>-<kind>` verifies semantic sufficiency.
45
+ - **AI-prepared proposal:** every referenced PREP item MUST record `kind`, `stageRefs`, `need`, evidence-cited `knownFacts`, `openQuestions`, a concrete evidence-backed `aiProposal` (`summary`, `details`, `assumptions`, `evidence`, `confidence`), `humanConfirmation`, explicit `status`, and the safest reversible default available. **Enforced:** `schemas/final-report-v1.0.schema.json` `$defs.DesignPrepItem` / `$defs.DesignPrepProposal` enforce required fields, `validators/validate-run.py` `_validate_prep_references` enforces the bidirectional stage/kind link, and `prompts/lead/plan-body-verification.md` rejects empty or non-implementable proposals.
46
+ - **Status choice:** prefer `provisional` with a `workingAssumption`, concrete `guardrails`, `reviewAt`, `ifStillOpen`, and canonical `requestPath`; use `blocked` only for business policy, external authority, a destructive migration decision, or the absence of any safe reversible assumption. **Enforced:** `schemas/final-report-v1.0.schema.json` `$defs.DesignPrepItem` enforces state-specific fields, `validators/validate-run.py` `_validate_design_prep_states` enforces confirmation/request invariants, and `prompts/lead/plan-body-verification.md` judges whether the disposition is justified. A declared `blocked` status does not by itself fail plan-body verification.
47
+ - **Planner-only test surface:** add `manual-user-test` only when a test prerequisite changes the implementation interface or acceptance contract; the V1 detector never emits it. **Enforced:** `validators/validate-run.py` `_validate_detector_coverage` rejects detector-produced `manual-user-test`, `schemas/final-report-v1.0.schema.json` permits its planner-authored shape, and `prompts/lead/plan-body-verification.md` verifies the stage-action rationale.
48
+ - **Trivial task:** when the detector returns no surfaces and no interface/acceptance-changing manual test input exists, `designPreparation` MUST use `mode: no-design-inputs`, an empty `items` array, and a concrete reason tied to the plan. **Enforced:** `schemas/final-report-v1.0.schema.json` `$defs.DesignPreparation` requires the reason and empty array for that mode; `validators/validate-run.py` `_validate_design_prep_contract` validates the marked V1 payload.
41
49
  - Approval gate (phase-specific addendum to shared authority rule):
42
50
  - The YAML frontmatter `approved: true|false` field is the only authorised approval gate. report-writer always emits `approved: false`. The user clears it either by (a) editing the frontmatter line to `approved: true` directly, or (b) invoking the next phase with `--approve` so the CLI flips the frontmatter on the user's behalf. `okstra_ctl.run._validate_approved_plan` reads this field and refuses entry until it is `true`.
43
51
  - Cross-verification mode:
44
52
  - Phase 5.5 finding convergence runs in **adversarial mode** for this phase (`convergence.adversarial=true`). Verifiers actively try to refute each worker finding (requirement gap / risk / option) by re-inspecting its cited evidence; the burden of proof sits on the claim. See `prompts/lead/convergence.md` §"Adversarial Verification Mode".
45
53
  - §5.5.9 plan-body verification runs with an **adversarial posture** (`prompts/lead/plan-body-verification.md` §"Adversarial plan-body posture"): verifiers open and confirm every cited path / command and put the burden of proof on the plan. The gate threshold is majority-based for kinds `b`/`c`/`e`, but a single `DISAGREE` blocks on its own for the concrete, safety-critical kinds `a` (path/symbol mismatch) / `d` (rollback order) — and `f` on `P-Req-*` items. A majority also needs ≥2 participating votes, so a lone dissent whose peer returned a non-result does not block on a majority-gated kind (see that contract's §"Adversarial plan-body posture").
46
54
  - **Incremental re-verification scope (clarification re-runs):** when the lead's `okstra incremental-scope` decision is `mode == "incremental"` (procedure in `prompts/launch.template.md` §"Clarification Response Carried In"), workers re-analyze ONLY the stages listed in `reverify_stages` (the downstream closure of the impacted stages). Workers MUST NOT re-open, re-score, or re-judge any stage in `carry_stages` — those stages' prior plan-item verdicts are carried forward verbatim, and a worker never overwrites a carried verdict with its own judgement. When the decision is `mode == "full"` (the default), every stage is re-analyzed as usual.
55
+ - **Single incremental-scope decision:** the lead calls `okstra incremental-scope` exactly once for the re-run, passing answered-clarification stage impacts through `--impacted` and changed design-preparation IDs through `--prep-items`; the CLI unions their `stageRefs` before applying the existing dependency closure and cutoff. An answer that changes the selected option, Stage Map, or recommended approach is not a local impact: pass both CSVs empty so the same call returns `mode == "full"`. Unknown PREP IDs or invalid `stageRefs` also return an explicit full decision instead of being guessed.
56
+ - **Stage-aware carry:** for an incremental decision, pass its `carry_stages` and `reverify_stages` CSVs unchanged to `okstra incremental-carry`. The helper carries the prior whole stage rows and their owned PREP / `P-Prep-*` artifacts; overlap, cross-scope ownership, scope leaks, or canonical conflicts return `CarryError`. On that error, discard the partial merge and run full re-verification.
47
57
  {{INCLUDE:_coverage-critic.md}}
48
58
  - Non-goals:
49
59
  - code-level micro-optimization unless it changes the implementation approach
@@ -56,8 +66,8 @@
56
66
  {{INCLUDE:_clarification-recommendation.md}}
57
67
  - **Evidence note required inside `Statement`**: every clarification row includes `Evidence checked: <path:line>` or `Evidence checked: none — <human-only reason>` in the `Statement` cell. `none` is allowed ONLY when the row's nature is "only a human can answer this" (reporter intent, business priority, organisational decision). A row with `none` that *could* have been answered by the codebase is a defect of this phase, restated from the pre-planning rule above.
58
68
  - Section heading contract (BLOCKING — validator scans for these literal English substrings):
59
- - The final report MUST include section headings containing each of the following exact strings: `Option Candidates`, `Trade-off`, `Recommended Option`, `Stage Map`, `Stage Exit Contract`, `Stage Validation`, `Dependency`, `Cross-Project Dependencies`, `Decision Drafts`, `Validation Checklist`, `Rollback`, `Requirement Coverage`. (Approval is no longer a body section — it is the YAML frontmatter `approved` field.)
60
- - Korean translations are allowed in parentheses (e.g. `### Recommended Option (권장 옵션)`), but the English keyword must be present verbatim in the heading line.
69
+ - The final report MUST include section headings containing each of the following exact strings: `Option Candidates`, `Trade-off`, `Recommended Option`, `Stage Map`, `Stage Exit Contract`, `Stage Validation`, `Dependency`, `Cross-Project Dependencies`, `Decision Drafts`, `Validation Checklist`, `Rollback`, `Requirement Coverage`, `Implementation Design Preparation`. (Approval is no longer a body section — it is the YAML frontmatter `approved` field.) `validators/validate-run.py` enforces the planning headings it scans, while `schemas/final-report-v1.0.schema.json` plus `templates/reports/final-report.template.md` enforce and render the design-preparation heading from its required data block.
70
+ - Korean translations are allowed in parentheses (e.g. `### Recommended Option (Korean gloss)`), but the English keyword must be present verbatim in the heading line.
61
71
  - The shape and ordering follow `final-report-template.md` sections 5.4 (`Implementation Plan Deliverables`) + 5.5 (`Stage Map`). `validators/validate-run.py` substring-matches the raw report text, so a Korean-only heading fails the gate — the cause of repeated observed failures.
62
72
  - Beyond substring matching, when the Plan Body Verification gate result is `passed` / `passed-with-dissent`, `validators/validate-run.py` runs the **structural** Stage Map validator (`validators/validate-implementation-plan-stages.py`) at the planning boundary — not deferred to the `implementation` entry gate. It enforces: the exact `## 5.5 Stage Map` heading, each `## 5.5.<i> Stage <i>:` section with its four required subsections, the per-stage effective step count (≤8), the `depends-on` DAG, and the per-stage vertical-slice contract (S10). S10 scans for the literal in-section strings `Slice value:`, `Acceptance:`, the three `Test case (success):` / `Test case (boundary):` / `Test case (failure):` lines (S10d), and the Stepwise `action`-cell prefixes `RED:` / `GREEN:` (or a `TDD exemption:` line, which waives both the test-case lines and the RED/GREEN check) — keep these tokens verbatim for the same reason as the heading keywords above.
63
73
  - Required deliverable shape (final report, in addition to the standard sections):
@@ -72,7 +82,7 @@
72
82
  - **Keep the table at exactly 5 columns** — do NOT add a column. `validators/validate-implementation-plan-stages.py` parses `stage | title | depends-on | step-count | exit-contract-summary` and silently skips any row that is not exactly 5 cells, so a 6th column would drop every stage and bypass S2–S11.
73
83
  - **Multi-project plans only** (the plan's work spans more than one project — see the Project-boundary partition rule below): prefix each stage's `title` cell with a `[<project>]` tag (e.g. `[okstra] Add X`) so the project each stage belongs to is readable at a glance, and add exactly one line directly under the Stage Map table — `Cross-project parallelism: <which per-project stages run in parallel, which are sequenced, and the cross-project dependency that forces each sequencing>`. Single-project plans omit both the tag and the line.
74
84
  - **Per-stage slice declaration (mandatory lines, directly under the `## 5.5.<i> Stage <i>:` heading, before `### Carry-In`):**
75
- - `Slice value: <the one user-observable increment this stage delivers, end-to-end>` — describe WHAT starts working from the consumer's view (e.g. "X 를 조회하면 Y 가 반환된다"), NOT a layer name ("repository 추가"). Validator S10a rejects a missing/empty value.
85
+ - `Slice value: <the one user-observable increment this stage delivers, end-to-end>` — describe WHAT starts working from the consumer's view (e.g. "querying X returns Y"), NOT a layer name ("add a repository"). Validator S10a rejects a missing/empty value.
76
86
  - `Acceptance: <the observable pass condition or the exact command>` — the signal that proves the slice is done; normally the same test command that the `RED:` step below flips to PASS. Validator S10b rejects a missing/empty value.
77
87
  - **Test-case design (mandatory three lines unless the stage carries a `TDD exemption:`):** the plan, not the executor, decides which cases the stage's tests must cover, so a stage cannot ship with only one happy-path assertion. Declare all three:
78
88
  - `Test case (success): <input → expected, the command/test name>` — the happy path: a valid input that proves the slice works end-to-end.
@@ -81,9 +91,9 @@
81
91
  Validator S10d rejects a missing/empty line in any of the three categories (skipped only when a `TDD exemption:` line is present). The `RED:` step below must encode these cases, not a single assertion.
82
92
  - **Per-stage subsections** (`## 5.5.<i> Stage <i>: <title>` for each `i`), each containing the four required subsections:
83
93
  - `### Carry-In` — for `depends-on (none)`: task-brief only. Otherwise: each depended-on stage's static exit contract + runtime sidecar path `runs/<impl-key>/carry/stage-<i>.json` placeholder.
84
- - `### Stepwise Execution Order` — bite-sized table with `step | action | files | command | expected`. The `files` cell lists each touched path in full and `<PROJECT_ROOT>`-relative — never ellipsis-abbreviated (`…` / `...`), which does not resolve and is rejected by plan-body verification as a kind-b path mismatch. **Effective row count ≤ 8** (excluding header / divider / blank). Each step is one cohesive, self-contained change (시간 하한 없음; 함께 바뀌는 여러 파일을 포함할 수 있다); for code steps include actual code or diff sketch. **TDD ordering is MUST, not a preference:** the **first** effective step's `action` cell MUST start with the literal `RED:` and describe the failing test(s) that capture this stage's `Acceptance` **and the three declared `Test case (success|boundary|failure)` lines** (`expected` = FAIL) — the RED step encodes the case set, not a single happy-path assertion; at least one later `action` cell MUST start with the literal `GREEN:` and describe the minimal implementation that makes it pass (`expected` = PASS); an optional refactor step starts with `REFACTOR:`. **Exemption:** doc-only / config-only / pure-rename stages with no observable runtime behaviour may omit RED/GREEN by declaring one line `TDD exemption: <reason>` in the stage section (mirrors the executor's per-step exemption in `_implementation-executor.md`). Validator S10c enforces RED-first + GREEN **and** that the `RED:` step's `expected` reads FAIL / the `GREEN:` step's reads PASS; S10e rejects a `TDD exemption:` whose reason is not one of doc-only / config-only / pure-rename (both in `validators/validate-implementation-plan-stages.py`).
94
+ - `### Stepwise Execution Order` — bite-sized table with `step | action | files | command | expected`. The `files` cell lists each touched path in full and `<PROJECT_ROOT>`-relative — never ellipsis-abbreviated (`…` / `...`), which does not resolve and is rejected by plan-body verification as a kind-b path mismatch. **Effective row count ≤ 8** (excluding header / divider / blank). Each step is one cohesive, self-contained change (no lower time bound; it may span several files that change together); for code steps include actual code or diff sketch. **TDD ordering is MUST, not a preference:** the **first** effective step's `action` cell MUST start with the literal `RED:` and describe the failing test(s) that capture this stage's `Acceptance` **and the three declared `Test case (success|boundary|failure)` lines** (`expected` = FAIL) — the RED step encodes the case set, not a single happy-path assertion; at least one later `action` cell MUST start with the literal `GREEN:` and describe the minimal implementation that makes it pass (`expected` = PASS); an optional refactor step starts with `REFACTOR:`. **Exemption:** doc-only / config-only / pure-rename stages with no observable runtime behaviour may omit RED/GREEN by declaring one line `TDD exemption: <reason>` in the stage section (mirrors the executor's per-step exemption in `_implementation-executor.md`). Validator S10c enforces RED-first + GREEN **and** that the `RED:` step's `expected` reads FAIL / the `GREEN:` step's reads PASS; S10e rejects a `TDD exemption:` whose reason is not one of doc-only / config-only / pure-rename (both in `validators/validate-implementation-plan-stages.py`).
85
95
  - **Per-stage conformance declaration (mandatory one line, in the stage section — same placement freedom as `TDD exemption:`):** the stage MUST carry exactly one of:
86
- - `Conformance tests: stage-<N> — <task_root>/qa/scripts/stage-<N>.<ext> (requires=[db|io|http|external,...])` — a Tier3 verification script that proves this stage's upstream requirements (brief / requirements-discovery / error-analysis / improvement-discovery → this stage's `Acceptance`) hold against **real** DB rows, real endpoints, or the real external API — NOT mocks. When you emit this line you MUST also (a) write the script to `<task_root>/qa/scripts/stage-<N>.<ext>` and (b) add a matching entry to `<task_root>/qa/conformance-manifest.json` with fields `stageKey` (= `<task-id>-stage-<N>`), `script`, `runCommand`, `requirementIds`, `requires` (subset of `{db, io, http, external}`), `passContract`, `exemption: null`, `waiver: null`. The script's standard interface: a `main` that exits `0`=PASS / non-zero=FAIL, and whose stdout ends with `QA-RESULT: PASS|FAIL` followed by one `REQ <id>: PASS|FAIL: <근거>` line per requirement. When the verification body is a test spec, author it with the project's own test framework (devDependency) invoked via a discovery override at `<task_root>/qa/scripts/` (jest: `--config <project config> --roots <task_root>/qa/scripts`) — never hand-roll `describe`/`expect` and never widen the project's own test config; for TypeScript specs also write `<task_root>/qa/scripts/tsconfig.json` extending the project tsconfig with the runner's `types` entry so editors resolve the file.
96
+ - `Conformance tests: stage-<N> — <task_root>/qa/scripts/stage-<N>.<ext> (requires=[db|io|http|external,...])` — a Tier3 verification script that proves this stage's upstream requirements (brief / requirements-discovery / error-analysis / improvement-discovery → this stage's `Acceptance`) hold against **real** DB rows, real endpoints, or the real external API — NOT mocks. When you emit this line you MUST also (a) write the script to `<task_root>/qa/scripts/stage-<N>.<ext>` and (b) add a matching entry to `<task_root>/qa/conformance-manifest.json` with fields `stageKey` (= `<task-id>-stage-<N>`), `script`, `runCommand`, `requirementIds`, `requires` (subset of `{db, io, http, external}`), `passContract`, `exemption: null`, `waiver: null`. The script's standard interface: a `main` that exits `0`=PASS / non-zero=FAIL, and whose stdout ends with `QA-RESULT: PASS|FAIL` followed by one `REQ <id>: PASS|FAIL: <reason>` line per requirement. When the verification body is a test spec, author it with the project's own test framework (devDependency) invoked via a discovery override at `<task_root>/qa/scripts/` (jest: `--config <project config> --roots <task_root>/qa/scripts`) — never hand-roll `describe`/`expect` and never widen the project's own test config; for TypeScript specs also write `<task_root>/qa/scripts/tsconfig.json` extending the project tsconfig with the runner's `types` entry so editors resolve the file.
87
97
  - `Conformance exemption: <reason>` — only for stages that touch no db/io/http/external surface, or where unit tests fully cover the increment. (If the eventual `implementation` diff actually touches one of those surfaces, `validate-run.py`'s diff-surface cross-check is BLOCKING — an exemption cannot hide a real db/io/http/external change.)
88
98
  The manifest lives at the **task level** (`<task_root>/qa/`, path token `TASK_QA_PATH`) and is shared across planning → implementation → final-verification. Layout split: executable scripts (conformance + any real-IO test) live under `<task_root>/qa/scripts/`; data sidecars (`conformance-manifest.json`, `result-*.json`) stay at the `qa/` root. This declaration is enforced at four layers: `validators/validate-implementation-plan-stages.py` check **S11** forces every stage to carry one of the two lines; at the planning boundary `validators/validate-run.py` `_validate_planning_conformance_declared` fails when a stage that declared `Conformance tests:` has no matching `-stage-<N>` entry in the shared manifest (a declaration that was never materialized); the manifest JSON structure — including each entry's `script` living under `qa/scripts/` — is enforced by `validate_conformance_manifest` (called from both the run path and validate-run); and the result gate (each script's `QA-RESULT`) is enforced by the verifier Tier3 + validate-run.
89
99
  - `### Stage Exit Contract` — predicted added/modified files, newly exposed identifiers/types/endpoints, downstream-usable resources.
@@ -94,14 +104,14 @@
94
104
  - **Different repos** — a single okstra task **cannot** span repos: every stage worktree is a `git worktree add` in one repo's main checkout (one `<project-id>`), the run-index / manifests / registry are keyed to that one project, and the edit allowlist only covers that project's tree plus `~/.okstra/worktrees/**`. This is a structural limit, not a style preference. Therefore cross-repo work MUST be split into **a separate okstra run per repo** — never modelled as stages of one task. State, in the Stage Map `Cross-project parallelism:` line, whether those per-repo runs can proceed in parallel. To avoid re-deriving shared analysis, the second repo's run should consume the first run's relevant plan/decision artifacts as brief Source Material (see the cross-project dependency rows below).
95
105
  - **Parallel-feasibility check (mandatory for every multi-project plan):** disjoint files (S9 below) is necessary but NOT sufficient for parallelism — a cross-project API/contract/schema/deploy-order dependency forces sequencing even when no file overlaps. For each pair of projects, explicitly determine and record (in the `Cross-project parallelism:` line) whether they are independent (run in parallel) or sequenced (and the exact dependency that forces the order).
96
106
  - **Parallel-safety invariant (BLOCKING):** any two stages that are both `depends-on (none)` MUST predict disjoint file sets in their `Stage Exit Contract`. Two parallel `implementation` runs would otherwise edit the same file concurrently. Work touching a shared file must either go in one stage or be ordered with `depends-on`. Enforced by `validators/validate-implementation-plan-stages.py` check S9.
97
- - **Cross-project dependency rows (`crossProjectDependencies` 배열 — 옛 `## Cross-Repo Carry` 부록을 대체):** 타 프로젝트(다른 repo / 다른 top-level 독립 배포 모듈 / published 패키지)에 대한 의존은 freeform `## Cross-Repo Carry` 부록이 아니라 **구조 필드 `crossProjectDependencies` 의 `XP-NNN` 행**으로 기록한다. 한 프로젝트 의존마다 한 XP 행을 두고, 양방향 중 해당하는 `direction` 을 채운다(렌더는 `### Cross-Project Dependencies` §5.4; 단일 프로젝트 계획은 빈 배열). 행 필드는 옛 세 subsection 을 다음으로 대체한다 — `requiredWork`(상대가 만들어야 할 구체 작업) / `verificationSignal`(이 run 이 관측할 신호) / `linkedWork`(신호 충족 전 막히는 이 계획의 stage·step) / `howToStart`(상대 repo 에서의 정확한 handoff).
98
- - `direction: upstream-precondition` — 이 run 이 상대의 **선행 작업을 기다림(선행 필수)**: `requiredWork` = 상대가 먼저 구현해야 할 구체 작업, `verificationSignal` = 이 run 이 진행 전 관측해야 할 신호(PR 머지 / 엔드포인트 live / 버전 publish), `linkedWork` = 신호 충족 전 막히는 이 계획의 stage·step, `howToStart` = 상대 repo 에서 `okstra-brief-gen`(**이 보고서의 절대경로를 Source Material 로 인용** — 유일하게 허용된 cross-`<PROJECT_ROOT>` read) → `okstra-run`.
99
- - `direction: downstream-carry` — 이 run 이 상대가 쓸 것을 **만들어 줌(기존 carry)**: `requiredWork` = 상대가 후속 구현할 self-contained B-portion(상대 관점의 신규 `R-NNN`·제안 stage·영향 파일), `verificationSignal` = 상대가 진행 전 확인할(이 run 이 인도한) 신호, `linkedWork` = 그 신호를 인도하는 이 계획의 stage·step, `howToStart` 는 upstream 과 동일. 이 run 의 이미 `done` 인 stage 를 상대 stage 인 양 넘기지 않는다 — 상대가 아직 만들어야 할 portion 만 담는다.
100
- - **cross-repo 의존은 `depends-on` 으로 표현 불가:** 그 게이트는 한 repo git graph 내부 커밋만 해소하므로(`scripts/okstra_ctl/run.py` `_resolve_stage_base_commit`) 다른 repo 의 커밋을 가리킬 수 없다. cross-repo 작업은 stage 가 아니라 별도 okstra run + XP 행으로 분리한다(Different-repos 규칙). 상대 run 은 구조적으로 독립이라 이 run 의 완료 stage 를 `done` 으로 자동 인식하지 않으며(인식해서도 안 됨 — 이 repo 의 작업), XP 행은 상대 planning 을 seed 하는 narrative 입력일 뿐이다. 이 run 은 상대 repo 트리·그 `.okstra/` 에 쓰지 않는다(이 보고서의 XP 행만 emit). 옛 "Recognition caveat" 문구는 렌더 노트(i18n `crossProjectRecognitionNote`)로 이전돼 `### Cross-Project Dependencies` 섹션에 자동 출력된다.
107
+ - **Cross-project dependency rows (`crossProjectDependencies` array — replaces the old `## Cross-Repo Carry` appendix):** a dependency on another project (a different repo / a different top-level independently-deployable module / a published package) is recorded not as the freeform `## Cross-Repo Carry` appendix but as an **`XP-NNN` row of the structured field `crossProjectDependencies`**. Put one XP row per project dependency and fill in whichever `direction` applies (rendered as `### Cross-Project Dependencies` §5.4; a single-project plan uses an empty array). The row fields replace the old three subsections with — `requiredWork` (the concrete work the other side must build) / `verificationSignal` (the signal this run will observe) / `linkedWork` (the stage·step of this plan that is blocked until the signal is met) / `howToStart` (the exact handoff in the other repo).
108
+ - `direction: upstream-precondition` — this run **waits on the other side's prerequisite work (precondition required)**: `requiredWork` = the concrete work the other side must implement first, `verificationSignal` = the signal this run must observe before proceeding (PR merged / endpoint live / version published), `linkedWork` = the stage·step of this plan that is blocked until the signal is met, `howToStart` = in the other repo, `okstra-brief-gen` (**cite this report's absolute path as Source Material** — the only permitted cross-`<PROJECT_ROOT>` read) → `okstra-run`.
109
+ - `direction: downstream-carry` — this run **produces what the other side will consume (the existing carry)**: `requiredWork` = the self-contained B-portion the other side will implement next (from the other side's perspective, a new `R-NNN`·proposed stages·affected files), `verificationSignal` = the (this-run-delivered) signal the other side confirms before proceeding, `linkedWork` = the stage·step of this plan that delivers that signal, `howToStart` is the same as upstream. Do not hand over this run's already-`done` stages as if they were the other side's stages — include only the portion the other side still has to build.
110
+ - **A cross-repo dependency cannot be expressed with `depends-on`:** that gate only resolves commits inside a single repo's git graph (`scripts/okstra_ctl/run.py` `_resolve_stage_base_commit`), so it cannot point at a commit in another repo. Cross-repo work is split into a separate okstra run + XP row rather than a stage (the Different-repos rule). The other run is structurally independent, so it does not automatically recognise this run's completed stages as `done` (nor should it — that is this repo's work); the XP row is only a narrative input that seeds the other side's planning. This run does not write to the other repo's tree or its `.okstra/` (it emits only this report's XP rows). The old "Recognition caveat" wording has been moved into a render note (i18n `crossProjectRecognitionNote`) and is emitted automatically in the `### Cross-Project Dependencies` section.
101
111
  - **Stage exit contract is the carry surface:** keep it as narrow as possible. Wider surface = more downstream coupling.
102
112
  - dependency / migration risk assessment (ordering constraints, data backfills, feature-flag prerequisites, repo-internal sequencing)
103
- - **Cross-Project Dependencies (조건부 필수):** 계획이 다른 프로젝트 / repo / published 패키지의 작업에 의존하면 (a) `dependencyMigrationRisk` 에 `kind: cross-project` DM 행을, (b) `crossProjectDependencies` 에 매칭 `XP-NNN` 행을 둔다. upstream-precondition 행은 `requiredWork`·`verificationSignal`·`howToStart` 가 구체적이어야 한다 — `validators/validate-run.py` 가 DM `cross-project` ⇒ `direction: upstream-precondition` XP 행 ≥1 을, 스키마가 행 필드 비-빈을 강제한다. cross-project 의존은 soft Recommended Next Step 이 아니라 이 구조화된 선행 필수로 기록한다. 단일 프로젝트 계획은 빈 배열.
104
- - **recommendedNextSteps 정책:** cross-project 선행/후속의 substance 는 `crossProjectDependencies` 에 두고, `§3 Recommended Next Steps` 에는 그 섹션(`§5.4 Cross-Project Dependencies`)을 가리키는 포인터만 둔다 — 이중 기록 금지.
113
+ - **Cross-Project Dependencies (conditionally required):** when the plan depends on work in another project / repo / published package, add (a) a `kind: cross-project` DM row to `dependencyMigrationRisk`, and (b) a matching `XP-NNN` row to `crossProjectDependencies`. An upstream-precondition row must have concrete `requiredWork`·`verificationSignal`·`howToStart` — `validators/validate-run.py` enforces that a DM `cross-project` ⇒ at least one `direction: upstream-precondition` XP row, and the schema enforces non-empty row fields. A cross-project dependency is recorded as this structured precondition, not as a soft Recommended Next Step. A single-project plan uses an empty array.
114
+ - **recommendedNextSteps policy:** keep the substance of cross-project preconditions/carries in `crossProjectDependencies`, and put in `§3 Recommended Next Steps` only a pointer to that section (`§5.4 Cross-Project Dependencies`) — no double recording.
105
115
  - validation checklist (pre / mid / post) — each item is an exact command or observable outcome
106
116
  - rollback strategy — exact revert path (commits, flags, migrations) and the signal that triggers rollback
107
117
  - **Requirement Coverage (mandatory, §5.5.8):** one row per concrete requirement from the task brief / packet. Assign stable IDs `R-001`, `R-002`, ... in source order. Columns: `ID | Source | Requirement | Covered by option / stage / step | Status`. `Source` cites the brief heading or file/line where the requirement came from. `Covered by` must name the specific Option Candidate and Stage/Step that satisfies it, not just "recommended option". **Enforced:** `validators/validate-run.py` `_validate_requirement_coverage_covered_by` fails a `covered` row whose `coveredBy` is bare "recommended option", names no Option/Stage/Step anchor, or cites a Stage number absent from the Stage Map (whether the cited step *actually satisfies* the requirement remains a worker `DISAGREE(f)` judgment). `Status` is one of `covered`, `gap`, or `blocked C-NNN`. If any row is `gap` or `blocked C-NNN`, the Plan Body Verification gate MUST NOT be `passed` / `passed-with-dissent`; add a matching `Blocks=approval` row for the blocker and keep `approved: false`.
@@ -110,7 +120,7 @@
110
120
  - the YAML frontmatter MUST include the line `implementation-option:` directly under `approved:` (report-writer always emits it with an **empty value**). The user selects which Option Candidate the next `implementation` run executes by filling this line with that option's name (manual edit or `--implementation-option <name>` CLI). When left empty, the `implementation` run falls back to the `Recommended Option`.
111
121
  - **the frontmatter `approved: false` line is rendered unconditionally; if the plan-body verification gate (§5.5.9) returns `blocked-by-disagreement` or `aborted-non-result`, the writer MUST keep `approved: false` and the validator refuses any report that ships with `approved: true` under such a gate result.**
112
122
  - every ambiguity flagged during pre-planning that the user must resolve before approval registered as a `Blocks=approval` row in the `## 1. Clarification Items` table (the unified table is the single home for these — the "no separate `Open Questions` block" rule is in the shared `_common-contract.md` clarification policy)
113
- - **§5.5.9 Plan Body Verification (BLOCKING).** After report-writer finishes the draft, the lead MUST run a worker peer-review round on the consolidated plan body (Option Candidates / Trade-off Matrix / Recommended Option / Stage Map and per-stage sections / Dependency / Validation Checklist / Rollback / Requirement Coverage) and populate `### 5.5.9 Plan Body Verification` in the final report. The round protocol, plan-item ID scheme (`P-Opt-*` / `P-Step-*` / `P-Dep-*` / `P-Val-*` / `P-Rb-*`), verdict semantics, gate-result classification, and dissent log format are defined in `prompts/lead/plan-body-verification.md`. The four gate-result values are `passed`, `passed-with-dissent`, `blocked-by-disagreement`, `aborted-non-result`. When the gate would have been `blocked-by-disagreement` or `aborted-non-result`, the lead MUST NOT silently flip it to one of the passing values to "unblock" the run — that is a contract violation. **Enforced:** `validators/validate-run.py` `_validate_plan_body_gate_recompute` re-derives the gate from `planItems[].verdicts` and fails when the declared `gateResult` claims a healthier outcome than the recorded votes support; `_validate_plan_item_extraction_completeness` fails when any plan-body deliverable category is under-extracted into `planItems`, so a dropped item can no longer dodge the gate. When `convergence.adversarial=true` (the default for this phase), this round uses the adversarial posture — verifiers confirm cited paths/commands and the burden of proof is on the plan — but the gate threshold stays `majority-disagree` (see that skill's §"Adversarial plan-body posture"). majority-disagree 항목 중 `planner-fixable` 다수는 사용자 clarification 으로 승격하기 전에 report-writer 자가수정 1회를 거친다(`prompts/lead/plan-body-verification.md` "Self-fix round"). self-fix 로 해소되지 않았거나 `needs-user-input` 다수인 항목만 `Blocks=approval` clarification 이 된다. `validators/validate-run.py` `_validate_self_fix_before_clarification` 가 self-fix 없이 승격된 planner-fixable 다수 항목을 `contract-violated` 로 실패시킨다.
123
+ - **§5.5.9 Plan Body Verification (BLOCKING).** After report-writer finishes the draft, the lead MUST run a worker peer-review round on the consolidated plan body (Option Candidates / Trade-off Matrix / Recommended Option / Stage Map and per-stage sections / Dependency / Validation Checklist / Rollback / Requirement Coverage) and populate `### 5.5.9 Plan Body Verification` in the final report. The round protocol, plan-item ID scheme (`P-Opt-*` / `P-Step-*` / `P-Dep-*` / `P-Val-*` / `P-Rb-*`), verdict semantics, gate-result classification, and dissent log format are defined in `prompts/lead/plan-body-verification.md`. The four gate-result values are `passed`, `passed-with-dissent`, `blocked-by-disagreement`, `aborted-non-result`. When the gate would have been `blocked-by-disagreement` or `aborted-non-result`, the lead MUST NOT silently flip it to one of the passing values to "unblock" the run — that is a contract violation. **Enforced:** `validators/validate-run.py` `_validate_plan_body_gate_recompute` re-derives the gate from `planItems[].verdicts` and fails when the declared `gateResult` claims a healthier outcome than the recorded votes support; `_validate_plan_item_extraction_completeness` fails when any plan-body deliverable category is under-extracted into `planItems`, so a dropped item can no longer dodge the gate. When `convergence.adversarial=true` (the default for this phase), this round uses the adversarial posture — verifiers confirm cited paths/commands and the burden of proof is on the plan — but the gate threshold stays `majority-disagree` (see that skill's §"Adversarial plan-body posture"). Among the majority-disagree items, those that are majority-`planner-fixable` go through one report-writer self-fix pass before being promoted to a user clarification (`prompts/lead/plan-body-verification.md` "Self-fix round"). Only items not resolved by self-fix, or that are majority-`needs-user-input`, become `Blocks=approval` clarifications. `validators/validate-run.py` `_validate_self_fix_before_clarification` fails a planner-fixable majority item promoted without a self-fix as `contract-violated`.
114
124
  - **Decision-record evaluation (sole owner)**: this phase is the **single owner** of decision-record evaluation in the okstra lifecycle. The brief never evaluates or drafts decision records — it only forwards `adr-candidate:*` signals. Every `adr-candidate:*` entry inherited from the brief's `Open Questions` is a mandatory evaluation target. In addition, evaluate every decision the recommended option introduces against the three criteria:
115
125
  1. **Hard to reverse** — would changing the decision later cost meaningfully more than deciding now?
116
126
  2. **Surprising without context** — would a future reader, seeing only the code, wonder "why was it built this way?"?
@@ -132,5 +142,5 @@
132
142
  6. **Review-rule preflight check** — if a project review rule pack exists, map each relevant rule to the recommended option. Reject the draft if it knowingly creates a violation that the later PR reviewer would flag, unless the plan records a specific rationale and follow-up. In particular, scan for repeated helper stacks across planned files, tests that assert delegation to the same calculator/helper they exercise, public names that hide side effects, domain rules placed in repositories/adapters, and APIs made dead by this change.
133
143
  7. **Plan-body verification reconciliation (BLOCKING for implementation-planning).** For every §5.5.9 `planItems[]` entry whose verdicts make it `majority-disagree`, set that item's `clarificationId` to a `C-<N>` row that MUST exist in `## 1. Clarification Items` with `Kind` chosen per the standard policy and `Blocks=approval`. **Enforced:** `validators/validate-run.py` `_validate_plan_body_clarification_matching` recomputes each item's class and fails when a majority-disagree item has no `clarificationId`, or its `clarificationId` is dangling / points at a non-`approval` row. For `partial-consensus` and `dissent-isolated` plan-items, the dissenting opinion lives in §5.5.9 `Dissent log` and is NOT promoted to §5.
134
144
  8. **Stage Map self-check** — for every stage, count the effective rows of its `Stepwise Execution Order` table by hand; reject the draft if any stage exceeds 8. Confirm each stage declares a non-empty `Slice value:` and `Acceptance:` line, the three `Test case (success|boundary|failure):` lines (or carries a `TDD exemption:` line), and that its first step `action` starts with `RED:` with a later `GREEN:` — this is what validator S10 enforces, including S10d on the test-case lines. Read each stage's three test-case lines as a reviewer: reject any that restates the happy path in all three slots, leaves `boundary` blank, or writes `N/A` where a real edge input exists. Walk the `depends-on` graph and confirm it is a DAG (no cycle, no self-reference). For each `depends-on` link, confirm it encodes a real data/contract dependency — do NOT add links to serialise unrelated work, and do NOT split a stage merely to create more parallel stages. **Parallel-safety:** for every pair of `depends-on (none)` stages, confirm their `Stage Exit Contract` predicted file sets are disjoint; if they share a file, merge them or add a `depends-on` link (validator S9 rejects overlap). **Project-boundary:** confirm no stage mixes edits from two projects (different repo/`PROJECT_ROOT` or different top-level deployable module); if any stage does, split it per project. For multi-project plans, confirm each stage's `title` carries its `[<project>]` tag and the `Cross-project parallelism:` line under the table records the parallel-vs-sequenced determination (with the forcing dependency) for every project pair; for cross-repo work, confirm it is split into separate per-repo runs (required — one run structurally cannot touch another repo) rather than crammed into one task's stages.
135
- 9. **Cross-project dependency check** — 타 repo / 다른 top-level 배포 모듈 / published 패키지에 대한 의존을 빠뜨리지 않았는지 확인한다. `dependencyMigrationRisk` 에 `kind: cross-project` 행이 있으면 매칭되는 `direction: upstream-precondition` `XP-NNN` 행이 `crossProjectDependencies` 에 있고, 그 `requiredWork` 가 추상 표현("상대 작업 완료")이 아니라 상대가 실제로 만들어야 할 구체 작업인지 reviewer 로서 다시 읽는다(validator S 가 존재만 보므로 구체성은 self-review 가 책임짐). cross-repo 작업을 한 task 의 stage 로 욱여넣지 않고 별도 run + XP 행으로 분리했는지, cross-project substance 가 `§3 Recommended Next Steps` 에 중복되지 않고 `§5.4 Cross-Project Dependencies` 에만 있는지 확인한다.
136
- 10. **Decision-draft materialization check** — `decisionDrafts` 가 비어있지 않으면, 매칭 materialization step(`.okstra/decisions/<NNNN>-<slug>.md` 생성)이 어느 stage 의 stepwise 에 존재하는지, draft 개수와 materialization step 이 1:1 로 대응하는지 reviewer 로서 확인한다. validator 는 step 의 *존재*만 보므로 `<NNNN>-<slug>` 정확성·개수 대응은 self-review 가 책임진다.
145
+ 9. **Cross-project dependency check** — confirm you have not missed a dependency on another repo / another top-level deployable module / a published package. If `dependencyMigrationRisk` has a `kind: cross-project` row, confirm a matching `direction: upstream-precondition` `XP-NNN` row exists in `crossProjectDependencies`, and re-read as a reviewer whether its `requiredWork` is the concrete work the other side must actually build rather than an abstract phrase ("other side's work done") — validator S only checks existence, so concreteness is the self-review's responsibility. Confirm cross-repo work is split into a separate run + XP row instead of being crammed into one task's stages, and that the cross-project substance is not duplicated in `§3 Recommended Next Steps` but lives only in `§5.4 Cross-Project Dependencies`.
146
+ 10. **Decision-draft materialization check** — when `decisionDrafts` is non-empty, confirm as a reviewer which stage's stepwise order contains the matching materialization step (creating `.okstra/decisions/<NNNN>-<slug>.md`) and that the number of drafts corresponds 1:1 with the materialization steps. The validator only checks the *existence* of the step, so the `<NNNN>-<slug>` correctness and count correspondence are the self-review's responsibility.
@@ -46,4 +46,4 @@ The bulk of this profile's body is split into three sidecars so the lead's Phase
46
46
  | `prompts/profiles/_implementation-verifier.md` | Phase 5, between Executor stage completion and the first verifier dispatch | Verifier roles, Two-tier command lookup, deny-list, discrepancy rule, Read-only command log, verifier-specific forbidden actions |
47
47
  | `prompts/profiles/_implementation-deliverable.md` | Start of Phase 6 (after Phase 5.5 convergence completes, before report-writer dispatch prompt construction) | Required deliverable shape, Validation / TDD evidence rules, Verifier results structure, Self-review pass, Lead post-stage persistence |
48
48
 
49
- **Phase 5 / 6 진입 시 해당 sidecar 가 lead context 에 없으면 BLOCKING — phase 진입 거부.** Lead 는 sidecar 를 read 한 후 1 회 turn 안에 phase 의 후속 action 으로 이어가야 한다 (즉 sidecar 의 룰은 read 한 그 turn 부터 효력 발생).
49
+ **Entering Phase 5 / 6 while the relevant sidecar is not in the lead's context is BLOCKING — the phase entry is refused.** After reading a sidecar, the lead must continue into the phase's follow-up action within a single turn (i.e. the sidecar's rules take effect starting from the very turn it is read).
@@ -39,7 +39,7 @@
39
39
  - Expected output emphasis:
40
40
  - the `## 5.9 Improvement Candidates` table populated with rows that obey the 10-column schema from `validators/validate_improvement_report.py` (Cand ID `I-NNN`, Lens from whitelist, Title, Scope ⊆ scan-scope, Severity, Effort, Consensus, Source workers `<worker>:<id>` from {claude, codex, antigravity}, Recommended next-phase ∈ {requirements-discovery, implementation-planning, error-analysis}, Evidence as path:line list)
41
41
  - `Consensus` cells in `## 5.9 Improvement Candidates` use the table enum exactly: `full`, `partial`, `contested`, `worker-unique`. Map convergence's `full-consensus` / `partial-consensus` labels to `full` / `partial` before writing the table.
42
- - `## 7. Final Verdict` Verdict Token ∈ {`candidates-ready`, `no-candidates`, `blocked`}; Direction `routing`; Next Step "사용자에게 후보 K개 선택 의뢰 (## 5.9 표 참조)"
42
+ - `## 7. Final Verdict` Verdict Token ∈ {`candidates-ready`, `no-candidates`, `blocked`}; Direction `routing`; Next Step "ask the user to select K candidates (see the ## 5.9 table)"
43
43
  - `## 3. Recommended Next Steps` first entry summarises per-candidate routing and proposes new task-key names of the form `<task-group>/imp-<Cand-ID>`
44
44
  - this report is authored free-form (improvement-discovery is not in the data.json schema enum); after the markdown is written, the report-writer runs `okstra inject-report-index <report.md> --report-language <en|ko>` to add the top-of-report Index + `I-NNN`/`C-NNN` scroll anchors. The run validator fails the report when the Index anchor is missing.
45
45
  - Clarification request policy (phase-specific addenda — shared policy is in `_common-contract.md`):
@@ -28,12 +28,12 @@
28
28
  `okstra handoff local-checkout --project-root <project root> --project-id <id> --task-group <g> --task-id <t>`
29
29
  Exit 1 means a precondition failed (MAIN worktree dirty, registry entry missing, or checkout failed): show the error verbatim and re-ask the action selection. On success the okstra task worktree no longer exists — the lead's cwd is gone, so EVERY subsequent step (final-report authoring included) MUST use absolute paths or run from the MAIN worktree. Then route to the final-report self-review pass.
30
30
  - **stage-group mode step order** (overrides the default Q1→Q3 sequence): after Q1 picks `push + PR`, run (1) base-branch selection first — identical to Q2 below, because the dependency-closure check needs `origin/<base>`; (2) G2 stage confirmation (step 1g); (3) assemble (step 2g); then Q2b and Q3 as usual, with the collector branch as the PR head.
31
- 1g. **G2 — stage confirmation**: the stage selection already happened before the run (wizard `handoff_stage_pick`, or the CLI `--stages` flag) and is fixed in `HANDOFF_STAGES`. Display it as a one-line confirmation (`PR 대상 stage: <csv> — 진행합니다`) and proceed; do NOT re-ask the multi-select. Only if `HANDOFF_MODE` is `stage-group` but `HANDOFF_STAGES` is empty (defensive, should not happen) run `okstra handoff eligible --plan-run-root <plan-run-root> --approved-plan <approved plan path>` and ask the user to pick from the eligible stages.
31
+ 1g. **G2 — stage confirmation**: the stage selection already happened before the run (wizard `handoff_stage_pick`, or the CLI `--stages` flag) and is fixed in `HANDOFF_STAGES`. Display it as a one-line confirmation (`PR target stage: <csv> — proceeding`) and proceed; do NOT re-ask the multi-select. Only if `HANDOFF_MODE` is `stage-group` but `HANDOFF_STAGES` is empty (defensive, should not happen) run `okstra handoff eligible --plan-run-root <plan-run-root> --approved-plan <approved plan path>` and ask the user to pick from the eligible stages.
32
32
  2g. **assemble**: run `okstra handoff assemble --plan-run-root <...> --approved-plan <...> --project-root <project root> --project-id <id> --task-group <g> --task-id <t> --work-category <c> --stages <csv> --base <chosen-base>`. Exit 2 means a stage-vs-stage merge conflict: show the `conflicts` paths and stop (route: reshape the group or resolve manually). Exit 1 means an eligibility/closure violation: show the error verbatim and re-ask G2. On success the returned `branch` is the PR head branch for every subsequent step.
33
33
  2. **PR base branch** (only when the user picked `push + PR`) — present four options and capture exactly one:
34
34
  - `preprod`
35
35
  - `main`
36
- - `직접 입력` (free-form branch name; lead validates the name exists on origin via `git ls-remote --heads origin <name>` and re-asks on failure)
36
+ - `direct input` (free-form branch name; lead validates the name exists on origin via `git ls-remote --heads origin <name>` and re-asks on failure)
37
37
  The chosen base MUST NOT equal the feature branch. If it does, re-ask.
38
38
  2b. **Pre-merge conflict probe** (only when the user picked `push + PR`) — before the push/PR step, the lead MUST refresh the base ref and probe for merge conflicts against it:
39
39
  - run `git fetch origin <chosen-base>` (read-only on the local working tree).
@@ -85,7 +85,7 @@
85
85
  - `- PR creation skipped: <reason>` for any user-driven cancellation
86
86
  - **Local Checkout Outcome**: one of
87
87
  - `- Not run (user picked push + PR or skip).`
88
- - `- Checked out <branch> into <main worktree path>; okstra task worktree <path> removed. Next: 로컬 테스트 후 PR 하려면 okstra-run 으로 release-handoff 재진입 → push + PR.`
88
+ - `- Checked out <branch> into <main worktree path>; okstra task worktree <path> removed. Next: to run a local test and then open a PR, re-enter release-handoff via okstra-run → push + PR.`
89
89
  - **Stage Group** (stage-group mode only): selected stages, each stage's single-stage verification report path + quoted `Verdict Token` row, collector branch name, merge commit SHAs from assemble, and the dependency-closure verdict (from the assemble output / error).
90
90
  - **Routing recommendation**: explicit `done` token, since release-handoff is the terminal lifecycle phase. If the run ended in `skip` or `cancel`, the recommendation MUST also state whether re-entry into release-handoff is appropriate.
91
91
  - Self-review pass before finalising the report (`Claude lead` runs this):
@@ -23,23 +23,23 @@
23
23
  - capture approval or confirmation points before the next phase starts
24
24
  - **domain alignment check**: read `<PROJECT_ROOT>/.okstra/glossary.md` and `<PROJECT_ROOT>/.okstra/decisions/` titles if present. Absent okstra memory files are normal — do not error. Validate that every `terminology:*` entry under the brief's `Open Questions` has a canonical resolution before routing. Fuzzy or overloaded terms in the brief MUST be resolved to a single canonical term in this phase.
25
25
  - Fan-out (multi-item / multi-domain requests only):
26
- - 단일 항목/단일 도메인 요청은 fan-out 하지 않는다(현행 단일 라우팅 보존).
27
- - 요청이 2개 이상 도메인에 걸치거나 독립 착수 가능한 작업 항목이 2개 이상이면,
28
- 각 항목을 `runs/requirements-discovery/fan-out/unit-<NNN>.md` packet 으로 발행한다.
29
- packet 은 `templates/reports/fan-out-unit.template.md` 형식을 따르며 frontmatter
26
+ - A single-item / single-domain request is not fanned out (preserving the current single-routing behaviour).
27
+ - When a request spans two or more domains, or has two or more independently-startable work items,
28
+ publish each item as a `runs/requirements-discovery/fan-out/unit-<NNN>.md` packet.
29
+ The packet follows the `templates/reports/fan-out-unit.template.md` format, and its frontmatter
30
30
  `domain`(work-category 5-enum: bugfix / feature / refactor / ops / improvement),
31
- `depends-on`(같은 fan-out 내 unit-id 의 inline 리스트 `[unit-001]`, 없으면 `[]`),
32
- `recommended-next-phase`(error-analysis | implementation-planning)를 채운다.
33
- - `runs/requirements-discovery/fan-out/index.md` 에 packet 을 depends-on 위상순서로
34
- 번호목록(`1. unit-001`)으로 나열한다(생성 뷰; 직접 편집 금지 명시). depends-on 그래프는
35
- DAG 여야 한다 — `validate_fanout` 가 순환을 검증 실패로 거부하므로, 순환이 생기면
36
- packet 을 finalize 하기 전에 문제 의존을 끊어 DAG 로 만든다.
37
- - brief 의 `Related Task Graph` 에서 seed 된 ordering edge 는 각 packet 의 `depends-on`
38
- 과 final report 의 routing 근거에 보존한다. `duplicates` / `related-to` edge 는
39
- fan-out 순서로 해석하지 말고 중복 작업 방지 / scope alignment 참고로만 사용한다.
40
- - 최종 리포트에는 분해 결과를 중복하지 말고 "fan-out: N packets → fan-out/index.md" 한 줄
41
- 포인터만 둔다. packet 실행은 별도다: 사용자가 `okstra-run --task-brief <packet 경로>` 로
42
- 각 단위를 새 task-key 로 시작한다(이 phase 는 다운스트림 run 을 직접 시작하지 않는다).
31
+ `depends-on`(an inline list of unit-ids within the same fan-out `[unit-001]`, or `[]` if none),
32
+ `recommended-next-phase`(error-analysis | implementation-planning) are filled in.
33
+ - in `runs/requirements-discovery/fan-out/index.md`, list the packets in depends-on topological order
34
+ as a numbered list (`1. unit-001`) (a generated view; explicitly do not hand-edit). The depends-on graph
35
+ must be a DAG — `validate_fanout` rejects a cycle as a validation failure, so if a cycle appears,
36
+ break the offending dependency to make it a DAG before finalizing the packet.
37
+ - an ordering edge seeded from the brief's `Related Task Graph` is preserved in each packet's `depends-on`
38
+ and in the final report's routing rationale. A `duplicates` / `related-to` edge is
39
+ not interpreted as fan-out ordering; use it only as a reference for avoiding duplicate work / scope alignment.
40
+ - in the final report, do not duplicate the decomposition result; keep only the one-line "fan-out: N packets → fan-out/index.md"
41
+ pointer. Packet execution is separate: the user starts each unit as a new task-key via
42
+ `okstra-run --task-brief <packet path>` (this phase does not directly start any downstream run).
43
43
  - Decision-tree walk (bounded):
44
44
  - When the brief's `Desired Outcome`, classification, or routing target depends on a chain of decisions, walk that chain one branch at a time. Each branch is one `Clarification Items` row, not a free-form interview.
45
45
  - For every clarification row, put the single best answer and one-line rationale in `Expected form` as `Recommended: (a) ...`. Put other options and one-sentence consequences in the same cell as `Alternatives: (b) ... (c) ...` — the recommended answer is option `(a)` and alternatives continue from `(b)`, so the labels read `(a) (b) (c) …` in order.
@@ -63,5 +63,5 @@
63
63
  - full implementation design unless it is required to decide the next phase
64
64
  - **source code edits, plan authoring, builds, or deployments** — this run only classifies the work and routes it; deeper analysis and planning belong to subsequent phases
65
65
  - **writes outside `<PROJECT_ROOT>/.okstra/`** — this phase only uses okstra's artifact root. Glossary additions land in `<PROJECT_ROOT>/.okstra/glossary.md` (via `okstra-brief-gen` Step 4.5); decision drafts land in `<PROJECT_ROOT>/.okstra/decisions/` (via `implementation-planning`).
66
- - 작업 단위 분해(fan-out)는 이 phase 의 in-scope 다 — 단, 각 단위의 *해법 설계*·소스
67
- 편집·plan 작성은 여전히 non-goal 이며 다운스트림 phase 가 담당한다
66
+ - work-unit decomposition (fan-out) is in-scope for this phase — but each unit's *solution design*·source
67
+ edits·plan authoring remain non-goals and are handled by downstream phases
@@ -233,6 +233,50 @@
233
233
  "unknown_option": "보고서에서 내보낸 승인 기록의 옵션 `{option}` 이 plan 의 Option Candidates 에 없습니다. 유효 후보: {candidates}. 보고서에서 다시 내보내거나 '예 — 승인만' 을 선택하세요."
234
234
  }
235
235
  },
236
+ "design_prep_decision": {
237
+ "label": "설계 준비 항목 {item_id} — {title}\n대상 stage: {stage_refs}\n\nAI 초안\n- 요약: {proposal_summary}\n- 상세: {details}\n- 가정: {assumptions}\n- 가드레일: {guardrails}\n- 요청 문서: {request_path}\n\n이 초안을 어떻게 처리할까요?",
238
+ "echo_template": "design-prep decision: {value}",
239
+ "options": {
240
+ "accept-draft": "AI 초안 수락",
241
+ "modify-draft": "AI 초안 수정",
242
+ "reject-draft": "AI 초안 거절",
243
+ "later": "나중에 결정"
244
+ },
245
+ "echo_variants": {
246
+ "later": "design-prep {item_id}: 나중에 결정 — 입력 파일을 만들지 않음"
247
+ },
248
+ "errors": {
249
+ "invalid_decision": "설계 준비 결정은 accept-draft, modify-draft, reject-draft, later 중 하나여야 합니다."
250
+ }
251
+ },
252
+ "design_prep_overrides": {
253
+ "label": "설계 준비 항목 {item_id}의 {decision} 내용을 입력하세요. modify-draft는 JSON 객체, reject-draft는 거절 사유를 입력합니다.",
254
+ "echo_template": "design-prep details: {value}",
255
+ "echo_variants": {
256
+ "overrides": "design-prep overrides: JSON 객체 확인됨",
257
+ "note": "design-prep rejection note: 확인됨"
258
+ },
259
+ "errors": {
260
+ "invalid_json": "수정 내용이 올바른 JSON이 아닙니다: {error}",
261
+ "object_required": "수정 내용은 JSON 객체여야 합니다.",
262
+ "note_required": "reject-draft에는 비어 있지 않은 note가 필요합니다."
263
+ }
264
+ },
265
+ "design_prep_confirm": {
266
+ "label": "설계 준비 입력을 저장하기 전 의미 내용을 확인하세요.\n\nitem: {item_id}\ndecision: {decision}\noverrides: {overrides}\nnotes: {notes}\n\n승인된 스냅샷의 모든 의미 필드:\n{item_snapshot}\n\nrevision / input-id / created-at은 확정 후 원자적으로 할당됩니다.",
267
+ "echo_template": "design-prep confirm: {value}",
268
+ "options": {
269
+ "yes": "예 — 이 의미 내용으로 입력 파일 생성",
270
+ "no": "아니오 — 결정 화면으로 돌아가기"
271
+ },
272
+ "echo_variants": {
273
+ "revise": "design-prep {item_id}: 저장하지 않고 결정 화면으로 돌아감",
274
+ "written": "design-prep {item_id}: revision {revision}, input-id {input_id}, path {path}"
275
+ },
276
+ "errors": {
277
+ "confirmation_required": "설계 준비 입력 저장은 yes 또는 no로 확인해야 합니다."
278
+ }
279
+ },
236
280
  "stage_pick": {
237
281
  "label": "진행할 stage 를 선택하세요(여러 개 선택 가능). '전체' 는 남은 미완료 stage 를 모두, 개별 선택 시 의존하는 미완료 stage 가 자동 포함됩니다.",
238
282
  "label_final_verification": "검증할 implementation stage 를 선택하세요.",
@@ -452,7 +452,11 @@ def _render_missing_analysis_worker_prompt(
452
452
  lines.extend(_worktree_headers(manifest, active_context, role))
453
453
  lines.extend(_analysis_prompt_body(manifest, active_context, worker_id, model, role))
454
454
  executor_tail = _implementation_executor_tail(
455
- manifest, workspace_root, role
455
+ manifest,
456
+ active_context,
457
+ project_root,
458
+ workspace_root,
459
+ role,
456
460
  )
457
461
  if executor_tail:
458
462
  lines.extend(["", executor_tail.rstrip()])
@@ -806,6 +810,8 @@ def _worktree_headers(
806
810
 
807
811
  def _implementation_executor_tail(
808
812
  manifest: Mapping[str, Any],
813
+ active_context: Mapping[str, Any],
814
+ project_root: Path,
809
815
  workspace_root: Path,
810
816
  role: str,
811
817
  ) -> str:
@@ -813,6 +819,22 @@ def _implementation_executor_tail(
813
819
  return ""
814
820
  profiles = workspace_root / "prompts" / "profiles"
815
821
  parts: list[str] = []
822
+ analysis_profile = _instruction_path(
823
+ manifest,
824
+ active_context,
825
+ "analysisProfilePath",
826
+ )
827
+ if not analysis_profile:
828
+ raise DispatchError("implementation executor profile path is missing")
829
+ executor_profile = (
830
+ _resolve_project_path(project_root, analysis_profile).parent
831
+ / "implementation-executor.md"
832
+ )
833
+ if not executor_profile.is_file():
834
+ raise DispatchError(
835
+ f"resolved implementation executor profile not found: {executor_profile}"
836
+ )
837
+ parts.append(executor_profile.read_text(encoding="utf-8"))
816
838
  preflight_path = profiles / "_coding-conventions-preflight.md"
817
839
  if preflight_path.is_file():
818
840
  parts.append(preflight_path.read_text(encoding="utf-8"))