okstra 0.191.2 → 0.193.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 (37) hide show
  1. package/docs/architecture.md +2 -2
  2. package/docs/cli.md +3 -2
  3. package/docs/project-structure-overview.md +3 -1
  4. package/docs/task-process/README.md +2 -2
  5. package/docs/task-process/common-flow.md +4 -5
  6. package/package.json +1 -1
  7. package/runtime/BUILD.json +2 -2
  8. package/runtime/agents/workers/translator-worker.md +1 -1
  9. package/runtime/prompts/launch.template.md +1 -1
  10. package/runtime/prompts/lead/convergence.md +13 -3
  11. package/runtime/prompts/lead/okstra-lead-contract.md +2 -2
  12. package/runtime/prompts/lead/plan-body-verification.md +1 -1
  13. package/runtime/prompts/lead/report-writer.md +11 -8
  14. package/runtime/prompts/profiles/_implementation-executor.md +1 -1
  15. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  16. package/runtime/prompts/profiles/implementation-planning.md +1 -1
  17. package/runtime/prompts/wizard/prompts.ko.json +52 -23
  18. package/runtime/python/okstra_ctl/conformance.py +74 -0
  19. package/runtime/python/okstra_ctl/convergence.py +63 -1
  20. package/runtime/python/okstra_ctl/convergence_reverify_prompt.py +238 -0
  21. package/runtime/python/okstra_ctl/dispatch_core.py +42 -22
  22. package/runtime/python/okstra_ctl/execution_mutation_audit.py +96 -8
  23. package/runtime/python/okstra_ctl/next_phase.py +18 -8
  24. package/runtime/python/okstra_ctl/plan_items.py +6 -4
  25. package/runtime/python/okstra_ctl/plan_items_cli.py +91 -6
  26. package/runtime/python/okstra_ctl/report_finalize.py +57 -10
  27. package/runtime/python/okstra_ctl/report_translation_dispatch.py +300 -0
  28. package/runtime/python/okstra_ctl/verdict_blocks.py +37 -7
  29. package/runtime/python/okstra_ctl/wizard/engine.py +16 -2
  30. package/runtime/python/okstra_ctl/wizard/registry.py +11 -2
  31. package/runtime/python/okstra_ctl/wizard/roles.py +364 -361
  32. package/runtime/python/okstra_ctl/wizard/state.py +39 -27
  33. package/runtime/python/okstra_ctl/wizard/steps_identity.py +50 -8
  34. package/runtime/python/okstra_ctl/wizard/steps_roles.py +1 -0
  35. package/runtime/python/okstra_ctl/worker_prompt_contract.py +11 -0
  36. package/runtime/skills/okstra-run/SKILL.md +2 -2
  37. package/runtime/validators/validate-run.py +78 -16
@@ -452,7 +452,7 @@ The fourth column is the `workflow.nextRecommendedPhase` pointer Phase 7 leaves
452
452
  |---|---|---|---|---|
453
453
  | `requirements-discovery` | Classify the request as bugfix, feature, refactor, ops, or improvement, then route it to a safe next phase | work category, routing decision, missing-input list, clarification requests | from `requirementsDiscovery.routing.nextTaskType`: `ready` at `error-analysis` or `implementation-option-selection`; `pending` when the run settles on neither | No |
454
454
  | `error-analysis` | Analyze the symptoms, causes, and reproduction gaps of a reported error/incident based on evidence | symptom/trigger summary, root-cause hypotheses, reproduction gap, validation path | from `errorAnalysis.routing.nextTaskType`: `ready` at `implementation-option-selection` after a credible cause, or at `error-analysis` for continued investigation | No |
