okstra 0.175.1 → 0.176.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 (81) hide show
  1. package/docs/architecture/storage-model.md +2 -2
  2. package/docs/architecture.md +22 -19
  3. package/docs/cli.md +18 -14
  4. package/docs/for-ai/skills/okstra-inspect.md +2 -3
  5. package/docs/for-ai/skills/okstra-rollup.md +1 -0
  6. package/docs/project-structure-overview.md +57 -56
  7. package/docs/task-process/README.md +11 -9
  8. package/docs/task-process/common-flow.md +13 -16
  9. package/docs/task-process/error-analysis.md +9 -10
  10. package/docs/task-process/final-verification.md +7 -7
  11. package/docs/task-process/implementation-planning.md +9 -9
  12. package/docs/task-process/implementation.md +6 -6
  13. package/docs/task-process/release-handoff.md +8 -7
  14. package/docs/task-process/requirements-discovery.md +8 -8
  15. package/package.json +1 -1
  16. package/runtime/BUILD.json +2 -2
  17. package/runtime/bin/lib/okstra/interactive.sh +12 -6
  18. package/runtime/bin/lib/okstra/usage.sh +3 -2
  19. package/runtime/bin/okstra-spawn-followups.py +4 -2
  20. package/runtime/prompts/launch.template.md +2 -2
  21. package/runtime/prompts/lead/context-loader.md +1 -2
  22. package/runtime/prompts/lead/okstra-lead-contract.md +3 -4
  23. package/runtime/prompts/lead/report-writer.md +15 -1
  24. package/runtime/prompts/lead/team-contract.md +16 -12
  25. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  26. package/runtime/prompts/profiles/_implementation-executor.md +0 -3
  27. package/runtime/prompts/profiles/_implementation-verifier.md +3 -7
  28. package/runtime/prompts/profiles/change-impact-analysis.md +9 -5
  29. package/runtime/prompts/profiles/error-analysis.md +14 -8
  30. package/runtime/prompts/profiles/feature-analysis.md +9 -5
  31. package/runtime/prompts/profiles/final-verification.md +10 -7
  32. package/runtime/prompts/profiles/implementation-option-selection.md +9 -5
  33. package/runtime/prompts/profiles/implementation-planning.md +13 -7
  34. package/runtime/prompts/profiles/implementation.md +9 -5
  35. package/runtime/prompts/profiles/improvement-discovery.md +10 -6
  36. package/runtime/prompts/profiles/project-analysis.md +9 -5
  37. package/runtime/prompts/profiles/requirements-discovery.md +15 -9
  38. package/runtime/prompts/wizard/prompts.ko.json +18 -1
  39. package/runtime/python/okstra_ctl/adapters/runtime/__init__.py +1 -0
  40. package/runtime/python/okstra_ctl/adapters/runtime/assembly.py +27 -0
  41. package/runtime/python/okstra_ctl/adapters/runtime/cli_wrapper.py +49 -0
  42. package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +74 -0
  43. package/runtime/python/okstra_ctl/application/open_worker.py +29 -0
  44. package/runtime/python/okstra_ctl/assignment_resolver.py +27 -27
  45. package/runtime/python/okstra_ctl/dispatch_core.py +120 -100
  46. package/runtime/python/okstra_ctl/domain/host.py +3 -1
  47. package/runtime/python/okstra_ctl/domain/wizard/interaction.py +5 -0
  48. package/runtime/python/okstra_ctl/domain/worker_runtime.py +44 -0
  49. package/runtime/python/okstra_ctl/implementation_outcome.py +21 -5
  50. package/runtime/python/okstra_ctl/legacy_model_selection.py +115 -27
  51. package/runtime/python/okstra_ctl/manager_sync.py +4 -1
  52. package/runtime/python/okstra_ctl/next_phase.py +236 -0
  53. package/runtime/python/okstra_ctl/ports/worker_runtime.py +26 -0
  54. package/runtime/python/okstra_ctl/recap.py +4 -1
  55. package/runtime/python/okstra_ctl/render.py +46 -47
  56. package/runtime/python/okstra_ctl/role_requirements.py +28 -35
  57. package/runtime/python/okstra_ctl/rollup.py +4 -1
  58. package/runtime/python/okstra_ctl/run.py +8 -5
  59. package/runtime/python/okstra_ctl/stage_fix_carry.py +17 -1
  60. package/runtime/python/okstra_ctl/team.py +14 -18
  61. package/runtime/python/okstra_ctl/wizard.py +248 -56
  62. package/runtime/python/okstra_ctl/worker_prompt_body.py +18 -2
  63. package/runtime/python/okstra_ctl/worker_prompt_contract.py +40 -9
  64. package/runtime/python/okstra_ctl/worker_prompt_headers.py +16 -1
  65. package/runtime/python/okstra_ctl/workflow.py +18 -32
  66. package/runtime/python/okstra_ctl/worktree.py +3 -3
  67. package/runtime/python/okstra_ctl/worktree_registry.py +5 -4
  68. package/runtime/python/okstra_project/state.py +54 -6
  69. package/runtime/schemas/final-report-v2.0.schema.json +43 -5
  70. package/runtime/skills/okstra-inspect/facets/recap.md +2 -0
  71. package/runtime/skills/okstra-inspect/facets/report.md +1 -1
  72. package/runtime/skills/okstra-inspect/facets/status.md +15 -13
  73. package/runtime/skills/okstra-rollup/SKILL.md +1 -0
  74. package/runtime/skills/okstra-run/SKILL.md +5 -5
  75. package/runtime/templates/implementation-worker-preamble.md +3 -18
  76. package/runtime/templates/project-docs/task-index.template.md +0 -1
  77. package/runtime/templates/reports/html/macros/forms.html +5 -4
  78. package/runtime/templates/reports/html/tasks/final-verification.template.html +1 -1
  79. package/runtime/templates/reports/html/tasks/implementation.template.html +1 -1
  80. package/runtime/templates/worker-prompt-preamble.md +3 -36
  81. package/runtime/validators/validate-run.py +57 -99
@@ -253,9 +253,9 @@ Examples of key fields:
253
253
  - `workflow.currentPhaseState`
254
254
  - `workflow.phaseStates`
255
255
  - `workflow.lastCompletedPhase`
256
- - `workflow.nextRecommendedPhase`
256
+ - `workflow.nextRecommendedPhase` *(object `{phase, status, rationale}`; `status` is `ready` / `pending` / `blocked` / `terminal`. The lead writes `phase` only under `ready` — an authoring rule, not a constraint the struct enforces: `prepare` lowers a `ready` pointer to `pending` and keeps its `phase`, so a stored non-`ready` pointer that still names a phase is a normal manifest state. Read `status` to decide whether anything can be launched.)*
257
+ - `workflow.nextRecommendedPhaseCorrection` *(present only when Phase 7 validation replaced the authored pointer — `{authored, applied}`)*
257
258
  - `workflow.awaitingApproval`
258
- - `workflow.routingStatus`
259
259
  - `workflow.lastSafeCheckpoint`
260
260
  - `phaseOutcome` *(artifact-derived semantic phase outcome; for example, when the implementation carry proves that all stages passed, the implementation phase outcome may be recorded as completed even if run contract validation failures remain)*
261
261
  - `inputs`
@@ -429,25 +429,27 @@ Selection rules:
429
429
 
430
430
  - With `--task-type <name>`, `prompts/profiles/<name>.md` is rendered as the task bundle's `instruction-set/analysis-profile.md`.
431
431
  - The single selector in the external interface is `task-type`.
432
- - The selected task type is reflected unchanged in `taskType`, `workflow.currentPhase`, and `workflow.nextRecommendedPhase` in task-manifest.json.
432
+ - The selected task type is reflected unchanged in `taskType` and `workflow.currentPhase` in task-manifest.json. It is not written into `workflow.nextRecommendedPhase`: prepare only lowers an inherited `ready` pointer to `pending`, and the pointer's next value comes from the report this run produces.
433
433
  - It is also used as the run-directory path segment (`runs/<task-type>/...`).
434
434
 
435
435
  ### Standard task types
436
436
 
437
437
  Each task type enforces phase-specific allowed and forbidden actions. A run creates only the artifacts for its own task type and does not advance to the next phase. The next phase always begins with a new `okstra.sh` execution.
438
438
 
