okstra 0.221.0 → 0.222.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 (39) hide show
  1. package/docs/architecture/storage-model.md +1 -1
  2. package/docs/architecture.md +4 -4
  3. package/docs/cli.md +7 -7
  4. package/docs/project-structure-overview.md +4 -0
  5. package/package.json +1 -1
  6. package/runtime/BUILD.json +2 -2
  7. package/runtime/prompts/lead/convergence.md +3 -1
  8. package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
  9. package/runtime/prompts/lead/phase-routing.md +1 -1
  10. package/runtime/python/okstra_ctl/business_flow/engine.py +1 -0
  11. package/runtime/python/okstra_ctl/convergence.py +155 -19
  12. package/runtime/python/okstra_ctl/convergence_critic_prompt.py +3 -2
  13. package/runtime/python/okstra_ctl/convergence_migration.py +10 -1
  14. package/runtime/python/okstra_ctl/convergence_provenance.py +69 -24
  15. package/runtime/python/okstra_ctl/convergence_reverify_prompt.py +4 -1
  16. package/runtime/python/okstra_ctl/correction_cli.py +7 -7
  17. package/runtime/python/okstra_ctl/correction_round.py +40 -0
  18. package/runtime/python/okstra_ctl/fix_cycles.py +14 -1
  19. package/runtime/python/okstra_ctl/git_reconcile.py +62 -8
  20. package/runtime/python/okstra_ctl/next_phase.py +22 -9
  21. package/runtime/python/okstra_ctl/phases/final_verification/target.py +4 -2
  22. package/runtime/python/okstra_ctl/phases/implementation/entry.py +6 -2
  23. package/runtime/python/okstra_ctl/phases/implementation/host-rules.md +1 -0
  24. package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-deliverable.md +2 -2
  25. package/runtime/python/okstra_ctl/process_group.py +4 -0
  26. package/runtime/python/okstra_ctl/report_assembly.py +50 -13
  27. package/runtime/python/okstra_ctl/report_html/common.py +3 -1
  28. package/runtime/python/okstra_ctl/report_projections.py +45 -4
  29. package/runtime/python/okstra_ctl/report_synthesis_packet.py +22 -1
  30. package/runtime/python/okstra_ctl/run.py +7 -5
  31. package/runtime/python/okstra_ctl/stage_reconcile.py +49 -1
  32. package/runtime/python/okstra_ctl/wizard/steps_plan.py +3 -3
  33. package/runtime/schemas/final-report-v3.0.schema.json +36 -0
  34. package/runtime/skills/okstra-run/SKILL.md +1 -0
  35. package/runtime/templates/reports/html/base.template.html +6 -1
  36. package/runtime/templates/reports/html/i18n/en.json +1 -0
  37. package/runtime/templates/reports/html/i18n/ko.json +1 -0
  38. package/runtime/validators/validate-run.py +5 -2
  39. package/runtime/validators/validate_session_conformance.py +19 -1
@@ -429,7 +429,7 @@ Convergence keeps its decision trail under the run's `state/` directory:
429
429
  - `state/convergence-<task-type>-<seq>.json` — validated terminal schema v1.4 state consumed by reporting and Phase 7. Historical v1.0-v1.3 artifacts are read as they are.
430
430
  - `state/migrations/` — byte-for-byte archives of invalid or partial legacy state replaced during an explicit Round 0 restart.
431
431
 
432
- A valid terminal legacy final is reused unchanged, and a matching valid working state resumes. Invalid new-engine working state fails closed unless seeding receives `--restart-from-round0`; an existing final can be replaced only when its migration archive still matches its original bytes.
432
+ A valid terminal legacy final is reused unchanged, and a matching valid working state resumes. Invalid new-engine working state fails closed unless seeding receives `--restart-from-round0`, which is refused for a valid or absent working state; an implementation correction round's re-convergence writes `convergence-{groups-,work-,}correction-r<N>-<task-type>-<seq>.json` beside the initial files; an existing final can be replaced only when its migration archive still matches its original bytes.
433
433
 
434
434
  Recommended worker status values:
435
435
 
@@ -370,7 +370,7 @@ Coverage critic and plan-body verification remain separate from finding converge
370
370
 
371
371
  `okstra convergence` and `okstra plan-items` are lead-contract admin interfaces, not public skill entry points. The former `okstra-convergence` skill stays obsolete; these commands do not expand the installed public skill roster.
372
372
 
373
- Seeding provides the migration boundary for in-progress runs. A valid terminal legacy final is reused unchanged. A matching valid working state resumes. A malformed or partial legacy state is archived byte-for-byte under `state/migrations/` before an explicit Round 0 restart; an invalid new-engine working state fails closed until `--restart-from-round0` is supplied. Replacement of an existing final is allowed only while its recorded archive still matches the original bytes.
373
+ Seeding provides the migration boundary for in-progress runs. A valid terminal legacy final is reused unchanged. A matching valid working state resumes. A malformed or partial legacy state is archived byte-for-byte under `state/migrations/` before an explicit Round 0 restart; an invalid new-engine working state fails closed until `--restart-from-round0` is supplied, and the flag is refused when there is no invalid working state to recover. An implementation correction round re-converges in its own `correction-r<N>` namespace of state, plan and reverify names, so the initial final is never reseeded. Replacement of an existing final is allowed only while its recorded archive still matches the original bytes.
374
374
 
375
375
  ## Stable task identity