455
- | `implementation-option-selection` | Compare or validate implementation directions before detailed planning | up to three ranked directions, per-direction `coveragePercent` and `scopePrecisionPercent`, rejected-candidate audit, separate `DIRECTION SELECTION` response | from the `implementationOptionSelection.routing` string enum: `ready` at `implementation-planning` once a direction is confirmed, `pending` on `pending-direction-selection`, `blocked` on `blocked` | No (strictly read-only; source edits, builds, tests, migrations, and deploys are prohibited) |
455
+ | `implementation-option-selection` | Compare or validate implementation directions before detailed planning | up to three ranked directions, per-direction `coveragePercent` and `scopePrecisionPercent`, rejected-candidate audit, separate `DIRECTION SELECTION` response | from the `implementationOptionSelection.routing` string enum: `ready` at `implementation-planning` both for a confirmed direction and for `pending-direction-selection` with ranked candidates (the planning wizard's `selected_direction_pick` takes the choice; the rationale names the candidates), `pending` when no candidate was ranked, `blocked` on `blocked` | No (strictly read-only; source edits, builds, tests, migrations, and deploys are prohibited) |
456
456
  | `implementation-planning` | Expand one selected direction into an executable plan without changing its mechanism or architecture boundary | selected-direction snapshot/reference, direction realization, affected-file list, Stage Map, validation/rollback, exact plan coverage, YAML frontmatter `approved: false`, **§5.5.9 Plan Body Verification**. Existing plans without `planningContract: selected-direction` retain the legacy option-candidate and `implementation-option:` contract | from `implementationPlanning.outcome`: `ready` at `implementation` on `plan-ready` or on a candidate-comparison plan with no `outcome` (the plan still needs its separate approval before that run starts), `ready` at `implementation-option-selection` on `direction-invalidated` | No |
457
457
  | `implementation` | Modify source code according to the approved `implementation-planning` final report. **One run executes exactly one stage** (selected with `--stage <auto\|N>`) | commit list, diff summary, out-of-plan edits block, validation/TDD evidence, rollback verification, verifier results (Antigravity/Codex/Claude), `carry/stage-<N>.json` evidence sidecar | from `implementation.routingRecommendation.target`: `ready` at that phase — `final-verification` on a clean stage, otherwise `error-analysis`, `implementation-planning`, or `implementation` | Yes (limited to the approved plan's file list; `git push`/publish/deploy/real migration prohibited) |
458
458
  | `final-verification` | Check completed work for residual defects and regression risk, then make a release judgment | acceptance verdict, residual risk, follow-up routing (`error-analysis`/`implementation-option-selection`/`implementation-planning`/`release-handoff`) | from `finalVerification.routingRecommendation.target`: `ready` at that value — `release-handoff` only on an `accepted` verdict, otherwise the phase owning the defect (cause, selected direction, or detailed plan). `release-handoff(stage-group)` is a scope qualifier on the same phase, so it projects to `release-handoff`. `done` becomes `terminal` | No (read-only tests only) |
@@ -863,7 +863,7 @@ The manifest-provided `lead-events-*.jsonl` file is the canonical record for str
863
863
 
864
864
  `activityContractVersion: 1` is an interpretation version for new `implementation-planning` artifacts. A run without that field remains a historical run and does not require activity events or an `agentActivity[]` projection.
865
865
 
866
- The shared `okstra report-finalize` entrypoint reads canonical activity before translation source checking. Under contract v3 its in-process `project-activity` step assembles the report, filtering events by run identity and validating activity ID order before `agentActivity[]` is published. A schema-invalid composed record is still published so `validate-run` can scan it; the step itself fails. A historical v2 manifest retains the in-place projection path. A failed Phase 7 step does not skip later checks: `validate-run` still runs, and only `record-group-memory` and `teardown-stages` are skipped. For a non-English report, the lead runs `token-usage`, `project-activity`, and `check-source` before translator dispatch, then resumes at `render-views` after the translation sidecar exists. Conformance compares the resulting `agentActivity[]` IDs, order, and core fields with the canonical events for every lead host.
866
+ The shared `okstra report-finalize` entrypoint reads canonical activity before translation source checking. Under contract v3 its in-process `project-activity` step assembles the report, filtering events by run identity and validating activity ID order before `agentActivity[]` is published. A schema-invalid composed record is still published so `validate-run` can scan it; the step itself fails. A historical v2 manifest retains the in-place projection path. A failed Phase 7 step does not skip later checks: `validate-run` still runs, and only `record-group-memory` and `teardown-stages` are skipped. For a non-English report, the `translate` step between `check-source` and `render-views` materializes and dispatches the translator worker itself (`okstra_ctl.report_translation_dispatch`) and requires the `*.i18n.<lang>.json` sidecar; the lead no longer splits the sequence around a manual dispatch. Conformance compares the resulting `agentActivity[]` IDs, order, and core fields with the canonical events for every lead host.
867
867
 
868
868
  Approval blockers use `open`, `answered`, `resolved`, and `obsolete`. `open` blocks until the user judges. `answered` with `accept-risk` / `select` / `answer` does not block approval or the next phase; the DISAGREE votes stay on the plan item as evidence. A response sidecar with a proceeding disposition unblocks the same way. `request-revision` and `reject` still withhold the next phase until this report's `supersessionLedger` records that the answer was incorporated (`superseded` or `no-dependent-statement`).
869
869
 
package/docs/cli.md CHANGED
@@ -790,6 +790,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
790
790
  | `okstra convergence collect-results --plan <round-plan.json> --mode <adversarial\|collaborative> --result <worker>=<path>… --run-manifest <run-manifest.json> --output <round-results.json>` | Read one round's worker responses into the `apply-round --results` shape. `--mode` picks the verdict vocabulary — the adversarial prompt answers `REFUTED` / `SURVIVES` / `SURVIVES-WITH-CAVEAT` / `UNVERIFIABLE`, which this maps to `disagree` / `agree` / `supplement` / `unverifiable`, and copies `**Basis**` into `disagreeBasis`. `--dispatch` supplies the terminal status and duration, which live in the dispatch rather than the response; a worker that never returned gets a `--dispatch` and no `--result`. Exits 2 on a dispatched finding with no verdict, a verdict for a finding the plan did not dispatch to that worker, a planned worker with no recorded outcome, or a vote with no explanation |
791
791
  | `okstra convergence apply-round --work-state <path> --plan <path> --results <path>` | Validate one complete structured result set and atomically reduce it into working state |
792
792
  | `okstra convergence critic-prompt --run-manifest <path>` | Render the coverage-critic task instructions to stdout; the lead writes the output verbatim into the file the prompt materializer's `--instruction` takes. The body carries the run's Round 0 consolidated findings, one line per Phase 4 analyser (worker id, result path, its finding ids), the two mandates plus the `duplicateOf` declaration rule, and — on an implementation-planning re-run — an already-covered index of the prior report's requirement-coverage row ids, clarification row ids, and stage titles (ids and titles only, never body text). Emits the `**Prompt Delivery Mode:**` header and the `Primary analysis packet` line the critic dispatch contract requires, so no part of the critic body is hand-written. Exits 2 when the run has published no grouping yet, or when the manifest carries no `analysisPacketPath` |
793
+ | `okstra convergence reverify-prompt --run-manifest <path> --plan <round-plan.json> --worker <worker-id>` | Render one worker's reverify task instructions to stdout; the lead writes the output verbatim into the file the prompt materializer's `--instruction` takes. The body carries the round's mandate (adversarial or collaborative, from the grouping's `config`), the plan row's findings in plan order — each with its summary, origin worker, cited-evidence line, the origin worker's result file and `### <item-id>` section, and the origin audit sidecar the verifier is told it may open — and the response format the collector parses. Exits 2 when the plan dispatches nothing to that worker, names a finding the grouping lacks, or is not a `dispatch` plan |
793
794
  | `okstra convergence apply-critic-gaps --work-state <path> --results <path>` | Apply one verified coverage-critic batch after the main queue reaches a terminal state |
794
795
  | `okstra convergence finalize --work-state <path> --output <path>` | Materialize the terminal schema v1.3 convergence state |
795
796
  | `okstra convergence validate --state <path> --kind <working\|final>` | Validate replayable working state or a terminal final state |
@@ -800,7 +801,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
800
801
  | `okstra plan-items derivations --data <data.json> --response <user-response sidecar> [--clarification C-NNN]` | List the plan statements an answered clarification may have falsified. Extracts the symbols, paths, and ids the answer names (backticked spans plus `R-001` / `DEV-10174`-style ids) and reports every string in the plan body that mentions one, as a JSON pointer plus excerpt. Advisory: it says where a decision's subject is mentioned, never which mentions are now wrong — the supersession rule (`prompts/profiles/_common-contract.md`) requires the author to enumerate before editing, and this supplies the enumeration |
801
802
  | `okstra plan-items <prepare\|prompt\|validate-prepared> --run-manifest <path> …` | Bind the implementation-planning verification queue to the run manifest. `prepare` extracts the exact queue from `--narrative` and, when `designPreparation.mode` is `no-design-inputs` and the Stage Map has one row, flips `convergence.planBodyVerification.gating` to `false` (stdout `Gating`). `prompt` emits its fixed lossless view, ending with the parser-facing `## Response format` block so the block reaches every verifier with the queue; `validate-prepared` proves the prepared queue still matches the narrative. With `--state <plan-body-verification.json>` the round is a re-verification: `prepare` also carries each queued item's recorded votes and `selfFixNote` into the envelope as `priorRounds`, `prompt` renders them as that item's `**Prior round dissent**` block behind a re-verification preamble, and `validate-prepared` re-derives the carry and rejects an envelope that dropped it. Python resolves the convergence-owned state path, so model callers never choose it. |
802
803
  | `okstra plan-items seed --narrative <report-narrative.md> --state <plan-body-verification.json> [--prior-state <previous plan-body-verification.json>]` | Create the convergence-owned `planBodyVerification.planItems[]` rows every verdict lands in, from the same deterministic extraction `extract` uses. The historical v2 form is `--data <data.json>`. Idempotent by id: an existing row keeps its verdicts and carried fields. Reports `seeded` / `existing` counts. `--prior-state` carries the previous **run**'s verdicts into this one: a newly seeded item whose `contentHash` equals that run's `verifiedContentHash` for the same id inherits its `verdicts[]` and is tagged `carriedForwardFromSeq` with the seq read off the prior filename, so round 1 does not re-judge text nobody changed. A matching id alone never carries — `P-*` ids are positional and shift. It requires `--state`, refuses a prior state whose task root differs from the one `--state` lives under (the state file carries no task identity, so its path is the only identity there is), and when it carries anything it rewrites the sibling `plan-items-*.json` `dispatchQueue` the way `incremental-carry` does. Adds `carried` / `carriedForwardFromSeq` to the reported counts. |
803
- | `okstra plan-items apply-verdicts --state <plan-body-verification.json> --result <worker-id>=<result.md>… --round <N>` | Read each worker's Markdown verdict directly, validate every current `P-*` id, reject duplicate worker submissions, and overwrite that round's stored verdicts. The historical `--verdicts <file>` form remains automation compatibility only. |
804
+ | `okstra plan-items apply-verdicts --state <plan-body-verification.json> --result <worker-id>=<result.md>… --round <N>` | Read each worker's Markdown verdict directly, validate every current `P-*` id, reject duplicate worker submissions, and overwrite that round's stored verdicts. The historical `--verdicts <file>` form remains automation compatibility only. Without `--append` every recorded verdict row of the queued items is replaced; when a row belongs to a round `complete-round` never closed, the command refuses before writing and names the `complete-round --round <M>` to run first. `--discard-open-rounds` replaces anyway — the recovery path when those rounds are being re-applied from their result files in order (the discarded rows are printed). |
804
805
  | `okstra plan-items complete-round --state <plan-body-verification.json> --run-manifest <current-run-manifest.json> --round <N> [--self-fix-note <item-id>=<markdown-file>]… [--self-fix-group <cause-file>=<item-id>[,<item-id>...]]… [--self-fix-stop-reason <all-resolved\|no-progress\|max-rounds-reached>]` | After `plan-verify` succeeds, atomically derive and record the round's per-item votes, gate result, participant counts from the actual assigned roster, immutable completion time, convergence history, and optional self-fix notes/groups read from Markdown files. `--self-fix-group` requires `--self-fix-stop-reason` — there is no default. `--self-fix-stop-reason` alone records a stop for a round that rewrote nothing and leaves `selfFixGroups` / `selfFixRoundsApplied` untouched. Models do not write the state JSON. Stdout also carries `nextDispatch`. |
805
806
  | `okstra plan-items next-dispatch --state <plan-body-verification.json> [--run-manifest <path>]` | After `apply-verdicts`, decide whether this round opens a worker batch. `kind: none` — missing-dependency `UNVERIFIABLE` only, no new batch. `kind: worker-correction` — re-prompt only those workers; peers stay idle. `kind: critic-tie` — unsettled analyser 1-1 on a run that rostered a critic, `critic-worker` on those item ids only. `kind: user-decision` — the same 1-1 on a run with no critic rostered: no in-band vote can break it, so open one `okstra approval-decision open` per item (classification `noncritical-dissent`) plus its `## 1. Clarification Items` row and dispatch no further verification for them. `--run-manifest` is what tells the two apart (`invocationAssignments` `critic/*`); without it the answer stays `critic-tie`. A missing path is never environment-unverifiable. |
806
807
  | `okstra plan-items correction-prompt --state <plan-body-verification.json> --run-manifest <path> --worker <id>` | Emit the planning-time environment-gap paragraph, then the assigned queue. The environment exception is first. Exits 2 unless `next-dispatch` named that worker as a blanket-UNVERIFIABLE correction target. |
@@ -846,7 +847,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
846
847
  | `okstra approval-decision <open\|resolve\|carry> --ledger <approval-decisions.json> …` | Write the lead-owned clarification and approval ledger. `open` validates classification-specific dispositions and complete option fields, `resolve` requires real `A-NNN` check references, and `carry` keeps prior resolved decisions outside the active clarification list. `carry --from-responses <instruction-set/clarification-response.md>` is the source of truth for an answer given in an earlier run: the bundle is task-level and cumulative, each response section names the report that posed the question, and `--clarification-id` repeats to carry several ids in one call. `carry --source-ledger` remains for a prior run's ledger that is still on disk and needs `--source-run-ref`. Prepare seeds `carriedDecisions[]` itself when it creates a run's ledger from a `--clarification-response` that names a report record — every row that record answered or resolved, plus rows its user-responses sidecars answered (`scripts/okstra_ctl/approval_decisions.py` `seed_carried_decisions`) — so `carry` is for ids that record does not answer. |
847
848
  | `okstra design-snapshot --narrative <report-narrative.md> --output <design-preparation.json>` | Detect implementation-planning design surfaces and write the detector-owned snapshot consumed by final report assembly. |
848
849
  | `okstra plan-verify --narrative <report-narrative.md> --state <plan-body-verification.json>` | Recompute the plan-body gate from the convergence-owned state before `data.json` publication. `--report <historical-data.json>` remains the v2 reader. |
849
- | `okstra report-finalize --project-root <dir> --run-manifest <path> --report <final-report.md>` | Run Phase 7 in the manifest's contract order. Contract v3 collects usage into team state, assembles all single-owner inputs into `data.json` once, then checks, renders, spawns follow-ups, validates, records the run's conclusion and the group's start order into the task-group's `group-context.md` (`record-group-memory`, creating the file when absent), and tears down eligible stage worktrees. Contract v2 retains its historical in-place projection sequence as a read-only compatibility path. A failed step still runs every later check through `validate-run`; `record-group-memory` and `teardown-stages` are skipped so a failed run neither hands an unvalidated conclusion to sibling tasks nor reclaims worktrees. The result carries `nextInGroup` (the first task in start order not yet started) and, for a terminal pointer, `nextCommand` closes on starting it from its brief. Reports each step and prints the ordered `--only` recovery tail from the earliest failure. This is the shared path for every lead adapter. |
850
+ | `okstra report-finalize --project-root <dir> --run-manifest <path> --report <final-report.md>` | Run Phase 7 in the manifest's contract order. Contract v3 collects usage into team state, assembles all single-owner inputs into `data.json` once, checks the English source, translates (`translate`: for a non-English `reportLanguage` it materializes and dispatches the translator worker unless the `*.i18n.<lang>.json` sidecar already exists, and fails when the worker leaves none), then renders with that sidecar overlaid, spawns follow-ups, validates, records the run's conclusion and the group's start order into the task-group's `group-context.md` (`record-group-memory`, creating the file when absent), and tears down eligible stage worktrees. Contract v2 retains its historical in-place projection sequence as a read-only compatibility path. A failed step still runs every later check through `validate-run`; `record-group-memory` and `teardown-stages` are skipped so a failed run neither hands an unvalidated conclusion to sibling tasks nor reclaims worktrees. The result carries `nextInGroup` (the first task in start order not yet started) and, for a terminal pointer, `nextCommand` closes on starting it from its brief. Reports each step and prints the ordered `--only` recovery tail from the earliest failure. This is the shared path for every lead adapter. |
850
851
  | `okstra render-views <final-report.data.json\|final-report.md>` | The Phase 7 `render-views` step, runnable on its own. Schema v2 data is rendered directly, and schema v3 data uses the same always-generated, task-specific human HTML path. The full reading copy uses `templates/reports/final-report-v2.template.md` and is rendered on demand with `okstra render-final-report`. Passing the Markdown sibling locates the same data.json. Schema v1 and quick reports keep the legacy conditional renderer. The Node wrapper calls `scripts/okstra-render-report-views.py`; `validators/validate-report-views.py` verifies source/schema/template digests, required human fields, form controls, external assets, diagram/table ID parity, Response ID parity, and that every in-page `href="#…"` lands on an element of the page. For a non-English report the command prints two counts: `translated N string(s) into <lang> (M left in English, K unresolved)` from the sidecar overlay, and `rendered R line(s) still in English on the <lang> page` from the written page itself — the second sees fields the extractor does not offer, so `M = 0` with `R > 0` means a reader-facing key is missing from `PROSE_KEYS`. |
851
852
  | `okstra design-prep <list\|show\|write>` | Review AI-prepared implementation design requests, inspect their effective confirmed response, or append a confirmed user/wizard response without editing the planning report |
852
853
  | `okstra wizard <init\|step\|render-args\|confirmation\|outcome> --state-file <path>` | Interactive input state machine for okstra-run, implemented by `okstra_ctl.wizard`. Seed a state file with `init`, then repeatedly call `step --answer <val>` to receive the next `Prompt` JSON. `--answer` is **required**; use `--no-submit` to peek at the next prompt without submitting a response. A `pick` with more choices than the host picker can display keeps `kind: "pick"` but adds `presentation: "numbered-text"`; render every option as a numbered Markdown list and submit the user's 1-based number, exact value, or exact label. Invalid, out-of-range, and ambiguous answers re-prompt without dropping choices. `render-args` returns the final `render-bundle` argument map, and `confirmation` returns the user echo block. On a completed wizard, `outcome` returns `renderArgs`, `persistActions`, and `confirmationText` together; project/global release-handoff PR-template persistence appears as `persistActions[].command == "config.set"`. For an `implementation` task type, `stage_pick` follows `approved_plan_pick` and selects the stage before `executor_pick`. The brief step appears only for entry task types—requirements-discovery, error-analysis, improvement-discovery, project-analysis, feature-analysis, and change-impact-analysis. Analysis inputs use `feature_evidence_pick` / `feature_evidence`, `project_evidence_pick` / `project_evidence`, and `analysis_target_pick` / `analysis_target`; a revision-requested report prioritizes its same-task, same-type rerun. Downstream lifecycle phases automatically carry the manifest brief, with a three-option `brief_carry` fallback when none is registered; `release-handoff` has no brief and enters multi-select `handoff_stage_pick` for eligible stage groups or the whole task |
@@ -352,13 +352,15 @@ Important modules:
352
352
  | `convergence_engine.py` | pure `ConvergenceEngine` reducer — seeds Round 0 working state, plans roster-aware rounds, applies structured outcomes and one critic-gap batch, finalizes schema v1.3, and validates replayable state without dispatch or filesystem ownership |
353
353
  | `convergence_store.py`, `convergence_migration.py` | atomic JSON persistence plus legacy/new-engine seed decisions; valid terminal finals are reused, while invalid state requires byte-preserving archival before restart |
354
354
  | `convergence.py` | `okstra convergence` internal CLI orchestration for `seed`, `plan-round`, `apply-round`, `critic-prompt`, `apply-critic-gaps`, `finalize`, `validate`, and `example`; it composes the reducer, store, and migration policy without duplicating their decisions |
355
+ | `convergence_reverify_prompt.py` | renders one worker's reverify instruction body `okstra convergence reverify-prompt` prints — the round mandate, the plan row's findings with each origin worker's result file, item id, and audit sidecar (declared openable), and the collector's response format. Replaces the hand-written instruction whose abbreviated evidence line and `- Verdict:` format cost a round |
355
356
  | `convergence_critic_prompt.py` | renders the coverage-critic seed body `okstra convergence critic-prompt` prints — the Round 0 consolidated findings, one line per Phase 4 analyser (worker id, result path, its finding ids), the two mandates plus the `duplicateOf` rule, and, on an implementation-planning re-run, an already-covered index of the prior report's requirement-coverage row ids, clarification row ids, and stage titles. Ids and titles only; the prior report's body is never copied |
356
357
  | `plan_items.py`, `plan_items_cli.py` | deterministic extraction of the report-writer narrative `P-*` plan-item queue plus the `okstra plan-items extract` / `validate` / `seed` / `collect-verdicts` / `apply-verdicts` / `derivations` adapter; v2 data.json remains a read input |
357
358
  | `claim_reproduction.py` | reproduces a plan-body single-vote `fact` claim before it can block on one vote — runs the declared probe (`path-exists` / `path-absent` / `literal-present` / `literal-absent` / `citations-differ`) inside the resolved project root and returns `reproduced` / `not-reproduced` / `not-runnable`, which `plan-items apply-verdicts --run-manifest` writes into `reproductionResult` (always overwriting the worker-sent value so a verifier cannot score its own claim). A `judgement` claim, or a `fact` that does not reproduce, takes the quorum route |
358
359
  | `plan_derivations.py` | the supersession sweep `_common-contract.md` requires an author to do by hand — extracts the symbols, paths, and ids an answered clarification names and reports every plan string that mentions one. Advisory: it locates candidates and never judges which are now false |
359
360
  | `scope_provenance.py` | single source of truth for the scope-provenance grammar every phase-emitted requirement must declare, shared by `validators/validate-run.py` and `validators/validate_fanout.py` so the planning report and fan-out packets cannot drift |
360
361
  | `worker_artifact_paths.py` | canonical worker artifact path derivation (e.g. `audit_sidecar_rel` inserts `-audit-` after the first `-worker-` token), so dispatch and validation agree on non-canonical-path rejection |
361
- | `report_finalize.py` | Phase 7 post-report sequence **SSOT** runs `check-source` → `token-usage` `render-views` `spawn-followups` `validate-run` `record-group-memory` `teardown-stages` in that load-bearing order. A non-zero exit still runs every later check through `validate-run` and names the earliest failure; `record-group-memory` (this run's conclusion into the task-group's `group-context.md`, plus `nextInGroup` for the closeout) and `teardown-stages` are skipped when any earlier step failed. Both lead paths converge here: the Codex adapter calls it in-process (`codex_dispatch`), a Claude-led run reaches it through `okstra report-finalize`. Neither reimplements the sequence |
362
+ | `report_translation_dispatch.py` | Phase 7 `translate` stepfor a non-English `reportLanguage` and no `*.i18n.<lang>.json` sidecar, reuses or materializes this run's translator reservation (`agent-prompt materialize --audience translator` in-process, instruction file under `state/`), runs the CLI-wrapper dispatch, and succeeds only when the sidecar exists afterwards. Replaces the manual lead sequence that was skipped in practice |
363
+ | `report_finalize.py` | Phase 7 post-report sequence **SSOT** — runs `check-source` → `translate` → `token-usage` → `render-views` → `spawn-followups` → `validate-run` → `record-group-memory` → `teardown-stages` in that load-bearing order. A non-zero exit still runs every later check through `validate-run` and names the earliest failure; `record-group-memory` (this run's conclusion into the task-group's `group-context.md`, plus `nextInGroup` for the closeout) and `teardown-stages` are skipped when any earlier step failed. Both lead paths converge here: the Codex adapter calls it in-process (`codex_dispatch`), a Claude-led run reaches it through `okstra report-finalize`. Neither reimplements the sequence |
362
364
  | `wrapper_status.py` | worker wrapper status sidecar reader — the host-side reader of the sidecar `worker_runner.py` writes. `is_terminal` is the one question it answers for the dispatch record and the pane reclaim: does `stage` read `exited` |
363
365
  | `worker_runner.py` | runs one worker CLI and records what happened — shared by every provider entrypoint. Owns the `selectors` pump over the child's streams, the stream-arrival idle watchdog (`killpg` on breach), the run-wide progress cap on the log copy, and the status sidecar's whole life. A run that dies after launch still closes its sidecar, so `worker_liveness` never reads a dead worker as running |
364
366
  | `session_transcript.py` | worker session transcript — one line per event (time, speaker, body) with a run-wide progress-line cap (`LOG_LINE_CAP`, elision notice) so a single-file dispatch's tool echo cannot dominate the project's `.okstra/` bytes; the fixed shape lets a later lead write share the same file |
@@ -34,7 +34,7 @@ flowchart TD
34
34
 
35
35
  `okstra-run` does not call `scripts/okstra.sh`. Instead it goes through `okstra wizard` and `okstra render-bundle` and converges on the same single Python entrypoint, `prepare_task_bundle()`.
36
36
 
37
- Launch selection is role slots and model refs, not a provider roster. The wizard asks role counts (`min..max`, default **recommended**), then `--role-model <role>=<provider>/<model>` per slot. current-session lead is this session and is listed on the confirmation summary. Roles with `min = 0` stay closed unless the user adds them. There is no provider multi-pick and no `Use defaults / Customize` fork for worker selection. `--workers` is compatibility-only. `lead` is a compatibility alias for `leader`. `executor` is a compatibility alias for `implementer`. New records write `leader` and `implementer`.
37
+ Launch selection is role slots and model refs, not a provider roster. The wizard asks one screen per static role: a checkbox of candidate models for a role that runs several instances (the number checked is the instance count, rendered as `--role-count <role>=<N>` plus one `--role-model <role>=<provider>/<model>` per checked model; the label states the profile range and recommended count), a single pick for a fixed single-instance role. current-session lead is this session and is listed on the confirmation summary. Roles with `min = 0` stay closed unless the user adds them. There is no provider multi-pick and no `Use defaults / Customize` fork for worker selection. `--workers` is compatibility-only. `lead` is a compatibility alias for `leader`. `executor` is a compatibility alias for `implementer`. New records write `leader` and `implementer`.
38
38
 
39
39
  ## 3. task-type documents
40
40
 
@@ -75,7 +75,7 @@ The last column is the `workflow.nextRecommendedPhase` pointer Phase 7 leaves be
75
75
  |---|---|---|---|---|
76
76
  | `requirements-discovery` | common questions only | profile/brief/base-ref exist | multi-worker analysis, convergence 1 round default | `ready` at `error-analysis` or `implementation-option-selection`; `pending` when neither is settled |
77
77
  | `error-analysis` | common questions only | profile/brief/base-ref exist | multi-worker analysis, convergence 2 rounds default | `ready` at `implementation-option-selection`, or at `error-analysis` while the investigation continues |
78
- | `implementation-option-selection` | comparison or preselected-validation context | stable brief IDs and at least three analysers | read-only candidate validation, exact coverage, separate direction confirmation | `ready` at `implementation-planning`, `pending` on `pending-direction-selection`, or `blocked` |
78
+ | `implementation-option-selection` | comparison or preselected-validation context | stable brief IDs and at least three analysers | read-only candidate validation, exact coverage, separate direction confirmation | `ready` at `implementation-planning` (a confirmed direction, or ranked candidates the planning wizard lets the user pick from), `pending` when no candidate was ranked, or `blocked` |
79
79
  | `implementation-planning` | selected-direction report for a new plan | selection report/sidecar/digest or same-task planning rerun | one-direction realization + Phase 6 plan-body verification | `ready` at `implementation` on approvable `plan-ready` (`awaitingApproval` until the user flips `approved`); `blocked` when the gate is blocking or a `Blocks=approval` row is open; `ready` at `implementation-option-selection` on `direction-invalidated` |
80
80
  | `implementation` | approved plan, stage multi-pick, executor | approved marker, Stage Lifecycle Snapshot, stage-key reservation, QA command deny-list | one run = one stage; executor writes in isolated stage worktree, verifiers read-only | `ready` at the stage report's `routingRecommendation.target` — `final-verification` on a clean stage |
81
81
  | `final-verification` | approved plan, stage pick (whole-task or single-stage) | `VERIFICATION_TARGET` resolved; whole-task auto integration/teardown or single-stage worktree reuse | whole-task may integrate stages first; analyser verification itself is read-only | `ready` at `release-handoff` on an `accepted` verdict, otherwise at the phase owning the defect; `terminal` on `done` |
@@ -58,7 +58,7 @@ stateDiagram-v2
58
58
  BaseRef --> ImplementationExtras: implementation only
59
59
  BaseRef --> LeaderSession: non-implementation
60
60
  ImplementationExtras --> LeaderSession
61
- LeaderSession --> RoleSlots: role-count then role-model
61
+ LeaderSession --> RoleSlots: one model screen per role
62
62
  RoleSlots --> OptionalInputs: directive / related / clarification
63
63
  OptionalInputs --> Confirm
64
64
  Confirm --> EditTarget: Edit
@@ -151,9 +151,8 @@ flowchart TD
151
151
  T[task-type selected] --> W{active worktree in registry?}
152
152
  W -->|yes| Reuse[reuse existing worktree<br/>base-ref prompt skipped]
153
153
  W -->|no| Base[ask base-ref<br/>validate with git rev-parse]
154
- Base --> C[role-count min..max<br/>omit uses recommended]
155
- Reuse --> C
156
- C --> M[role-model provider/model per slot]
154
+ Base --> M[one screen per static role<br/>checkbox: models checked = instances<br/>single pick: fixed single role]
155
+ Reuse --> M
157
156
  M --> O[directive / related / clarification]
158
157
  O --> Special{release-handoff?}
159
158
  Special -->|yes| PR[PR template override/scope]
@@ -161,7 +160,7 @@ flowchart TD
161
160
  PR --> Confirm
162
161
  ```
163
162
 
164
- Launch selection is role slots and model refs. The wizard does not show a provider roster multi-pick and does not fork on `Use defaults / Customize` for workers. Omitting `--role-count` keeps each static role at its profile **recommended** count within `min..max`. Duplicate model refs in the same role are rejected. `--workers` remains a CLI compatibility input only.
163
+ Launch selection is role slots and model refs. The wizard does not show a provider roster multi-pick and does not fork on `Use defaults / Customize` for workers. A multi-instance role is one checkbox screen whose checked models become the instances (the wizard renders `--role-count` from that number); on the CLI, omitting `--role-count` keeps each static role at its profile **recommended** count within `min..max`. Duplicate model refs in the same role are rejected. `--workers` remains a CLI compatibility input only.
165
164
 
166
165
  Worktree rules differ per phase. From `requirements-discovery` through `implementation-planning`, the task-key worktree is reused. `implementation` uses the task-key worktree as an anchor but does the actual execution isolated one stage at a time in a stage-key (`stage-<N>`) worktree and the `runs/implementation/stage-<N>/` deliverables. `final-verification --stage N` reuses that implementation stage worktree as a read target, and whole-task mode auto-integrates the stage commits into the task-key worktree and then builds the verification target. The Stage Lifecycle Snapshot is a read-side view that does not change this storage structure.
167
166
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.191.2",
3
+ "version": "0.193.0",
4
4
  "description": "Host-aware multi-provider cross-verification orchestrator runtime and agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.191.2",
3
- "builtAt": "2026-09-09T06:51:26.651Z",
2
+ "package": "0.193.0",
3
+ "builtAt": "2026-09-09T09:27:49.565Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -7,7 +7,7 @@ description: |
7
7
  Context: okstra finished Phase 6 with `meta.reportLanguage: "ko"` and is entering Phase 7.
8
8
  user: "okstra this task bundle"
9
9
  assistant: "Phase 7 — dispatching translator-worker to write the ko translation sidecar."
10
- <commentary>The okstra skill dispatches this agent before `okstra report-finalize` so `render-views` has a sidecar to overlay.</commentary>
10
+ <commentary>`okstra report-finalize` dispatches this agent in its `translate` step so `render-views` has a sidecar to overlay.</commentary>
11
11
  </example>
12
12
  color: cyan
13
13
  model: inherit
@@ -56,7 +56,7 @@ For every other task type:
56
56
  - Pointer `status: ready` → `/okstra-run` for that `phase`. Quote the pointer's `rationale` — that sentence is this run's report saying why that phase comes next, and it is the analysis the user asked for.
57
57
  - Pointer `status: terminal` → the lifecycle ends here. Say the task is finished and quote the pointer's `rationale`. Do not propose a run and do not send the user to `/okstra-inspect`: the decision is already made, so there is nothing to inspect. If this run registered follow-up tasks, name them and the command that starts one.
58
58
  - Pointer `status: blocked` → the pointer's `rationale` names what is in the way and what to run. Quote it and issue that command — `/okstra-user-response` for the `C-NNN` ids it lists, or `/okstra-run` for the phase it names.
59
- - Pointer `status: pending` with a `rationale` → the comparison finished and the user's own choice is what comes next. Quote the `rationale` and issue the command it names. Do not re-run the phase that just completed, and do not send the user to `/okstra-inspect`: a finished phase has nothing to inspect, and re-running it discards the result the user is being asked to choose from.
59
+ - Pointer `status: pending` with a `rationale` → the user's own input is what comes next. Quote the `rationale` and issue the command it names. Do not re-run the phase that just completed, and do not send the user to `/okstra-inspect`: a finished phase has nothing to inspect, and re-running it discards the result the user is being asked to act on.
60
60
  - Phase 7 `validate-run` failed → one line naming the blocking cause, then `/okstra-run` to re-run this phase. Only a failure matching the blocking allowlist (`okstra_ctl.blocking_checks`) reaches this row; every other finding was demoted to an advisory, printed as `validate-run: advisory — <finding>`, and the run passed. Name those advisories in one line and take the command from the matching pointer row above — an advisory is not a reason to re-run.
61
61
  - Otherwise → `/okstra-inspect status` for this task.
62
62
 
@@ -240,7 +240,9 @@ Every finding re-verification, coverage critic, acceptance critic, and critic-ve
240
240
 
241
241
  For every finding reverify row and critic-gap verification row, first write a
242
242
  call-specific task-instructions file under the current run's `state/`
243
- directory. For a v2 run, take `participantRef` and
243
+ directory for a finding reverify row, the verbatim output of `okstra
244
+ convergence reverify-prompt --run-manifest <run-manifest> --plan
245
+ <round-plan.json> --worker <workerId>`. For a v2 run, take `participantRef` and
244
246
  `sourceRoleExecutionRef` from the canonical round-plan dispatch row or the
245
247
  matching canonical working-state worker row. That stored reference is the
246
248
  selected source `RoleExecution` row's `roleExecutionRef`, not that row's
@@ -295,7 +297,7 @@ Call `await_workers(handles)` through the same adapter and apply the shared term
295
297
 
296
298
  **Enforced:** `verify_agent_invocation` in `scripts/okstra_ctl/agent/invocation.py` and `validate_reverify_prompt` in `scripts/okstra_ctl/worker_prompt_contract.py` validate generated delivery before dispatch.
297
299
 
298
- Use `okstra agent-prompt materialize` for every reverify prompt. Write only the assigned claims, evidence, questions, and the response format in the instruction file. The materializer generates path headers through `worker_prompt_headers`, the source Worktree from the active run, and the execution identity from the run manifest. It also generates the model, task type, and exact `workflow.forbiddenActions` before the instructions. These values are checked by `verify_agent_invocation` and `validate_reverify_prompt` before publication and dispatch.
300
+ Use `okstra agent-prompt materialize` for every reverify prompt. Render the instruction file with `okstra convergence reverify-prompt --run-manifest <run-manifest> --plan <round-plan.json> --worker <worker-id>` and write its output verbatim; it carries the round's mandate, every planned finding with the origin worker's result file, item id, and audit sidecar (which the verifier is told it may open), and the response format the collector parses. Do not hand-copy evidence or the response format: a hand-copied `**Cited evidence**` line carried part of the origin's citation, so the verifier judged the lead's transcription and refuted five claims as `burden-not-met`, and a hand-written `- Verdict:` format cost the same round (2026-09-09). **Enforced (rendering):** `okstra_ctl.convergence_reverify_prompt`. **Enforced (pre-dispatch):** the rendered body's first line is `**Rendered by:** okstra convergence reverify-prompt`, and `validate_reverify_prompt` in `scripts/okstra_ctl/worker_prompt_contract.py` refuses a `reverify-r*` instruction without that line — a hand-written instruction cannot be materialized. The materializer generates path headers through `worker_prompt_headers`, the source Worktree from the active run, and the execution identity from the run manifest. It also generates the model, task type, and exact `workflow.forbiddenActions` before the instructions. These values are checked by `verify_agent_invocation` and `validate_reverify_prompt` before publication and dispatch.
299
301
 
300
302
  The generated `**Project Root:**` owns .okstra artifacts; `**Worktree:**` names the source checkout. Use the prompt's `**Invocation metadata path:**` for its invocation metadata. The result passed as `--result` is the worker's own result and carries the canonical `-worker-` token used by `audit_sidecar_rel`. Reverify has one result path, so omit `--audit-source`. The materializer supplies the audit, errors, read scope, and provider-specific plain-file write instructions.
301
303
 
@@ -334,9 +336,13 @@ This is the single largest avoidable cost in `requirements-discovery`, `error-an
334
336
 
335
337
  ### Lightweight Re-verification Prompt
336
338
 
339
+ Rendered by `okstra convergence reverify-prompt` when `config.adversarial` is false; the block below is the reference shape, and the rendered body additionally carries each finding's `**Origin item**` and `**Origin audit sidecar**` lines.
340
+
337
341
  ```
338
342
  ## Instructions
339
343
 
344
+ **Rendered by:** okstra convergence reverify-prompt
345
+
340
346
  Perform re-verification for <task-key> (round <N>).
341
347
 
342
348
  Review the following findings discovered by other workers.
@@ -374,11 +380,13 @@ For each finding, respond as:
374
380
 
375
381
  ### Adversarial Re-verification Prompt
376
382
 
377
- Used instead of the lightweight/full-reanalysis prompt when `config.adversarial == true`. The required anchor headers (§"Required reverify-prompt anchor headers") are identical. The `[Required reading]` clause is suppressed; only the cited-evidence paths of the items under attack are injected (see §"Adversarial Verification Mode" → Scoped full-reanalysis).
383
+ Used instead of the lightweight/full-reanalysis prompt when `config.adversarial == true`. Rendered by `okstra convergence reverify-prompt`; the block below is the reference shape, and the rendered body additionally carries each finding's `**Origin item**` and `**Origin audit sidecar**` lines. The required anchor headers (§"Required reverify-prompt anchor headers") are identical. The `[Required reading]` clause is suppressed; only the cited-evidence paths of the items under attack are injected (see §"Adversarial Verification Mode" → Scoped full-reanalysis).
378
384
 
379
385
  ```
380
386
  ## Instructions
381
387
 
388
+ **Rendered by:** okstra convergence reverify-prompt
389
+
382
390
  Perform ADVERSARIAL re-verification for <task-key> (round <N>).
383
391
 
384
392
  Your job is to BREAK each finding below, not to confirm it. For EACH finding,
@@ -430,6 +438,8 @@ UNVERIFIABLE is **not** `verification-error`. A verifier that opened the evidenc
430
438
  ```
431
439
  ## Instructions
432
440
 
441
+ **Rendered by:** okstra convergence reverify-prompt
442
+
433
443
  Perform deep re-verification for <task-key> (round <N>).
434
444
 
435
445
  Independently verify the following findings by examining the original materials.
@@ -465,7 +465,7 @@ The detailed persistence sequence lives in [report-writer](./report-writer.md).
465
465
  Order of operations:
466
466
 
467
467
  1. Run `okstra report-finalize ...`. Contract v3 collects usage, assembles the role-owned inputs into `data.json` once, checks the source, renders views, persists follow-ups, validates the run, records this run's conclusion into the task-group's `group-context.md` for the group's sibling tasks, and performs eligible teardown in order. When the task's pointer is terminal and the group has a task not yet started, the result's `nextInGroup` names it and `nextCommand` closes on starting it from its brief — the group's start order is the brief ordinal; the briefs' Related Task Graph edges are quoted as `waits for` and do not reorder it.
468
- 2. When `meta.reportLanguage` is not `en`, first run only `token-usage`, `project-activity`, and `check-source`. Dispatch the translator against that assembled record, then resume with only `render-views`, `spawn-followups`, `validate-run`, `record-group-memory`, and `teardown-stages` so assembly is not repeated.
468
+ 2. When `meta.reportLanguage` is not `en`, the same call dispatches the translator: its `translate` step (after `check-source`, before `render-views`) materializes the translator prompt when this run has no undispatched reservation, runs the CLI-wrapper dispatch, and requires the `*.i18n.<lang>.json` sidecar. Do not dispatch the translator by hand or split the sequence around it; if the step fails, clear the cause and resume with the `--only translate --only render-views …` hint the result prints (**Enforced:** `okstra_ctl.report_translation_dispatch`).
469
469
 
470
470
  Keep the assigned worker prompt history paths stable in `team-state`, `run-manifest`, and `task-manifest`. Do not rewrite prompt artifacts to `/tmp` or omit prompt metadata for attempted workers.
471
471
 
@@ -485,7 +485,7 @@ For every other task type:
485
485
  - Pointer `status: terminal` → the lifecycle ends here. Say the task is finished and quote the pointer's `rationale`. Do not propose a run and do not send the user to `/okstra-inspect`: the decision is already made, so there is nothing to inspect. If this run registered follow-up tasks, name them and the command that starts one.
486
486
  - Pointer `status: terminal` and the result carries `nextInGroup` → the task is finished and the task-group has a task not yet started: say this task is finished, then close on `/okstra-run` for `nextInGroup.briefId` from its `brief` path — the group's start order is the brief ordinal, and `nextCommand.note` already names the task and its brief.
487
487
  - Pointer `status: blocked` → the pointer's `rationale` names what is in the way and what to run. Quote it and issue that command — `/okstra-user-response` for the `C-NNN` ids it lists, or `/okstra-run` for the phase it names.
488
- - Pointer `status: pending` with a `rationale` → the comparison finished and the user's own choice is what comes next. Quote the `rationale` and issue the command it names. Do not re-run the phase that just completed, and do not send the user to `/okstra-inspect`: a finished phase has nothing to inspect, and re-running it discards the result the user is being asked to choose from.
488
+ - Pointer `status: pending` with a `rationale` → the user's own input is what comes next. Quote the `rationale` and issue the command it names. Do not re-run the phase that just completed, and do not send the user to `/okstra-inspect`: a finished phase has nothing to inspect, and re-running it discards the result the user is being asked to act on.
489
489
  - Phase 7 `validate-run` failed → one line naming the blocking cause, then `/okstra-run` to re-run this phase with the recorded sidecar. Only a failure matching the blocking allowlist (`okstra_ctl.blocking_checks`) reaches this row; every other finding was demoted to an advisory, printed as `validate-run: advisory — <finding>`, and the run passed. Name those advisories in one line and take the command from the matching pointer row above — an advisory is not a reason to re-run.
490
490
  - Otherwise → `/okstra-inspect status` for this task.
491
491
 
@@ -427,7 +427,7 @@ round before any host or provider process starts.
427
427
  - **Drop plan items whose element the round deleted.** A self-fix rewrite may remove a plan element (a validation check, a rollback row). `P-*` ids are positional, so a deletion shifts every later row and silently re-points surviving verdicts at their neighbours — and a verdict recorded against a removed element keeps blocking a gate while being unfindable in the plan, so reading the plan never reveals the cause. After each round, re-extract plan items with `okstra plan-items extract` and re-verify any item whose `subject` no longer matches; never carry the old vote forward across a shift. **Enforced:** `validators/validate-run.py` `_validate_verdicts_match_current_subjects` (re-pointing) and `_validate_plan_item_extraction_completeness` (dangling ids).
428
428
  - **Classify each cause group before instructing it (BLOCKING).** A group is either an *authoring* defect — the plan says something wrong, incomplete, or self-contradictory, which self-fix owns — or a *citation* defect, where the plan points at an analysis artifact incorrectly. Only the first is self-fix work. For the second the finding already exists and already went through convergence, so the fix is to re-cite the converged artifact; instructing report-writer to re-derive the fact means the author reads the source material and produces a **finding that never went through convergence**, which the plan then carries as if it had. That is the role boundary the lead contract draws ("keep analysis, execution, verification, and report authoring responsibilities distinct; return defects to the role that owns them"), and report-writer is authoring-only by its own contract. `P-Req-*` items with breakage kind `f` are where this goes wrong most often: the question is usually whether a coverage row points correctly at something already measured, not whether the measurement is right. State the classification in the group's instruction so the author knows which of the two it is being asked to do.
429
429
  - **A verdict older than the last self-fix is not a verdict unless the item's content is unchanged (BLOCKING).** A verdict cast in round 1 judged the text before the only automatic rewrite. Once that rewrite runs, a changed item's judgement is about a plan that no longer exists. `--round <N>` on `apply-verdicts` stamps each row and copies `contentHash` onto `verifiedContentHash`. `validators/validate-run.py` `_validate_verdict_rounds_outlive_self_fix` fails an in-scope item whose verdict round is at or before `selfFixRoundsApplied` **and** whose `contentHash` does not match `verifiedContentHash`. Matching hashes keep the prior verdict — that is what avoids a sweep round over unchanged stages. Deferred and observed items are out of the gate and do not need a post-self-fix verdict. **Enforced:** `_validate_verdict_rounds_outlive_self_fix`.
430
- - Lead re-runs plan-body verification, then records each worker Markdown result through `okstra plan-items apply-verdicts --state <plan-body-verification.json> --result <worker>=<result.md> --round <N>`. Score the result with `okstra plan-verify --narrative <report-writer-narrative.md> --state <plan-body-verification.json>`, then call `okstra plan-items complete-round --state <plan-body-verification.json> --run-manifest <current-run-manifest.json> --round <N>`. These commands fail on an assigned item the worker left unanswered, on a verdict for an item outside the queue, and on a duplicate worker result.
430
+ - Lead re-runs plan-body verification, then records each worker Markdown result through `okstra plan-items apply-verdicts --state <plan-body-verification.json> --result <worker>=<result.md> --round <N>`. Score the result with `okstra plan-verify --narrative <report-writer-narrative.md> --state <plan-body-verification.json>`, then call `okstra plan-items complete-round --state <plan-body-verification.json> --run-manifest <current-run-manifest.json> --round <N>`. These commands fail on an assigned item the worker left unanswered, on a verdict for an item outside the queue, and on a duplicate worker result. `apply-verdicts` without `--append` replaces every recorded row of the queued items, so it also refuses — before writing — when a row belongs to a round that `complete-round` never closed, naming the round to close first; the votes of a closed round live in `planItems[].rounds`. Skipping `complete-round` between rounds and applying the next one lost 19 items' round-1 votes (2026-09-09). `--discard-open-rounds` is only for re-applying the lost rounds from their result files in order. **Enforced:** `okstra_ctl.plan_items_cli._reject_uncompleted_round_loss`.
431
431
  - For a self-fix, record the correction through the typed convergence command rather than writing `selfFixNote` or `selfFixGroups` JSON. A resolved item does not create a clarification.
432
432
  - **Each round is a worker batch.** Before dispatching round N ≥ 2, reclaim the previous round's completed verifiers exactly as at any other batch boundary ([okstra-lead-contract](./okstra-lead-contract.md) "Run-scoped worker-resource lifecycle") and emit `PROGRESS: phase-batch-cleanup panes=<n>`, then announce the round with `PROGRESS: phase-5.5.9-plan-verify round=<N> items=<count>`. Saying a round will "reuse" the previous verifiers and then dispatching under fresh names leaves every prior round holding its panes — five rounds of that is what exhausts the pane budget and blocks the next dispatch. **Enforced:** `validators/validate_session_conformance.py` `_check_plan_verify_cleanup_checkpoints` requires both lines once the state file records two or more rounds.
433
433
  - **Round completion.** A round is complete only after `okstra plan-verify` exits 0 and `okstra plan-items complete-round` succeeds. A round left with a non-zero exit carries its defect into the next round's inputs. Exit 0 with a non-empty `advisories[]` is a complete round: those findings are recorded, not round-blocking (step 5, §"`failures[]` carries only the blocking findings"). Report assembly and rendering occur only after the convergence state is terminal. **Enforced:** `validators/validate-run.py` `_validate_plan_body_state_rounds` requires one stored round per round number and a corresponding item vote.
@@ -123,22 +123,25 @@ For historical schema-v1 Markdown only, the following heading table remains a re
123
123
 
124
124
  **Enforced:** `okstra_ctl.report_finalize.V3_STEP_ORDER` is the order — `report-finalize` runs the steps from that tuple, so the sequence cannot be reordered by a caller. Running the steps by hand is what this rule forbids, and that path is not reachable through the CLI.
125
125
 
126
- Do not run the eight steps below manually. Invoke `okstra report-finalize`; contract 3.0 runs them in this order:
126
+ Do not run the nine steps below manually. Invoke `okstra report-finalize`; contract 3.0 runs them in this order:
127
127
 
128
128
  1. **`token-usage`** — collect usage into team state without touching the final record.
129
129
  2. **`project-activity`** — report assembly validates every owner input and publishes the final record once.
130
130
  3. **`check-source`** — verify the assembled English source.
131
- 4. **`render-views`** — render the Markdown reading copy and human HTML.
132
- 5. **`spawn-followups`** — materialize registered follow-up tasks.
133
- 6. **`validate-run`** — validate the record, views, run manifest, and team state.
134
- 7. **`record-group-memory`** — write this run's conclusion (headline, decisions, watch-outs, open follow-ups, record path, next phase) and the group's start order into the task-group's `group-context.md` okstra region, creating the file when the group has none; skipped, like teardown, when an earlier step failed. Sibling tasks read it as `## Task-Group Memory`.
135
- 8. **`teardown-stages`** — remove eligible stage worktrees after successful validation.
131
+ 4. **`translate`** — for a non-English `reportLanguage`, materialize and dispatch the translator worker and require its `*.i18n.<lang>.json` sidecar; a no-op for English or when the sidecar already exists.
132
+ 5. **`render-views`** — render the Markdown reading copy and human HTML, with the translation sidecar overlaid.
133
+ 6. **`spawn-followups`** — materialize registered follow-up tasks.
134
+ 7. **`validate-run`** — validate the record, views, run manifest, and team state.
135
+ 8. **`record-group-memory`** — write this run's conclusion (headline, decisions, watch-outs, open follow-ups, record path, next phase) and the group's start order into the task-group's `group-context.md` okstra region, creating the file when the group has none; skipped, like teardown, when an earlier step failed. Sibling tasks read it as `## Task-Group Memory`.
136
+ 9. **`teardown-stages`** — remove eligible stage worktrees after successful validation.
136
137
 
137
138
  After `report-finalize` returns, the lead — not the report writer — closes the run with the launch prompt's User closeout: one command the user can run now.
138
139
 
139
- ### Before `report-finalize`: the translation sidecar
140
+ ### The translation sidecar: the `translate` step
140
141
 
141
- Never dispatch the translator before report assembly and `check-source`. For a non-English human report, first run `report-finalize --only token-usage --only project-activity --only check-source --only render-views`; the extraction command refuses to build a work list from a non-English source. `render-views` belongs in that first call: the HTML view renders from the report body and never reads the translation sidecar, so rendering it early costs one rerun and means a translator that never starts does not also cost the run its only human-readable artifact. Then dispatch the translator worker with `okstra agent-prompt materialize --audience translator --worker-id translator --dispatch-kind translator --assignment-ref translator --result <run>/worker-results/translator-translations-<task-type>-<seq>.md --audit-source <run>/worker-results/translator-worker-<task-type>-<seq>.md`, `okstra agent-prompt record-dispatch`, `okstra worker-dispatch --workers translator`, and `okstra agent-prompt link-result`. `--result` is the translator's own report and `--audit-source` the worker result the audit sidecar derives from; the two must differ, and the materializer refuses a translator prompt without a distinct `--audit-source` because `worker-dispatch` reads both from the prompt's `**Result Path:**` and `**Worker Result Path:**` anchors and refuses a prompt missing the second. A translator prompt that failed a pre-dispatch gate is re-materialized under the **same** invocation id with `--replace-undispatched`; a second invocation id leaves two undispatched reservations for one run, there is no command that withdraws a reservation, and `worker-dispatch` refuses to choose between them. **Enforced:** `_materialize_run` in `scripts/okstra_ctl/agent/prompt_cli/materialize.py`; `_translator_job_from_reservation` in `scripts/okstra_ctl/dispatch_core.py`. Resume with `report-finalize --only render-views --only spawn-followups --only validate-run --only record-group-memory --only teardown-stages`; do not assemble the record a second time. `render-views` is idempotent and overwrites the view it already wrote, this time with the translation overlaid. When the translator cannot run at all — a host approval gate, an unavailable provider — say so and finish the sequence anyway: the run keeps an English view rather than no view, and `report-finalize` inserts the missing `render-views` itself when a `--only validate-run` call would otherwise validate a report that has none (**Enforced:** `okstra_ctl.report_finalize._with_view_the_validator_reads`).
142
+ `report-finalize` dispatches the translator itself. Its `translate` step runs after `check-source` (the extractor refuses a work list from a non-English source) and before `render-views` (which overlays the sidecar), and it is a no-op when `reportLanguage` is `en` or the `*.i18n.<lang>.json` sidecar already exists. When the sidecar is missing, the step reuses this run's undispatched translator reservation if the lead already materialized one, otherwise it materializes the prompt itself instruction file `state/translator-instructions-<task-type>-<seq>.md` (kept when the lead wrote one), prompt `prompts/translator-worker-prompt-<task-type>-<seq>.md`, `--result worker-results/translator-translations-<task-type>-<seq>.md`, `--audit-source worker-results/translator-worker-<task-type>-<seq>.md`, invocation id `<task-type>-<seq>-translator` (`-r2`, `-r3` after a failed attempt) then runs the CLI-wrapper dispatch, which records the dispatch and links the result. Do not run `agent-prompt materialize --audience translator` or `worker-dispatch --workers translator` by hand before `report-finalize`; the sequence used to be a manual lead step and was skipped in practice (2026-09-09, fontsninja-v3-site dev-10628-3: a `ko` run finalized in one call, zero translator reservations, English HTML). **Enforced:** `okstra_ctl.report_finalize.V3_STEP_ORDER` places the step; `okstra_ctl.report_translation_dispatch.translate_report` owns it; `_translator_job_from_reservation` in `scripts/okstra_ctl/dispatch_core.py` still refuses two undispatched reservations for one run, which only a hand-made second reservation produces.
143
+
144
+ The step succeeds only when the sidecar exists afterwards; a translator that exits 0 without publishing it fails the step. A failed `translate` does not stop the sequence: `render-views` still writes the view from the English body, `validate-run` records the missing sidecar as an advisory, and the result's resume hint starts at `--only translate --only render-views …` — run that once the cause (a host approval gate, an unavailable provider) is cleared rather than closing the run on an English view.
142
145
 
143
146
  ## Routing pointer
144
147
 
@@ -46,7 +46,7 @@ template's check; that template is gone.
46
46
  - **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.
47
47
  - **External-source adapters — structure AND fixture both derive from a captured real sample; a self-authored fixture is NOT reality evidence:** when this run's diff builds or changes an `external-interface` or `transformation-mapping` surface (an HTTP / network client, or a parser / mapper of a third-party payload — HTML / JSON / XML / CSV originating outside this repo), the adapter's structural assumptions (selectors, field paths, expected response shape) AND the static fixture / golden that tests them MUST BOTH derive from a **captured real sample** of that payload — the capture cited in the stage's `external-interface` / `transformation-mapping` design-prep item, or one captured this run and recorded with its `source` + capture time. The captured sample is a static fixture (no live socket), so a parser test against it stays in source like any unit test — the Real-IO isolation rule below governs *live* calls, not the captured bytes. Do NOT hand-invent the shape and then hand-write a fixture that agrees with it: the passing test then only proves the code matches your assumption, never that the assumption matches reality (self-confirming oracle — the observed failure was a parser whose selectors existed in its synthetic fixture and in zero real pages: hundreds of green units over a fiction, and the whole structure built on the wrong shape). When no real sample is reachable (no network this run, or the brief supplied none), do NOT synthesize a stand-in and present its green tests as correctness: mark the adapter's shape `reality-unverified (no captured sample)` in `Validation evidence`, keep any placeholder fixture explicitly labelled an assumption (never validation evidence), and surface an explicit **user-owned** item in the routing recommendation to confirm against real data. Unlike the DB gate above this does NOT itself block acceptance — live external verification stays a user-owned item per `final-verification`'s External QA advisory policy — but a synthetic external fixture presented as reality-verified is exactly the mock-only external evidence the `final-verification` test-correctness pass is meant to reject.
48
48
  - **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).
