okstra 0.180.0 → 0.183.2

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 (223) hide show
  1. package/dist/cli-registry.mjs +16 -2
  2. package/dist/cli-registry.mjs.map +1 -1
  3. package/dist/commands/execute/render-bundle.d.mts +4 -2
  4. package/dist/commands/execute/render-bundle.mjs +46 -5
  5. package/dist/commands/execute/render-bundle.mjs.map +1 -1
  6. package/dist/commands/execute/run.mjs +11 -3
  7. package/dist/commands/execute/run.mjs.map +1 -1
  8. package/dist/commands/inspect/model-io.d.mts +1 -0
  9. package/dist/commands/inspect/model-io.mjs +25 -0
  10. package/dist/commands/inspect/model-io.mjs.map +1 -0
  11. package/dist/commands/inspect/stage-map.mjs +29 -8
  12. package/dist/commands/inspect/stage-map.mjs.map +1 -1
  13. package/dist/commands/inspect/task-list.mjs +52 -6
  14. package/dist/commands/inspect/task-list.mjs.map +1 -1
  15. package/dist/commands/inspect/user-response.mjs +14 -4
  16. package/dist/commands/inspect/user-response.mjs.map +1 -1
  17. package/dist/commands/lifecycle/check-project.d.mts +1 -0
  18. package/dist/commands/lifecycle/check-project.mjs +69 -50
  19. package/dist/commands/lifecycle/check-project.mjs.map +1 -1
  20. package/dist/commands/lifecycle/contract-check.d.mts +1 -0
  21. package/dist/commands/lifecycle/contract-check.mjs +18 -0
  22. package/dist/commands/lifecycle/contract-check.mjs.map +1 -0
  23. package/dist/commands/lifecycle/preflight.mjs +154 -51
  24. package/dist/commands/lifecycle/preflight.mjs.map +1 -1
  25. package/dist/commands/pr/pr.d.mts +1 -0
  26. package/dist/commands/pr/pr.mjs +19 -1
  27. package/dist/commands/pr/pr.mjs.map +1 -1
  28. package/dist/commands/report/agent-activity.mjs +2 -2
  29. package/dist/commands/report/translate.mjs +3 -0
  30. package/dist/commands/report/translate.mjs.map +1 -1
  31. package/dist/lib/host-registry-client.mjs +13 -9
  32. package/dist/lib/host-registry-client.mjs.map +1 -1
  33. package/docs/architecture.md +11 -0
  34. package/docs/cli.md +26 -13
  35. package/docs/container.md +6 -4
  36. package/docs/contributor-change-matrix.md +1 -1
  37. package/docs/for-ai/README.md +2 -2
  38. package/docs/for-ai/skills/okstra-brief-gen.md +5 -3
  39. package/docs/for-ai/skills/okstra-code-review.md +4 -4
  40. package/docs/for-ai/skills/okstra-container-build.md +20 -17
  41. package/docs/for-ai/skills/okstra-inspect.md +20 -23
  42. package/docs/for-ai/skills/okstra-manager.md +19 -18
  43. package/docs/for-ai/skills/okstra-memory.md +2 -2
  44. package/docs/for-ai/skills/okstra-pr-gen.md +3 -3
  45. package/docs/for-ai/skills/okstra-rollup.md +14 -13
  46. package/docs/for-ai/skills/okstra-run.md +7 -3
  47. package/docs/for-ai/skills/okstra-schedule-gen.md +15 -18
  48. package/docs/for-ai/skills/okstra-setup.md +7 -7
  49. package/docs/for-ai/skills/okstra-usage.md +5 -4
  50. package/docs/for-ai/skills/okstra-user-response.md +50 -32
  51. package/docs/project-structure-overview.md +29 -26
  52. package/docs/task-process/implementation.md +7 -4
  53. package/package.json +1 -1
  54. package/runtime/BUILD.json +2 -2
  55. package/runtime/agents/workers/claude-worker.md +4 -4
  56. package/runtime/agents/workers/report-writer-worker.md +3 -3
  57. package/runtime/agents/workers/translator-worker.md +5 -13
  58. package/runtime/bin/okstra-error-log.py +51 -11
  59. package/runtime/bin/okstra-report-translate.py +210 -23
  60. package/runtime/prompts/host-orchestration/implementation.md +1 -1
  61. package/runtime/prompts/launch.template.md +3 -7
  62. package/runtime/prompts/lead/context-loader.md +41 -141
  63. package/runtime/prompts/lead/convergence.md +8 -6
  64. package/runtime/prompts/lead/okstra-lead-contract.md +25 -35
  65. package/runtime/prompts/lead/plan-body-verification.md +187 -14
  66. package/runtime/prompts/lead/report-writer.md +20 -1
  67. package/runtime/prompts/lead/team-contract.md +8 -53
  68. package/runtime/prompts/profiles/_coding-conventions-preflight.md +3 -2
  69. package/runtime/prompts/profiles/_common-contract.md +1 -1
  70. package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
  71. package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
  72. package/runtime/prompts/profiles/final-verification.md +1 -1
  73. package/runtime/prompts/profiles/implementation-planning.md +8 -2
  74. package/runtime/prompts/profiles/release-handoff.md +0 -1
  75. package/runtime/prompts/wizard/prompts.ko.json +0 -1
  76. package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +69 -17
  77. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +13 -4
  78. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +6 -1
  79. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
  80. package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +50 -5
  81. package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
  82. package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +66 -5
  83. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +70 -2
  84. package/runtime/python/okstra_ctl/agent_activity.py +118 -35
  85. package/runtime/python/okstra_ctl/agent_invocation.py +19 -6
  86. package/runtime/python/okstra_ctl/agent_prompt_cli.py +65 -18
  87. package/runtime/python/okstra_ctl/analysis_inputs.py +5 -4
  88. package/runtime/python/okstra_ctl/analysis_packet.py +81 -1
  89. package/runtime/python/okstra_ctl/approval_decisions.py +3 -2
  90. package/runtime/python/okstra_ctl/attempt_evidence.py +2 -2
  91. package/runtime/python/okstra_ctl/backfill.py +13 -10
  92. package/runtime/python/okstra_ctl/batch.py +2 -4
  93. package/runtime/python/okstra_ctl/build_tools.py +6 -3
  94. package/runtime/python/okstra_ctl/claim_reproduction.py +101 -0
  95. package/runtime/python/okstra_ctl/clarification_items.py +27 -13
  96. package/runtime/python/okstra_ctl/cmux.py +130 -52
  97. package/runtime/python/okstra_ctl/code_review_target.py +34 -8
  98. package/runtime/python/okstra_ctl/consumers.py +5 -4
  99. package/runtime/python/okstra_ctl/container.py +103 -8
  100. package/runtime/python/okstra_ctl/context_cost.py +2 -1
  101. package/runtime/python/okstra_ctl/contract_graph.py +497 -0
  102. package/runtime/python/okstra_ctl/contract_graph_cli.py +62 -0
  103. package/runtime/python/okstra_ctl/convergence.py +338 -17
  104. package/runtime/python/okstra_ctl/convergence_engine.py +10 -18
  105. package/runtime/python/okstra_ctl/convergence_provenance.py +58 -8
  106. package/runtime/python/okstra_ctl/convergence_store.py +55 -34
  107. package/runtime/python/okstra_ctl/design_prep.py +7 -4
  108. package/runtime/python/okstra_ctl/dispatch_core.py +35 -65
  109. package/runtime/python/okstra_ctl/dispatch_state.py +134 -59
  110. package/runtime/python/okstra_ctl/doctor.py +6 -3
  111. package/runtime/python/okstra_ctl/domain/worker_presentation.py +70 -9
  112. package/runtime/python/okstra_ctl/entrypoints/hosts.py +16 -30
  113. package/runtime/python/okstra_ctl/error_log_write.py +35 -30
  114. package/runtime/python/okstra_ctl/error_report.py +26 -1
  115. package/runtime/python/okstra_ctl/error_zip.py +27 -5
  116. package/runtime/python/okstra_ctl/execution_identity.py +3 -2
  117. package/runtime/python/okstra_ctl/execution_manifest.py +7 -4
  118. package/runtime/python/okstra_ctl/final_report_schema.py +2 -2
  119. package/runtime/python/okstra_ctl/fix_cycles.py +2 -2
  120. package/runtime/python/okstra_ctl/fixed_text.py +39 -0
  121. package/runtime/python/okstra_ctl/git_reconcile.py +41 -9
  122. package/runtime/python/okstra_ctl/handoff.py +5 -4
  123. package/runtime/python/okstra_ctl/i18n.py +4 -2
  124. package/runtime/python/okstra_ctl/implementation_direction.py +22 -14
  125. package/runtime/python/okstra_ctl/implementation_outcome.py +4 -7
  126. package/runtime/python/okstra_ctl/incremental_carry.py +2 -1
  127. package/runtime/python/okstra_ctl/incremental_scope.py +8 -3
  128. package/runtime/python/okstra_ctl/index.py +8 -11
  129. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +79 -7
  130. package/runtime/python/okstra_ctl/invocation.py +3 -6
  131. package/runtime/python/okstra_ctl/json_boundary.py +366 -0
  132. package/runtime/python/okstra_ctl/json_registry.py +10 -12
  133. package/runtime/python/okstra_ctl/jsonl.py +19 -2
  134. package/runtime/python/okstra_ctl/lead_events.py +33 -1
  135. package/runtime/python/okstra_ctl/listing.py +3 -3
  136. package/runtime/python/okstra_ctl/log_report.py +24 -2
  137. package/runtime/python/okstra_ctl/manager_cli.py +92 -7
  138. package/runtime/python/okstra_ctl/manager_store.py +12 -10
  139. package/runtime/python/okstra_ctl/material.py +5 -1
  140. package/runtime/python/okstra_ctl/migrate.py +29 -25
  141. package/runtime/python/okstra_ctl/model_cli.py +3 -15
  142. package/runtime/python/okstra_ctl/model_io_cli.py +1051 -0
  143. package/runtime/python/okstra_ctl/mutation_probe.py +13 -4
  144. package/runtime/python/okstra_ctl/pane_reclaim.py +3 -2
  145. package/runtime/python/okstra_ctl/paths.py +9 -0
  146. package/runtime/python/okstra_ctl/plan_items.py +115 -4
  147. package/runtime/python/okstra_ctl/plan_items_cli.py +513 -18
  148. package/runtime/python/okstra_ctl/pr_template.py +3 -2
  149. package/runtime/python/okstra_ctl/project_meta.py +5 -7
  150. package/runtime/python/okstra_ctl/recap.py +5 -4
  151. package/runtime/python/okstra_ctl/reconcile.py +21 -27
  152. package/runtime/python/okstra_ctl/registry/host_discovery.py +3 -2
  153. package/runtime/python/okstra_ctl/registry/provider_registry.py +3 -2
  154. package/runtime/python/okstra_ctl/render.py +26 -15
  155. package/runtime/python/okstra_ctl/render_final_report.py +3 -2
  156. package/runtime/python/okstra_ctl/report_assembly.py +172 -17
  157. package/runtime/python/okstra_ctl/report_finalize.py +7 -10
  158. package/runtime/python/okstra_ctl/report_html/render.py +3 -2
  159. package/runtime/python/okstra_ctl/report_language.py +3 -2
  160. package/runtime/python/okstra_ctl/report_markdown.py +13 -1
  161. package/runtime/python/okstra_ctl/report_narrative.py +40 -8
  162. package/runtime/python/okstra_ctl/report_synthesis_packet.py +518 -0
  163. package/runtime/python/okstra_ctl/report_views.py +3 -2
  164. package/runtime/python/okstra_ctl/rollup.py +65 -4
  165. package/runtime/python/okstra_ctl/run.py +159 -56
  166. package/runtime/python/okstra_ctl/run_audit.py +3 -2
  167. package/runtime/python/okstra_ctl/run_context.py +6 -9
  168. package/runtime/python/okstra_ctl/run_index_row.py +2 -8
  169. package/runtime/python/okstra_ctl/schedule_semantics.py +5 -2
  170. package/runtime/python/okstra_ctl/schema_excerpt.py +4 -2
  171. package/runtime/python/okstra_ctl/session_transcript.py +27 -1
  172. package/runtime/python/okstra_ctl/set_work_status.py +64 -38
  173. package/runtime/python/okstra_ctl/stage_fix_carry.py +4 -2
  174. package/runtime/python/okstra_ctl/stage_map.py +26 -6
  175. package/runtime/python/okstra_ctl/stage_targets.py +3 -4
  176. package/runtime/python/okstra_ctl/team.py +2 -1
  177. package/runtime/python/okstra_ctl/team_reconcile.py +11 -2
  178. package/runtime/python/okstra_ctl/time_report.py +51 -4
  179. package/runtime/python/okstra_ctl/usage_identity.py +2 -1
  180. package/runtime/python/okstra_ctl/usage_report.py +58 -4
  181. package/runtime/python/okstra_ctl/user_response.py +1431 -66
  182. package/runtime/python/okstra_ctl/wizard.py +11 -44
  183. package/runtime/python/okstra_ctl/work_categories.py +3 -2
  184. package/runtime/python/okstra_ctl/worker_prompt_body.py +18 -7
  185. package/runtime/python/okstra_ctl/worker_prompt_contract.py +3 -2
  186. package/runtime/python/okstra_ctl/worker_runner.py +14 -12
  187. package/runtime/python/okstra_ctl/workflow.py +2 -1
  188. package/runtime/python/okstra_ctl/worktree.py +3 -2
  189. package/runtime/python/okstra_ctl/wrapper_status.py +4 -2
  190. package/runtime/python/okstra_ctl/write_policy.py +4 -2
  191. package/runtime/python/okstra_token_usage/antigravity.py +39 -12
  192. package/runtime/python/okstra_token_usage/collect.py +90 -38
  193. package/runtime/python/okstra_token_usage/grok.py +127 -0
  194. package/runtime/schemas/final-report-v2.0.schema.json +9 -0
  195. package/runtime/schemas/final-report-v3.0.schema.json +9 -0
  196. package/runtime/schemas/report-synthesis-packet-v1.0.schema.json +140 -0
  197. package/runtime/skills/okstra-brief-gen/SKILL.md +9 -7
  198. package/runtime/skills/okstra-code-review/SKILL.md +21 -11
  199. package/runtime/skills/okstra-container-build/SKILL.md +18 -18
  200. package/runtime/skills/okstra-inspect/SKILL.md +12 -11
  201. package/runtime/skills/okstra-inspect/facets/error-zip.md +8 -8
  202. package/runtime/skills/okstra-inspect/facets/errors.md +2 -2
  203. package/runtime/skills/okstra-inspect/facets/history.md +9 -14
  204. package/runtime/skills/okstra-inspect/facets/logs.md +2 -2
  205. package/runtime/skills/okstra-inspect/facets/recap.md +5 -5
  206. package/runtime/skills/okstra-inspect/facets/report.md +6 -10
  207. package/runtime/skills/okstra-inspect/facets/status.md +9 -8
  208. package/runtime/skills/okstra-inspect/facets/time.md +3 -3
  209. package/runtime/skills/okstra-manager/SKILL.md +16 -14
  210. package/runtime/skills/okstra-memory/SKILL.md +3 -3
  211. package/runtime/skills/okstra-pr-gen/SKILL.md +5 -4
  212. package/runtime/skills/okstra-rollup/SKILL.md +6 -16
  213. package/runtime/skills/okstra-run/SKILL.md +7 -7
  214. package/runtime/skills/okstra-schedule-gen/SKILL.md +21 -17
  215. package/runtime/skills/okstra-setup/SKILL.md +21 -13
  216. package/runtime/skills/okstra-setup/references/project-config.md +2 -2
  217. package/runtime/skills/okstra-usage/SKILL.md +10 -10
  218. package/runtime/skills/okstra-user-response/SKILL.md +78 -107
  219. package/runtime/templates/report-writer-prompt-preamble.md +17 -1
  220. package/runtime/templates/reports/schedule.template.md +4 -4
  221. package/runtime/templates/worker-error-contract.md +17 -29
  222. package/runtime/validators/validate-run.py +379 -63
  223. package/runtime/validators/validate_session_conformance.py +43 -7