376
376
 
@@ -469,7 +469,7 @@ The fourth column is the `workflow.nextRecommendedPhase` pointer Phase 7 leaves
469
469
  | `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 |
470
470
  | `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) |
471
471
  | `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`/`final-verification`) | 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`; either verification scope may route there, since a handoff opens one PR per stage. `final-verification` re-runs this phase on the same head, and is for a run whose every remaining blocker is an environment or configuration fault this report already diagnosed. `done` becomes `terminal` | No (read-only tests only) |
472
- | `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. |
472
+ | `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 | `terminal` once every selected stage is delivered — the lifecycle ends here and the routing field is free text. `ready` at `release-handoff` when H1 was `skip` or any `pullRequestOutcomes` row is `skipped`, so the task stays open for re-entry | 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. |
473
473
  | `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) |
474
474
  | `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) |
475
475
  | `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) |
@@ -498,9 +498,9 @@ Common constraints:
498
498
 
499
499
  If a bug is discovered in artifacts after a task has completed release-handoff, fix it by reentering the same task ID through an entry phase (`requirements-discovery` / `error-analysis` / `implementation-option-selection` / `implementation-planning`). There is no dedicated hotfix task type, and the phase gates remain unchanged. This set of reentry runs is a **fix cycle**. Its source of truth is the append-only event rows (`opened` / `run` / `closed`) in `<task_root>/history/fix-cycles.jsonl`, owned exclusively by the module `scripts/okstra_ctl/fix_cycles.py`. The entry-phase list is defined only in `fix_cycles.FIX_CYCLE_ENTRY_PHASES` and is shared by the prepare gate and wizard detection predicate.
500
500
 
501
- - **Entry**: The okstra-run wizard detects reentry into an entry phase for a completed task and confirms it at the `fix_cycle_confirm` step. The CLI uses `--fix-cycle <yes|no>` (when omitted, nothing is recorded). `--fix-cycle yes` opens a cycle only if both guards pass: task type is an entry phase, and the manifest's `workflow.lastCompletedPhase` is `release-handoff`; a violation raises `PrepareError`. A task may have only one open cycle at a time.
501
+ - **Entry**: The okstra-run wizard detects reentry into an entry phase for a completed task and confirms it at the `fix_cycle_confirm` step. The CLI uses `--fix-cycle <yes|no>` (when omitted, nothing is recorded). `--fix-cycle yes` opens a cycle only if both guards pass: task type is an entry phase, and the manifest's `workflow.lastCompletedPhase` is `release-handoff` with `workflow.nextRecommendedPhase` not pointing back at `release-handoff` (`fix_cycles.delivery_completed`; a handoff that ended in `skip` or left a `skipped` PR row is not complete); a violation raises `PrepareError`. The wizard asks `fix_cycle_confirm` under the same predicate. A task may have only one open cycle at a time.
502
502
  - **Attachment**: While a cycle is open, all runs for the same task attach to it as `run` rows even without a later `--fix-cycle` flag, and `fixCycleId` is recorded in run-manifest and timeline entries.
503
- - **Closure**: After a release-handoff run attaches to an open cycle and the manifest's `workflow.lastCompletedPhase` becomes `release-handoff`, the next prepare lazily appends a `closed` row.
503
+ - **Closure**: After a release-handoff run attaches to an open cycle and the manifest's `workflow.lastCompletedPhase` becomes `release-handoff`, the next prepare lazily appends a `closed` row. A handoff that left delivery owed (H1 `skip` or a `skipped` PR row, so `workflow.nextRecommendedPhase` points back at `release-handoff`) does not close the cycle; the next release-handoff run attaches to it.
504
504
  - **Consumers (all derived views)**: ① the `## Fix History` section of a later run's analysis-packet ② quotation in Task Continuity Notes from okstra-brief-gen ③ final-report `## 5.10 Fix History` ④ a `fixCycles` summary in task-manifest plus a one-line entry in task-index / task-catalog. All four consumers read only derived views from `fix_cycles.summarize()` / `packet_summary()`.
505
505
 
506
506
  ### release-handoff: one stage, one PR
package/docs/cli.md CHANGED
@@ -441,7 +441,7 @@ scripts/okstra.sh --task-type implementation-planning ... \
441
441
  ### `--fix-cycle`
442
442
 
443
443
  - `--fix-cycle <yes|no>` records whether re-entry into an entry phase (`requirements-discovery` / `error-analysis` / `implementation-option-selection` / `implementation-planning`) after completion through release-handoff is a bug-fix cycle. When omitted, no cycle is recorded. The `fix_cycle_confirm` step in the okstra-run wizard accepts the same input.
444
- - `yes` opens a new cycle only when both conditions hold: the task type is an entry phase, and the manifest's `workflow.lastCompletedPhase` is `release-handoff`. Preparation rejects the request if either condition is false. Once a cycle is open, later runs for the same task attach to it automatically without this flag.
444
+ - `yes` opens a new cycle only when both conditions hold: the task type is an entry phase, and the manifest's `workflow.lastCompletedPhase` is `release-handoff` with delivery finished — a release-handoff that ended in `skip` or left a stage's PR `skipped` points the next phase back at `release-handoff`, and that task is refused. Preparation rejects the request if either condition is false. Once a cycle is open, later runs for the same task attach to it automatically without this flag.
445
445
 
446
446
  ### `--lead-runtime`
447
447
 