49
- - **Stage conformance script (BLOCKING when the approved plan declared `Conformance tests:`).** Planning only declared the path and `requires`. This run MUST write the script to that path under `<task_root>/qa/scripts/` and add the matching `<task_root>/qa/conformance-manifest.json` entry: `stageKey` (= `<task-id>-stage-<N>`), `script`, `runCommand`, `requirementIds`, `requires` (the set the plan declared), `passContract`, `exemption: null`, `waiver: null`. Do not skip this when the plan declared tests. If the plan declared `Conformance exemption:`, do not invent a script. 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. The verifier runs `runCommand` from the **worktree cwd**, and that cwd is the tree under test. `runCommand` MUST NOT repoint it: a leading `cd <checkout> &&` sends the script at a tree without this stage's changes. Absolute paths are fine and usually necessary — the script and its `tsconfig` live under `<task_root>/qa/scripts/`, i.e. under `.okstra/`, and a worktree does not carry `.okstra/`. Point at those by absolute path; leave the cwd alone. **Enforced:** `scripts/okstra_ctl/conformance.py` `_check_entry` rejects a `runCommand` whose first word in any `&&` / `;` segment changes directory; `validators/validate-run.py` `_validate_conformance` fails the run if the inherited declaration has no script file.
49
+ - **Stage conformance script (BLOCKING when the approved plan declared `Conformance tests:`).** Planning only declared the path and `requires`. This run MUST write the script to that path under `<task_root>/qa/scripts/` and add the matching `<task_root>/qa/conformance-manifest.json` entry: `stageKey` (= `<task-id>-stage-<N>`), `script`, `runCommand`, `requirementIds`, `requires` (the set the plan declared), `passContract`, `exemption: null`, `waiver: null`. Do not skip this when the plan declared tests. If the plan declared `Conformance exemption:`, do not invent a script — with one exception: when this stage's diff touches a db/io/http/external surface the exemption promised it would not (the verifier's diff-surface cross-check names the surface), write the script and the manifest entry for this stage exactly as for a declared stage, with `requires` covering those surfaces. The approved plan is not rewritten; `validate-run.py` `_declared_conformance_errors` accepts an entry for a stage the plan exempted and still rejects one for a stage the plan does not have. 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. The verifier runs `runCommand` from the **worktree cwd**, and that cwd is the tree under test. `runCommand` MUST NOT repoint it: a leading `cd <checkout> &&` sends the script at a tree without this stage's changes. Absolute paths are fine and usually necessary — the script and its `tsconfig` live under `<task_root>/qa/scripts/`, i.e. under `.okstra/`, and a worktree does not carry `.okstra/`. Point at those by absolute path; leave the cwd alone. **Enforced:** `scripts/okstra_ctl/conformance.py` `_check_entry` rejects a `runCommand` whose first word in any `&&` / `;` segment changes directory; `validators/validate-run.py` `_validate_conformance` fails the run if the inherited declaration has no script file.
50
50
  - read the approved plan at this prompt's `**Approved plan:**` anchor end-to-end and parse the `## 5.5 Stage Map`. Read this prompt's `**Stage for this implementation run:**` anchor: 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`. Both anchors are generated headers; when either is missing, stop and report `contract-violated` rather than inferring the value.
51
51
  - 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.
52
52
  - 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.
@@ -82,7 +82,7 @@ also remain contract violations.
82
82
  ```