@@ -58,7 +58,6 @@ okstra/
58
58
  ├── skills/ Claude Code skills (13); `_fragments/` holds shared marker blocks
59
59
  ├── .agents/skills/ Codex repo-local maintainer skills
60
60
  ├── .claude/skills/ Claude Code project-only mirror-sync surface
61
- ├── .claude/settings.json Claude Code project-only mirror-sync surface
62
61
  ├── .codex/hooks.json Codex project lifecycle hooks
63
62
  ├── agents/ native Claude execution-adapter definitions
64
63
  ├── prompts/ launch/profile contracts, duty catalog, wizard prompt JSON
@@ -67,7 +66,7 @@ okstra/
67
66
  ├── validators/ run / brief / schedule / view validators
68
67
  ├── tools/build.mjs source → runtime sync
69
68
  ├── tools/sync-skill-fragments.mjs expand shared fragment blocks into skills/*/SKILL.md
70
- ├── tools/korean-sources/ maintainer-only Korean mirror sync engine
69
+ ├── tools/korean-sources/ maintainer-only Korean mirror drift report
71
70
  ├── config/korean-sources.json configured English Markdown source roots
72
71
  ├── runtime/ generated install payload; do not edit directly
73
72
  ├── tests/ pytest unit suite
@@ -199,15 +198,15 @@ Runtime/install asset changes follow this checklist:
199
198
  | `token-usage` | `src/commands/execute/token-usage.mts` | Wrap installed Python token usage CLI |
200
199
  | `spawn-followups`, `error-log` | `src/commands/execute/*.mts` | Follow-up task bundle creation and run error-log append helpers |
201
200
  | `memory` | `src/commands/memory/memory.mts` | Store/find global conversation memory under `~/.okstra/memory-book` |
202
- | `pr` | `src/commands/pr/pr.mts` | `okstra pr <template\|branches\|gen>` — PR body template store under `~/.okstra/template/pr/` (bundled fallback `src/commands/pr/default.md`), base-branch recommendation, and the `gen` JSON bundle (template + `<base>..HEAD` commits + `<base>...HEAD` diffstat) backing the okstra-pr-gen skill. Git-only; no project registration required |
201
+ | `pr` | `src/commands/pr/pr.mts` | `okstra pr <template\|branches\|gen>` — PR body template store under `~/.okstra/template/pr/` (bundled fallback `src/commands/pr/default.md`), base-branch recommendation, and a fixed-text generation bundle for the okstra-pr-gen skill. `--json` preserves the template + `<base>..HEAD` commits + `<base>...HEAD` diffstat machine contract. Git-only; no project registration required |
203
202
  | `recap` | `src/commands/inspect/recap.mts` | `okstra recap <assemble\|record\|note>` Node wrapper backing the okstra-inspect `recap` facet — `assemble` is a read-only phase-transition summary, `record` appends one line to `recap/recap-log.jsonl`, and `note` writes an agent-authored note under `notes/` and prints the `--clarification-response` argument for a follow-up run |
204
203
  | `stage-map` | `src/commands/inspect/stage-map.mts` | `okstra stage-map <task-key>` — exposes a task's implementation-planning Stage Map as JSON (`stages[].{stage_number,title,depends_on,step_count}` + consumer-state-based `doneStages[]`). If there is no Stage Map, `stages: []`. The read-side basis from which `okstra-schedule-gen` derives stage units and dependency closure |
205
204
  | `design-prep` | `src/commands/inspect/design-prep.mts` | `okstra design-prep <list\|show\|write>` thin shim into `scripts/okstra_ctl/design_prep.py` — queries (`list`/`show`) the design items that implementation-planning pre-authored with AI, and records the user-confirmed responses as an append-only sidecar under `design-prep-inputs/` (`write`, `--confirmed` required). It never modifies the report snapshot |
206
- | `rollup` | `src/commands/inspect/rollup.mts` | `okstra rollup` thin shim into `scripts/okstra_ctl/rollup.py` read-only cross-task roll-up backing the okstra-rollup skill |
207
- | `usage-report` | `src/commands/inspect/usage-report.mts` | `okstra usage-report` thin shim into `scripts/okstra_ctl/usage_report.py` read-only project usage snapshot backing the okstra-usage skill |
208
- | `container` | `src/commands/inspect/container.mts` | `bin okstra container` thin shim into `scripts/okstra_ctl/container.py` for the okstra-container-build skill |
205
+ | `rollup` | `src/commands/inspect/rollup.mts` | Read-only roll-up; `--text` is the fixed model projection and machine mode remains JSON |
206
+ | `usage-report` | `src/commands/inspect/usage-report.mts` | Read-only usage snapshot; `--text` is the fixed model projection and machine mode remains JSON |
207
+ | `container` | `src/commands/inspect/container.mts` | Container lifecycle shim; `--text` is the command-specific model projection and machine mode remains JSON |
209
208
  | `code-review` | `src/commands/inspect/code-review.mts` | `okstra code-review target` thin shim into `scripts/okstra_ctl/code_review_target.py` — resolves what one implementation stage's or one branch's review reads (worktree, branch, base/head commits) and where its result file goes, for the okstra-code-review skill. Read-only; creates no directory or file |
210
- | `manager` | `src/commands/manager.mts` | Thin shim into `scripts/okstra_ctl/manager_cli.py` for cross-project manager state and child launch packets |
209
+ | `manager` | `src/commands/manager.mts` | Cross-project manager; fixed text is the default and `--json` selects machine output |
211
210
 
212
211
  `src/lib/python-helper.mts` centralizes Node → Python execution so command modules do not duplicate subprocess wiring.
213
212
 
@@ -305,7 +304,7 @@ Important modules:
305
304
  | `container.py` | the `okstra container` convergence entrypoint of the okstra-container-build public skill — `provision_container_group` + `up`/`status`/`logs`/`stop-watcher`/`down` dispatch, env-override synthesis, compose argv assembly, and per-container watcher startup |
306
305
  | `container_registry.py` | flock-guarded auxiliary index — tracks per-container-group tmux session/pane and watcher findings |
307
306
  | `plan_run_root.py` | shared helper deriving `approved_plan_path` → `plan_run_root` and back-tracing the task-key |
308
- | `manager_cli.py` | `okstra manager` Python entrypoint — manager init/discover/new/task subcommands and JSON output |
307
+ | `manager_cli.py` | `okstra manager` Python entrypoint — purpose-specific fixed text by default, machine JSON with `--json` |
309
308
  | `manager_paths.py` | Manager state path SSOT under `~/.okstra/managers/<manager-id>/`; slug fallback uses `u-<sha1-prefix>` when a safe segment would be empty |
310
309
  | `manager_store.py` | Manager-owned state mutation — project membership, task planning, assignment, directives, event append |
311
310
  | `manager_sync.py` | One-way child project `.okstra` snapshot reader; corrupt child state becomes row-level `error` so other children continue |
@@ -339,6 +338,7 @@ Important modules:
339
338
  | `convergence_store.py`, `convergence_migration.py` | atomic JSON persistence plus legacy/new-engine seed decisions; valid terminal finals are reused, while invalid state requires byte-preserving archival before restart |
340
339
  | `convergence.py` | `okstra convergence` internal CLI orchestration for `seed`, `plan-round`, `apply-round`, `apply-critic-gaps`, `finalize`, `validate`, and `example`; it composes the reducer, store, and migration policy without duplicating their decisions |
341
340
  | `plan_items.py`, `plan_items_cli.py` | deterministic extraction of the report-writer narrative `P-*` plan-item queue plus the `okstra plan-items extract` / `validate` / `seed` / `collect-verdicts` / `apply-verdicts` / `derivations` adapter; v2 data.json remains a read input |
341
+ | `claim_reproduction.py` | reproduces a plan-body single-vote `fact` claim before it can block on one vote — runs the declared probe (`path-exists` / `path-absent` / `literal-present` / `literal-absent` / `citations-differ`) inside the resolved project root and returns `reproduced` / `not-reproduced` / `not-runnable`, which `plan-items apply-verdicts --run-manifest` writes into `reproductionResult` (always overwriting the worker-sent value so a verifier cannot score its own claim). A `judgement` claim, or a `fact` that does not reproduce, takes the quorum route |
342
342
  | `plan_derivations.py` | the supersession sweep `_common-contract.md` requires an author to do by hand — extracts the symbols, paths, and ids an answered clarification names and reports every plan string that mentions one. Advisory: it locates candidates and never judges which are now false |
343
343
  | `scope_provenance.py` | single source of truth for the scope-provenance grammar every phase-emitted requirement must declare, shared by `validators/validate-run.py` and `validators/validate_fanout.py` so the planning report and fan-out packets cannot drift |
344
344
  | `worker_artifact_paths.py` | canonical worker artifact path derivation (e.g. `audit_sidecar_rel` inserts `-audit-` after the first `-worker-` token), so dispatch and validation agree on non-canonical-path rejection |
@@ -352,6 +352,10 @@ Important modules:
352
352
  | `domain/worker_stream.py` | the normalised event vocabulary (`Text` / `ToolCall` / `ToolResult` / `Denial` / `Result`) plus its three pure projections: `format_live` (one readable row per event, for the pane), `format_log` (the same plus bodies, for the archive), `final_text` (the closing message alone). Also `content_block_events`, the normaliser for the wire shape keyed on `type` with `message.content` blocks, which three providers share. No files, no clock |
353
353
  | `domain/worker_role.py` | per-role execution budgets — the 1500s/600s idle pair lives here once instead of being re-declared in each wrapper |
354
354
  | `task_target.py` | shared helper resolving `task-key → (task_root, project_root)` (`resolve_task_root`) |
355
+ | `contract_graph.py`, `contract_graph_cli.py` | runtime-contract graph loader + cross-reference/dependency-closure validator and its `okstra contract-check --root <dir> (--profile\|--operation)` CLI boundary. Loads the agent contract schemas (`common`/`role`/`duty`/`profile`/`operation`), validates known role capabilities, and reports the dependency closure with per-file `path`/`schemaVersion`/`sha256`; an invalid contract raises `ContractGraphError` |
356
+ | `json_boundary.py` | strict JSON persistence boundaries for okstra-owned artifacts — a sealed `ExternalJsonSource` (validated producer + path) is the only way owned JSON is read, and `JsonBoundaryError` names artifact / reason / path when a write cannot satisfy its contract; the SSOT that keeps the model out of internal JSON key/path authorship |
357
+ | `fixed_text.py` | shared scalar-line format for the model-facing fixed-text projections — `scalar` neutralises complex values and control characters, `line` renders one static-labelled Markdown list row, and `value_lines` losslessly flattens a JSON-shaped value into fixed name/order/value rows |
358
+ | `model_io_cli.py` | renders purpose-scoped fixed Markdown input from okstra-owned JSON for the model boundary — resolves the current run/project through the run manifest (`validated_run_authority`, `canonical_run_state_artifact`) and emits only each command's allow-listed fields in fixed order instead of expanding arbitrary nested objects |
355
359
 
356
360
  > `i18n.py` (the final-report i18n dictionary loader + Jinja2 lookup) is an intentionally undocumented internal helper — it is a render helper that users and contributors do not need to know about in the canonical docs, so it is excluded from the module map.
357
361
 
@@ -367,7 +371,7 @@ Project resolver and read-only state helpers:
367
371
 
368
372
  Token/cost accounting:
369
373
 
370
- - provider adapters: `claude.py`, `codex.py`, `antigravity.py`
374
+ - provider adapters: `claude.py`, `codex.py`, `antigravity.py`, `grok.py` (`grok.py` reads the Grok Build session docs under `~/.grok/sessions/<percent-encoded-cwd>/`, taking cumulative tokens from the last `params.update.usage.modelUsage` snapshot in `updates.jsonl`)
371
375
  - aggregation: `collect.py`, `blocks.py`, `jsonl_io.py`, `paths.py`
372
376
  - incremental scan cache: `cursor.py` (`$OKSTRA_HOME/cache/token-usage/` byte cursor + usage event extracts; bypass with `--no-cache`)
373
377
  - pricing: `pricing.py`
@@ -475,24 +479,23 @@ These files are native Claude execution adapters, not provider-neutral LLM trans
475
479
 
476
480
  ### 4.13 `tools/korean-sources/`
477
481
 
478
- The maintainer-only Korean review-mirror lifecycle has one stateful public
479
- boundary: `lifecycle.mjs`. `cli.mjs` and `hooks.mjs` are adapters that call it;
480
- they do not reconstruct observations or transitions. `config.mjs` and
481
- `markdown.mjs` remain pure configuration and protected-Markdown validators.
482
+ The maintainer-only Korean review mirrors. The tool reports which mirrors have
483
+ drifted and checks a translation's structure; a maintainer session does the
484
+ translating and the writing. Nothing here observes edits or runs on its own.
482
485
 
483
486
  | File | Role |
484
487
  |---|---|
485
- | `lifecycle.mjs` | Public lifecycle seam: bootstrap, inspect, evidence transitions, and reviewed-packet application |
486
- | `lifecycle-store.mjs` | Versioned lifecycle store, canonical JSON IDs, v1 migration, and repository lock |
487
- | `lifecycle-observation.mjs` | Current source/mirror snapshots, rename detection, and derived change observations |
488
- | `lifecycle-evidence.mjs` | Decision, direction, review-packet, semantic-confirmation, and approval validation |
489
- | `lifecycle-application.mjs` | Allowed packet operations, journaled idempotent apply, and completion verification |
490
- | `lifecycle-index.mjs` | Korean mirror `.project-docs/INDEX.md` row validation and updates |
491
-
492
- Maintainers follow `tools/korean-sources/workflow.md` through observation,
493
- conditional direction, review packet, semantic confirmation, conditional
494
- approval, and apply. The paired local skills only delegate to that workflow;
495
- they are not published user skills.
488
+ | `cli.mjs` | The three commands: `status`, `validate --source`, `mark --source` |
489
+ | `baseline.mjs` | Drift report and the source/mirror hash pair recorded at the last sync |
490
+ | `config.mjs` | `config/korean-sources.json` validation and source↔mirror path mapping |
491
+ | `markdown.mjs` | Protected-Markdown structure comparison behind `validate` |
492
+ | `path-safety.mjs` | Symlink-refusing repository reads and atomic writes |
493
+
494
+ Source and mirror hold the same document in two languages, so they never hash
495
+ alike and drift cannot be read from the two files alone. `mark` records the pair
496
+ of hashes in `.project-docs/ko-sources/.sync-baseline.json`, which is the only
497
+ state the tool keeps. Maintainers follow `tools/korean-sources/workflow.md`; the
498
+ paired local skills only delegate to it and are not published user skills.
496
499
 
497
500
  ---
498
501
 
@@ -661,7 +664,7 @@ When changing code, keep these docs in sync:
661
664
  - New report field/section: update schema, template, report-writer worker, validator tests, this file's report model if user-visible.
662
665
  - New phase/profile behavior: update `prompts/profiles/*`, `docs/architecture.md`, `docs/cli.md`, and `README.md` if user-facing.
663
666
 
664
- Edit English canonical Markdown sources directly. After changing a path registered in `config/korean-sources.json`, process the local Korean mirror queue with `$sync-korean-sources` or `/sync-korean-sources`. `.project-docs/ko-sources/**` is maintainer-local only: it is neither published nor committed.
667
+ Edit English canonical Markdown sources directly; nothing asks you to touch the Korean mirror in the same change. A maintainer session reconciles the mirrors on its own schedule with `$sync-korean-sources` or `/sync-korean-sources`, which begins by reading `node tools/korean-sources/cli.mjs status`. `.project-docs/ko-sources/**` is maintainer-local only: it is neither published nor committed.
665
668
 
666
669
  ---
667
670
 
@@ -692,4 +695,4 @@ Clarifications now live in the unified `## 1. Clarification Items` table. Deprec
692
695
 
693
696
  ---
694
697
 
695
- *Updated: 2026-08-21 · Source of truth checked against `package.json`, `bin/okstra`, `src/cli-registry.mts`, `src/lib/skill-catalog.mts`, `tools/build.mjs`, `scripts/`, `skills/`, `agents/`, `templates/`, `schemas/`, `validators/`, and tests.*
698
+ *Updated: 2026-08-23 · Source of truth checked against `package.json`, `bin/okstra`, `src/cli-registry.mts`, `src/lib/skill-catalog.mts`, `tools/build.mjs`, `scripts/`, `skills/`, `agents/`, `templates/`, `schemas/`, `validators/`, and tests.*
@@ -53,10 +53,13 @@ here because they were previously readable only by tracing the runtime sources.
53
53
  Clarification Items` rows — the `user-response-*.md` sidecars under
54
54
  `runs/implementation-planning/user-responses/`, a sibling of the directory holding the
55
55
  plan itself — are collected into `instruction-set/clarification-response.md`. Those
56
- sidecars are written by the user, not by the report renderer: the report HTML's `Export
57
- user response` button downloads a file the user then saves there, and `okstra
58
- user-response write` writes one directly. The renderer at most pre-creates that
59
- directory empty so the user does not have to; it never puts a sidecar in it.
56
+ sidecars are written by the user, not by the report renderer. The report HTML's `Export
57
+ user response` button downloads a file the user then saves there. The in-session flow
58
+ reads `user-response list-view` and `user-response show-view --report <path>
59
+ --project-root <root>`, then uses `user-response begin`, typed `user-response answer`
60
+ and decision commands, and `user-response finalize` to publish the sidecar. The
61
+ renderer at most pre-creates that directory empty so the user does not
62
+ have to; it never puts a sidecar in it.
60
63
  The plan document is *not* copied: it reaches the run as the `--approved-plan` path and
61
64
  the executor re-reads it there. An explicit `--clarification-response` wins when given;
62
65
  the automatic attachment is the fallback for an implementation run that supplies none
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.180.0",
3
+ "version": "0.183.2",
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.180.0",
3
- "builtAt": "2026-08-21T05:25:42.034Z",
2
+ "package": "0.183.2",
3
+ "builtAt": "2026-08-23T08:48:03.022Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -48,7 +48,7 @@ from the provider name. `lead` is a compatibility alias for `leader`.
48
48
  4. Anchor all file operations to the absolute `Project Root` from the lead prompt. Use absolute paths — do NOT rely on inherited cwd. Never use `cd` to change directory.
49
49
  - **Executor exception (implementation phase only):** when this worker is dispatched as the `Executor` and the lead prompt provides an `EXECUTOR_WORKTREE_PATH` that differs from the session's inherited cwd, cwd-sensitive Bash commands (`cargo *`, `npm *`, `pnpm *`, `bun *`, `pytest`, `make *`, `go *`, language-toolchain test/build commands) MUST be prefixed with `cd <EXECUTOR_WORKTREE_PATH> && ` in the same Bash invocation — e.g. `cd /Users/.../worktrees/foo && cargo test -p bar`. Do NOT wrap the whole thing in `bash -lc "..."` or `bash -c "..."`; pass the chained command directly to the Bash tool so the leading `cd` token remains visible to the permission layer. The `cd` is scoped to the single Bash subshell and does not mutate the session's shell state, so this does not conflict with the "never use cd" rule above (which prevents the worker from drifting the session cwd across calls).
50
50
  - **Executor coding-conventions preflight (BLOCKING, before your first `Edit` / `Write`):** when dispatched as the `Executor`, you MUST run the coding-conventions preflight defined in the executor sidecar (`prompts/profiles/_implementation-executor.md` → "Pre-implementation context exploration") before writing any code. Use this worker prompt's `**Coding preflight pack:**` anchor header; read that pack's `overview.md` and `clean-code.md`, then follow the routed pack's language → framework → architecture stages, iterating every rule and loading every matching resource (for example `frameworks/node-server.md` and `architectures/hexagonal.md` when their conditions match). The preflight pack is a runtime resource, not an auto-invoked skill; read the files via the Read tool by absolute path.
51
- - **Executor post-write gates (BLOCKING, before your final commit / before claiming done):** the same dispatch prompt carries two gate blocks the lead appends after the preflight — `Pre-commit diff review sweep` (`prompts/profiles/_implementation-diff-review.md`) and `Implementation self-check` (`prompts/profiles/_implementation-self-check.md`). Execute both and record their coverage lines in your worker result exactly as the blocks specify. The codex/antigravity wrappers refuse to launch when an executor prompt lacks these blocks (`*_POSTWRITE_GATE_MISSING`); this worker runs in-process with no wrapper gate, so the contract lands on you directly — if either block is missing from your dispatch prompt, record a `tool-failure` in the errors sidecar and tell the lead to re-dispatch with the blocks included instead of skipping the gates.
51
+ - **Executor post-write gates (BLOCKING, before your final commit / before claiming done):** the same dispatch prompt carries two gate blocks the lead appends after the preflight — `Pre-commit diff review sweep` (`prompts/profiles/_implementation-diff-review.md`) and `Implementation self-check` (`prompts/profiles/_implementation-self-check.md`). Execute both and record their coverage lines in your worker result exactly as the blocks specify. The codex/antigravity wrappers refuse to launch when an executor prompt lacks these blocks (`*_POSTWRITE_GATE_MISSING`); this worker runs in-process with no wrapper gate, so the contract lands on you directly — if either block is missing, record a typed `tool-failure` through `okstra error-log append-observed` and tell the lead to re-dispatch with the blocks included.
52
52
  - **Verifier QA-gate exception:** verifier roles MAY use the same `cd <WORKTREE> && <cmd>` shape when executing project-declared `qaCommands` (lint / format / typecheck / test) from `project.json`, since those commands are cwd-sensitive by nature. Outside the QA gate, verifiers still read with absolute paths only — do NOT use `cd` for file inspection.
53
53
  - **Shell commands must not be able to prompt:** this worker runs inside the host session, so its Bash calls see the user's own shell, where `cp`, `mv`, and `rm` are commonly aliased to their `-i` form. The confirmation that alias raises has nobody to answer it and the dispatch hangs until it is killed. Invoke these as `command cp` / `command mv` / `command rm` — alias expansion is skipped and the tool behaves exactly as written. Do not reach for `-f` instead; it also changes what the tool does on failure (`rm -f` reports success on a path that never existed).
54
54
  - **No extra chaining beyond `cd && cmd`:** the permission matcher only allows the exact two-segment shape `cd <PATH> && <single-command>`. Do NOT append additional pipes, semicolons, redirects, or `&&` chains — e.g. `cd ... && cargo test ... 2>&1 | tail -20; echo "exit:$?"` will trigger a permission prompt every dispatch because the trailing `| tail`, `; echo`, and `2>&1` tokens disqualify the prefix match against `Bash(cargo:*)`. Let Claude Code capture the full stdout/stderr and exit code natively — do not post-process with `tail`, `head`, or `echo "exit:$?"`. If output truncation is genuinely needed, run the command first and read the result in a separate tool call.
@@ -61,7 +61,7 @@ from the provider name. `lead` is a compatibility alias for `leader`.
61
61
 
62
62
  Before producing any output, you MUST:
63
63
 
64
- 1. Extract `**Worker Preamble Path:**` and `**Worker Error Contract Path:**` from the lead prompt and Read both selected files end-to-end with one full-file `Read` each. The preamble owns audience procedure; the error contract owns sidecar schema and write rules. Never replace the selected path with a hard-coded analysis preamble.
64
+ 1. Extract `**Worker Preamble Path:**` and `**Worker Error Contract Path:**` from the lead prompt and Read both selected files end-to-end with one full-file `Read` each. The preamble owns audience procedure; the error contract owns typed error-log write rules. Never replace the selected path with a hard-coded analysis preamble.
65
65
  2. Read every primary input file the lead enumerated under `## Inputs` (or equivalent heading) end-to-end, following the selected preamble. Analysis workers normally receive `analysis-packet.md`; implementation workers receive their role sidecar and approved deliverable inputs.
66
66
  3. When the prompt carries `**Evidence ledger:** required-v1`, follow the selected preamble's `Evidence read ledger` procedure for every claim-evidence file you open. Do not invent a separate audit-row format here.
67
67
 
@@ -91,13 +91,13 @@ After your `Write` to the assigned worker-results file (path provided by Lead as
91
91
  The `**Model:**` line precedes whatever you return — analysis status above, or a convergence reverify verdict summary.
92
92
  2. Do NOT perform additional `Read`, `Grep`, `Glob`, MCP, or self-review tool calls after the file is written.
93
93
  3. Do NOT rewrite the worker-results file with `Write` more than once. If a correction is genuinely required, perform a single `Edit` and then return immediately.
94
- 4. The only exception is recording a `tool-failure` in the errors sidecar when a post-Write failure is itself the failure being reported — return immediately after that single sidecar append.
94
+ 4. The only exception is recording a `tool-failure` with the typed error-log command when a post-Write failure is itself the failure being reported — return immediately after that command.
95
95
 
96
96
  If you find yourself thinking "let me double-check section 3" or "I should read one more file to be safer" after the Write succeeded — stop. Convergence (Phase 5.5) and the Report writer worker (Phase 6) will reconcile gaps across all three workers; over-investing in single-worker depth at the expense of returning quickly is a net loss for the run.
97
97
 
98
98
  ## Error reporting
99
99
 
100
- Record your own tool failures per the file selected by `**Worker Error Contract Path:**`: extract `**Errors sidecar path:**` from the dispatch prompt (return `CLAUDE_WORKER_ERRORS_PATH_MISSING` without proceeding if absent), then append `tool-failure` entries exactly as that shared contract requires. This worker has no external CLI, so MCP and Bash failures use the same sidecar protocol.
100
+ Record your own tool failures per the file selected by `**Worker Error Contract Path:**`: extract `**Errors log path:**` from the dispatch prompt (return `CLAUDE_WORKER_ERRORS_PATH_MISSING` without proceeding if absent), then invoke its typed `okstra error-log append-observed` command. This worker has no external CLI, so MCP and Bash failures use the same typed protocol.
101
101
 
102
102
  ## Notes
103
103
 
@@ -14,8 +14,6 @@ Write only the report narrative Markdown at `**Result Path:**`, the pointer at `
14
14
 
15
15
  Read every dispatched input end-to-end. Full context is available to write the plan and explanation; reading an artifact does not grant authority to reproduce or repair its machine metadata.
16
16
 
17
- The analysis packet is the only MCP authority. If its MCP section is absent or says none, do not infer an MCP server from the task brief or another input.
18
-
19
17
  The narrative must not contain `designPreparation`, `designSurfaceCoverage`, `executionStatus`, `executionRoles`, `tokenUsage`, `crossVerification`, `approvalContext`, `clarificationItems`, `agentActivity`, or `planBodyVerification`. Do not invent a future round, gate result, activity identifier, resolution, or usage value.
20
18
 
21
19
  **Implementation-planning direction branch.** A selected-direction narrative carries `selectedDirectionRef` and `directionRealization`; `P-Dir-1` checks that realization against the selected core mechanism, architecture boundaries, planning invariants, and any hidden direction change. A legacy candidate-comparison narrative retains `P-Opt-*` option comparison semantics.
@@ -26,6 +24,8 @@ The narrative must not contain `designPreparation`, `designSurfaceCoverage`, `ex
26
24
  {"candidateDetailBoundary":{"expectedChangeAreas":"direction-level-only","expectedVerification":"direction-level-signals-only","forbidden":["exact-file-lists","stage-lists","test-commands"]}}
27
25
  ```
28
26
 
29
- Use `# OKSTRA Report Narrative`, nested `- **Field**` rows, `- Item N` array entries, and `> value` scalar lines. Do not write JSON, YAML, JSON Pointer, or fenced JSON.
27
+ The narrative is not free-form Markdown. After the line `# OKSTRA Report Narrative`, every line is one of `- **Field Name**`, `- Item <N>`, or `> value`; blank lines are ignored and **everything else is rejected** — headings (`#`, `##`, `###`), column-0 pipe tables, code fences, bare paragraphs, JSON, YAML, JSON Pointer. Put such text inside a `> ` value instead.
28
+
29
+ Only these top-level names are allowed: `Analysis Common`, `Change Impact Analysis`, `Error Analysis`, `Feature Analysis`, `Final Verdict`, `Final Verification`, `Follow Up Tasks`, `Human Summary`, `Implementation`, `Implementation Option Selection`, `Implementation Planning`, `Improvement Discovery`, `Project Analysis`, `Rationale`, `Recommended Next Steps`, `Release Handoff`, `Requirements Discovery`, `Summary`, `Ticket Coverage`, `Verdict Card`. A section title from a lead procedure document is not a field name. On a refusal, read the allowed names the parser lists for that position instead of guessing again.
30
30
 
31
31
  Report assembly validates every owner input and publishes the final record once. An assembly error naming another owner must be returned to that owner, not repaired in the narrative.
@@ -28,24 +28,16 @@ roster.
28
28
  2. Build your work list:
29
29
 
30
30
  ```bash
31
- okstra report-translate extract <data.json path>
31
+ okstra report-translate source --run-manifest <run-manifest>
32
32
  ```
33
33
 
34
- It writes `<stem>.translation-source.json`, a map of JSON Pointer to the English text at it. Those pointers are the whole job — you never invent one, and every value the renderer reads as machinery (ids, paths, commands, enum tokens, CSS-class values, verbatim quotes) has already been withheld from you.
35
- 3. Write the sidecar at `Result Path`, same shape, with each value translated:
34
+ The command prints a fixed `Source digest` followed by `T-NNN` sections containing only translatable English text.
35
+ 3. Write one translated Markdown section per item using the same `## T-NNN` headings, then publish it with `okstra report-translate write --run-manifest <run-manifest> --source-digest <source-digest> --translations <markdown path>`. Copy the digest from step 2. Python rejects a stale report and owns every pointer and the result path.
36
36
 
37
- ```json
38
- {
39
- "lang": "<language>",
40
- "sourceData": "final-report-<task-type>-<seq>.data.json",
41
- "strings": { "/humanSummary/headline": "…" }
42
- }
43
- ```
44
-
45
- 4. Verify before you return:
37
+ 4. Verify before you return with `okstra report-translate check-data --run-manifest <run-manifest>`:
46
38
 
47
39
  ```bash
48
- okstra report-translate check <Result Path>
40
+ okstra report-translate check-data --run-manifest <run-manifest>
49
41
  ```
50
42
 
51
43
  A non-zero exit means a pointer resolves nowhere — you altered or invented one. Fix it and re-run. Do not return on a failing check.
@@ -8,7 +8,6 @@ re-exports the writer's names so the contract tests keep reading one module.
8
8
  from __future__ import annotations
9
9
 
10
10
  import argparse
11
- import json
12
11
  from pathlib import Path
13
12
 
14
13
  from okstra_ctl.error_log_write import (
@@ -62,16 +61,24 @@ def _build_parser():
62
61
  obs.add_argument("--agent-role", required=True, choices=sorted(ALLOWED_AGENT_ROLES))
63
62
  obs.add_argument("--model", required=True)
64
63
  obs.add_argument("--error-type", required=True, choices=sorted(ALLOWED_ERROR_TYPES))
65
- obs.add_argument("--command", required=True)
64
+ command = obs.add_mutually_exclusive_group(required=True)
65
+ command.add_argument("--command")
66
+ command.add_argument("--command-file", type=Path)
66
67
  obs.add_argument("--command-kind", required=True)
67
68
  obs.add_argument("--exit-code", type=int, default=None)
68
69
  obs.add_argument("--duration-ms", type=int, default=None)
69
- obs.add_argument("--message", required=True)
70
+ message = obs.add_mutually_exclusive_group(required=True)
71
+ message.add_argument("--message")
72
+ message.add_argument("--message-file", type=Path)
70
73
  grp = obs.add_mutually_exclusive_group()
71
74
  grp.add_argument("--stderr-excerpt", default=None)
72
75
  grp.add_argument("--stderr-excerpt-file", default=None)
73
- obs.add_argument("--context-json", default=None,
74
- help="JSON object string for context")
76
+ obs.add_argument("--cause", choices=sorted(ALLOWED_CAUSES), default=None)
77
+ obs.add_argument(
78
+ "--evidence", action="append", default=[], metavar="<kind=value>",
79
+ help="cause evidence; repeat for targetProbe and controlProbe",
80
+ )
81
+ obs.add_argument("--evidence-file", action="append", default=[], metavar="<kind=file>")
75
82
  _add_identity_flags(obs)
76
83
 
77
84
  dump = sub.add_parser("append-from-worker",
@@ -115,15 +122,48 @@ def _read_stderr(args):
115
122
  return args.stderr_excerpt
116
123
 
117
124
 
125
+ def _read_message(args):
126
+ return args.message_file.read_text(encoding="utf-8").strip() if args.message_file else args.message
127
+
128
+
129
+ def _read_command(args):
130
+ return args.command_file.read_text(encoding="utf-8").strip() if args.command_file else args.command
131
+
132
+
118
133
  def _read_context(args):
119
- if args.context_json is None:
134
+ if args.cause is None and not args.evidence and not args.evidence_file:
120
135
  return None
121
- return json.loads(args.context_json)
136
+ if args.cause is None:
137
+ raise ValueError("--evidence requires --cause")
138
+ evidence: dict[str, str] = {}
139
+ evidence_files = [
140
+ f"{kind}={Path(filename).read_text(encoding='utf-8').strip()}"
141
+ for kind, _, filename in (raw.partition("=") for raw in args.evidence_file)
142
+ ]
143
+ for raw in [*args.evidence, *evidence_files]:
144
+ kind, separator, value = raw.partition("=")
145
+ if not separator or kind not in CAUSE_EVIDENCE_FIELDS or not value.strip():
146
+ raise ValueError(
147
+ "--evidence must be one of "
148
+ f"{', '.join(f'{field}=<value>' for field in CAUSE_EVIDENCE_FIELDS)}"
149
+ )
150
+ if kind in evidence:
151
+ raise ValueError(f"--evidence cannot repeat {kind}")
152
+ evidence[kind] = value.strip()
153
+ context: dict[str, object] = {"cause": args.cause}
154
+ if evidence:
155
+ context["causeEvidence"] = evidence
156
+ return context
122
157
 
123
158
 
124
159
  def main(argv=None):
125
- args = _build_parser().parse_args(argv)
160
+ parser = _build_parser()
161
+ args = parser.parse_args(argv)
126
162
  if args.cmd == "append-observed":
163
+ try:
164
+ context = _read_context(args)
165
+ except ValueError as exc:
166
+ parser.error(str(exc))
127
167
  append_observed(
128
168
  out_path=args.out,
129
169
  task_key=args.task_key,
@@ -132,13 +172,13 @@ def main(argv=None):
132
172
  agent_role=args.agent_role,
133
173
  model=args.model,
134
174
  error_type=args.error_type,
135
- command=args.command,
175
+ command=_read_command(args),
136
176
  command_kind=args.command_kind,
137
177
  exit_code=args.exit_code,
138
178
  duration_ms=args.duration_ms,
139
- message=args.message,
179
+ message=_read_message(args),
140
180
  stderr_excerpt=_read_stderr(args),
141
- context=_read_context(args),
181
+ context=context,
142
182
  identity=_identity_from_args(args),
143
183
  )
144
184
  return 0