@@ -839,12 +839,12 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
839
839
  | `okstra setup --project-id <id>` | Create or update `.okstra/project.json` in the current project. When `<PROJECT_ROOT>/CLAUDE.md` or `AGENTS.md` already exists, it also appends — or refreshes in place — an okstra-managed block between `<!-- okstra:citation-guidance:begin -->` / `end` telling agents not to cite okstra-internal references (report section numbers, `C-NNN` ids, run/stage ids, `.okstra/...` paths) outside an okstra report. Neither file is created, and a failure is a warning, not an error |
840
840
  | `okstra check-project [--json]` | Verify that the current project is registered |
841
841
  | `okstra preflight [--runtime <name>] [--cwd <dir>] [--machine]` | Single skill-preflight call combining `ensure-installed`, with silent reinstall when stale, `check-project`, and host-specific `runtimeReadiness`. It defaults to a fixed text projection. `--machine` returns the automation JSON contract, and `--json` is a deprecated one-release alias. A `claude-code` host checks project workspace trust. A `codex` current-session host verifies write access to `~/.okstra/worktrees/registry.lock`; a sandbox denial blocks before the wizard with the `switch-codex-to-full-access-and-rerun` action. `antigravity` and `external` hosts return ready without reading Claude Code state. Step 0 of every project-scoped skill converges on this command |
842
- | `okstra convergence seed --groups <path> --work-state <path> --final-state <path> --migration-dir <dir> [--restart-from-round0]` | Create, resume, reuse, or explicitly recover deterministic convergence state |
843
- | `okstra convergence plan-round --work-state <path> --plan <path>` | Persist the next roster-aware dispatch plan without mutating working state |
844
- | `okstra convergence collect-results --plan <round-plan.json> --result <worker>=<path>… --run-manifest <run-manifest.json> --output <round-results.json>` | Read one round's worker responses into the `apply-round --results` shape. The reverify 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 |
842
+ | `okstra convergence seed --groups <path> --work-state <path> --final-state <path> --migration-dir <dir> [--restart-from-round0]` | Create, resume, reuse, or explicitly recover deterministic convergence state. `--restart-from-round0` is refused unless the working state is invalid. State paths named `…-correction-r<N>-<task-type>-<seq>.json` seed an implementation correction round's re-convergence, checked against that round's verifier results |
843
+ | `okstra convergence plan-round --work-state <path> --plan <path>` | Persist the next roster-aware dispatch plan without mutating working state; refuses to overwrite a different plan at `--plan` whose round its namespace already dispatched |
844
+ | `okstra convergence collect-results --plan <round-plan.json> --result <worker>=<path>… --run-manifest <run-manifest.json> --output <round-results.json> [--correction-round <N>]` | Read one round's worker responses into the `apply-round --results` shape. The reverify 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 |
845
845
  | `okstra convergence apply-round --work-state <path> --plan <path> --results <path>` | Validate one complete structured result set and atomically reduce it into working state |
846
846
  | `okstra convergence critic-prompt --run-manifest <path>` | Render the critic task instructions to stdout — the acceptance critic's on `final-verification`, the coverage critic's elsewhere; 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. The acceptance body carries the analysis packet, the analyser result lines and the acceptance mandate with the `### [AC-NNN]` candidate format, and omits the Round 0 list to stay inside the final-verification 96-line cap. Exits 2 when the run has published no grouping yet, or when the manifest carries no `analysisPacketPath` |
847
- | `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 adversarial mandate, 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 |
847
+ | `okstra convergence reverify-prompt --run-manifest <path> --plan <round-plan.json> --worker <worker-id> [--correction-round <N>]` | 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 adversarial mandate, 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 |
848
848
  | `okstra convergence critic-verify-prompt --run-manifest <path> --gaps <critic-batch.json> --worker <worker-id>` | Render one Phase 4 analyser's coverage-critic gap verification instructions to stdout; the lead writes the output verbatim into the file the prompt materializer's `--instruction` takes and materializes it with `--dispatch-kind critic-verify` under the analyser's `reverify/<worker-id>` assignment ref. Input is the pre-vote coverage batch (`mode: coverage`, `gaps[]` with `gapId`, `summary`, `category`, `ticketIds`, `originEvidence`, optional `duplicateOf`) the lead later passes to `apply-critic-gaps`; gaps are assigned round-robin over the grouping's analysis roster exactly as `apply-critic-gaps` checks them, and the body carries only this analyser's share — each gap with the critic's result file (`<provider>-worker-critic-<task-type>-<seq>.md`) and `### [<gapId>]` section, the critic audit sidecar, and the adversarial response format. A batch with `mode: acceptance-devils-advocate` and `candidates[]` (`candidateId`, `statement`, `severity`, `originEvidence`) renders the confirm-or-downgrade instructions instead: every analyser gets every candidate, each pointing at the critic result section whose heading names that `candidateId`, in either the bracketed or the plain heading form. Exits 2 when the batch's taskKey or mode does not match, the run has no critic assignment, the critic result is not collected yet, the worker is not an analyser, or round-robin assigns it no gap |
849
849
  | `okstra convergence collect-acceptance-votes --run-manifest <path> --candidates <batch.json> --result <worker>=<path>… --output <batch.json>` | Read the analysers' `critic-verify` responses to the acceptance candidates and write the `apply-acceptance-critic --results` batch: a candidate any analyser answers `SURVIVES` or `SURVIVES-WITH-CAVEAT` is `confirmed`, every other candidate `downgraded`; each candidate row keeps the per-analyser votes. Exits 2 when a worker is not an analyser or returned no verdict for a candidate |