83
83
  `overall` is exactly one of `PASS` / `FAIL` / `MISSING`. Writing the honest sidecar is mandatory whenever the script runs and on the exemption/waiver skip path. A missing `io`-only sidecar blocks; a missing external-advisory sidecar is reported as `ADVISORY` rather than accepted as hidden evidence.
84
84
  - **Read-only command log.** Record the `runCommand` exact line + its exit code in the Read-only command log. Tier 3 external non-PASS evidence MUST remain visible with status `ADVISORY`. Unlike Tiers 1·2, a conformance script MAY mutate the **replica datastore** (exercising integrated state is its whole purpose) — but only the `qaEnv` replica target, never a shared/staging/prod store. The `runCommand` itself is still subject to the same source/lockfile mutation deny-list as Tier 2 (`--fix`, `npm install` without `ci`, etc.); a denied token aborts with `contract-violated`.
85
- - **No manifest / no entry for this stage.** If the approved plan declared `Conformance exemption:` for this stage, and the manifest is absent or has no matching `stageKey`, record `conformance: no manifest entry for <stageKey>` and proceed. If the approved plan declared `Conformance tests:` and the script file or matching entry is absent, that is a FAIL — do not treat it as a skip. **Enforced:** `validators/validate-run.py` `_validate_conformance`.
85
+ - **No manifest / no entry for this stage.** If the approved plan declared `Conformance exemption:` for this stage, and the manifest is absent or has no matching `stageKey`, record `conformance: no manifest entry for <stageKey>` and proceed. If, in that same situation, the stage diff touches a db/io/http/external surface (`validate-run.py` `_validate_conformance_surfaces`, default patterns in `okstra_ctl.conformance._DEFAULT_SURFACE_PATTERNS`), the run cannot pass on the exemption alone: report the touched surface and the missing entry as the blocking finding, and name the way forward — a Tier 3 script plus manifest entry for this stage with `requires` covering the surface, which the executor may add even though the plan exempted the stage. If the approved plan declared `Conformance tests:` and the script file or matching entry is absent, that is a FAIL — do not treat it as a skip. **Enforced:** `validators/validate-run.py` `_validate_conformance`.
86
86
 