439
- | task type | Purpose | Core artifacts | Next recommended phase | Code changes allowed? |
439
+ The fourth column is the `workflow.nextRecommendedPhase` pointer Phase 7 leaves behind. The pointer is an object `{phase, status, rationale}`; `status` is `ready`, `pending`, `blocked`, or `terminal`, and `phase` names a phase only under `ready`. Phase 7 does not decide the route — it projects the pointer from the report's own routing field (`scripts/okstra_ctl/next_phase.py::project`), named per row below. The authoring rule for the field is stated once, in the Phase 6 checklist of [`prompts/lead/report-writer.md`](../prompts/lead/report-writer.md); this table describes the outcome, not the rule.
440
+
441
+ | task type | Purpose | Core artifacts | Next-phase pointer after Phase 7 | Code changes allowed? |
440
442
  |---|---|---|---|---|
441
- | `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 | `error-analysis`, `implementation-option-selection`, or `pending-routing-decision` | No |
442
- | `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 | `implementation-option-selection` after a credible cause, or `error-analysis` for continued investigation | No |
443
- | `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 | `implementation-planning` after a valid direction is confirmed; otherwise `blocked` | No (strictly read-only; source edits, builds, tests, migrations, and deploys are prohibited) |
444
- | `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 | `implementation` after separate plan approval, or `implementation-option-selection` when the direction is invalidated | No |
445
- | `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 | `final-verification` | Yes (limited to the approved plan's file list; `git push`/publish/deploy/real migration prohibited) |
446
- | `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`) | `pending-release-handoff` when accepted; otherwise route by whether the defect is in the cause, selected direction, or detailed plan | No (read-only tests only) |
447
- | `release-handoff` | Deliver `accepted` changes as a commit, push, or PR according to the user's chosen method | user menu responses (H1 action / H2 PR base / H3 message handling), executed git/gh command log, commit SHA list, PR URL | `done-or-follow-up` | Yes—but execute **only the mutating commands selected by the user in the menu**. `git push --force*`, direct push to the base branch, `--no-verify`, `gh release`, and publish/deploy are prohibited. The source code itself must not be changed; package the existing `implementation` diff unchanged. |
448
- | `project-analysis` | Map the current project structure and feature index | components, dependencies, entry points, data stores, external systems, feature index | `pending-routing-decision` | No (strictly read-only; tests are also prohibited) |
449
- | `feature-analysis` | Trace one confirmed existing feature | flows, domain rules, state changes, external interactions, test coverage | `pending-routing-decision` | No (strictly read-only; tests are also prohibited) |
450
- | `change-impact-analysis` | Map the impact of one proposed change | preserved behavior, impact items, dependency blast radius, test and operational impact | `pending-routing-decision` | No (strictly read-only; tests are also prohibited) |
443
+ | `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 |
444
+ | `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 |
445
+ | `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) |
446
+ | `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` (the plan still needs its separate approval before that run starts), `ready` at `implementation-option-selection` on `direction-invalidated` | No |
447
+ | `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) |
448
+ | `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) |
449
+ | `release-handoff` | Deliver `accepted` changes as a commit, push, or PR according to the user's chosen method | user menu responses (H1 action / H2 PR base / H3 message handling), executed git/gh command log, commit SHA list, PR URL | always `terminal` — the lifecycle ends here and this report has no routing field Phase 7 projects from | Yes—but execute **only the mutating commands selected by the user in the menu**. `git push --force*`, direct push to the base branch, `--no-verify`, `gh release`, and publish/deploy are prohibited. The source code itself must not be changed; package the existing `implementation` diff unchanged. |
450
+ | `project-analysis` | Map the current project structure and feature index | components, dependencies, entry points, data stores, external systems, feature index | always `pending` with no phase — a sidetrack settles no route | No (strictly read-only; tests are also prohibited) |
451
+ | `feature-analysis` | Trace one confirmed existing feature | flows, domain rules, state changes, external interactions, test coverage | always `pending` with no phase — a sidetrack settles no route | No (strictly read-only; tests are also prohibited) |
452
+ | `change-impact-analysis` | Map the impact of one proposed change | preserved behavior, impact items, dependency blast radius, test and operational impact | always `pending` with no phase — a sidetrack settles no route | No (strictly read-only; tests are also prohibited) |
451
453
 
452
454
  Common constraints:
453
455
 
@@ -752,7 +754,7 @@ The report writer records the decision unchanged in `implementationPlanning.incr
752
754
  scripts/okstra.sh --task-type implementation-planning --workers claude,codex --project-id <project-id> --task-group <task-group> --task-id <task-id> --task-brief <brief-path>
753
755
  ```
754
756
 