850
850
  | `okstra convergence census-audit --run-manifest <path> --result <worker>=<path>… [--gapfill <worker>=<path>…]` | Read every analysis worker's `Coverage Verdicts` against the run's coverage census and write `state/coverage-census-audit-<task-type>-<seq>.json`: each worker's cells without a usable verdict and why, the cell × worker matrix, the cells one worker judged `finding` and another `clean`, and per-cell grouping suggestions. `--gapfill` merges a worker's one `census-gapfill` result; a `--result` or `--gapfill` path that does not exist is recorded as missing. Prints a JSON summary with `gapfillNeeded` and per-worker `unverdicted` counts. Unjudged cells never change the exit code; a run without a census prints `"census": "absent"` and writes nothing |
@@ -871,7 +871,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
871
871
  | `okstra memory <add\|list\|search\|show\|archive>` | Manage global conversation memory in `~/.okstra/memory-book`, a user-home store separate from project `.okstra/` and the CLI basis of the `save this in okstra` natural-language skill. `add --source-key <key>` records the source an entry stands for: adding the same key again returns the stored entry (`created: false`) instead of a second one, also when two adds race, and with `--json` a likely-secret refusal is printed as `{ ok: false, refused: "secret", secrets, message }`. An add without `--source-key` is unchanged |