87
87
  ### Self-mock detection (changed test files)
88
88
 
@@ -174,7 +174,7 @@ roles:
174
174
  - **Never read an `.okstra/` artifact back out of a git object.** `.okstra/**` is gitignored and never committed — the executor aborts a commit that stages an ignored path and the verifier reports a committed `.okstra` path as a branch defect — so `git cat-file -e <tag>:.okstra/…`, `git show <tag>:.okstra/…`, and every variant of that read can never resolve, at any tag, in any stage. A later stage that needs a QA artifact reads it from the working tree or receives it through the carry sidecar / verifier result; do not design a stage contract around one being reachable from a tag. Validator S12 rejects the read.
175
175
  - **Per-stage conformance declaration (mandatory one line, in the stage section — same placement freedom as `TDD exemption:`):** the stage MUST carry exactly one of:
176
176
  - `Conformance tests: stage-<N> — <task_root>/qa/scripts/stage-<N>.<ext> (requires=[db|io|http|external,...])` — declare that a Tier3 verification script will prove 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. This phase emits the line and the `requires` set only. Do NOT write `<task_root>/qa/scripts/stage-<N>.*` and do NOT add a `runCommand` or `conformance-manifest.json` entry here — the matching `implementation` stage run creates the script file and the manifest `runCommand`. A plan that declares tests with no script file on disk is valid at this gate. The data.json `conformanceTests` value carries only the remainder after the `Conformance tests: stage-<N> — ` prefix — never the `stage-<N> — ` label itself (report assembly strips a leftover label at publication, and the implementation entry gate rejects one).
