okstra 0.178.0 → 0.179.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/commands/execute/plan-verify.mjs +1 -1
- package/dist/commands/execute/worktree-status.mjs +8 -2
- package/dist/commands/execute/worktree-status.mjs.map +1 -1
- package/dist/commands/lifecycle/install.mjs +1 -1
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/dist/commands/report/render-final-report.mjs +3 -3
- package/docs/architecture/storage-model.md +3 -3
- package/docs/architecture.md +10 -9
- package/docs/cli.md +11 -13
- package/docs/for-ai/skills/okstra-inspect.md +3 -3
- package/docs/for-ai/skills/okstra-schedule-gen.md +2 -2
- package/docs/for-ai/skills/okstra-user-response.md +2 -2
- package/docs/project-structure-overview.md +10 -11
- package/docs/task-process/implementation-planning.md +1 -1
- package/docs/task-process/implementation.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +11 -12
- package/runtime/bin/lib/okstra/globals.sh +2 -2
- package/runtime/bin/lib/okstra/interactive.sh +1 -1
- package/runtime/bin/lib/okstra/usage.sh +11 -9
- package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +1 -1
- package/runtime/bin/okstra-central.sh +2 -2
- package/runtime/bin/okstra-render-final-report.py +1 -1
- package/runtime/bin/okstra-token-usage.py +1 -1
- package/runtime/prompts/launch.template.md +1 -1
- package/runtime/prompts/lead/adapters/cmux.md +6 -1
- package/runtime/prompts/lead/context-loader.md +3 -2
- package/runtime/prompts/lead/convergence.md +1 -1
- package/runtime/prompts/lead/okstra-lead-contract.md +4 -4
- package/runtime/prompts/lead/plan-body-verification.md +3 -3
- package/runtime/prompts/lead/report-writer.md +21 -20
- package/runtime/prompts/lead/team-contract.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +5 -4
- package/runtime/prompts/profiles/_implementation-deliverable.md +1 -0
- package/runtime/prompts/profiles/_implementation-executor.md +2 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +9 -5
- package/runtime/prompts/profiles/implementation.md +4 -4
- package/runtime/prompts/profiles/improvement-discovery.md +2 -2
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +5 -4
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +5 -4
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +71 -9
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +5 -4
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +55 -3
- package/runtime/python/okstra_ctl/analysis_inputs.py +5 -3
- package/runtime/python/okstra_ctl/analysis_packet.py +21 -0
- package/runtime/python/okstra_ctl/backfill.py +12 -5
- package/runtime/python/okstra_ctl/consumers.py +70 -3
- package/runtime/python/okstra_ctl/convergence_engine.py +43 -17
- package/runtime/python/okstra_ctl/dispatch_core.py +47 -11
- package/runtime/python/okstra_ctl/dispatch_state.py +20 -19
- package/runtime/python/okstra_ctl/domain/worker_exec.py +13 -34
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +128 -0
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +5 -0
- package/runtime/python/okstra_ctl/final_report_paths.py +77 -1
- package/runtime/python/okstra_ctl/handoff.py +1 -2
- package/runtime/python/okstra_ctl/implementation_outcome.py +1 -1
- package/runtime/python/okstra_ctl/index.py +4 -4
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +26 -12
- package/runtime/python/okstra_ctl/listing.py +4 -2
- package/runtime/python/okstra_ctl/manager_launch.py +1 -1
- package/runtime/python/okstra_ctl/manager_sync.py +1 -1
- package/runtime/python/okstra_ctl/path_hints.py +2 -2
- package/runtime/python/okstra_ctl/paths.py +13 -10
- package/runtime/python/okstra_ctl/plan_run_root.py +9 -5
- package/runtime/python/okstra_ctl/recap.py +3 -2
- package/runtime/python/okstra_ctl/reconcile.py +3 -1
- package/runtime/python/okstra_ctl/render.py +22 -22
- package/runtime/python/okstra_ctl/report_finalize.py +4 -4
- package/runtime/python/okstra_ctl/rollup.py +1 -1
- package/runtime/python/okstra_ctl/run.py +139 -284
- package/runtime/python/okstra_ctl/run_audit.py +5 -5
- package/runtime/python/okstra_ctl/run_index_row.py +2 -2
- package/runtime/python/okstra_ctl/session_transcript.py +89 -0
- package/runtime/python/okstra_ctl/stage_ledger.py +72 -0
- package/runtime/python/okstra_ctl/stage_map.py +28 -29
- package/runtime/python/okstra_ctl/stage_targets.py +61 -0
- package/runtime/python/okstra_ctl/user_response.py +97 -12
- package/runtime/python/okstra_ctl/wizard.py +43 -65
- package/runtime/python/okstra_ctl/worker_prompt_body.py +6 -7
- package/runtime/python/okstra_ctl/worker_runner.py +76 -213
- package/runtime/python/okstra_ctl/workflow.py +1 -1
- package/runtime/python/okstra_ctl/wrapper_status.py +23 -0
- package/runtime/python/okstra_ctl/write_policy.py +45 -6
- package/runtime/python/okstra_project/state.py +2 -2
- package/runtime/python/okstra_token_usage/__init__.py +1 -1
- package/runtime/python/okstra_token_usage/cli.py +3 -3
- package/runtime/python/okstra_token_usage/report.py +7 -24
- package/runtime/schemas/convergence-groups-v1.0.schema.json +1 -1
- package/runtime/schemas/convergence-groups-v2.0.schema.json +1 -1
- package/runtime/schemas/final-report-v2.0.schema.json +11 -1
- package/runtime/skills/okstra-inspect/facets/history.md +3 -3
- package/runtime/skills/okstra-inspect/facets/recap.md +1 -1
- package/runtime/skills/okstra-inspect/facets/report.md +5 -5
- package/runtime/skills/okstra-inspect/facets/status.md +2 -2
- package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +1 -1
- package/runtime/skills/okstra-schedule-gen/SKILL.md +1 -1
- package/runtime/skills/okstra-user-response/SKILL.md +3 -3
- package/runtime/templates/project-docs/task-index.template.md +1 -1
- package/runtime/templates/report-writer-prompt-preamble.md +1 -1
- package/runtime/validators/forbidden_actions.py +76 -5
- package/runtime/validators/lib/fixtures.sh +14 -10
- package/runtime/validators/lib/runners.sh +1 -1
- package/runtime/validators/validate-implementation-plan-stages.py +3 -0
- package/runtime/validators/validate-report-views.py +1 -1
- package/runtime/validators/validate-run.py +95 -37
|
@@ -194,7 +194,7 @@ Runtime/install asset changes follow this checklist:
|
|
|
194
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
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
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
|
|
197
|
+
| `render-final-report`, `inject-report-index` | `src/commands/report/*.mts` | Render the full reading copy Markdown from data.json on demand; v1 index injection remains compatibility-only |
|
|
198
198
|
| `wizard` | `src/commands/execute/wizard.mts` | Drive the `okstra-run` interactive state machine, including the final outcome envelope |
|
|
199
199
|
| `token-usage` | `src/commands/execute/token-usage.mts` | Wrap installed Python token usage CLI |
|
|
200
200
|
| `spawn-followups`, `error-log` | `src/commands/execute/*.mts` | Follow-up task bundle creation and run error-log append helpers |
|
|
@@ -272,8 +272,8 @@ Important modules:
|
|
|
272
272
|
| `qa_commands.py` | QA command deny-list validation for plans |
|
|
273
273
|
| `conformance.py` | validates task-level Tier 3 manifests, parses `QA-RESULT`, detects diff capability surfaces, and reduces results to PASS/ADVISORY/BLOCKING; DB/HTTP/external non-PASS is user-owned advisory while local IO and contract defects remain blocking, enforced by `scripts/okstra_ctl/conformance.py::decide_conformance_gate` and `validators/validate-run.py::_validate_conformance`. Also the single definition of the plan's `Conformance tests:` declaration format (`parse_conformance_tests`, `malformed_conformance_stages`), read both at the approval boundary (`run.py::_validate_approved_plan`) and at the end of an implementation run (`validators/validate-run.py`) so the two cannot disagree |
|
|
274
274
|
| `pr_template.py` | PR body template resolution for release-handoff |
|
|
275
|
-
| `report_views.py`, `render_final_report.py`, `final_report_schema.py` | Final-report contract: schema v2 data independently produces
|
|
276
|
-
| `report_markdown.py` | Schema-ordered Markdown serialisation of a data.json subtree for the
|
|
275
|
+
| `report_views.py`, `render_final_report.py`, `final_report_schema.py` | Final-report contract: schema v2 data independently produces the full reading copy Markdown and human HTML |
|
|
276
|
+
| `report_markdown.py` | Schema-ordered Markdown serialisation of a data.json subtree for the full reading copy — headings, tables for uniform row sets, prose for narrative fields; field order read from the schema, not from the mapping |
|
|
277
277
|
| `final_report_paths.py`, `report_view_artifacts.py` | Path-helper SSOT for the final-report markdown/data.json pair and the generated view artifacts (HTML view, user-responses directory) |
|
|
278
278
|
| `wizard.py` | `okstra-run` prompt state machine; user-facing Korean strings live in `prompts/wizard/prompts.ko.json` |
|
|
279
279
|
| `wizard_stage_intent.py` | stage-related intent projection of the `okstra-run` wizard output — normalizes whole-task (`__whole_task__`) vs single/multi stage selection into render-args (`resolve_wizard_stage_intent`) |
|
|
@@ -381,9 +381,9 @@ Token/cost accounting:
|
|
|
381
381
|
|
|
382
382
|
| Path | Role |
|
|
383
383
|
|---|---|
|
|
384
|
-
| `templates/reports/final-report-v2.template.md` |
|
|
385
|
-
| `templates/reports/final-report-v2.template.md` | Schema v2
|
|
386
|
-
| `templates/reports/md/tasks/*.template.md`, `md/macros/sections.md` | Eleven dedicated task bodies for the
|
|
384
|
+
| `templates/reports/final-report-v2.template.md` | Full reading copy Markdown spine |
|
|
385
|
+
| `templates/reports/final-report-v2.template.md` | Schema v2 full reading copy Markdown spine |
|
|
386
|
+
| `templates/reports/md/tasks/*.template.md`, `md/macros/sections.md` | Eleven dedicated task bodies for the full reading copy Markdown, sibling of `html/tasks/`; shared section macro |
|
|
387
387
|
| `templates/reports/html/base.template.html`, `html/tasks/*.template.html` | Shared HTML shell plus eleven dedicated task templates for human reports; task bodies are not shared |
|
|
388
388
|
| `templates/reports/report.css`, `report.js` | Inline assets for self-contained HTML report views |
|
|
389
389
|
| `templates/reports/*.template.md` | Inputs, schedule, user-response, settings templates |
|
|
@@ -397,7 +397,7 @@ Token/cost accounting:
|
|
|
397
397
|
|
|
398
398
|
### 4.8 `schemas/`
|
|
399
399
|
|
|
400
|
-
`schemas/final-report-v2.0.schema.json` is the current final-report data.json contract. The report-writer worker writes `final-report-<task-type>-<seq>.data.json`; independent renderers produce
|
|
400
|
+
`schemas/final-report-v2.0.schema.json` is the current final-report data.json contract. The report-writer worker writes `final-report-<task-type>-<seq>.data.json`; independent renderers produce the full reading copy Markdown and task-specific human HTML.
|
|
401
401
|
|
|
402
402
|
The deterministic convergence inputs are `schemas/convergence-groups-v1.0.schema.json`, `schemas/convergence-round-results-v1.0.schema.json`, and `schemas/convergence-critic-results-v1.0.schema.json`. `tools/build.mjs` syncs the entire source `schemas/` directory to `runtime/schemas/`; these JSON Schema files are runtime contracts, not Markdown publication-inventory entries.
|
|
403
403
|
|
|
@@ -410,7 +410,7 @@ Optional (v1.0 backward-compatible) top-level keys:
|
|
|
410
410
|
|
|
411
411
|
| File | Role |
|
|
412
412
|
|---|---|
|
|
413
|
-
| `validate-run.py` | Run/final-report validation: schema v2
|
|
413
|
+
| `validate-run.py` | Run/final-report validation: schema v2 record + structured data rules |
|
|
414
414
|
| `validate-brief.py`, `validate-brief.sh` | Brief frontmatter/body contract validation |
|
|
415
415
|
| `validate-report-views.py` | HTML view validation (form-control placement / no external URLs / stale source digest / Response ID parity) |
|
|
416
416
|
| `validate_analysis_report.py` | Cross-field validation for the three read-only analysis reports: frozen target/evidence snapshots, current-code evidence, review-source identity, and exact affected-ID resolution coverage on revision reruns |
|
|
@@ -537,9 +537,8 @@ Current report pipeline:
|
|
|
537
537
|
2. The lead writes semantic groups; the convergence engine persists working state, per-round plans/results, an optional critic transition, and then a validated `state/convergence-<task-type>-<seq>.json` terminal state: schema v1.3 when newly finalized, or an unchanged historical final schema v1.0, v1.1, or v1.2 returned by `reuse-final`.
|
|
538
538
|
3. Report-writer worker writes `reports/final-report-<task-type>-<seq>.data.json` against the current schema v2 contract, including `humanSummary` and one task-type deliverable.
|
|
539
539
|
4. For implementation-planning, `okstra plan-items extract` creates the complete `P-*` queue, `validate` proves it still matches data.json, and the analyser instances run the separate plan-body verification round.
|
|
540
|
-
5.
|
|
541
|
-
6.
|
|
542
|
-
7. `scripts/okstra-render-report-views.py` independently selects one of eleven dedicated task templates and emits human-facing HTML directly from the same data.json; run validation checks both derived artifacts. A quick Markdown input retains its legacy conditional path.
|
|
540
|
+
5. Token usage substitution fills usage/cost cells in the report record. The full reading copy is rendered on demand with `okstra render-final-report` from `templates/reports/final-report-v2.template.md`.
|
|
541
|
+
6. `scripts/okstra-render-report-views.py` independently selects one of eleven dedicated task templates and emits human-facing HTML directly from the same data.json; run validation checks the record and the human HTML. A quick Markdown input retains its legacy conditional path.
|
|
543
542
|
|
|
544
543
|
For the three analysis sidetracks, the HTML view also exports an immutable-source `## ANALYSIS REVIEW` sidecar. A revision rerun carries that sidecar, reanalyzes the whole confirmed scope, and records one `analysisReviewResolution` row for every affected ID before `validate_analysis_report.py` accepts the result.
|
|
545
544
|
|
|
@@ -152,7 +152,7 @@ The detailed selected-direction plan retains these deliverable surfaces:
|
|
|
152
152
|
- `Requirement Coverage`
|
|
153
153
|
- `Implementation Design Preparation`
|
|
154
154
|
|
|
155
|
-
Approval is recorded
|
|
155
|
+
Approval is recorded as `frontmatter.approved: true` on the report record (`--approve` or the in-session wizard). A selected-direction plan has no `implementationOption` field and rejects `--implementation-option` before any approval-file mutation. If a `Blocks=approval` clarification row is unresolved, implementation prepare rejects the plan even when the record is approved. Existing candidate plans keep their legacy option field and execution behavior.
|
|
156
156
|
|
|
157
157
|
`plan-ready` requires 100% requirement coverage, 100% scope precision, and no unmapped stage or file change. If the selected mechanism or boundary cannot meet those conditions, planning emits `direction-invalidated` without an executable Stage Map and routes back to `implementation-option-selection`. It does not choose another direction automatically.
|
|
158
158
|
|
|
@@ -98,7 +98,7 @@ sequenceDiagram
|
|
|
98
98
|
P-->>W: prepared implementation prompt or PrepareError
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
`--approve` exists in the Python runtime, but the okstra-run wizard does not emit it as args. On the shell path, `--approve`
|
|
101
|
+
`--approve` exists in the Python runtime, but the okstra-run wizard does not emit it as args. On the shell path, `--approve` sets the report record `frontmatter.approved` to `true` and then follows the same validation path.
|
|
102
102
|
|
|
103
103
|
### 3.1 design-preparation preflight
|
|
104
104
|
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -37,21 +37,20 @@ The final-report `executionRoles[]` set must equal the execution-manifest
|
|
|
37
37
|
## Host file procedure
|
|
38
38
|
|
|
39
39
|
Use the assigned `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json`
|
|
40
|
-
path
|
|
40
|
+
path. Do not invoke the full reading copy renderer.
|
|
41
41
|
|
|
42
|
-
The data.json is the **single source of truth** for two audiences.
|
|
42
|
+
The data.json is the **single source of truth** for two audiences. Phase 7 produces the human HTML (`final-report-<task-type>-<seq>.html`) through the task-specific HTML renderer. The full reading copy is rendered on demand with `okstra render-final-report <data.json>`. HTML is rendered directly from the data.json; it is not a presentation of the Markdown. You do NOT hand-write either derived artifact.
|
|
43
43
|
|
|
44
|
-
If you find yourself thinking "I'll just write the markdown directly" — stop. Write the data.json with your `Write` tool
|
|
44
|
+
If you find yourself thinking "I'll just write the markdown directly" — stop. Write the data.json with your `Write` tool. The full reading copy is rendered on demand.
|
|
45
45
|
|
|
46
46
|
## Worker Result File (MANDATORY)
|
|
47
47
|
|
|
48
48
|
Write the required worker-result record at the lead-registered `**Worker Result Path:**`. Both dispatch adapters include it in `WorkerJob.completion_paths` and refuse `completed` while it is absent. Schema: short YAML frontmatter (`workerId: "report-writer"`, plus the canonical fields copied verbatim from `analysis-material.md` per `team-contract`) followed by:
|
|
49
49
|
|
|
50
50
|
1. The canonical data.json path you wrote (project-relative).
|
|
51
|
-
2. The
|
|
52
|
-
3. The convergence-state input path from the prompt (project-relative).
|
|
51
|
+
2. The convergence-state input path from the prompt (project-relative).
|
|
53
52
|
|
|
54
|
-
Keep the data.json contents and analysis-worker result list out of this file: the data.json is the canonical artifact and the analysis results remain prompt inputs. This file is the dispatch-required
|
|
53
|
+
Keep the data.json contents and analysis-worker result list out of this file: the data.json is the canonical artifact and the analysis results remain prompt inputs. This file is the dispatch-required two-path pointer record.
|
|
55
54
|
|
|
56
55
|
## Heartbeat (BLOCKING)
|
|
57
56
|
|
|
@@ -88,13 +87,13 @@ Before writing the data.json, you MUST:
|
|
|
88
87
|
For the report writer specifically, the `## Inputs` list always includes:
|
|
89
88
|
|
|
90
89
|
- `<instruction-set>/final-report-schema.json` — the **per-task-type excerpt** of schema v2 (other task-types' deliverable blocks and unreachable `$defs` are stripped). This is the shape you must author. Read this, NOT the full `schemas/final-report-v2.0.schema.json` source outside the task bundle. Validation still runs against the installed full schema, so the excerpt never relaxes the contract. The excerpt is frozen at prep time and carries the okstra version it was cut from (`x-okstraCutFromVersion`); if the renderer rejects a field the excerpt told you to write, the installed schema wins.
|
|
91
|
-
- `<instruction-set>/final-report-template.md` — the
|
|
90
|
+
- `<instruction-set>/final-report-template.md` — the full reading copy template. Read it to understand which IDs, routing fields, evidence, task deliverable, and audit blocks appear in the AI artifact; do NOT edit it, and do NOT use it as the human presentation contract.
|
|
92
91
|
- `templates/reports/i18n/en.json` and `templates/reports/i18n/ko.json`.
|
|
93
92
|
- Every analysis worker's result file under `worker-results/`.
|
|
94
93
|
- `state/convergence-<task-type>-<seq>.json` (if present). When present, reproduce its `roundHistory[]`, `round2SkippedReason`, and `finalClassificationCounts` verbatim into the final report's Section 6 Round History sub-table — do not recompute from worker results.
|
|
95
94
|
- `<instruction-set>/task-brief.md` — the brief this run was prepared from. The lead already lists it under `## Inputs`; what it is FOR is the `## Expected Behavior` / `## Preserved Behavior` / `## Expected Outcome` items, whose `EB-NNN` / `PB-NNN` / `EO-NNN` ids are exactly what `endStateCoverage` maps. Read the instruction-set copy, NOT the project's `.okstra/briefs/...` path from the task manifest — that path is not resolvable here, same as the full schema source. A report that omits an id the brief pinned is rejected by the run validator.
|
|
96
95
|
|
|
97
|
-
For a carry-in `clarification-response.md`, reconcile every prior `clarificationItems[]` row, including an open row with blank user input. Record the current status and user decision in data.json; the
|
|
96
|
+
For a carry-in `clarification-response.md`, reconcile every prior `clarificationItems[]` row, including an open row with blank user input. Record the current status and user decision in data.json; the full reading copy renderer places it under `## Clarification and User Decisions`, while HTML renders any still-open response controls. When no carry-in path was provided, omit `clarificationCarryIn` entirely.
|
|
98
97
|
|
|
99
98
|
Write a Reading Confirmation block to `**Audit sidecar path:**`, per the selected report-writer preamble's `Required reading` section (the main final-report and worker-results files carry no Section 0 heading). If you cannot truthfully confirm a file end-to-end, record a `tool-failure` in the errors sidecar instead of fabricating the report.
|
|
100
99
|
|
|
@@ -102,7 +101,7 @@ Write a Reading Confirmation block to `**Audit sidecar path:**`, per the selecte
|
|
|
102
101
|
|
|
103
102
|
You author the final-report data.json (the JSON SSOT). You author it against the `<instruction-set>/final-report-schema.json` excerpt — its `$defs` enumerate every row shape, enum value, and cross-field constraint that applies to this run's task-type. The validator and renderers both consume the **full** `schemas/final-report-v2.0.schema.json` (the excerpt is a faithful task-type-scoped subset of it), so a data.json that satisfies the excerpt can independently produce both audience artifacts.
|
|
104
103
|
|
|
105
|
-
The
|
|
104
|
+
The full reading copy is an agent-facing ledger: verdict, routing, clarification decisions, evidence, one structured task deliverable, and execution audits. The human HTML is the reader-facing explanation: `humanSummary` plus the selected task block's `userNarrative` and structured facts. Populate both human fields in data.json even though the Markdown intentionally omits their full prose. Worker discussion, convergence mechanics, and token usage belong to audit data and must not be copied into the HTML human main body.
|
|
106
105
|
|
|
107
106
|
### Implementation-planning frontmatter contract
|
|
108
107
|
|
|
@@ -159,13 +158,13 @@ Rules (the schema enforces most of these — they are listed here so you know *w
|
|
|
159
158
|
- For legacy candidate-comparison `implementation-planning`, each `requirementCoverage` row's `source` is a graded cell, not prose — free text like `"carry-in from requirements-discovery C-001"` is rejected. Write exactly one of: `brief:EB-001` / `brief:PB-001` / `brief:EO-001`, an end-state id the brief declares — when the brief pins ids, citing a heading instead is rejected, because every brief carries the same generic headings and a heading cannot say WHICH reporter line the requirement came from (only a brief authored before the end-state sections existed still takes the older `brief:<heading>` form, and there the heading must literally exist in it); `derived:R-NNN — <one-line reason>`, whose chain must terminate at a `brief:` or `contract:` row of the same table without cycling; or `contract:<rule>`, for artifacts okstra's own phase contract mandates, whose allowlist is exactly the two tokens `decision-record-step` (the §5.4 Decision Drafts materialization step) and `glossary-step` (the glossary proposal step) — any other rule name is rejected, so never invent one. (Maintainer SSOT for that allowlist: `scripts/okstra_ctl/scope_provenance.py` in the okstra repo.) A requirement you cannot source this way does not belong in the table: put it in `clarificationItems[]` with `Blocks=approval`. **Enforced:** `validators/validate-run.py` `_validate_requirement_provenance`. In the same table, anchor every stage number in `coveredBy` to a `Stage` / `Stages` word (`Stage 2`, `Stages 1-3`) — `_validate_stage_has_requirement` reads that cell as prose and fails the plan when a Stage Map stage is cited by no row.
|
|
160
159
|
- For legacy candidate-comparison `implementation-planning`, also populate `implementationPlanning.decisionDrafts` (one row per decision meeting all three decision-record criteria; `[]` otherwise) and `implementationPlanning.skippedAdrCandidates` (evaluated-but-dropped adr-candidates; `[]` otherwise). The schema excerpt enumerates the row shape; the renderer emits §5.4 `### Decision Drafts`. When `decisionDrafts` is non-empty, the plan's stages MUST carry a stepwise step that creates `.okstra/decisions/<NNNN>-<slug>.md` (validate-run gates this).
|
|
161
160
|
- For `implementation-planning`, populate `implementationPlanning.variationPointAnalysis` — a `hasMultipleImplementations` judgement synthesized from the analysis workers' output, not a field filled in last. When it is `true`, write one `points[]` row per varying behavior carrying `behavior`, the two or more `implementations` that serve it, `evidence` (a `path:line`, or the sibling task / stage that already implements that behavior), and an `extractionDecision` of `extract` / `interfaceKind` / `coveredBy` (the Stage Map stage that builds the interface) / `rationale`; when it is `false`, write a non-empty `noVariationRationale` and leave `points` empty (the two branches are mutually exclusive). Do NOT pass a boilerplate rationale. Populate test seams under `implementationPlanning.directionRealization.testSeams` for selected-direction plans and under `implementationPlanning.recommendedOption.testSeams` for legacy candidate-comparison plans. An empty list is a conscious "no seam needed" claim, never a default for a field nobody filled. Every point becomes a `P-Var-*` plan item judged in §5.5.9.
|
|
162
|
-
- When the `Task Type` is `improvement-discovery`, populate `improvementDiscovery.candidates[]`, `improvementDiscovery.lensCoverage[]`, `improvementDiscovery.selectionLimit`, and `improvementDiscovery.userNarrative`. Each candidate carries the 11 logical fields enforced by `validators/validate_improvement_report.py`; each lens-coverage row records candidate IDs or an evidence-backed no-candidate rationale. Source IDs, lens names, and worker prefixes from `scripts/okstra_ctl/improvement_lenses.py`. The standard renderer derives the
|
|
161
|
+
- When the `Task Type` is `improvement-discovery`, populate `improvementDiscovery.candidates[]`, `improvementDiscovery.lensCoverage[]`, `improvementDiscovery.selectionLimit`, and `improvementDiscovery.userNarrative`. Each candidate carries the 11 logical fields enforced by `validators/validate_improvement_report.py`; each lens-coverage row records candidate IDs or an evidence-backed no-candidate rationale. Source IDs, lens names, and worker prefixes from `scripts/okstra_ctl/improvement_lenses.py`. The standard renderer derives the full reading copy; never author a free-form improvement report.
|
|
163
162
|
|
|
164
|
-
Write the
|
|
163
|
+
Write the report record, the Worker Result Path pointer, and the separate audit sidecar with your `Write` tool — that is the canonical authoring path. A Bash heredoc is acceptable ONLY when a specific `Write` call is genuinely rejected by the host environment, and it MUST produce byte-identical content — do not reach for it pre-emptively. Do not invoke `okstra render-final-report`; the full reading copy is on-demand. Confirm data.json, the pointer, and the audit sidecar exist before responding with a short status line prefixed by your model identity, per the preamble §"Return message to the lead". **Enforced:** dispatch `completionPaths` requires the record and the pointer; `validators/validate_session_conformance.py` validates the audit sidecar.
|
|
165
164
|
|
|
166
165
|
```
|
|
167
166
|
**Model:** Report writer worker, <modelExecutionValue>
|
|
168
|
-
data.json written to <abs path
|
|
167
|
+
data.json written to <abs path>. Sections populated: <count>.
|
|
169
168
|
```
|
|
170
169
|
|
|
171
170
|
## Error reporting
|
|
@@ -97,7 +97,7 @@ REPORT_WRITER_WORKER_PROMPT_FILE=""
|
|
|
97
97
|
REPORT_WRITER_WORKER_PROMPT_RELATIVE_PATH=""
|
|
98
98
|
FINAL_REPORT_PATH=""
|
|
99
99
|
FINAL_STATUS_PATH=""
|
|
100
|
-
|
|
100
|
+
FINAL_REPORT_RECORD_RELATIVE_PATH=""
|
|
101
101
|
FINAL_STATUS_RELATIVE_PATH=""
|
|
102
102
|
FINAL_REPORT_TEMPLATE_PATH=""
|
|
103
103
|
FINAL_REPORT_TEMPLATE_RELATIVE_PATH=""
|
|
@@ -147,7 +147,7 @@ RUN_SESSIONS_RELATIVE_PATH=""
|
|
|
147
147
|
LATEST_RUN_PATH=""
|
|
148
148
|
LATEST_RUN_RELATIVE_PATH=""
|
|
149
149
|
LATEST_REPORT_PATH=""
|
|
150
|
-
|
|
150
|
+
LATEST_REPORT_RECORD_RELATIVE_PATH=""
|
|
151
151
|
CURRENT_TASK_STATUS=""
|
|
152
152
|
CURRENT_RUN_STATUS=""
|
|
153
153
|
VALIDATION_STATUS="not-run"
|
|
@@ -319,7 +319,7 @@ PY
|
|
|
319
319
|
)" || return 1
|
|
320
320
|
|
|
321
321
|
if [[ -z "$found" ]]; then
|
|
322
|
-
printf 'resume-clarification: no final-report
|
|
322
|
+
printf 'resume-clarification: no final-report found for task %s:%s under %s/runs\n' \
|
|
323
323
|
"$task_group" "$task_id" "$task_root" >&2
|
|
324
324
|
return 1
|
|
325
325
|
fi
|
|
@@ -47,19 +47,21 @@ optional arguments:
|
|
|
47
47
|
Required for a new implementation-planning run. Existing planning
|
|
48
48
|
reruns continue to use --clarification-response with their prior report.
|
|
49
49
|
--approved-plan Path to the approved final-report.md from a prior implementation-planning run.
|
|
50
|
-
Required when --task-type=implementation;
|
|
50
|
+
Required when --task-type=implementation; that report's record
|
|
51
|
+
(final-report-*.data.json) MUST carry \`frontmatter.approved: true\`.
|
|
51
52
|
--approve Treat the user's CLI invocation itself as the plan-approval signal. Only meaningful
|
|
52
|
-
together with --approved-plan and --task-type=implementation.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
together with --approved-plan and --task-type=implementation. Sets
|
|
54
|
+
\`frontmatter.approved\` to true on the report record and re-renders the full reading
|
|
55
|
+
copy from it; editing that reading copy does not approve the plan. Use this for
|
|
56
|
+
scripted/CI flows or when you want a single command to both approve and launch the
|
|
57
|
+
next phase.
|
|
57
58
|
--implementation-option <name>
|
|
58
59
|
Name of the Option Candidate the user chose from the implementation-planning
|
|
59
60
|
final-report. Only meaningful together with --approved-plan and
|
|
60
|
-
--task-type=implementation. The runtime
|
|
61
|
-
\`
|
|
62
|
-
falls back to the plan's
|
|
61
|
+
--task-type=implementation. The runtime records <name> as
|
|
62
|
+
\`frontmatter.implementationOption\` on the report record and re-renders the full
|
|
63
|
+
reading copy from it. When omitted, the implementation run falls back to the plan's
|
|
64
|
+
\`Recommended Option\`.
|
|
63
65
|
--qa-waiver <stageKey>:<reason>
|
|
64
66
|
User-recorded waiver for a blocking local io conformance gate.
|
|
65
67
|
Only meaningful with --task-type=implementation; prepare records
|
|
@@ -254,7 +254,7 @@ for original in targets:
|
|
|
254
254
|
task_group=row["taskGroup"], task_id=row["taskId"],
|
|
255
255
|
task_type=row["taskType"], run_seq=next_seq,
|
|
256
256
|
when=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
257
|
-
run_dir_rel=base_run,
|
|
257
|
+
run_dir_rel=base_run, final_report_record_rel=final_rel,
|
|
258
258
|
final_status_rel=final_status_rel,
|
|
259
259
|
)
|
|
260
260
|
# 원본 invocation 시점에 캡처된 envOverrides 를 base 에 layer 한다.
|
|
@@ -108,7 +108,7 @@ print(json.dumps({k: v for k, v in zip(it, it)}, ensure_ascii=False))
|
|
|
108
108
|
RECOMMENDED_ANALYSERS "${RECOMMENDED_ANALYSERS-}" \
|
|
109
109
|
LEAD_MODEL "${LEAD_MODEL-}" \
|
|
110
110
|
RUN_DIR_RELATIVE_PATH "${RUN_DIR_RELATIVE_PATH-}" \
|
|
111
|
-
|
|
111
|
+
FINAL_REPORT_RECORD_RELATIVE_PATH "${FINAL_REPORT_RECORD_RELATIVE_PATH-}" \
|
|
112
112
|
FINAL_STATUS_RELATIVE_PATH "${FINAL_STATUS_RELATIVE_PATH-}" \
|
|
113
113
|
OKSTRA_INVOCATION_ARGV_JSON "$_argv_json" \
|
|
114
114
|
OKSTRA_INVOCATION_CWD "${OKSTRA_INVOCATION_CWD-}" \
|
|
@@ -140,7 +140,7 @@ with lockfile.open("r+") as lock:
|
|
|
140
140
|
workers=[w for w in payload.get("RECOMMENDED_ANALYSERS", "").split(",") if w],
|
|
141
141
|
lead_model=payload.get("LEAD_MODEL", ""),
|
|
142
142
|
run_dir_rel=payload.get("RUN_DIR_RELATIVE_PATH", ""),
|
|
143
|
-
|
|
143
|
+
final_report_record_rel=payload.get("FINAL_REPORT_RECORD_RELATIVE_PATH", ""),
|
|
144
144
|
final_status_rel=payload.get("FINAL_STATUS_RELATIVE_PATH", ""),
|
|
145
145
|
argv=json.loads(payload.get("OKSTRA_INVOCATION_ARGV_JSON", "[]")),
|
|
146
146
|
cwd=payload.get("OKSTRA_INVOCATION_CWD", ""),
|
|
@@ -49,7 +49,7 @@ For a new `implementation-planning` run, the plan-body sequence is initial verif
|
|
|
49
49
|
## Run Paths
|
|
50
50
|
|
|
51
51
|
- Team state: `{{TEAM_STATE_RELATIVE_PATH}}`
|
|
52
|
-
- Final report: `{{
|
|
52
|
+
- Final report: `{{FINAL_REPORT_RECORD_RELATIVE_PATH}}`
|
|
53
53
|
- Final status: `{{FINAL_STATUS_RELATIVE_PATH}}`
|
|
54
54
|
- Validator: `{{RUN_VALIDATOR_RELATIVE_PATH}}`
|
|
55
55
|
|
|
@@ -59,7 +59,12 @@ Use the screen to tell "still working" from "stuck", and to see at a glance whic
|
|
|
59
59
|
- Worker completion is valid only from `workerDispatches[]`, terminal status sidecars, and required Result Paths. Pane creation alone is not completion.
|
|
60
60
|
- Reverify uses a fresh jobs file at `runs/<task-type>/state/reverify-jobs-r<N>-<task-type>-<seq>.json`, sets `dispatchKind: "reverify-r<N>"`, and dispatches with `okstra team dispatch --project-root <root> --run-manifest <path> --dispatch-kind reverify-r<N> --jobs-file <jobs-file>`.
|
|
61
61
|
- Report-writer uses a fresh one-job jobs file with `dispatchKind: "report-writer"` and the same schema, then dispatches through `okstra team dispatch --project-root <root> --run-manifest <path> --jobs-file <jobs-file>`.
|
|
62
|
-
-
|
|
62
|
+
- A jobs file is `{"dispatchKind": "<kind>", "workers": [ … ]}` — the array key is `workers`, not `jobs`. Each entry follows the run manifest's identity version, and `scripts/okstra_ctl/dispatch_state.py` `worker_execution_identity` is the contract:
|
|
63
|
+
- **v2 entry** (`schemaVersion: "2.0"`, `executionIdentityVersion: 2`): carries `participantRef`, `roleExecutionRef`, `assignmentRef`, `executionLabel`, `dutyId`, `invocationRef`, a positive integer `attempt`, `provider`, `promptPath`, `workerResultPath`, and a `digests` object holding `catalogDigest`, `assignmentDigest`, `dutyDigest`, `instructionDigest`, and `promptDigest`. **Do not include `workerId`** — a v2 entry that carries it is refused as mixing v1 and v2 identity; the worker key is projected from `assignmentRef`.
|
|
64
|
+
- **v1 entry**: carries `workerId`, `provider`, `promptPath`, and `workerResultPath`, and must carry none of the v2 identity fields.
|
|
65
|
+
- `resultPath` and `completionPaths` are advisory in both: dispatch derives them from the same rules the roster path uses, so a jobs file cannot disagree with a roster dispatch about which artifact is the result.
|
|
66
|
+
- `workerResultPath` must carry the canonical `-worker-` token (`<role>-worker-<task-type>-<seq>.md`); the audit sidecar name is derived from it by inserting `-audit-` after that token, so a reverify name like `<role>-reverify-r1-<task-type>-<seq>.md` is refused with `worker result path has no canonical -worker- token`. The round belongs in `dispatchKind` and `invocationRef`, not in the artifact name.
|
|
67
|
+
- `role` names the role execution's own role — `verifier` for reverify, `report-writer` for the report writer. It is not a per-round label: `dispatch_state.py` requires the entry's `role` to equal both the role execution's `role` and the duty's role, so a value like `worker-reverify-r<N>` is refused as `jobs file v2 identity does not match role execution authority`. The round lives in `dispatchKind` and in `invocationRef`. The report-writer completion paths include both the report record and the worker-results audit file.
|
|
63
68
|
- After either dispatch, run `okstra team await --project-root <root> --run-manifest <path>` before evaluating terminal status or completion paths.
|
|
64
69
|
|
|
65
70
|
## Completion, cleanup, and resume
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
| `latestRunPath` | latest run path |
|
|
55
55
|
| `latestRunStatus` | latest run status |
|
|
56
56
|
| `latestRunPromptsPath` | latest run prompt directory path |
|
|
57
|
-
| `
|
|
57
|
+
| `latestReportRecordPath` | latest report path |
|
|
58
58
|
| `latestResumeCommandPath` | resume helper path |
|
|
59
59
|
| `historyTimelinePath` | timeline path |
|
|
60
60
|
| `resultContract` | team contract and expected artifact metadata |
|
|
@@ -82,7 +82,8 @@ After identifying the task root in `task-manifest.json`, derive all paths accord
|
|
|
82
82
|
│ ├── manifests/ (run-manifest-<task-type>-<seq>.json)
|
|
83
83
|
│ ├── state/ (team-state-<task-type>-<seq>.json, convergence-<task-type>-<seq>.json)
|
|
84
84
|
│ ├── prompts/ (run prompt path and worker prompt history paths recorded in the manifest)
|
|
85
|
-
│ ├── reports/ (final-report-<task-type>-<seq>.
|
|
85
|
+
│ ├── reports/ (final-report-<task-type>-<seq>.data.json + .html;
|
|
86
|
+
│ the .md reading copy is rendered on demand)
|
|
86
87
|
│ ├── status/ (final-<task-type>-<seq>.status)
|
|
87
88
|
│ ├── sessions/ (runtime-specific resume artifacts when the selected adapter supports them)
|
|
88
89
|
│ ├── logs/ (errors-<task-type>-<seq>.jsonl, optional)
|
|
@@ -84,7 +84,7 @@ Read the worker result files generated in Phase 4/5 and extract individual findi
|
|
|
84
84
|
- Same semantics but disjoint ticket sets → separate groups (do NOT over-merge across tickets).
|
|
85
85
|
- Only one worker confirms a finding → one single-source group.
|
|
86
86
|
4. When grouping is ambiguous, prefer splitting over merging (avoid over-merging). Semantic matching, ticket-set equality, and evidence interpretation remain lead judgments; the engine does not perform fuzzy matching or decide whether evidence is credible.
|
|
87
|
-
5. Write `runs/<task-type>/state/convergence-groups-<task-type>-<seq>.json`. Each group carries its `ticketIds`, `originWorker`, `originEvidence`, `discoveredBy`, and every `<worker>:<item-id>` source in `sourceItems`. For analysis sidetracks where ticket tagging is not required, `ticketIds: []` is the canonical value; never synthesize `"unknown"` or another placeholder. `scripts/okstra_ctl/convergence_engine.py` and the version-selected convergence-groups schema enforce the required array field and reject non-string or blank entries while allowing the empty array. When a live command or external read produced reproducible evidence, also include `evidenceArtifacts[]` with its `.okstra/` path, SHA-256 digest, command, and environment. The field is optional because historical or inaccessible evidence may not have a captured artifact. The lead and verifier MUST NOT infer live or external evidence from wording or keyword matching; they use the finding's explicit claim, provenance, and supplied artifacts. Include the resolved worker roster in order with functional `audience` values; do not derive scope from provider or model identity. In a v2 run, set top-level `schemaVersion: "2.0"`, `executionIdentityVersion: 2`, and `runManifestPath` to the current run manifest's exact canonical project-relative path. Write the groups artifact under that same resolved run directory's `state/` directory. Every v2 worker row also carries the paired `participantRef` and `sourceRoleExecutionRef` from that run manifest's canonical role state. Set `sourceRoleExecutionRef` to the selected source `RoleExecution` row's `roleExecutionRef`, not that row's `sourceRoleExecutionRef` field; a static source row has null in the latter field. Copy `participantRef` from that same selected row. Never derive those references from the worker name, provider, model, or execution label. A legacy v1 document keeps `schemaVersion: "1.0"` and omits `executionIdentityVersion`, `runManifestPath`, and both worker reference fields. The `audience` enum is a convergence role, not a phase label: every finding-producing worker uses `analysis` and selects an `analyser`, `designer`, `planner`, or `verifier` source role. Only the report author uses `report-writer`, paired with a `report-writer` source role. There is no `implementation-verifier` audience here; map an implementation verifier to `analysis`.
|
|
87
|
+
5. Write `runs/<task-type>/state/convergence-groups-<task-type>-<seq>.json`. Each group carries its `ticketIds`, `originWorker`, `originEvidence`, `discoveredBy`, and every `<worker>:<item-id>` source in `sourceItems`. For analysis sidetracks where ticket tagging is not required, `ticketIds: []` is the canonical value; never synthesize `"unknown"` or another placeholder. `scripts/okstra_ctl/convergence_engine.py` and the version-selected convergence-groups schema enforce the required array field and reject non-string or blank entries while allowing the empty array. When a live command or external read produced reproducible evidence, also include `evidenceArtifacts[]` with its `.okstra/` path, SHA-256 digest, command, and environment. The field is optional because historical or inaccessible evidence may not have a captured artifact. The lead and verifier MUST NOT infer live or external evidence from wording or keyword matching; they use the finding's explicit claim, provenance, and supplied artifacts. Include the resolved worker roster in order with functional `audience` values; do not derive scope from provider or model identity. In a v2 run, set top-level `schemaVersion: "2.0"`, `executionIdentityVersion: 2`, and `runManifestPath` to the current run manifest's exact canonical project-relative path. Write the groups artifact under that same resolved run directory's `state/` directory. Every v2 worker row also carries the paired `participantRef` and `sourceRoleExecutionRef` from that run manifest's canonical role state. Set `sourceRoleExecutionRef` to the selected source `RoleExecution` row's `roleExecutionRef`, not that row's `sourceRoleExecutionRef` field; a static source row has null in the latter field. Copy `participantRef` from that same selected row. Never derive those references from the worker name, provider, model, or execution label. A legacy v1 document keeps `schemaVersion: "1.0"` and omits `executionIdentityVersion`, `runManifestPath`, and both worker reference fields. The `audience` enum is a convergence role, not a phase label: every finding-producing worker uses `analysis` and selects an `analyser`, `designer`, `planner`, or `verifier` source role. Only the report author uses `report-writer`, paired with a `report-writer` source role. There is no `implementation-verifier` audience here; map an implementation verifier to `analysis`. **Your own review findings use `audience: "lead"`.** The phases that ask you to review the deliverable yourself produce findings that belong in this state — it is what the report author reads — and declaring yourself an analysis worker to get them in is forbidden. A `lead` row is a source, never a vote: `originWorker`, `discoveredBy` and `sourceItems` accept it, and the consensus count ignores it, so a finding only you saw stays queued for verification instead of resolving itself.
|
|
88
88
|
6. Do not write a queue or classification in this grouped-input artifact. `okstra convergence seed` classifies Round 0 by mode:
|
|
89
89
|
- Collaborative mode: multi-source groups become `full-consensus` immediately; only single-source groups enter the working queue.
|
|
90
90
|
- Adversarial mode: every finding enters the working queue regardless of source count. Semantic grouping merges provenance only; it does not decide a finding is reliable.
|
|
@@ -350,7 +350,7 @@ If convergence is disabled, `seed`/`finalize` produce the auto-disabled final st
|
|
|
350
350
|
|
|
351
351
|
### Authoring ownership (BLOCKING)
|
|
352
352
|
|
|
353
|
-
If `Report writer worker` is in the selected roster (`recommendedWorkers` / `resultContract.requiredWorkerRoles`), **Lead MUST dispatch it to author the final report data.json**. The worker writes the JSON SSOT at `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json
|
|
353
|
+
If `Report writer worker` is in the selected roster (`recommendedWorkers` / `resultContract.requiredWorkerRoles`), **Lead MUST dispatch it to author the final report data.json**. The worker writes the JSON SSOT at `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json`. Phase 7 renders the human HTML from that record. The full reading copy is rendered on demand with `okstra render-final-report <data.json>`. Lead writes none of these files; it prepares the prompt, dispatches, and reviews the human HTML. See [report-writer](./report-writer.md) "File-author ownership".
|
|
354
354
|
|
|
355
355
|
Before constructing the dispatch prompt, the lead MUST:
|
|
356
356
|
|
|
@@ -402,9 +402,9 @@ For a new `implementation-planning` run, the fixed order is initial verification
|
|
|
402
402
|
2. Dispatch a single plan-body reverify round to every analyser worker in the roster (`claude`, `codex`, and `antigravity` when opted in). `Report writer worker` is NOT a participant in this round.
|
|
403
403
|
3. Aggregate verdicts and resolve the gate result to one of `passed` / `passed-with-dissent` / `blocked-by-disagreement` / `aborted-non-result`.
|
|
404
404
|
4. Write `runs/<task-type>/state/plan-body-verification.json` (schema in the plan-body-verification contract), appending round 1 and, if the one automatic rewrite ran, round 2 to `roundHistory[]`; data.json keeps only the final verdicts.
|
|
405
|
-
5. Populate `implementationPlanning.planBodyVerification` in data.json with round count, gate result, per-item verdicts, and dissent log. The
|
|
405
|
+
5. Populate `implementationPlanning.planBodyVerification` in data.json with round count, gate result, per-item verdicts, and dissent log. The full reading copy task-deliverable block carries this structure without a second prose rendering.
|
|
406
406
|
6. For every `majority-disagree` plan item, append one `clarificationItems[]` row with `blocks=approval` and the 1:1 ID match in the verdict classification (`majority-disagree → C-<N>`). Do not create a parallel open-questions structure.
|
|
407
|
-
7. Publish the
|
|
407
|
+
7. Publish the report record `frontmatter.approved` field as `false`. There is no in-body `- [ ] Approved` marker line — approval lives only in the record (see [plan-body-verification](./plan-body-verification.md) §"Round protocol" step 9). The user may set it to `true` (via `--approve` or the in-session wizard) only when the gate is `passed` or `passed-with-dissent`. **Enforced:** `validators/validate-run.py` `validate_phase_boundary` fails a report shipping `approved: true` under `blocked-by-disagreement` / `aborted-non-result`, and run-prep (`scripts/okstra_ctl/run.py` `_validate_approved_plan`) fail-closes the same case. Manually flipping a blocked gate to passing is a contract violation.
|
|
408
408
|
|
|
409
409
|
If `convergence.planBodyVerification.enabled == false` (set by `--no-plan-verification` or by `okstra config set plan-verification off`), the entire sub-step is skipped and the top-of-report Approval marker is rendered unconditionally (legacy behaviour). This opt-out is intended for fast iteration only and is not recommended for handoff-ready plans.
|
|
410
410
|
|
|
@@ -433,7 +433,7 @@ jq -s 'group_by(.errorType) | map({type: .[0].errorType, count: length})' <runDi
|
|
|
433
433
|
|
|
434
434
|
The errors log is informational. Its presence/absence does not affect the final verdict. Do not block report writing on it.
|
|
435
435
|
|
|
436
|
-
After persistence, reply briefly in the resolved Report Language with: completion status,
|
|
436
|
+
After persistence, reply briefly in the resolved Report Language with: completion status, the human report path, the report record path, team-state path, validator result, resume command path, any remaining blocker. **Lead this reply with the run's task identity** — state `<task-group>/<task-id>` (or the full `taskKey`) first, so the reader knows which task the reply is about. **Every run-artifact path in this reply MUST be task-qualified** — report the human report as `.okstra/tasks/<task-group>/<task-id>/runs/<task-type>/reports/final-report-<task-type>-<seq>.html` rooted at the task bundle, NOT the bare `runs/<task-type>/reports/...` form (byte-for-byte identical across every task of the same task-type, so it cannot identify the task). Under that, cite the report record (`.data.json`) and one line to render the full reading copy: `okstra render-final-report <task-qualified data.json>`. The same task-qualified rule applies to the team-state path, resume command path, and any other run-artifact path this reply cites.
|
|
437
437
|
|
|
438
438
|
## Run-scoped worker-resource lifecycle
|
|
439
439
|
|
|
@@ -245,7 +245,7 @@ round before any host or provider process starts.
|
|
|
245
245
|
**Score the gate with `okstra plan-verify`, never by hand (BLOCKING).** Once this round's verdicts are in the data.json, lead runs
|
|
246
246
|
|
|
247
247
|
```
|
|
248
|
-
okstra plan-verify --report <runs/<task-type>/reports/final-report-<task-type>-<seq>.
|
|
248
|
+
okstra plan-verify --report <runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json>
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
and records what it returns: `gate.recomputed` is the round's gate value, `gate.blockedBy` its `gateBlockedBy` causes, `gate.blockingItems` the items that block. The same call runs every plan-body check a round can be judged on alone — provenance, fixability, subject substance, self-fix grouping, round recording, clarification matching, state-file rounds — and exits 2 with `failures[]` when the round is not contract-clean. **A round is not complete while that exit code is non-zero.**
|
|
@@ -301,7 +301,7 @@ round before any host or provider process starts.
|
|
|
301
301
|
- `open → obsolete` only when a plan change removes the question
|
|
302
302
|
`open` and `answered` continue to block approval; only `resolved` and `obsolete` are non-blocking. A user-directed correction does not consume the automatic self-fix limit, and a failed check does not restart the automatic loop.
|
|
303
303
|
- A terminal row may preserve its original dissent classification only from audited history. Keep superseded votes in `state/plan-body-verification-implementation-planning-<seq>.json`; `validators/validate-run.py` `_historical_plan_item_evidence` recomputes criticality from the recorded `DISAGREE(a|f)` tokens and does not trust the row's classification alone. Every referenced `user-decision-required` / `user-decision-evaluated` activity must cite exactly that row's `C-NNN` and exactly the linked `approvalContext.planItemIds` set. The evaluated activity occurs after every required activity, has `outcome: resolved`, records at least one command whose every `exitCode` is `0`, points `resultPath` at the matching plan-body state artifact, and cites exactly one `plan-body-verification:round-N` evidence token. Round `N` is later than the recorded blocking round; its state votes are all `AGREE` or `SUPPLEMENT`, and they exactly match the final-report verdicts. Its immutable `completedAt` is later than every referenced required activity's canonical event timestamp and no later than every referenced evaluated activity's canonical event timestamp, so an older successful round cannot be relabelled as the response check. This user-response round is recorded in `roundHistory[]` but does not increment `selfFixRoundsApplied` or create another automatic `verification-round-completed` activity. Only the exact round token in `resolution.checkRefs` of a resolved `correctness-critical` row receives that exclusion; the referenced resolved `user-decision-evaluated` activity must match the row's exact `C-NNN` and plan-item set. **Enforced:** `validators/validate-run.py` `_validate_approval_activity_refs`, `_validate_correctness_resolution`, and `_validate_target_round_causality`, plus `validators/validate_session_conformance.py` `_resolved_correctness_reverification_rounds` and `_check_activity_round_counts`. When an independent coverage-only blocker is corrected, keep the `C-NNN` in the now non-blocking Requirement Coverage row's `decisionRefs` and in the matching state-sidecar plan item's `clarificationId`; that item must have no historical blocking dissent and must participate in a round whose `gateBlockedBy` contains `coverage-gap`. A run-wide `coverage-gap` without this item-level `C-NNN` link cannot classify another row. `obsolete` is valid only after current evidence shows that the question or blocker disappeared, or the linked item is historical and removed; a current linked item remains active even when audited history preserves an older classification.
|
|
304
|
-
9. Approval lives in the report
|
|
304
|
+
9. Approval lives in the report record `frontmatter.approved` field — there is no in-body marker line. The user may set it to `true` (via `--approve` or the in-session wizard) only when the Gate result is `passed` or `passed-with-dissent`. **Enforced:** run-prep (`scripts/okstra_ctl/run.py` `_validate_approved_plan`) fail-closes an `approved: true` plan whose record carries a blocking `gateResult` or an open/answered `Blocks: approval` clarification row, and `validators/validate-run.py` `_validate_plan_body_gate_recompute` rejects a declared `gateResult` healthier than the recorded votes.
|
|
305
305
|
|
|
306
306
|
## `plan-body-verification-<task-type>-<seq>.json` schema
|
|
307
307
|
|
|
@@ -561,4 +561,4 @@ Mirrors finding convergence ([convergence](./convergence.md) §"Worker failure h
|
|
|
561
561
|
|
|
562
562
|
- A dispatch that returns terminal non-result MUST NOT be aggregated as `DISAGREE`.
|
|
563
563
|
- If at least one dispatch was issued AND **all** plan-body dispatches return non-result, the Gate result is `aborted-non-result`. Record one `contract-violation` event per non-result dispatch.
|
|
564
|
-
- When the gate is `aborted-non-result`, report-writer MUST keep the frontmatter `approved: false` (publishing `approved: true` under this gate result is a validator failure). A single row is added to `## 1. Clarification Items` with `Statement="plan-body verification could not run — all workers returned non-result"`, `Kind=decision`, `Blocks=approval`, allowing the user to either retry the phase or override by
|
|
564
|
+
- When the gate is `aborted-non-result`, report-writer MUST keep the frontmatter `approved: false` (publishing `approved: true` under this gate result is a validator failure). A single row is added to `## 1. Clarification Items` with `Statement="plan-body verification could not run — all workers returned non-result"`, `Kind=decision`, `Blocks=approval`, allowing the user to either retry the phase or override by running `--approve` on the resume command (or confirming in the in-session wizard). The row MUST name which dispatches returned no result and what re-running them requires. **Enforced:** `validators/validate-run.py` `_validate_aborted_gate_has_clarification` — `_validate_plan_body_clarification_matching` cannot cover this case because it walks `majority-disagree` items and an aborted round produces none, which is exactly how an aborted run used to reach the user with no stated blocker and stall.
|