872
872
  | `okstra chat <rooms\|create\|join\|members\|send\|unread\|inbox\|log\|ack>` | Global rooms under `~/.okstra/chat`. Join takes an explicit `--name`. Send takes exactly one of `--to` (`all` or a member; may equal `--as`) or `--reply-to` (an id in that room), and exactly one of `--body` or `--body-file`; a body may span several lines. A reply inherits `to` from the parent (`all` stays `all`, otherwise the parent's `from`). Unread is the inbox after the read cursor minus messages the participant sent; it does not move the cursor. `inbox` and `log` keep those arrivals, including after ack. Rows are `id @from YYYY-MM-DD HH:MM body`; a reply inserts `↑parentId` after the time; the second and later lines of a body follow on rows indented by two spaces. `ack --through` rejects an id behind the current cursor. Room and display names `.` and `..` are reserved. A `.lock` left by a dead process (pid recorded in the file) or older than 5 s is reclaimed. CLI basis of the `okstra-chat` skill. |
873
873
  | `okstra model-io active-context-input --project-root <dir> --run-manifest <path>` | Resolve the prior implementation-planning active context only through the current project and run authority, then emit fixed fields such as the executor base ref. Caller-supplied active-context paths are not accepted. |
874
- | `okstra convergence prepare-groups --run-manifest <path> --input <grouping.md>` | Parse fixed grouping Markdown, validate distinct per-worker evidence and group semantics against the run authority, then publish the schema-valid groups artifact at the run-owned path. |
874
+ | `okstra convergence prepare-groups --run-manifest <path> --input <grouping.md> [--correction-round <N>]` | Parse fixed grouping Markdown, validate distinct per-worker evidence and group semantics against the run authority, then publish the schema-valid groups artifact at the run-owned path. |
875
875
  | `okstra manager <init\|discover-projects\|new\|task\|list\|remove\|view> [--json]` | Public CLI for grouping cross-project okstra tasks into manager-owned context. The default is purpose-specific fixed text for model use; `--json` preserves the machine contract and exit codes. `new project --project-root` accepts only existing directories and performs setup-equivalent registration only if `.okstra/project.json` is absent. `list managers`, `list projects --manager-id <id>` and `list tasks --manager-id <id>` enumerate manager state; `view --manager-id <id>` writes `~/.okstra/managers/<id>/view/index.html` and prints its path and `file://` URL. `task split --plan <json>` writes one validated brief with a `## Project Scope` section into each project an issue is assigned to and registers those children; `task run` then passes `--task-brief`. `task run` re-reads the target child's state from its project and refuses a child that is already done there, naming its last phase and report, unless `--allow-completed` is given; `task status` prints `Synced at` and `Sync age` and marks children whose task manifest changed after that sync. For a child whose task type is `discussion`, `task sync` records a `discussion` block read from the discussion record without writing to it — state (`draft`, `completed`, or `unknown` when the record cannot be read), phase, pending questions, revisions with their change reasons, and each follow-up's pinned revision and impact-review state, re-read from the follow-up's brief — and `task status` counts the child under `discussion`, never under `done`. `task run` for a child without a registered brief takes the brief that `okstra discussion follow-up-brief` wrote for it, found in the last-synced links of a discussion child of the same manager task. `list task-groups` lists groups including empty ones; `task update` edits a task's objective, common brief or progress mode; `remove project`, `remove task`, `remove task-group` and `task remove-child` print their targets and delete manager files only with `--confirm`, and `remove project` keeps the project's children marked unlinked. |
876
876
  | `okstra rollup [--task-group <group>] [--project-root <dir>] [--cwd <dir>] [--text\|--json]` | Read-only backend for the okstra-rollup skill. `--text` emits ordered fixed labels for model use. The default and `--json` preserve the full machine JSON contract and exit codes. Omitting `--task-group` targets the whole project catalog. |
877
877
  | `okstra usage-report [--days <positive-int>] [--project-root <dir>] [--cwd <dir>] [--text\|--json]` | Read-only backend for the okstra-usage skill. `--text` emits ordered fixed labels for model use. The default and `--json` preserve the full machine JSON contract and exit codes. Defaults to the current project's last 30 days. |
@@ -892,7 +892,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
892
892
  | `okstra stage-map <task-key> [--cwd <dir>\|--project <dir>]` | Dump the task's implementation-planning Stage Map as JSON: `{ ok, taskKey, taskRoot, state, sourcePlanPath, stages:[{stage_number,title,depends_on,step_count,cancelled}], doneStages:[int] }`. Each stage row carries `cancelled: true` when the plan marks that Stage Map row `status: cancelled`; such a stage is never selected, cannot be closed, and is left out of whole-task final verification and release handoff. `sourcePlanPath` is the task's latest implementation-planning report — the authority on which stages exist, so a plan amendment's added stages are selectable as soon as the amended plan lands. `state` is `ready` for a resolved source and `missing` when no Stage Map exists; a corrupt source returns a structured non-zero error instead of silently selecting another report. `doneStages` is read from the implementation-planning stage consumer state (with carry recovery). This is the read-side source `/okstra-schedule-gen [task-group]` uses to derive selectable unfinished stages and their completed dependency closure |
893
893
  | `okstra stage-close <task-key> --stage <N> --from-commit <sha> [--supersede-failed <reason>] [--cwd <dir>\|--project <dir>]` | Record the `done` stage-consumer row an implementation run would have written, for a stage whose work is already committed but which never registered as done — the run ended before writing its carry sidecar, and `backfill_done_from_carry` recovers only from that file. Without it `stage-map` reports `doneStages: []` while the branch carries the commit, the next plan re-describes the stage, and every RED expectation it produces is unreachable. Refuses unless the Stage Map has that stage and does not mark it cancelled, no `done`/`failed` row exists for it, `--from-commit` resolves to a commit in the project repo, and the stage's conformance gate permits progress — the same `decide_conformance_gate` the run validator uses, so a stage closed here is not one the validator would have blocked. `--supersede-failed <reason>` closes a stage whose last row is `failed` under the same checks, for a failure that no longer applies (the checks ran in the wrong environment and the commit needs no product edit); the `done` row records `superseded_status`, the reason, the latest plan path and the conformance verdict. Closing also moves the `stage-<N>-exit` tag and releases the stage reservation, exactly as a normal stage completion does. Emits `{ ok, taskKey, taskRoot, stage, headCommit, conformance, consumersPath, supersededFailed, sourcePlanPath }` |
894
894
  | `okstra stage-reopen <task-key> --stage <N> --reason <why> [--evidence <path>] [--cwd <dir>\|--project <dir>]` | Append a `failed` stage-consumer row after a stage's `done` row, so `/okstra-run` offers that stage again for a fix run and its dependents stop branching from the withdrawn head. Use it when a later verification found a defect in a done stage — a single-stage final-verification that ended `blocked` and points at `implementation` is the case the wizard otherwise refuses. The row records the reason, `reopened_by: stage-reopen`, the head it withdraws and, with `--evidence`, the project-relative report path. Refuses a stage that is not in the latest Stage Map, is cancelled, or whose last row is not `done`. Emits `{ ok, taskKey, stage, headCommit, reason, evidencePath, consumersPath }` |
895
- | `okstra correction prompt --run-manifest <path> --round <N> --role <executor\|verifier> --base <sha> [--stage-base <sha>] [--finding <F-id> ...]` | Print the instruction body for one correction-round prompt of an implementation run, built from the run's convergence record: the findings to fix, the stage HEAD before the round, and the executor's or verifiers' rules. `--stage-base` adds one `FILE-SIZE:<path>` finding per file that `<stage-base>..<base>` leaves over the 400-line limit (advisory 300-line lines are not added); a round needs at least one finding from either source. Pass it to `okstra agent-prompt materialize --dispatch-kind correction-r<N>`. At most 2 rounds; exit 1 on an unknown finding or round. |
895
+ | `okstra correction prompt --run-manifest <path> --round <N> --role <executor\|verifier> --base <sha> [--stage-base <sha>] [--finding <F-id> ...]` | Print the instruction body for one correction-round prompt of an implementation run, built from the convergence record that preceded the round (the run's initial one for round 1, round N-1's `convergence-correction-r<N-1>-…` re-convergence for round N): the findings to fix, the stage HEAD before the round, and the executor's or verifiers' rules. `--stage-base` adds one `FILE-SIZE:<path>` finding per file that `<stage-base>..<base>` leaves over the 400-line limit (advisory 300-line lines are not added); a round needs at least one finding from either source. Pass it to `okstra agent-prompt materialize --dispatch-kind correction-r<N>`. At most 2 rounds; exit 1 on an unknown finding or round. |
896
896
  | `okstra correction file-size --base <stage-base> [--head <rev>] [--repo <dir>]` | Check a stage diff against the file-size limit (400 lines, blocking) and target (300 lines, advisory). A threshold is broken by a file the diff adds over it, takes past it, or grows while already over it. Prints `FILE-SIZE:<path>:401 <before>-><after> lines (<kind>; limit 400)` per limit breach and `FILE-SIZE-ADVISORY:<path>:301 … (<kind>; target 300)` per file that breaks only the target, then `QA-RESULT: PASS\|FAIL`; only a limit breach fails. Lines are physical lines. Only `.py`, `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.kt`, `.kts`, `.rs` and `.java` files are checked; `.okstra`, `node_modules`, `vendor`, `fixtures`, `__fixtures__`, `__snapshots__`, `generated` and `__generated__` directories, `*.min.*` files, and files whose first 5 lines say `@generated` or `do not edit` are exempt. Writes nothing. Exit 1 on FAIL, 2 when git cannot read the range. |
897
897
  | `okstra stage-done <task-key> --stage <N> --carry <carry.json> --report <path> [--cwd <dir>\|--project <dir>]` | Settle an implementation stage whose verdict is not FAIL: write the executor's Stage Carry Evidence JSON to the flat `runs/implementation/carry/stage-<N>.json`, then append the `done` row with that carry path, the report path relative to its run directory, the carry head and the completion time. A reopened stage's previous sidecar moves to `carry/superseded/stage-<N>-from-<task-type>-<seq>.json`. Refuses a carry for another stage, a failed carry, a stage already done at another head, and a carry whose `stageCommitRange` diff breaks the 400-line file-size limit (`okstra correction file-size`; the refusal names the `--stage-base` for a correction round); a repeat with the same carry writes nothing new. Prints JSON with `fileSizeCap` (`pass: <range>` or `not checked: <reason>`); exit 1 on a refusal. |
898
898
  | `okstra stage-fail <task-key> --stage <N> --head-commit <sha> --report <path> --reason <why> [--cwd <dir>\|--project <dir>]` | Append the `failed` stage-consumer row an implementation run writes when its verdict is FAIL or no verifier returned a result. Dependents stay blocked and the stage becomes selectable for a fix run. Refuses a stage missing from or cancelled in the latest Stage Map, or already `done` (use `stage-reopen`). Prints JSON; exit 1 on a refusal. |
@@ -215,6 +215,7 @@ The Module column below is where the command's behaviour lives — a `src/` modu
215
215
  | `container` | `scripts/okstra_ctl/container.py` | Container lifecycle shim; `--text` is the command-specific model projection and machine mode remains JSON |
216
216
  | `code-review` | `scripts/okstra_ctl/code_review_target.py` | `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 |
217
217
  | `manager` | `scripts/okstra_ctl/manager_cli.py` | Cross-project manager; fixed text is the default and `--json` selects machine output |
218
+ | `mcp` | `scripts/okstra_ctl/mcp_cli.py` | `okstra mcp <add\|list\|remove\|check>` — validates and writes project MCP server declarations in `.okstra/project.json` `mcpServers` (name, description, tools, aliases), and reports per provider whether each declared server is `reachable`, `disabled`, `missing`, or `unknown`; `check` and prepare list each provider CLI's servers through `ProviderSpec.mcp_inventory` with no inference call |
218
219
 
219
220
  `src/lib/python-helper.mts` centralizes Node → Python execution so nothing duplicates subprocess wiring.
220
221
 
@@ -321,6 +322,7 @@ Important modules:
321
322
  | `usage_report.py` | Read-only okstra-usage backend — scans the whole current project's recent run timelines, defaults to 30 days, and returns task-type coverage, raw/billable tokens, known USD cost, CPU-sum and wall-clock milliseconds, unavailable reason counts, and unmatched pricing models |
322
323
  | `json_registry.py` | shared flock + atomic JSON persistence for small okstra registries (`registry_lock`/`load_registry_json`/`save_registry_json`) — used by `worktree_registry` |
323
324
  | `stage_integrate.py` | whole-task stage integration (merge) + worktree teardown core (`integrate_stages`) — shared by whole-task final-verification entry, `okstra integrate-stages`, and container up |
325
+ | `file_size_cap.py` | Stage-diff file-size thresholds for the correction round and `stage-done` (`okstra correction file-size` backend) — a 300-line target (advisory) and a 400-line limit (blocking), measured on the stage diff in physical lines for the source/test extensions okstra's coding preflight covers; vendored, fixture, snapshot, generated and `.okstra` paths, and files whose head declares itself generated, are exempt |
324
326
  | `resolve_task_key.py` | shared skill helper resolving a bare task-id → `task-catalog.json` candidate entries (`okstra resolve-task-key`) |
325
327
  | `code_review_paths.py` | filesystem-layout SSOT for code-review result files — `stage_review_dir` / `branch_review_dir` plus `next_stage_review` / `next_branch_review`, which read the existing files to derive the next round's name (`stage-<NN>.md`, then `-r2`, `-r3`, …) or the next same-day sequence (`<YYYY-MM-DD>-<NN>.md`), so skill markdown never re-derives a literal review path |
326
328
  | `code_review_target.py` | `okstra code-review target` backend — argument validation and JSON shaping only. Stage mode delegates whole to `okstra_project.state.code_review_target_snapshot`; branch mode is resolved here, defaulting the diff base to the merge-base with the default branch (`refs/remotes/origin/HEAD`, else `main`/`master`). Read-only: it never creates the review directory |
@@ -336,6 +338,8 @@ Important modules:
336
338
  | `manager_launch.py` | Child launch packet and manager child context renderer; records `prepared` launch metadata/events without changing project-local task state |
337
339
  | `manager_view.py` | `okstra manager view` — renders one manager's projects, tasks, child summaries, directives and events into `view/index.html` from `templates/manager/view.template.html`; reads manager-owned files only |
338
340
  | `manager_split.py` | `okstra manager task split` — validates a tracker split plan, renders one brief per (issue, project) with a `## Project Scope` section, checks each with `validators/validate-brief.py` before writing, and registers the children |
341
+ | `mcp_cli.py` | `okstra mcp add\|list\|remove\|check` argparse surface — writes `.okstra/project.json` `mcpServers` entries and renders per-provider reachability; fixed text by default, machine JSON with `--json` |
342
+ | `mcp_servers.py` | MCP server declaration SSOT — `parse_declaration`/`upsert_declaration` validate and upsert `mcpServers` entries without touching other `project.json` fields, and `probe_inventories` runs each provider adapter's `mcp_inventory` listing once in the project root (20 s limit, no inference call) and matches rows against declared names/aliases to classify each server `reachable`/`disabled`/`missing`/`unknown` per provider; a provider without the field, or whose command fails, is `unknown`, never `missing` |
339
343
  | `agent/invocation.py` | Deep invocation-contract module — composes model assignment, common/functional duty, and task instructions; publishes immutable prompt/metadata pairs; verifies five digests; owns standalone result/completion envelopes |
340
344
  | `agent/evidence_recovery.py` | Issues a same-model, same-role evidence-recovery invocation over a terminal-state attempt — verifies the source invocation metadata and existing result, then republishes a suffixed prompt/metadata/result triple (`-evidence-recovery-<attempt>`) that preserves the original model assignment while confining the child to preserving the earlier result's evidence rather than re-running the completed work (`prepare_evidence_recovery`, driven from `dispatch_core`) |
341
345
  | `agent/prompt_cli/` | CLI boundary for run-backed and standalone materialization/verification plus host-native dispatch and result-link records. `inputs` resolves the path arguments this model-facing surface cannot trust and `emit` writes the result; above them `run_identity` refuses a role the run never issued and `dynamic_verifier` reserves a re-verification slot only after that role qualifies; `materialize` authors the specification (every report-writer prompt gets the okstra-rendered `## Output`; with `--corrections` it runs the ledger check in `corrections` and prepends `## Corrections`; without one it refuses a corrective dispatch over a parsing narrative), `results` links what came back, `batch` runs `materialize --batch` (each entry through the same parser and `materialize`, then the `jobs` generator for `--jobs-out`), and `cli` is the argparse surface with the command's canonical USAGE epilog (`check-corrections` runs the ledger check without materializing; `apply-corrections` writes a mechanical ledger to the narrative and records the `lead-correction-applied` activity row via `corrections.run_corrections_apply`) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.221.0",
3
+ "version": "0.222.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.221.0",
3
- "builtAt": "2026-10-20T00:55:16.972Z",
2
+ "package": "0.222.0",
3
+ "builtAt": "2026-10-21T15:15:40.772Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -140,10 +140,12 @@ After `seed`, the lead MUST NOT hand-edit queue IDs, classifications, `roundHist
140
140
 
141
141
  #### Resume and legacy-state recovery (BLOCKING)
142
142
 
143
- A valid historical final schema v1.0, v1.1, or v1.2 is reused unchanged under `reuse-final`; it remains read-only and is not upgraded in place. A malformed, unreadable, or partial legacy final is archived byte-for-byte under `state/migrations/` and restarted from Round 0 because its queue provenance cannot be reconstructed. A matching valid working state resumes. A malformed new-engine working state fails closed and names the explicit `--restart-from-round0` recovery flag; when supplied, every invalid existing state is archived before replacement. The final path can be replaced only when its recorded migration archive still matches the original bytes.
143
+ A valid historical final schema v1.0, v1.1, or v1.2 is reused unchanged under `reuse-final`; it remains read-only and is not upgraded in place. A malformed, unreadable, or partial legacy final is archived byte-for-byte under `state/migrations/` and restarted from Round 0 because its queue provenance cannot be reconstructed. A matching valid working state resumes. A malformed new-engine working state fails closed and names the explicit `--restart-from-round0` recovery flag; when supplied, every invalid existing state is archived before replacement. The flag recovers only an invalid working state: with a valid or absent one, `seed` refuses it instead of reusing or resuming. The final path can be replaced only when its recorded migration archive still matches the original bytes.
144
144
 
145
145
  **Enforced:** `scripts/okstra_ctl/convergence_migration.py` `decide_seed_action` picks the seed action (`reuse-final`, archive-and-restart, resume, or fail-closed) from the on-disk state and the `--restart-from-round0` flag.
146
146
 
147
+ An implementation correction round re-converges in its own namespace, never by reseeding the initial paths: every state, plan, reverify prompt and invocation id carries `correction-r<N>` (`_implementation-deliverable.md` §"Correction round" step 4). **Enforced:** `seed` derives the namespace from the state path names and reads that round's verifier results for provenance (`convergence_provenance.discovery_attempts`); `plan-round` refuses to overwrite a round plan its namespace already dispatched.
148
+
147
149
  #### Engine-owned Round 2 gate
148
150
 
149
151
  `plan-round` applies gate precedence in one place: auto-disabled, all reverify non-result, effective maximum of one, empty queue, then maximum rounds reached. `finalize` maps that internal reason to the public `round2SkippedReason` and `finalState`. The lead and adapters never reproduce this predicate.
@@ -291,7 +291,7 @@ Required checkpoints:
291
291
  - `PROGRESS: phase-1-intake complete` — after all intake reads return. Call `lead-progress append --phase phase-1-intake` again; the second call in a run renders this line.
292
292
  - `PROGRESS: phase-2-prompts preparing <N> worker prompts` — at the start of Phase 2, before any `Write` to the assigned prompt paths.
293
293
  - `PROGRESS: phase-3-team-create <adapter-specific-status>` — after selected-adapter setup is recorded in team-state. The stable phase id is retained for artifact compatibility. When the first dispatch writes the implicit-team marker, that dispatch command records the line.
294
- - `PROGRESS: phase-4-dispatch worker=<role> model=<model>` — once per worker, immediately before `dispatch_worker`. `<role>` is the **roster** role, exactly as team-state's `workers[].role` records it (`Claude worker`, `Codex worker`) — the checkpoint is matched against that entry, so a phase-specific functional label (`Claude verifier`, `Codex executor`) names no roster worker and fails the check. Only `claude-worker`-style hyphenation of the same roster role is also accepted.
294
+ - `PROGRESS: phase-4-dispatch worker=<role> model=<model>` — once per worker, immediately before `dispatch_worker`. `<role>` is the **roster** role, exactly as team-state's `workers[].role` records it (`Claude worker`, `Codex worker`) — the checkpoint is matched against that entry, so a phase-specific functional label (`Claude verifier`, `Codex executor`) names no roster worker and fails the check. Only `claude-worker`-style hyphenation of the same roster role is also accepted. The critic roster row is not named here or in `phase-5-collect`: it is dispatched only as `critic` and reported by `phase-5.6-critic`.
295
295
  - `PROGRESS: phase-5-poll pending=<n> done=<m>` — emitted when entering the wait and when the pending attempts or terminal outcomes change. A host wait returning without a worker state change does not create a new checkpoint.
296
296
  - `PROGRESS: phase-5-collect worker=<role> status=<terminal-status>` — once per worker, immediately after the result file is verified. `<role>` is the roster role, same rule as `phase-4-dispatch` above. `team await` (cmux-pane run only) and `worker-dispatch` record it for the dispatches they settle.
297
297
  - `PROGRESS: phase-5-stage stage=<N> title=<title> steps=<count>` — `implementation` only, immediately before the Executor's `phase-4-dispatch` line, after parsing the approved plan's Stage Map and this run's `**Stage for this implementation run:**` anchor. `<title>` is the stage's Stage Map title and `<count>` is its `stepwiseExecution` row count, both read from the approved plan — this is the line that tells the user WHICH plan stage this run executes. The numbering keeps the line sorted where the work happens — the stage runs in Phase 5.
@@ -21,7 +21,7 @@ Allowed targets:
21
21
 
22
22
  - if any cited verdict is `blocked`, a `conditional-accept` carrying a condition that blocks release, or any other token (including ambiguous phrasing like "looks good"), the run MUST end immediately with status `blocked` and a routing recommendation back to `error-analysis` or `implementation-planning`. Do NOT prompt the user; Do NOT run any git command.
23
23
 
24
- - **Routing recommendation**: explicit `done` token, since release-handoff is the terminal lifecycle phase. If the run ended in `skip` or `cancel`, or delivered only some of the selected stages, the recommendation MUST state which stages still need a PR and that re-entry into release-handoff is appropriate.
24
+ - **Routing recommendation**: explicit `done` token, since release-handoff is the terminal lifecycle phase. If the run ended in `skip` or `cancel`, or delivered only some of the selected stages, the recommendation MUST state which stages still need a PR and that re-entry into release-handoff is appropriate. Such a run keeps the task open: `okstra_ctl.next_phase.project` projects an H1 `skip` or any `skipped` PR outcome row to a `ready` `release-handoff` pointer instead of `terminal`, whatever token the recommendation starts with.
25
25
 
26
26
  1. **Entry-gate audit** — the report cites, for every stage it delivered, that stage's final-verification report path and the literal `Verdict Token` row with a release-ready value. If any is missing, or a `conditional-accept` stage's PR body omits its conditions, the run is invalid and MUST be re-routed to `final-verification`.
27
27
 
@@ -388,6 +388,7 @@ def _instructions(
388
388
  "In relationships, from and to must each be a projectId listed under projects in the Source Material; put modules, files and planned work in data, not in from/to.",
389
389
  "Cite evidence only from files Git tracks or would add in a candidate root. Git-ignored files (for example .project-docs/) are outside the captured source inventory and are rejected; name what you learned from them in gaps instead.",
390
390
  "Explain the product business from start to finish for a new developer: purpose, rules, input/output, states, failure, retry and recovery. Link each step and relationship to factKeys.",
391
+ "factKeys in steps and relationships may name only facts returned in this response. Stored knowledge claims are not accepted by key; to link one, re-read its source and return it as a fact with evidence.",
391
392
  "Use stable keys in business/subject/attribute form. Facts are source-backed assertions; expected changes remain expected. Cite inspected evidence with one-based line/endLine; each excerpt must copy the whole line..endLine range verbatim, including indentation, with no elision or omitted lines. Tests are static evidence unless a supplied existing verification record supports the execution claim.",
392
393
  "For post mode, use the supplied project-specific baseline. Never invent missing before-state. Describe the actual delta and distinguish confirmed side effects from risks.",
393
394
  "For contribute mode, extract newly observed business facts from the supplied validated task record and inspect source to confirm them. Empty facts are valid. Do not copy the report narrative as established truth.",