177
- - `Conformance exemption: <reason>` — only for stages that touch no db/io/http/external surface, or where unit tests fully cover the increment. Exemption stays a planning declaration; do not move it to implementation. (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.)
177
+ - `Conformance exemption: <reason>` — only for stages that touch no db/io/http/external surface, or where unit tests fully cover the increment. Exemption stays a planning declaration; do not move it to implementation. (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.) **Enforced at planning:** `validators/validate-run.py` `_validate_planning_conformance_declared` runs the same surface patterns over the exempted stage's `stepwiseExecution[].plannedPaths` (`okstra_ctl.conformance.exempt_stage_surface_conflicts`) and fails the planning run — a plan that exempts a stage while planning a `*repository*` / `*.controller.*` / `*migration*` path is corrected here, where the plan is still editable, not after the implementation is done (observed 2026-09-09, dev-10784 Stage 2).
178
178
  - **External QA outcome guideline:** after satisfying the S11 declaration above, a line whose `requires` contains
179
179
  `db`, `http`, or `external` should name those capabilities here so the later `runCommand` can be written against them.
180
180
  Okstra may start the environment and run it automatically, but `FAIL`, missing evidence, or an
@@ -3,7 +3,7 @@
3
3
  "locale": "ko",
4
4
  "steps": {
5
5
  "task_pick": {
6
- "label": "어느 task? (남은 작업 최신순 추천)",
6
+ "label": "어느 task? (1번이 추천, 나머지는 남은 작업 최신순)",
7
7
  "echo_template": "task: {value}",
8
8
  "options": {
9
9
  "__new__": "직접 입력 (새 작업 또는 목록에 없는 task)",
@@ -97,7 +97,7 @@
97
97
  "echo_template": "task-type: {value}"
98
98
  },
99
99
  "selected_direction_pick": {
100
- "label": "상세 계획의 입력으로 사용할 확정 구현 방향 보고서를 선택하세요 (같은 task의 최신 3개)",
100
+ "label": "상세 계획의 입력으로 사용할 확정 구현 방향 보고서를 선택하세요 (같은 task의 최신 3개). 고른 보고서의 user-responses/ 답변(방향 선택·C-NNN 답)이 함께 전달됩니다 — clarification-response 단계는 필요 없습니다",
101
101
  "echo_template": "selected-direction: {value}",
102
102
  "errors": {
103
103
  "none": "같은 task에서 선택할 implementation-option-selection 최종 보고서를 찾을 수 없습니다.",
@@ -503,28 +503,8 @@
503
503
  "no": "아니오 — 단계별로 다시 입력"
504
504
  }
505
505
  },
506
- "role_count": {
507
- "label": "{role} 역할 인스턴스 수를 선택하세요 ({minimum}..{maximum}, 적정 {default})",
508
- "echo_template": "role-count: {value}",
509
- "options": {
510
- "count": "{count}개{default_suffix}",
511
- "default_suffix": " (적정)"
512
- }
513
- },
514
- "role_add": {
515
- "label": "선택 역할 {role} 을(를) 이번 run 에 추가할까요? (최대 {maximum}개)",
516
- "echo_template": "role-add: {value}",
517
- "options": {
518
- "skip": "추가 안 함{skip_warning}{default_suffix}",
519
- "add": "{count}개 추가{default_suffix}",
520
- "default_suffix": " (기본)",
521
- "skip_warnings": {
522
- "critic": " — 차단 kind 에서 분석자 표가 1대1 동수면 가를 주체가 없어, 그 항목마다 승인 결정과 Blocks=approval clarification 행이 열립니다 (자동 tie-break 대신 사용자 질문. phase 는 계속 진행됩니다)"
523
- }
524
- }
525
- },
526
506
  "role_model": {
527
- "label": "{role} 역할 {ordinal}/{count} 인스턴스의 모델을 선택하세요",
507
+ "label": "{role} 역할의 모델을 선택하세요 (인스턴스 1개)",
528
508
  "echo_template": "role-model: {value}",
529
509
  "options": {
530
510
  "model": "{model_ref} — {display}{default_suffix}",
@@ -634,6 +614,55 @@
634
614
  "edit_target": {
635
615
  "label": "어느 step 으로 돌아갈까요?",
636
616
  "echo_template": "edit-target: {value}"
617
+ },
618
+ "role_models": {
619
+ "label": "{role} 역할의 모델을 고르세요 — 고른 만큼 인스턴스를 띄웁니다 ({range}). 앞줄 추천은 프로젝트 modelDefaults(없으면 카탈로그 기본값) 순서입니다",
620
+ "echo_template": "role-models: {value}",
621
+ "options": {
622
+ "model": "{model_ref} — {display}",
623
+ "skip": "추가 안 함{skip_warning}",
624
+ "__free_input__": "직접 선택 (실행 가능한 전체 후보 {total}개에서 고르기)",
625
+ "skip_warnings": {
626
+ "critic": " — 차단 kind 에서 분석자 표가 1대1 동수면 가를 주체가 없어, 그 항목마다 승인 결정과 Blocks=approval clarification 행이 열립니다 (자동 tie-break 대신 사용자 질문. phase 는 계속 진행됩니다)"
627
+ }
628
+ },
629
+ "labels": {
630
+ "range": "허용 {minimum}..{maximum}개, 권장 {recommended}개",
631
+ "exact": "정확히 {count}개"
632
+ },
633
+ "errors": {
634
+ "count_out_of_range": "{range} 골라야 합니다 — 고른 수 {count}개",
635
+ "min_one_required": "모델을 {range} 고르세요 (이 역할을 빼려면 '추가 안 함')",
636
+ "unknown_option": "목록에 없는 항목입니다: {values}"
637
+ },
638
+ "echo_variants": {
639
+ "custom": "role-models: (전체 후보에서 직접 선택)",
640
+ "skipped": "role-models: (추가 안 함)"
641
+ }
642
+ },
643
+ "role_models_custom": {
644
+ "label": "{role} 역할의 모델을 전체 후보에서 고르세요 — 고른 만큼 인스턴스를 띄웁니다 ({range})",
645
+ "echo_template": "role-models: {value}",
646
+ "options": {
647
+ "model": "{model_ref} — {display}",
648
+ "skip": "추가 안 함{skip_warning}",
649
+ "skip_warnings": {
650
+ "critic": " — 차단 kind 에서 분석자 표가 1대1 동수면 가를 주체가 없어, 그 항목마다 승인 결정과 Blocks=approval clarification 행이 열립니다 (자동 tie-break 대신 사용자 질문. phase 는 계속 진행됩니다)"
651
+ }
652
+ },
653
+ "labels": {
654
+ "range": "허용 {minimum}..{maximum}개, 권장 {recommended}개",
655
+ "exact": "정확히 {count}개"
656
+ },
657
+ "errors": {
658
+ "count_out_of_range": "{range} 골라야 합니다 — 고른 수 {count}개",
659
+ "min_one_required": "모델을 {range} 고르세요 (이 역할을 빼려면 '추가 안 함')",
660
+ "unknown_option": "목록에 없는 항목입니다: {values}"
661
+ },
662
+ "echo_variants": {
663
+ "custom": "role-models: (전체 후보에서 직접 선택)",
664
+ "skipped": "role-models: (추가 안 함)"
665
+ }
637
666
  }
638
667
  },
639
668
  "confirmation": {