755
- Abbreviated form for a later phase (if the manifest's `workflow.nextRecommendedPhase` is `implementation-planning`, `--task-type` may also be omitted):
757
+ Abbreviated form for a later phase (if the manifest's `workflow.nextRecommendedPhase` is `{"phase": "implementation-planning", "status": "ready", ...}`, `--task-type` may also be omitted):
756
758
 
757
759
  ```bash
758
760
  scripts/okstra.sh --task-key <project-id>:<task-group>:<task-id> --workers claude,codex
@@ -806,9 +808,9 @@ Checking the following fields first makes it easy to determine the current posit
806
808
  - `workflow.currentPhaseState`
807
809
  - `workflow.phaseStates`
808
810
  - `workflow.lastCompletedPhase`
809
- - `workflow.nextRecommendedPhase`
811
+ - `workflow.nextRecommendedPhase` *(object `{phase, status, rationale}`; `status` is `ready` / `pending` / `blocked` / `terminal`. The lead writes `phase` only under `ready` — an authoring rule, not a constraint the struct enforces: `prepare` lowers a `ready` pointer to `pending` and keeps its `phase`, so a non-`ready` pointer that still names a phase is a normal manifest state. Read `status` to decide whether anything can be launched.)*
812
+ - `workflow.nextRecommendedPhaseCorrection` *(present only when Phase 7 validation replaced the authored pointer — `{authored, applied}`, where `authored` is the pointer the lead wrote)*
810
813
  - `workflow.awaitingApproval`
811
- - `workflow.routingStatus`
812
814
  - `workflow.lastSafeCheckpoint`
813
815
  - `phaseOutcome`
814
816
 
@@ -826,8 +828,9 @@ Resume decision rules:
826
828
 
827
829
  - Resume the current run: if `latestResumeCommandPath` exists, use that path first.
828
830
  - Restart the current phase: run again with the same `task-key` and current `task-type`.
829
- - Start the next phase: if `workflow.nextRecommendedPhase` is a concrete phase, prepare the next `okstra.sh` run with that value.
830
- - Additional material required: if `routingStatus=pending` or `nextRecommendedPhase=pending-routing-decision`, augment the brief first.
831
+ - Start the next phase: if `workflow.nextRecommendedPhase.status` is `ready`, prepare the next `okstra.sh` run with its `phase`.
832
+ - Additional material required: if that `status` is `pending` or `blocked`, the pointer carries no phase. Read its `rationale`, augment the brief, and rerun the phase that left the pointer unsettled.
833
+ - Nothing left to run: `status` `terminal` means the lifecycle ended at this phase.
831
834
 
832
835
  ## Final report structure
833
836
 
@@ -925,7 +928,7 @@ Tokens used in each run are collected from lead/worker session transcripts and w
925
928
  - CLI execution evidence and token attribution are independent. A wrapper `.status.json` proves `not-started`, `started`, `exited`, `timeout`, or `failed` and supplies the worker's collection window; only a matching transcript with a final token snapshot proves attributable usage. If a wrapper exited successfully but no attributable transcript exists, the worker remains `source: "unavailable"` with `cliExecutionStatus: "exited"` and a reason instead of becoming zero usage or being described as never invoked.
926
929
  - Records billable-equivalent token math and USD cost estimates. It applies Anthropic billing ratios (`cache_creation_5m=1.25x`, `cache_creation_1h=2.0x`, `cache_read=0.1x`, `output=5x`). When the transcript provides separate `usage.cache_creation.ephemeral_5m_input_tokens` / `ephemeral_1h_input_tokens` values, they are counted separately.
927
930
  - Pricing is centrally managed in `scripts/okstra_token_usage/pricing.py`. Update it when model prices change. Model IDs that fail price matching are exposed to the user in `usageSummary.unmatchedModels`, preventing silent-zero incidents.
928
- - Project-wide historical usage is exposed through the read-only `okstra usage-report` command (`src/commands/inspect/usage-report.mjs` → `scripts/okstra_ctl/usage_report.py`) and the `okstra-usage` skill. It defaults to the whole current project's last 30 days and returns run coverage, raw and billable-equivalent tokens, known USD cost, CPU-sum milliseconds, and wall-clock milliseconds grouped by task type. Runs without usable Phase 7 usage are excluded from resource totals and reported through unavailable reason counts rather than treated as zero usage; unmatched model names remain visible when their tokens and time are included but their cost is not. Use `okstra-inspect` for one task's elapsed/context detail and `okstra-rollup` for task-group or project status/report digests.
931
+ - Project-wide historical usage is exposed through the read-only `okstra usage-report` command (`src/commands/inspect/usage-report.mts` → `scripts/okstra_ctl/usage_report.py`) and the `okstra-usage` skill. It defaults to the whole current project's last 30 days and returns run coverage, raw and billable-equivalent tokens, known USD cost, CPU-sum milliseconds, and wall-clock milliseconds grouped by task type. Runs without usable Phase 7 usage are excluded from resource totals and reported through unavailable reason counts rather than treated as zero usage; unmatched model names remain visible when their tokens and time are included but their cost is not. Use `okstra-inspect` for one task's elapsed/context detail and `okstra-rollup` for task-group or project status/report digests.
929
932
  - **Incremental scan cache (P6)**: To avoid rescanning session jsonl files, a per-file byte cursor and the extracted usage events before windowing are stored in `$OKSTRA_HOME/cache/token-usage/<transcript-dir>/<sessionId>.json` (`scripts/okstra_token_usage/cursor.py`). The run window (since/until) is reevaluated over events on every invocation, so even if a rerun narrows the window, the total matches a full scan. The cache is derived data; identifier mismatch, truncation, or corruption automatically falls back to a full rescan, and `okstra-token-usage.py --no-cache` forces a bypass.
930
933
  - **Phase timeline (P0 instrumentation)**: The collector extracts `PROGRESS: phase-*` checkpoint lines (see "Progress reporting" in prompts/lead/okstra-lead-contract.md) from the lead session jsonl scoped to the run window, and records them in team-state as a `phaseTimeline` block (`{source, phases: [{phase, firstAt, lastAt, markerCount, wallMsToNext}]}`) (`scripts/okstra_token_usage/collect.py :: phase_timeline`). This provides measurement points for per-phase wall-clock time within the run and is consumed by the "Per-run phase breakdown" in the `okstra-inspect` time facet. Runs without markers explicitly report that measurement is unavailable with `phases: []`.
931
934
 
@@ -942,7 +945,7 @@ Each validator blocks the phase with a `contract-violated` exit code when a cont
942
945
 
943
946
  **Blocking failures vs advisories.** `validate-run.py` splits its findings into `validation.failures` and `validation.advisories` (both written to the run manifest and team state, and printed on stdout; advisories also go to stderr one per line). Only `failures` decide `validation.status`, so a run can pass while carrying advisories. The dividing line is whether a remedy still exists at Phase 7: a defect in the report is fixable by re-authoring and re-rendering, while a defect in what an *already-ended session* wrote down is not — the worker that would restate its citation and the lead pane that would emit its PROGRESS line are both gone, so the only remaining move is to void the phase, which blocks the next one without establishing that any conclusion in the report is wrong. Two surfaces are advisory today: the worker citation ledger (`okstra_ctl.worker_audit_ledger.worker_results_audit_findings` — a `path:line` citation with no matching Evidence read row; a missing or unreadable sidecar stays blocking) and the PROGRESS narration lines (`validators/validate_session_conformance.py` `SessionConformanceResult.advisories`; the user-confirmation cross-check stays blocking because it verifies a claim the report makes). **Enforced:** `tests/contract/test_worker_audit_check.py::test_an_unmatched_citation_is_advisory_and_a_missing_sidecar_is_not` and `tests/contract/test_validate_session_conformance.py::test_missing_checkpoints_are_advisory_not_blocking`.
944
947
 
945
- `contractValidation.status=failed` means the run artifact contract failed; it does not necessarily mean the implementation itself is incomplete. An implementation run derives `phaseOutcome.implementation` from `runs/implementation/carry/stage-<N>.json`, `runs/implementation-planning/consumers.jsonl`, and the approved Stage Map together. If every Stage Map stage has pass-grade carry evidence, it can retain the contract failure as audit information while correcting `workflow.nextRecommendedPhase` to `final-verification`.
948
+ `contractValidation.status=failed` means the run artifact contract failed; it does not necessarily mean the implementation itself is incomplete. An implementation run derives `phaseOutcome.implementation` from `runs/implementation/carry/stage-<N>.json`, `runs/implementation-planning/consumers.jsonl`, and the approved Stage Map together. If every Stage Map stage has pass-grade carry evidence, it can retain the contract failure as audit information while raising `workflow.nextRecommendedPhase.status` to `ready`. It does not choose the phase: the reconciler's fact is that every stage finished, and the destination was already settled by the last stage's report routing, so it leaves `phase` untouched and raises the status only when `phase` is non-empty.
946
949
 
947
950
  ## Practical notes
948
951
 
package/docs/cli.md CHANGED
@@ -153,7 +153,7 @@ The lifecycle task types run in this order:
153
153
  - Primary-pass assignment: selected analyser instances are enumerated in `requiredWorkerRoles` order, then the lead rotates the primary pass across the resolved priority lenses. Provider/model names do not affect the order, and every analyser still covers every resolved lens after its primary pass.
154
154
  - Two bidirectional grilling points: an enhanced Step 4 in `okstra-brief-gen` with a budget of 8, and the lead's Phase 1.5 reflect-back with a budget of 12.
155
155
  - Validator: `validators/validate_improvement_report.py` enforces the 11-part contract for an `improvement-discovery` final report.
156
- - Because an `improvement-discovery` run is not in `PHASE_SEQUENCE`, the `--task-key` short form does not automatically populate `nextRecommendedPhase` for it.
156
+ - Because an `improvement-discovery` run is not in `PHASE_SEQUENCE`, its report has no routing field to project from, so the run leaves `workflow.nextRecommendedPhase` `pending` with no phase. The `--task-key` short form therefore cannot fill `--task-type` after one.
157
157
 
158
158
  #### Analysis sidetrack task types
159
159
 
@@ -165,7 +165,7 @@ The three read-only analysis types are independent sidetracks outside `PHASE_SEQ
165
165
  | `feature-analysis` | Trace one existing feature through flows, domain rules, state changes, external interactions, and test coverage. |
166
166
  | `change-impact-analysis` | Map the blast radius of a proposed change across preserved behavior, dependencies, tests, and operations. |
167
167
 
168
- Each one starts from a brief and produces a report only. The target project is strictly read-only: no edits, tests, builds, migrations, or deployments. A run ends at `pending-routing-decision`; it does not advance the normal phase sequence.
168
+ Each one starts from a brief and produces a report only. The target project is strictly read-only: no edits, tests, builds, migrations, or deployments. A run ends with `workflow.nextRecommendedPhase` `pending` and carrying no phase; it does not advance the normal phase sequence.
169
169
 
170
170
  ### `--analysis-target`
171
171
 
@@ -234,10 +234,10 @@ A short form that supplies `--project-id`, `--task-group`, and `--task-id` toget
234
234
  - Behavior:
235
235
  - Splits the value into the three IDs and populates `PROJECT_ID`, `TASK_GROUP`, and `TASK_ID`.
236
236
  - If the same values are also provided explicitly, they are accepted only when they match; a conflict exits immediately with an error.
237
- - If the task has a `task-manifest.json`, a missing `--task-brief` is filled from the manifest's `taskBriefPath`, and a missing `--task-type` is filled from `workflow.nextRecommendedPhase`.
237
+ - If the task has a `task-manifest.json`, a missing `--task-brief` is filled from the manifest's `taskBriefPath`, and a missing `--task-type` is filled from `workflow.nextRecommendedPhase.phase` — but only while that pointer's `status` is `ready`.
238
238
  - Without a manifest, as on initial entry, the normal missing-argument validation remains in effect and full arguments are required.
239
239
  - Explicit arguments always take precedence over manifest values.
240
- - When `nextRecommendedPhase` is `pending-routing-decision` or `done-or-follow-up`, it is not filled automatically. Supply `--task-type` explicitly or supplement the brief, then rerun the command.
240
+ - Under any other `status` `pending`, `blocked`, or `terminal` — the pointer names no runnable phase and `--task-type` is not filled automatically. Supply `--task-type` explicitly or supplement the brief, then rerun the command.
241
241
 
242
242
  Examples:
243
243
 
@@ -411,9 +411,11 @@ scripts/okstra.sh --task-type implementation-planning ... \
411
411
 
412
412
  ### `--workers`
413
413
 
414
- Directly specifies the workers used in this run.
415
- The default is `claude,codex,report-writer`. Antigravity is optional; add it explicitly when needed, as in `--workers claude,codex,antigravity,report-writer`.
416
- When supplied, only the deduplicated worker subset is recorded.
414
+ Compatibility input only. Prefer `--role-count` and `--role-model` for launch selection.
415
+
416
+ When supplied, the CSV of provider names converts into role slots and model refs for the profile's initial cross-verification role (for example analysers or planners). It is not a provider roster picker and is not the selection unit on the wizard start screen.
417
+
418
+ If the same role is also named by `--role-model` (or other canonical role flags), the converted providers must match the model-ref provider prefixes in order and slot count. Matching values keep the canonical role models and drop the legacy constraint. Different values fail before any worktree or state file is created.
417
419
 
418
420
  Example:
419
421
 
@@ -449,7 +451,7 @@ worker roster contains Claude, Codex, or Antigravity.
449
451
 
450
452
  ### Runtime auto-detection (`auto`)
451
453
 
452
- `okstra run` defaults to `auto`. `src/lib/host-registry-client.mjs` asks the Python host registry to resolve explicit IDs and aliases, the `OKSTRA_RUNTIME_HOST` environment declaration, Claude Code skill handoff, or the external tmux claim. It fails fast when no adapter claims the session. Installed CLI presence alone never selects a host.
454
+ `okstra run` defaults to `auto`. `src/lib/host-registry-client.mts` asks the Python host registry to resolve explicit IDs and aliases, the `OKSTRA_RUNTIME_HOST` environment declaration, Claude Code skill handoff, or the external tmux claim. It fails fast when no adapter claims the session. Installed CLI presence alone never selects a host.
453
455
 
454
456
  - Inside a registered host, the installed `okstra-run` skill uses `current-session`, declares the live semantic function list, and reuses the session you are already in.
455
457
  - From a terminal, `okstra run <host-id-or-alias>` uses `spawn-process` and starts the selected host CLI. The leading word is an alias for `--lead-runtime`.
@@ -476,15 +478,17 @@ The Codex worker (`--workers codex`, `--codex-model`) and Codex lead runtime are
476
478
 
477
479
  ### `--role-count`
478
480
 
479
- Sets the total instance count for one static role: `--role-count <role>=<N>`. Repeat the flag for each role. `N` is the profile total for that role, not an increment. Omit the flag to use the profile default. Dynamic roles, fixed-count roles, and roles absent from the profile reject the flag. lead is a compatibility alias for leader. New records write `leader`.
481
+ Sets how many slots open for one static role: `--role-count <role>=<N>`. Repeat the flag for each role. `N` must fall in that role's profile range `min..max`. Omit the flag to use the profile **recommended** count (not a separate roster default). Roles with `min == max` are fixed quantity and reject this flag. Dynamic roles and roles absent from the profile also reject it. `lead` is a compatibility alias for `leader`. New records write `leader`.
480
482
 
481
- Role count becomes `RoleInstance` ordinals. `ModelPool` then selects one model per ordinal. A pinned model is kept only when the host can bind it exactly.
483
+ Each confirmed count becomes `RoleInstance` ordinals. `ModelPool` then assigns one model ref per ordinal. A pinned model is kept only when the host can bind it exactly. Roles with `min = 0` do not open slots by default; optional add steps or an explicit count above zero open them.
482
484
 
483
485
  ### `--role-model`
484
486
 
485
- Pins one model reference onto a role instance, in ordinal order: `--role-model <role>=<modelRef>`. Repeat the flag to fill later ordinals. Fewer models than the confirmed count are filled from the first matching default array. Extra models do not raise the count; set `--role-count <role>=<N>` first. Unknown roles and unknown model refs fail before any worktree or state file is created. A known selectable model may be assigned to any canonical role.
487
+ Pins one model reference onto a role slot, in ordinal order: `--role-model <role>=<modelRef>`. Repeat the flag to fill later ordinals. `modelRef` is `<provider>/<model>`, for example `claude/opus-5` or `codex/gpt-5.6-sol`.
488
+
489
+ A known selectable model may be assigned to any canonical role. The same role must not receive the same model ref twice; duplicate refs in one role panel fail before any worktree or state file is created. Same provider with different models is allowed. Fewer models than the confirmed count are filled from the model-default chain. Extra models do not raise the count; set `--role-count <role>=<N>` first. Unknown roles and unknown model refs fail before side effects.
486
490
 
487
- `modelRef` is `<provider>/<model>`, for example `claude/opus-5` or `codex/gpt-5.6-sol`. Legacy `--lead-model`, `--claude-model`, `--codex-model`, `--antigravity-model`, `--worker-model`, `--report-writer-model`, and `--executor` remain compatibility inputs. They convert to role-model selections. A new flag and a legacy flag that name different models for the same role fail.
491
+ Legacy `--lead-model`, `--claude-model`, `--codex-model`, `--antigravity-model`, `--worker-model`, `--report-writer-model`, `--executor`, and `--workers` remain compatibility inputs. They convert to role-model selections. A new flag and a legacy flag that name different models (or, for `--workers`, different provider prefixes) for the same role fail.
488
492
 
489
493
  `okstra model list [--role <role>] [--host <host>] [--json]` prints the catalog without an inference call. `okstra model default set|unset <role> ... --scope project|global` writes `modelDefaults` to `.okstra/project.json` or `~/.okstra/config.json`.
490
494
 
@@ -780,7 +784,7 @@ chmod +x ~/.local/bin/okstra-ctl
780
784
 
781
785
  ### `okstra` Node CLI — introspection subcommands
782
786
 
783
- The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and introspection commands used by skills and agents. It goes through the Node wrapper instead of invoking the Python runtime directly, so `src/lib/python-helper.mjs` wires `PYTHONPATH`.
787
+ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and introspection commands used by skills and agents. It goes through the Node wrapper instead of invoking the Python runtime directly, so `src/lib/python-helper.mts` wires `PYTHONPATH`.
784
788
 
785
789
  | Command | Purpose |
786
790
  |---|---|
@@ -854,7 +858,7 @@ The convergence state lifecycle is `groups v1.0 → work v1.0 → final v1.3`; r
854
858
 
855
859
  `okstra convergence` and `okstra plan-items` are internal admin CLI families used by the lead protocol. Each is an internal admin CLI, not a user-facing skill, and their presence does not add a public skill. The former `okstra-convergence` skill remains obsolete; the installed `prompts/lead/convergence.md` and `prompts/lead/plan-body-verification.md` contracts tell the lead when to invoke these operations.
856
860
 
857
- > Every subcommand is wired to `PYTHONPATH` and `~/.okstra/lib/python` by the Python helper (`src/lib/python-helper.mjs`) spawned by `bin/okstra`. When invoking `python3 -m okstra_ctl.*` directly, you must configure `PYTHONPATH` yourself.
861
+ > Every subcommand is wired to `PYTHONPATH` and `~/.okstra/lib/python` by the Python helper (`src/lib/python-helper.mts`) spawned by `bin/okstra`. When invoking `python3 -m okstra_ctl.*` directly, you must configure `PYTHONPATH` yourself.
858
862
 
859
863
  #### `okstra design-prep`
860
864
 
@@ -87,7 +87,7 @@ Keep the table narrow.
87
87
  - workStatus
88
88
  - Next
89
89
 
90
- If `awaitingApproval` or `routingStatus == "pending"`, add a marker to the Next cell and explain it.
90
+ `nextRecommendedPhase` is an object `{phase, status, rationale}`. The Next cell is its `phase`, or `--` when that is empty. If `awaitingApproval`, or `nextRecommendedPhase.status` is anything but `ready`, add a marker to the Next cell and explain it.
91
91
 
92
92
  ### Specific task
93
93
 
@@ -98,9 +98,8 @@ Information to show:
98
98
  - work category
99
99
  - current phase/state
100
100
  - last completed phase
101
- - next recommended phase
101
+ - next-phase pointer: `phase` (or `--`), its `status`, its `rationale`
102
102
  - awaiting approval
103
- - routing status
104
103
  - task status, latest run status
105
104
  - latest report, resume command
106
105
  - workStatus, note
@@ -62,6 +62,7 @@ Top level:
62
62
 
63
63
  - `taskGroup` — the scope (`null` = whole project), `taskCount` — number of tasks.
64
64
  - `tasks[]` — per task: `taskKey, taskGroup, taskId, taskType, workCategory, workStatus, currentPhase, currentPhaseState, nextRecommendedPhase, latestRunStatus, updatedAt, reportPath, runCount, cpuSumMs, wallClockMs, errorCount`.
65
+ - `nextRecommendedPhase` is an object `{phase, status, rationale}`, not a string. Print `phase`, or `--` when it is empty. Never interpolate the object itself.
65
66
  - `totals` — `runs, cpuSumMs, wallClockMs, errors`, plus `byWorkStatus` / `byWorkCategory` / `byCurrentPhase` / `byTaskType` (each a `{value: count}` map).
66
67
 
67
68
  Numeric meanings (must observe):
@@ -160,58 +160,58 @@ Runtime/install asset changes follow this checklist:
160
160
 
161
161
  | Command | Module | Role |
162
162
  |---|---|---|
163
- | `paths` | `src/commands/lifecycle/paths.mjs` | Resolve package/runtime/home paths (library at `src/lib/paths.mjs`) |
164
- | `install`, `ensure-installed` | `src/commands/lifecycle/install.mjs` | Install or refresh runtime, skills, agents, templates |
165
- | `uninstall` | `src/commands/lifecycle/uninstall.mjs` | Remove managed runtime/skills/agents, optionally purge data |
166
- | `doctor` | `src/commands/lifecycle/doctor.mjs` | Diagnose runtime and Python imports |
167
- | `setup` | `src/commands/lifecycle/setup.mjs` | Create/update `<PROJECT_ROOT>/.okstra/project.json` |
168
- | `check-project` | `src/commands/lifecycle/check-project.mjs` | Verify project registration |
169
- | `preflight` | `src/commands/lifecycle/preflight.mjs` | One-call skill preflight: ensure-installed + check-project + host-specific runtime readiness (single JSON) |
170
- | `config` | `src/commands/lifecycle/config.mjs` | Read/write project/global settings such as PR template path |
171
- | `migrate` | `src/commands/lifecycle/migrate.mjs` | One-shot legacy `.project-docs/okstra` → `.okstra` migration helper |
172
- | `git-reconcile` | `src/commands/execute/git-reconcile.mjs` | Reconcile stale stage SHAs after external git history changes |
173
- | `handoff` | `src/commands/execute/handoff.mjs` | Stage-group release-handoff eligibility / assemble / record helpers |
174
- | `integrate-stages` | `src/commands/execute/integrate-stages.mjs` | Merge verified stages into the task worktree and clean stage worktrees |
175
- | `task-list`, `task-show` | `src/commands/inspect/task-list.mjs`, `src/commands/inspect/task-show.mjs` | Task/run introspection for skills; `task-show` consumes the Python task read-side snapshot |
176
- | `resolve-task-key` | `src/commands/inspect/resolve-task-key.mjs` | Resolve a bare task-id to candidate task-keys from the project catalog |
177
- | `set-work-status` | `src/commands/inspect/set-work-status.mjs` | Set a task's user-managed `workStatus` in task-manifest.json (Python: `okstra_ctl.set_work_status`) |
178
- | `time-report`, `log-report`, `error-report`, `error-zip` | `src/commands/inspect/*.mjs` | Read-side task runtime, wrapper log, and error aggregation helpers |
179
- | `run-audit` | `src/commands/inspect/run-audit.mjs` | Anomaly detection — checks run artifacts against progress invariants and reports invariant violations, read-only (Python: `okstra_ctl.run_audit`) |
180
- | `worker-liveness` | `src/commands/inspect/worker-liveness.mjs` | Report whether pending workers are still alive, so the lead's poll ends a stalled wait early instead of paying the deadline (Python: `okstra_ctl.worker_liveness`) |
181
- | `worker-audit-check` | `src/commands/execute/worker-audit-check.mjs` | Apply the Phase 7 worker audit-sidecar rules while the worker session is still alive, so it can fix its own citations (Python: `okstra_ctl.worker_audit_check`, rules in `okstra_ctl.worker_audit_ledger`) |
182
- | `context-cost` | `src/commands/inspect/context-cost.mjs` | Estimate task bundle file/read context cost |
183
- | `worktree-lookup` | `src/commands/execute/worktree-lookup.mjs` | Look up a task-key's registered worktree |
184
- | `worktree-status` | `src/commands/execute/worktree-status.mjs` | Clean-worktree check over source paths only, excluding okstra's provisioned entries and nested stage worktrees (Python: `okstra_ctl.worktree.dirty_entries_excluding_okstra`) |
185
- | `plan-validate` | `src/commands/execute/plan-validate.mjs` | Check approved-plan approval marker |
186
- | `render-bundle` | `src/commands/execute/render-bundle.mjs` | Preview `prepare_task_bundle(render_only=True)` |
187
- | `profile` | `src/commands/inspect/profile-show.mjs` | Print a phase profile with `{{INCLUDE:}}` expanded and its lazy-read sidecars appended transitively, so one grep answers whether a task-type covers a rule — a top-level grep alone returns false negatives (Python: `okstra_ctl.profile_show`). Read-only, unlike `render-bundle` |
188
- | `run` | `src/commands/execute/run.mjs` | Host-aware execution front door (`auto` → Claude/Codex/Antigravity/external path selection) |
189
- | `codex-run`, `codex-dispatch` | `src/commands/execute/codex-*.mjs` | Codex lead dry-run bundle preparation; `codex-dispatch` is the compatibility alias for provider-neutral worker dispatch |
190
- | `agent-prompt`, `worker-dispatch` | `src/commands/execute/{agent-prompt,worker-dispatch}.mjs` | Materialize/verify invocation prompts, record host-native specification/result links, and launch verified CLI assignments through the provider-neutral dispatcher |
191
- | `team` | `src/commands/execute/team.mjs` | External lead tmux-pane worker dispatch / await / teardown |
192
- | `convergence` | `src/commands/execute/convergence.mjs` | Internal admin CLI for the deterministic Phase 5.5 convergence engine (`seed`/`plan-round`/`apply-round`/`apply-critic-gaps`/`finalize`/`validate`/`example`; Python: `okstra_ctl.convergence`) |
193
- | `plan-items` | `src/commands/execute/plan-items.mjs` | Internal admin CLI for deterministic plan-body item extraction and exact-match validation (`extract`/`validate`; Python: `okstra_ctl.plan_items_cli`) |
194
- | `agent-activity` | `src/commands/report/agent-activity.mjs` | Thin Node shim for `okstra_ctl.agent_activity`; `append` records one run-bound activity and `project` writes the validated event projection into final-report data |
195
- | `report-finalize` | `src/commands/report/finalize.mjs` | Run the whole Phase 7 post-report sequence in contractual order (Python: `okstra_ctl.report_finalize`) — the single reference point shared with the Codex lead adapter |
196
- | `render-views` | `src/commands/report/render-views.mjs` | Render schema v2 data with its task-specific human template, or use the quick-report compatibility view |
197
- | `render-final-report`, `inject-report-index` | `src/commands/report/*.mjs` | Render version-selected AI handoff Markdown from data.json; v1 index injection remains compatibility-only |
198
- | `wizard` | `src/commands/execute/wizard.mjs` | Drive the `okstra-run` interactive state machine, including the final outcome envelope |
199
- | `token-usage` | `src/commands/execute/token-usage.mjs` | Wrap installed Python token usage CLI |
200
- | `spawn-followups`, `error-log` | `src/commands/execute/*.mjs` | Follow-up task bundle creation and run error-log append helpers |
201
- | `memory` | `src/commands/memory/memory.mjs` | Store/find global conversation memory under `~/.okstra/memory-book` |
202
- | `pr` | `src/commands/pr/pr.mjs` | `okstra pr <template\|branches\|gen>` — PR body template store under `~/.okstra/template/pr/` (bundled fallback `src/commands/pr/default.md`), base-branch recommendation, and the `gen` JSON bundle (template + `<base>..HEAD` commits + `<base>...HEAD` diffstat) backing the okstra-pr-gen skill. Git-only; no project registration required |
203
- | `recap` | `src/commands/inspect/recap.mjs` | `okstra recap <assemble\|record\|note>` Node wrapper backing the okstra-inspect `recap` facet — `assemble` is a read-only phase-transition summary, `record` appends one line to `recap/recap-log.jsonl`, and `note` writes an agent-authored note under `notes/` and prints the `--clarification-response` argument for a follow-up run |
204
- | `stage-map` | `src/commands/inspect/stage-map.mjs` | `okstra stage-map <task-key>` — exposes a task's implementation-planning Stage Map as JSON (`stages[].{stage_number,title,depends_on,step_count}` + consumer-state-based `doneStages[]`). If there is no Stage Map, `stages: []`. The read-side basis from which `okstra-schedule-gen` derives stage units and dependency closure |
205
- | `design-prep` | `src/commands/inspect/design-prep.mjs` | `okstra design-prep <list\|show\|write>` thin shim into `scripts/okstra_ctl/design_prep.py` — queries (`list`/`show`) the design items that implementation-planning pre-authored with AI, and records the user-confirmed responses as an append-only sidecar under `design-prep-inputs/` (`write`, `--confirmed` required). It never modifies the report snapshot |
206
- | `rollup` | `src/commands/inspect/rollup.mjs` | `okstra rollup` thin shim into `scripts/okstra_ctl/rollup.py` — read-only cross-task roll-up backing the okstra-rollup skill |
207
- | `usage-report` | `src/commands/inspect/usage-report.mjs` | `okstra usage-report` thin shim into `scripts/okstra_ctl/usage_report.py` — read-only project usage snapshot backing the okstra-usage skill |
208
- | `container` | `src/commands/inspect/container.mjs` | `bin okstra container` thin shim into `scripts/okstra_ctl/container.py` for the okstra-container-build skill |
209
- | `code-review` | `src/commands/inspect/code-review.mjs` | `okstra code-review target` thin shim into `scripts/okstra_ctl/code_review_target.py` — resolves what one implementation stage's or one branch's review reads (worktree, branch, base/head commits) and where its result file goes, for the okstra-code-review skill. Read-only; creates no directory or file |
210
- | `manager` | `src/commands/manager.mjs` | Thin shim into `scripts/okstra_ctl/manager_cli.py` for cross-project manager state and child launch packets |
211
-
212
- `src/lib/python-helper.mjs` centralizes Node → Python execution so command modules do not duplicate subprocess wiring.
213
-
214
- `src/lib/helper-scripts.mjs` is the SSOT list of the Python helper scripts that `okstra <cmd>` subcommands front through `runInstalledScript()`. `okstra preflight` asserts every one of them resolves under `~/.okstra/bin` using the same resolver the dispatch path uses, so a stale install fails at the cheap environment gate instead of at the blocking lead step that calls it mid-run. A contract test keeps the list in step with the `scriptName:` literals in `src/commands/**`.
163
+ | `paths` | `src/commands/lifecycle/paths.mts` | Resolve package/runtime/home paths (library at `src/lib/paths.mts`) |
164
+ | `install`, `ensure-installed` | `src/commands/lifecycle/install.mts` | Install or refresh runtime, skills, agents, templates |
165
+ | `uninstall` | `src/commands/lifecycle/uninstall.mts` | Remove managed runtime/skills/agents, optionally purge data |
166
+ | `doctor` | `src/commands/lifecycle/doctor.mts` | Diagnose runtime and Python imports |
167
+ | `setup` | `src/commands/lifecycle/setup.mts` | Create/update `<PROJECT_ROOT>/.okstra/project.json` |
168
+ | `check-project` | `src/commands/lifecycle/check-project.mts` | Verify project registration |
169
+ | `preflight` | `src/commands/lifecycle/preflight.mts` | One-call skill preflight: ensure-installed + check-project + host-specific runtime readiness (single JSON) |
170
+ | `config` | `src/commands/lifecycle/config.mts` | Read/write project/global settings such as PR template path |
171
+ | `migrate` | `src/commands/lifecycle/migrate.mts` | One-shot legacy `.project-docs/okstra` → `.okstra` migration helper |
172
+ | `git-reconcile` | `src/commands/execute/git-reconcile.mts` | Reconcile stale stage SHAs after external git history changes |
173
+ | `handoff` | `src/commands/execute/handoff.mts` | Stage-group release-handoff eligibility / assemble / record helpers |
174
+ | `integrate-stages` | `src/commands/execute/integrate-stages.mts` | Merge verified stages into the task worktree and clean stage worktrees |
175
+ | `task-list`, `task-show` | `src/commands/inspect/task-list.mts`, `src/commands/inspect/task-show.mts` | Task/run introspection for skills; `task-show` consumes the Python task read-side snapshot |
176
+ | `resolve-task-key` | `src/commands/inspect/resolve-task-key.mts` | Resolve a bare task-id to candidate task-keys from the project catalog |
177
+ | `set-work-status` | `src/commands/inspect/set-work-status.mts` | Set a task's user-managed `workStatus` in task-manifest.json (Python: `okstra_ctl.set_work_status`) |
178
+ | `time-report`, `log-report`, `error-report`, `error-zip` | `src/commands/inspect/*.mts` | Read-side task runtime, wrapper log, and error aggregation helpers |
179
+ | `run-audit` | `src/commands/inspect/run-audit.mts` | Anomaly detection — checks run artifacts against progress invariants and reports invariant violations, read-only (Python: `okstra_ctl.run_audit`) |
180
+ | `worker-liveness` | `src/commands/inspect/worker-liveness.mts` | Report whether pending workers are still alive, so the lead's poll ends a stalled wait early instead of paying the deadline (Python: `okstra_ctl.worker_liveness`) |
181
+ | `worker-audit-check` | `src/commands/execute/worker-audit-check.mts` | Apply the Phase 7 worker audit-sidecar rules while the worker session is still alive, so it can fix its own citations (Python: `okstra_ctl.worker_audit_check`, rules in `okstra_ctl.worker_audit_ledger`) |
182
+ | `context-cost` | `src/commands/inspect/context-cost.mts` | Estimate task bundle file/read context cost |
183
+ | `worktree-lookup` | `src/commands/execute/worktree-lookup.mts` | Look up a task-key's registered worktree |
184
+ | `worktree-status` | `src/commands/execute/worktree-status.mts` | Clean-worktree check over source paths only, excluding okstra's provisioned entries and nested stage worktrees (Python: `okstra_ctl.worktree.dirty_entries_excluding_okstra`) |
185
+ | `plan-validate` | `src/commands/execute/plan-validate.mts` | Check approved-plan approval marker |
186
+ | `render-bundle` | `src/commands/execute/render-bundle.mts` | Preview `prepare_task_bundle(render_only=True)` |
187
+ | `profile` | `src/commands/inspect/profile-show.mts` | Print a phase profile with `{{INCLUDE:}}` expanded and its lazy-read sidecars appended transitively, so one grep answers whether a task-type covers a rule — a top-level grep alone returns false negatives (Python: `okstra_ctl.profile_show`). Read-only, unlike `render-bundle` |
188
+ | `run` | `src/commands/execute/run.mts` | Host-aware execution front door (`auto` → Claude/Codex/Antigravity/external path selection) |
189
+ | `codex-run`, `codex-dispatch` | `src/commands/execute/codex-*.mts` | Codex lead dry-run bundle preparation; `codex-dispatch` is the compatibility alias for provider-neutral worker dispatch |
190
+ | `agent-prompt`, `worker-dispatch` | `src/commands/execute/{agent-prompt,worker-dispatch}.mts` | Materialize/verify invocation prompts, record host-native specification/result links, and launch verified CLI assignments through the provider-neutral dispatcher |
191
+ | `team` | `src/commands/execute/team.mts` | External lead tmux-pane worker dispatch / await / teardown |
192
+ | `convergence` | `src/commands/execute/convergence.mts` | Internal admin CLI for the deterministic Phase 5.5 convergence engine (`seed`/`plan-round`/`apply-round`/`apply-critic-gaps`/`finalize`/`validate`/`example`; Python: `okstra_ctl.convergence`) |
193
+ | `plan-items` | `src/commands/execute/plan-items.mts` | Internal admin CLI for deterministic plan-body item extraction and exact-match validation (`extract`/`validate`; Python: `okstra_ctl.plan_items_cli`) |
194
+ | `agent-activity` | `src/commands/report/agent-activity.mts` | Thin Node shim for `okstra_ctl.agent_activity`; `append` records one run-bound activity and `project` writes the validated event projection into final-report data |
195
+ | `report-finalize` | `src/commands/report/finalize.mts` | Run the whole Phase 7 post-report sequence in contractual order (Python: `okstra_ctl.report_finalize`) — the single reference point shared with the Codex lead adapter |
196
+ | `render-views` | `src/commands/report/render-views.mts` | Render schema v2 data with its task-specific human template, or use the quick-report compatibility view |
197
+ | `render-final-report`, `inject-report-index` | `src/commands/report/*.mts` | Render version-selected AI handoff Markdown from data.json; v1 index injection remains compatibility-only |
198
+ | `wizard` | `src/commands/execute/wizard.mts` | Drive the `okstra-run` interactive state machine, including the final outcome envelope |
199
+ | `token-usage` | `src/commands/execute/token-usage.mts` | Wrap installed Python token usage CLI |
200
+ | `spawn-followups`, `error-log` | `src/commands/execute/*.mts` | Follow-up task bundle creation and run error-log append helpers |
201
+ | `memory` | `src/commands/memory/memory.mts` | Store/find global conversation memory under `~/.okstra/memory-book` |
202
+ | `pr` | `src/commands/pr/pr.mts` | `okstra pr <template\|branches\|gen>` — PR body template store under `~/.okstra/template/pr/` (bundled fallback `src/commands/pr/default.md`), base-branch recommendation, and the `gen` JSON bundle (template + `<base>..HEAD` commits + `<base>...HEAD` diffstat) backing the okstra-pr-gen skill. Git-only; no project registration required |
203
+ | `recap` | `src/commands/inspect/recap.mts` | `okstra recap <assemble\|record\|note>` Node wrapper backing the okstra-inspect `recap` facet — `assemble` is a read-only phase-transition summary, `record` appends one line to `recap/recap-log.jsonl`, and `note` writes an agent-authored note under `notes/` and prints the `--clarification-response` argument for a follow-up run |
204
+ | `stage-map` | `src/commands/inspect/stage-map.mts` | `okstra stage-map <task-key>` — exposes a task's implementation-planning Stage Map as JSON (`stages[].{stage_number,title,depends_on,step_count}` + consumer-state-based `doneStages[]`). If there is no Stage Map, `stages: []`. The read-side basis from which `okstra-schedule-gen` derives stage units and dependency closure |
205
+ | `design-prep` | `src/commands/inspect/design-prep.mts` | `okstra design-prep <list\|show\|write>` thin shim into `scripts/okstra_ctl/design_prep.py` — queries (`list`/`show`) the design items that implementation-planning pre-authored with AI, and records the user-confirmed responses as an append-only sidecar under `design-prep-inputs/` (`write`, `--confirmed` required). It never modifies the report snapshot |
206
+ | `rollup` | `src/commands/inspect/rollup.mts` | `okstra rollup` thin shim into `scripts/okstra_ctl/rollup.py` — read-only cross-task roll-up backing the okstra-rollup skill |
207
+ | `usage-report` | `src/commands/inspect/usage-report.mts` | `okstra usage-report` thin shim into `scripts/okstra_ctl/usage_report.py` — read-only project usage snapshot backing the okstra-usage skill |
208
+ | `container` | `src/commands/inspect/container.mts` | `bin okstra container` thin shim into `scripts/okstra_ctl/container.py` for the okstra-container-build skill |
209
+ | `code-review` | `src/commands/inspect/code-review.mts` | `okstra code-review target` thin shim into `scripts/okstra_ctl/code_review_target.py` — resolves what one implementation stage's or one branch's review reads (worktree, branch, base/head commits) and where its result file goes, for the okstra-code-review skill. Read-only; creates no directory or file |
210
+ | `manager` | `src/commands/manager.mts` | Thin shim into `scripts/okstra_ctl/manager_cli.py` for cross-project manager state and child launch packets |
211
+
212
+ `src/lib/python-helper.mts` centralizes Node → Python execution so command modules do not duplicate subprocess wiring.
213
+
214
+ `src/lib/helper-scripts.mts` is the SSOT list of the Python helper scripts that `okstra <cmd>` subcommands front through `runInstalledScript()`. `okstra preflight` asserts every one of them resolves under `~/.okstra/bin` using the same resolver the dispatch path uses, so a stale install fails at the cheap environment gate instead of at the blocking lead step that calls it mid-run. A contract test keeps the list in step with the `scriptName:` literals in `src/commands/**`.
215
215
 
216
216
  ### 4.2 `scripts/` — Runtime source
217
217
 
@@ -258,11 +258,12 @@ Important modules:
258
258
  | `run_context.py` | Per-task mutex, run context and run-input persistence; `consumers_mutex` helper for atomic `consumers.jsonl` writes |
259
259
  | `path_hints.py` | Compact path-hint persistence + legacy context hydration — stores `run-context` / `active-run-context` in the schemaVersion `2.0` `identity` + `pathHints` compact schema, and hydrates the legacy flat path keys (`RUN_MANIFEST_RELATIVE_PATH`, `TEAM_STATE_PATH`, etc.) in memory the moment the host-side reader reads them |
260
260
  | `consumers.py` | Append-only `consumers.jsonl` writer + reader — records which `implementation` runs consumed which `implementation-planning` stage |
261
- | `implementation_outcome.py` | Artifact-derived reconstruction of the implementation phase outcome — reads `runs/implementation/carry/stage-<N>.json` + `consumers.jsonl` + the approved Stage Map to derive `phaseOutcome.implementation`, and when every stage has pass-grade carry evidence it corrects `workflow.nextRecommendedPhase` to `final-verification` (keeping the `contract-violated` audit information) |
261
+ | `implementation_outcome.py` | Artifact-derived reconstruction of the implementation phase outcome — reads `runs/implementation/carry/stage-<N>.json` + `consumers.jsonl` + the approved Stage Map to derive `phaseOutcome.implementation`, and when every stage has pass-grade carry evidence it raises `workflow.nextRecommendedPhase.status` to `ready` (keeping the `contract-violated` audit information). It never picks the phase — `phase` is left as the last stage's report routing settled it, and the status is raised only while `phase` is non-empty |
262
262
  | `paths.py` | Path/sequence computation for task/run artifacts (including recap directory/log paths) |
263
263
  | `recap.py` | deterministic backend for the okstra-inspect `recap` facet — `assemble` builds the cross-run phase transitions from the timeline, `record` append-only writes a summary/Q&A to `<task-root>/recap/recap-log.jsonl` (other task artifacts unchanged) |
264
264
  | `render.py` | task manifest, run manifest, timeline, task index, discovery, team-state, prompt/template render |
265
- | `workflow.py` | Phase sequence, allowed outputs, forbidden actions, next phase |
265
+ | `workflow.py` | Phase sequence (`PHASE_SEQUENCE`), per-phase allowed outputs, forbidden actions. It does not decide the next phase — that is `next_phase.py` |
266
+ | `next_phase.py` | `workflow.nextRecommendedPhase` SSOT — the pointer's shape (`make` / `is_pointer` over `{phase, status, rationale}`, `status` ∈ `ready`/`pending`/`blocked`/`terminal`), the promotion of a legacy string pointer (`promote`), the projection of one report's routing field into a pointer (`project`), and the `ready`-only read the shell and wizard autofill share (`autofill_task_type`). There is no static phase table and no sequence walk: the next phase comes from what the report authored, and nothing else may compute one |
266
267
  | `workers.py`, `models.py` | Worker roster; `models.py` is the model catalog SSOT (`ModelSpec` per alias + `ROLE_DEFAULTS`) — add-a-model single reference point from which picker options, codex pricing, and role defaults all derive |
267
268
  | `worktree.py`, `worktree_registry.py` | One worktree per task-key, branch registry, sync dirs/files/snapshots |
268
269
  | `project_meta.py`, `resolver.py`, `path_resolve.py` | Project/task/run resolution |
@@ -634,7 +635,7 @@ Project-local `<PROJECT_ROOT>/.claude/settings.local.json` is provisioned as a s
634
635
  | `final-verification` | Read-only acceptance verification | `release-handoff` if accepted |
635
636
  | `release-handoff` | User-selected commit/PR handoff | done or follow-up |
636
637
 
637
- The independent analysis sidetracks do not appear in this phase sequence. `project-analysis` maps the current project, `feature-analysis` traces one existing feature, and `change-impact-analysis` maps a proposed change's impact. Each returns to `pending-routing-decision` and remains read-only, including no test or build execution.
638
+ The independent analysis sidetracks do not appear in this phase sequence. `project-analysis` maps the current project, `feature-analysis` traces one existing feature, and `change-impact-analysis` maps a proposed change's impact. Each leaves the next-phase pointer `pending` with no phase, and remains read-only, including no test or build execution.
638
639
 
639
640
  ### 7.5 Report and follow-up
640
641
 
@@ -683,4 +684,4 @@ Clarifications now live in the unified `## 1. Clarification Items` table. Deprec
683
684
 
684
685
  ---
685
686
 
686
- *Updated: 2026-07-22 · Source of truth checked against `package.json`, `bin/okstra`, `src/cli-registry.mts`, `src/lib/skill-catalog.mts`, `tools/build.mjs`, `scripts/`, `skills/`, `agents/`, `templates/`, `schemas/`, `validators/`, and tests.*
687
+ *Updated: 2026-08-18 · Source of truth checked against `package.json`, `bin/okstra`, `src/cli-registry.mts`, `src/lib/skill-catalog.mts`, `tools/build.mjs`, `scripts/`, `skills/`, `agents/`, `templates/`, `schemas/`, `validators/`, and tests.*
@@ -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
- The wizard asks role count before role models. `--role-count <role>=<N>` and `--role-model <role>=<modelRef>` are the public inputs. `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 shows the leader session read-only, then role counts (`min..max`, default **recommended**), then `--role-model <role>=<provider>/<model>` per slot. 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
 
@@ -69,12 +69,14 @@ The wizard asks role count before role models. `--role-count <role>=<N>` and `--
69
69
 
70
70
  ## 5. Quick comparison table
71
71
 
72
- | task-type | wizard special question | runtime prepare gate | lead/worker mode | next phase default |
72
+ The last column is the `workflow.nextRecommendedPhase` pointer Phase 7 leaves behind an object `{phase, status, rationale}`, projected from the report's own routing field. There is no static default: a run that settles no route ends `pending` with no phase. The authoring rule is stated once, in the Phase 6 checklist of [`prompts/lead/report-writer.md`](../../prompts/lead/report-writer.md).
73
+
74
+ | task-type | wizard special question | runtime prepare gate | lead/worker mode | next-phase pointer |
73
75
  |---|---|---|---|---|
74
- | `requirements-discovery` | common questions only | profile/brief/base-ref exist | multi-worker analysis, convergence 1 round default | `pending-routing-decision` |
75
- | `error-analysis` | common questions only | profile/brief/base-ref exist | multi-worker analysis, convergence 2 rounds default | `implementation-option-selection` |
76
- | `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 | `implementation-planning` or `blocked` |
77
- | `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 | `implementation` after plan approval |
78
- | `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 | `final-verification` |
79
- | `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 | `pending-release-handoff` |
80
- | `release-handoff` | handoff scope (stage-group or whole-task), PR template override/scope | Stage Lifecycle Snapshot eligibility, generated `release-handoff-input.md`, empty worker roster | single-lead; whole-task PR or stage-group collector branch/PR | `done-or-follow-up` |
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
+ | `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` |
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 `plan-ready` (the plan still needs its separate approval), or at `implementation-option-selection` on `direction-invalidated` |
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
+ | `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` |
82
+ | `release-handoff` | handoff scope (stage-group or whole-task), PR template override/scope | Stage Lifecycle Snapshot eligibility, generated `release-handoff-input.md`, empty worker roster | single-lead; whole-task PR or stage-group collector branch/PR | always `terminal` |
@@ -56,18 +56,17 @@ stateDiagram-v2
56
56
  TaskType --> BaseRef: no active worktree
57
57
  TaskType --> ImplementationExtras: implementation only
58
58
  BaseRef --> ImplementationExtras: implementation only
59
- BaseRef --> DefaultsOrCustom: non-implementation
60
- ImplementationExtras --> DefaultsOrCustom
61
- DefaultsOrCustom --> WorkersOverride: non-implementation analyser roster
62
- WorkersOverride --> Confirm: defaults
63
- DefaultsOrCustom --> ModelAndOptionalInputs: customize
64
- ModelAndOptionalInputs --> Confirm
59
+ BaseRef --> LeaderSession: non-implementation
60
+ ImplementationExtras --> LeaderSession
61
+ LeaderSession --> RoleSlots: role-count then role-model
62
+ RoleSlots --> OptionalInputs: directive / related / clarification
63
+ OptionalInputs --> Confirm
65
64
  Confirm --> EditTarget: Edit
66
65
  EditTarget --> TaskType: rewind selected step
67
66
  Confirm --> Done: Proceed
68
67
  ```
69
68
 
70
- A new task receives its brief first. If the brief frontmatter has `task-group:` and `brief-id:`, the wizard shows the task group/id as recommended picks. An existing task shows the manifest's `workflow.nextRecommendedPhase` as the recommended task-type, and if an existing brief path exists it asks whether to keep or change it.
69
+ A new task receives its brief first. If the brief frontmatter has `task-group:` and `brief-id:`, the wizard shows the task group/id as recommended picks. An existing task shows the manifest's `workflow.nextRecommendedPhase.phase` as the recommended task-type, but only while that pointer's `status` is `ready`. Under any other status the recommended slot stays empty and the list falls back to rerunning `workflow.currentPhase` plus the full task-type choices. If an existing brief path exists it asks whether to keep or change it.
71
70
 
72
71
  ## 4. render-bundle and prepare_task_bundle
73
72
 
@@ -152,20 +151,18 @@ flowchart TD
152
151
  T[task-type selected] --> W{active worktree in registry?}
153
152
  W -->|yes| Reuse[reuse existing worktree<br/>base-ref prompt skipped]
154
153
  W -->|no| Base[ask base-ref<br/>validate with git rev-parse]
155
- Base --> D{Use defaults?}
156
- Reuse --> D
157
- D -->|defaults| R{non-implementation<br/>has analyser roster?}
158
- D -->|customize| M[lead/model/directive/related/clarification prompts]
159
- R -->|yes| WO[worker multi-pick still shown]
160
- R -->|no| Confirm
161
- WO --> Confirm
162
- M --> Special{release-handoff?}
154
+ Base --> L[leader session read-only]
155
+ Reuse --> L
156
+ L --> C[role-count min..max<br/>omit uses recommended]
157
+ C --> M[role-model provider/model per slot]
158
+ M --> O[directive / related / clarification]
159
+ O --> Special{release-handoff?}
163
160
  Special -->|yes| PR[PR template override/scope]
164
161
  Special -->|no| Confirm
165
162
  PR --> Confirm
166
163
  ```
167
164
 
168
- The important point is that `Use defaults` means the model defaults. For non-implementation task-types, the worker roster question still appears even if you choose defaults. Conversely, `implementation` has no worker override question; it uses the profile default roster and executor binding.
165
+ 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.
169
166
 
170
167
  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.
171
168
 
@@ -20,19 +20,18 @@ flowchart TD
20
20
  Start[/okstra-run/] --> Common[common task identity flow]
21
21
  Common --> Type[task-type = error-analysis]
22
22
  Type --> Worktree{active worktree exists?}
23
- Worktree -->|yes| Defaults[Use defaults / Customize]
23
+ Worktree -->|yes| Leader[leader session read-only]
24
24
  Worktree -->|no| BaseRef[base-ref pick/text<br/>main recommended]
25
- BaseRef --> Defaults
26
- Defaults --> Workers[analysis worker multi-pick]
27
- Workers --> D{Use defaults?}
28
- D -->|yes| Confirm
29
- D -->|customize| M[lead/worker model prompts]
30
- M --> X[directive, related tasks, clarification]
31
- X --> Confirm
25
+ BaseRef --> Leader
26
+ Leader --> RoleCount[role-count min..max<br/>omit uses recommended; skip if min==max]
27
+ RoleCount --> RoleModel[role-model provider/model per slot]
28
+ RoleModel --> RoleAdd[min=0 roles via role-add only<br/>default skip]
29
+ RoleAdd --> Extras[directive, related tasks, clarification]
30
+ Extras --> Confirm
32
31
  Confirm --> Render[render-bundle --render-only]
33
32
  ```
34
33
 
35
- The default required roster is `claude`, `codex`, `report-writer`. `antigravity` is an optional worker, and if selected it joins the analyser set.
34
+ Launch selection uses role slots and model refs only: leader is the current session (read-only), then each static role's count in `min..max` (default **recommended**; the count step is skipped when `min == max`), then one `provider/model` per slot. Roles with `min = 0` stay closed unless the user opens them with role-add (default skip). Duplicate model refs in the same role are rejected. There is no provider roster multi-pick and no `Use defaults / Customize` fork. Dynamic verifiers are not chosen at launch. `--workers` is a CLI compatibility input only, not a launch picker.
36
35
 
37
36
  ## 3. prepare_task_bundle handling
38
37
 
@@ -57,7 +56,7 @@ sequenceDiagram
57
56
 
58
57
  For canonical briefs, preflight runs before worker resolution, worktree provisioning, or report creation. A brief whose `reporter-confirmations` status is `pending` stops at this point; legacy briefs keep the compatibility path.
59
58
 
60
- The final report records its next phase in `errorAnalysis.routing.nextTaskType`. After report validation passes, workflow metadata persists that route as `nextRecommendedPhase`. A credible cause uses `implementation-option-selection`; continued investigation uses `error-analysis`. The static fallback also routes a missing or legacy error-analysis report to option selection.
59
+ The final report records its next phase in `errorAnalysis.routing.nextTaskType`. A credible cause uses `implementation-option-selection`; continued investigation uses `error-analysis`. When report validation passes, Phase 7 projects `workflow.nextRecommendedPhase` from that one field (`scripts/okstra_ctl/next_phase.py::project`) — a `ready` pointer naming it. A report that leaves the field empty leaves the pointer `pending`; there is no static fallback that supplies a phase the report did not author.
61
60
 
62
61
  ## 4. lead execution flow
63
62