okstra 0.179.2 → 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 (258) hide show
  1. package/README.md +1 -1
  2. package/dist/cli-registry.mjs +30 -2
  3. package/dist/cli-registry.mjs.map +1 -1
  4. package/dist/commands/execute/incremental-carry.mjs +9 -8
  5. package/dist/commands/execute/incremental-carry.mjs.map +1 -1
  6. package/dist/commands/execute/plan-verify.mjs +3 -1
  7. package/dist/commands/execute/plan-verify.mjs.map +1 -1
  8. package/dist/commands/execute/render-bundle.d.mts +4 -2
  9. package/dist/commands/execute/render-bundle.mjs +46 -5
  10. package/dist/commands/execute/render-bundle.mjs.map +1 -1
  11. package/dist/commands/execute/run.mjs +11 -3
  12. package/dist/commands/execute/run.mjs.map +1 -1
  13. package/dist/commands/inspect/model-io.d.mts +1 -0
  14. package/dist/commands/inspect/model-io.mjs +25 -0
  15. package/dist/commands/inspect/model-io.mjs.map +1 -0
  16. package/dist/commands/inspect/stage-map.mjs +29 -8
  17. package/dist/commands/inspect/stage-map.mjs.map +1 -1
  18. package/dist/commands/inspect/task-list.mjs +52 -6
  19. package/dist/commands/inspect/task-list.mjs.map +1 -1
  20. package/dist/commands/inspect/user-response.mjs +14 -4
  21. package/dist/commands/inspect/user-response.mjs.map +1 -1
  22. package/dist/commands/lifecycle/check-project.d.mts +1 -0
  23. package/dist/commands/lifecycle/check-project.mjs +69 -50
  24. package/dist/commands/lifecycle/check-project.mjs.map +1 -1
  25. package/dist/commands/lifecycle/contract-check.d.mts +1 -0
  26. package/dist/commands/lifecycle/contract-check.mjs +18 -0
  27. package/dist/commands/lifecycle/contract-check.mjs.map +1 -0
  28. package/dist/commands/lifecycle/preflight.mjs +154 -51
  29. package/dist/commands/lifecycle/preflight.mjs.map +1 -1
  30. package/dist/commands/pr/pr.d.mts +1 -0
  31. package/dist/commands/pr/pr.mjs +19 -1
  32. package/dist/commands/pr/pr.mjs.map +1 -1
  33. package/dist/commands/report/agent-activity.mjs +2 -2
  34. package/dist/commands/report/approval-decision.d.mts +1 -0
  35. package/dist/commands/report/approval-decision.mjs +21 -0
  36. package/dist/commands/report/approval-decision.mjs.map +1 -0
  37. package/dist/commands/report/design-snapshot.d.mts +1 -0
  38. package/dist/commands/report/design-snapshot.mjs +19 -0
  39. package/dist/commands/report/design-snapshot.mjs.map +1 -0
  40. package/dist/commands/report/translate.mjs +3 -0
  41. package/dist/commands/report/translate.mjs.map +1 -1
  42. package/dist/lib/host-registry-client.mjs +13 -9
  43. package/dist/lib/host-registry-client.mjs.map +1 -1
  44. package/docs/architecture/storage-model.md +1 -1
  45. package/docs/architecture.md +21 -10
  46. package/docs/cli.md +36 -20
  47. package/docs/container.md +6 -4
  48. package/docs/contributor-change-matrix.md +1 -1
  49. package/docs/for-ai/README.md +2 -2
  50. package/docs/for-ai/skills/okstra-brief-gen.md +5 -3
  51. package/docs/for-ai/skills/okstra-code-review.md +4 -4
  52. package/docs/for-ai/skills/okstra-container-build.md +20 -17
  53. package/docs/for-ai/skills/okstra-inspect.md +20 -23
  54. package/docs/for-ai/skills/okstra-manager.md +19 -18
  55. package/docs/for-ai/skills/okstra-memory.md +2 -2
  56. package/docs/for-ai/skills/okstra-pr-gen.md +3 -3
  57. package/docs/for-ai/skills/okstra-rollup.md +14 -13
  58. package/docs/for-ai/skills/okstra-run.md +7 -3
  59. package/docs/for-ai/skills/okstra-schedule-gen.md +15 -18
  60. package/docs/for-ai/skills/okstra-setup.md +7 -7
  61. package/docs/for-ai/skills/okstra-usage.md +5 -4
  62. package/docs/for-ai/skills/okstra-user-response.md +50 -32
  63. package/docs/project-structure-overview.md +43 -31
  64. package/docs/task-process/implementation-planning.md +2 -2
  65. package/docs/task-process/implementation.md +7 -4
  66. package/package.json +1 -1
  67. package/runtime/BUILD.json +2 -2
  68. package/runtime/agents/workers/claude-worker.md +4 -4
  69. package/runtime/agents/workers/report-writer-worker.md +15 -164
  70. package/runtime/agents/workers/translator-worker.md +5 -13
  71. package/runtime/bin/okstra-error-log.py +51 -11
  72. package/runtime/bin/okstra-report-translate.py +210 -23
  73. package/runtime/prompts/host-orchestration/implementation.md +1 -1
  74. package/runtime/prompts/launch.template.md +9 -12
  75. package/runtime/prompts/lead/adapters/cmux.md +1 -1
  76. package/runtime/prompts/lead/context-loader.md +41 -141
  77. package/runtime/prompts/lead/convergence.md +10 -8
  78. package/runtime/prompts/lead/okstra-lead-contract.md +40 -49
  79. package/runtime/prompts/lead/plan-body-verification.md +217 -23
  80. package/runtime/prompts/lead/report-writer.md +81 -421
  81. package/runtime/prompts/lead/team-contract.md +9 -54
  82. package/runtime/prompts/profiles/_clarification-recommendation.md +5 -4
  83. package/runtime/prompts/profiles/_coding-conventions-preflight.md +3 -2
  84. package/runtime/prompts/profiles/_common-contract.md +4 -4
  85. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  86. package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
  87. package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
  88. package/runtime/prompts/profiles/change-impact-analysis.md +1 -1
  89. package/runtime/prompts/profiles/error-analysis.md +1 -1
  90. package/runtime/prompts/profiles/feature-analysis.md +1 -1
  91. package/runtime/prompts/profiles/final-verification.md +1 -1
  92. package/runtime/prompts/profiles/implementation-planning.md +20 -12
  93. package/runtime/prompts/profiles/improvement-discovery.md +1 -1
  94. package/runtime/prompts/profiles/project-analysis.md +1 -1
  95. package/runtime/prompts/profiles/release-handoff.md +0 -1
  96. package/runtime/prompts/profiles/requirements-discovery.md +1 -1
  97. package/runtime/prompts/wizard/prompts.ko.json +0 -1
  98. package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +69 -17
  99. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +13 -4
  100. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +8 -2
  101. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
  102. package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +50 -5
  103. package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +1 -1
  104. package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
  105. package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +66 -5
  106. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +70 -2
  107. package/runtime/python/okstra_ctl/agent_activity.py +141 -38
  108. package/runtime/python/okstra_ctl/agent_invocation.py +19 -6
  109. package/runtime/python/okstra_ctl/agent_prompt_cli.py +70 -23
  110. package/runtime/python/okstra_ctl/analysis_inputs.py +5 -4
  111. package/runtime/python/okstra_ctl/analysis_packet.py +124 -3
  112. package/runtime/python/okstra_ctl/approval_decisions.py +328 -0
  113. package/runtime/python/okstra_ctl/attempt_evidence.py +2 -2
  114. package/runtime/python/okstra_ctl/backfill.py +13 -10
  115. package/runtime/python/okstra_ctl/batch.py +2 -4
  116. package/runtime/python/okstra_ctl/build_tools.py +6 -3
  117. package/runtime/python/okstra_ctl/claim_reproduction.py +101 -0
  118. package/runtime/python/okstra_ctl/clarification_items.py +27 -13
  119. package/runtime/python/okstra_ctl/cmux.py +130 -52
  120. package/runtime/python/okstra_ctl/code_review_target.py +34 -8
  121. package/runtime/python/okstra_ctl/consumers.py +5 -4
  122. package/runtime/python/okstra_ctl/container.py +103 -8
  123. package/runtime/python/okstra_ctl/context_cost.py +2 -1
  124. package/runtime/python/okstra_ctl/contract_graph.py +497 -0
  125. package/runtime/python/okstra_ctl/contract_graph_cli.py +62 -0
  126. package/runtime/python/okstra_ctl/convergence.py +338 -17
  127. package/runtime/python/okstra_ctl/convergence_engine.py +10 -18
  128. package/runtime/python/okstra_ctl/convergence_provenance.py +58 -8
  129. package/runtime/python/okstra_ctl/convergence_store.py +55 -34
  130. package/runtime/python/okstra_ctl/design_prep.py +7 -4
  131. package/runtime/python/okstra_ctl/design_snapshot.py +134 -0
  132. package/runtime/python/okstra_ctl/dispatch_core.py +97 -69
  133. package/runtime/python/okstra_ctl/dispatch_state.py +163 -63
  134. package/runtime/python/okstra_ctl/doctor.py +6 -3
  135. package/runtime/python/okstra_ctl/domain/worker_presentation.py +70 -9
  136. package/runtime/python/okstra_ctl/entrypoints/hosts.py +16 -30
  137. package/runtime/python/okstra_ctl/error_log_write.py +35 -30
  138. package/runtime/python/okstra_ctl/error_report.py +26 -1
  139. package/runtime/python/okstra_ctl/error_zip.py +27 -5
  140. package/runtime/python/okstra_ctl/execution_identity.py +3 -2
  141. package/runtime/python/okstra_ctl/execution_manifest.py +7 -4
  142. package/runtime/python/okstra_ctl/execution_mutation_audit.py +6 -2
  143. package/runtime/python/okstra_ctl/final_report_schema.py +26 -17
  144. package/runtime/python/okstra_ctl/fix_cycles.py +2 -2
  145. package/runtime/python/okstra_ctl/fixed_text.py +39 -0
  146. package/runtime/python/okstra_ctl/git_reconcile.py +41 -9
  147. package/runtime/python/okstra_ctl/handoff.py +5 -4
  148. package/runtime/python/okstra_ctl/i18n.py +4 -2
  149. package/runtime/python/okstra_ctl/implementation_direction.py +22 -14
  150. package/runtime/python/okstra_ctl/implementation_outcome.py +4 -7
  151. package/runtime/python/okstra_ctl/incremental_carry.py +130 -17
  152. package/runtime/python/okstra_ctl/incremental_scope.py +12 -4
  153. package/runtime/python/okstra_ctl/index.py +8 -11
  154. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +79 -7
  155. package/runtime/python/okstra_ctl/invocation.py +3 -6
  156. package/runtime/python/okstra_ctl/json_boundary.py +366 -0
  157. package/runtime/python/okstra_ctl/json_registry.py +10 -12
  158. package/runtime/python/okstra_ctl/jsonl.py +19 -2
  159. package/runtime/python/okstra_ctl/lead_events.py +33 -1
  160. package/runtime/python/okstra_ctl/listing.py +3 -3
  161. package/runtime/python/okstra_ctl/log_report.py +24 -2
  162. package/runtime/python/okstra_ctl/manager_cli.py +92 -7
  163. package/runtime/python/okstra_ctl/manager_store.py +12 -10
  164. package/runtime/python/okstra_ctl/material.py +5 -1
  165. package/runtime/python/okstra_ctl/migrate.py +29 -25
  166. package/runtime/python/okstra_ctl/model_cli.py +3 -15
  167. package/runtime/python/okstra_ctl/model_io_cli.py +1051 -0
  168. package/runtime/python/okstra_ctl/mutation_probe.py +13 -4
  169. package/runtime/python/okstra_ctl/pane_reclaim.py +3 -2
  170. package/runtime/python/okstra_ctl/path_hints.py +12 -0
  171. package/runtime/python/okstra_ctl/paths.py +21 -0
  172. package/runtime/python/okstra_ctl/plan_items.py +115 -4
  173. package/runtime/python/okstra_ctl/plan_items_cli.py +626 -34
  174. package/runtime/python/okstra_ctl/ports/worker_dispatch.py +2 -1
  175. package/runtime/python/okstra_ctl/pr_template.py +3 -2
  176. package/runtime/python/okstra_ctl/project_meta.py +5 -7
  177. package/runtime/python/okstra_ctl/recap.py +5 -4
  178. package/runtime/python/okstra_ctl/reconcile.py +21 -27
  179. package/runtime/python/okstra_ctl/registry/host_discovery.py +3 -2
  180. package/runtime/python/okstra_ctl/registry/provider_registry.py +3 -2
  181. package/runtime/python/okstra_ctl/render.py +74 -16
  182. package/runtime/python/okstra_ctl/render_final_report.py +10 -8
  183. package/runtime/python/okstra_ctl/report_assembly.py +509 -0
  184. package/runtime/python/okstra_ctl/report_contract.py +2 -1
  185. package/runtime/python/okstra_ctl/report_finalize.py +67 -32
  186. package/runtime/python/okstra_ctl/report_html/render.py +3 -2
  187. package/runtime/python/okstra_ctl/report_inputs.py +72 -0
  188. package/runtime/python/okstra_ctl/report_language.py +3 -2
  189. package/runtime/python/okstra_ctl/report_markdown.py +81 -8
  190. package/runtime/python/okstra_ctl/report_narrative.py +351 -0
  191. package/runtime/python/okstra_ctl/report_projections.py +265 -0
  192. package/runtime/python/okstra_ctl/report_synthesis_packet.py +518 -0
  193. package/runtime/python/okstra_ctl/report_views.py +3 -2
  194. package/runtime/python/okstra_ctl/rollup.py +65 -4
  195. package/runtime/python/okstra_ctl/run.py +184 -65
  196. package/runtime/python/okstra_ctl/run_audit.py +3 -2
  197. package/runtime/python/okstra_ctl/run_context.py +6 -9
  198. package/runtime/python/okstra_ctl/run_index_row.py +2 -8
  199. package/runtime/python/okstra_ctl/schedule_semantics.py +5 -2
  200. package/runtime/python/okstra_ctl/schema_excerpt.py +15 -8
  201. package/runtime/python/okstra_ctl/session_transcript.py +27 -1
  202. package/runtime/python/okstra_ctl/set_work_status.py +64 -38
  203. package/runtime/python/okstra_ctl/stage_fix_carry.py +8 -6
  204. package/runtime/python/okstra_ctl/stage_ledger.py +132 -18
  205. package/runtime/python/okstra_ctl/stage_map.py +96 -28
  206. package/runtime/python/okstra_ctl/stage_targets.py +3 -4
  207. package/runtime/python/okstra_ctl/team.py +3 -2
  208. package/runtime/python/okstra_ctl/team_reconcile.py +11 -2
  209. package/runtime/python/okstra_ctl/time_report.py +51 -4
  210. package/runtime/python/okstra_ctl/usage_identity.py +2 -1
  211. package/runtime/python/okstra_ctl/usage_report.py +58 -4
  212. package/runtime/python/okstra_ctl/user_response.py +1431 -66
  213. package/runtime/python/okstra_ctl/wizard.py +11 -44
  214. package/runtime/python/okstra_ctl/work_categories.py +3 -2
  215. package/runtime/python/okstra_ctl/worker_dispatch.py +5 -2
  216. package/runtime/python/okstra_ctl/worker_prompt_body.py +49 -3
  217. package/runtime/python/okstra_ctl/worker_prompt_contract.py +3 -2
  218. package/runtime/python/okstra_ctl/worker_prompt_policy.py +31 -3
  219. package/runtime/python/okstra_ctl/worker_runner.py +14 -12
  220. package/runtime/python/okstra_ctl/workflow.py +2 -1
  221. package/runtime/python/okstra_ctl/worktree.py +3 -2
  222. package/runtime/python/okstra_ctl/wrapper_status.py +4 -2
  223. package/runtime/python/okstra_ctl/write_policy.py +4 -2
  224. package/runtime/python/okstra_token_usage/antigravity.py +39 -12
  225. package/runtime/python/okstra_token_usage/collect.py +90 -38
  226. package/runtime/python/okstra_token_usage/grok.py +127 -0
  227. package/runtime/schemas/final-report-v2.0.schema.json +9 -0
  228. package/runtime/schemas/final-report-v3.0.schema.json +10219 -0
  229. package/runtime/schemas/report-narrative-v3.0.schema.json +30 -0
  230. package/runtime/schemas/report-synthesis-packet-v1.0.schema.json +140 -0
  231. package/runtime/skills/okstra-brief-gen/SKILL.md +9 -7
  232. package/runtime/skills/okstra-code-review/SKILL.md +21 -11
  233. package/runtime/skills/okstra-container-build/SKILL.md +18 -18
  234. package/runtime/skills/okstra-inspect/SKILL.md +12 -11
  235. package/runtime/skills/okstra-inspect/facets/error-zip.md +8 -8
  236. package/runtime/skills/okstra-inspect/facets/errors.md +2 -2
  237. package/runtime/skills/okstra-inspect/facets/history.md +9 -14
  238. package/runtime/skills/okstra-inspect/facets/logs.md +2 -2
  239. package/runtime/skills/okstra-inspect/facets/recap.md +5 -5
  240. package/runtime/skills/okstra-inspect/facets/report.md +6 -10
  241. package/runtime/skills/okstra-inspect/facets/status.md +9 -8
  242. package/runtime/skills/okstra-inspect/facets/time.md +3 -3
  243. package/runtime/skills/okstra-manager/SKILL.md +16 -14
  244. package/runtime/skills/okstra-memory/SKILL.md +3 -3
  245. package/runtime/skills/okstra-pr-gen/SKILL.md +5 -4
  246. package/runtime/skills/okstra-rollup/SKILL.md +6 -16
  247. package/runtime/skills/okstra-run/SKILL.md +7 -7
  248. package/runtime/skills/okstra-schedule-gen/SKILL.md +21 -17
  249. package/runtime/skills/okstra-setup/SKILL.md +21 -13
  250. package/runtime/skills/okstra-setup/references/project-config.md +2 -2
  251. package/runtime/skills/okstra-usage/SKILL.md +10 -10
  252. package/runtime/skills/okstra-user-response/SKILL.md +78 -107
  253. package/runtime/templates/report-writer-prompt-preamble.md +29 -19
  254. package/runtime/templates/reports/html/macros/forms.html +6 -4
  255. package/runtime/templates/reports/schedule.template.md +4 -4
  256. package/runtime/templates/worker-error-contract.md +17 -29
  257. package/runtime/validators/validate-run.py +636 -72
  258. package/runtime/validators/validate_session_conformance.py +43 -7
@@ -23,6 +23,7 @@ This script is the canonical single-reference-point. The Node CLI
23
23
  from __future__ import annotations
24
24
 
25
25
  import argparse
26
+ import hashlib
26
27
  import json
27
28
  import os
28
29
  import sys
@@ -57,18 +58,77 @@ from okstra_ctl.final_report_paths import ( # noqa: E402
57
58
  translation_sidecar_path,
58
59
  translation_source_path,
59
60
  )
61
+ from okstra_ctl.json_boundary import ( # noqa: E402
62
+ JsonBoundaryError,
63
+ load_owned_object,
64
+ load_owned_object_snapshot,
65
+ write_owned_object_atomic,
66
+ )
67
+ from okstra_ctl.fixed_text import line, scalar # noqa: E402
68
+ from okstra_ctl.convergence import ( # noqa: E402
69
+ ConvergenceContractError,
70
+ RunArtifactAuthority,
71
+ canonical_run_report_artifact,
72
+ validated_run_authority,
73
+ )
60
74
 
61
75
 
62
76
  def _load(path: Path) -> dict:
63
77
  try:
64
- payload = json.loads(path.read_text(encoding="utf-8"))
65
- except OSError as exc:
66
- raise SystemExit(f"error: cannot read {path}: {exc}") from exc
67
- except json.JSONDecodeError as exc:
68
- raise SystemExit(f"error: {path} is not valid JSON: {exc}") from exc
69
- if not isinstance(payload, dict):
70
- raise SystemExit(f"error: {path} must hold a JSON object")
71
- return payload
78
+ return load_owned_object(path, artifact="report translation artifact")
79
+ except JsonBoundaryError as exc:
80
+ raise SystemExit(f"error: {exc}") from exc
81
+
82
+
83
+ def _translation_authority(
84
+ manifest_path: Path,
85
+ ) -> tuple[RunArtifactAuthority, Path]:
86
+ try:
87
+ authority = validated_run_authority(manifest_path)
88
+ report = canonical_run_report_artifact(authority)
89
+ except (ConvergenceContractError, JsonBoundaryError) as exc:
90
+ raise SystemExit(f"error: {exc}") from exc
91
+ return authority, report
92
+
93
+
94
+ def _report_snapshot(path: Path):
95
+ try:
96
+ return load_owned_object_snapshot(
97
+ path, artifact="report translation source"
98
+ )
99
+ except JsonBoundaryError as exc:
100
+ raise SystemExit(f"error: {exc}") from exc
101
+
102
+
103
+ def _source_payload(
104
+ authority: RunArtifactAuthority, data_path: Path, raw_bytes: bytes, data: dict
105
+ ) -> dict:
106
+ return {
107
+ "taskKey": authority.task_key,
108
+ "runManifestPath": authority.manifest_ref,
109
+ "sourceData": data_path.name,
110
+ "sourceDataPath": data_path.relative_to(authority.project_root).as_posix(),
111
+ "sourceDataSha256": hashlib.sha256(raw_bytes).hexdigest(),
112
+ "lang": str((data.get("meta") or {}).get("reportLanguage") or ""),
113
+ "strings": extract(data),
114
+ }
115
+
116
+
117
+ def _validate_source_payload(
118
+ source: dict, expected: dict, supplied_digest: str
119
+ ) -> None:
120
+ for key in (
121
+ "taskKey", "runManifestPath", "sourceData", "sourceDataPath",
122
+ "sourceDataSha256", "lang", "strings",
123
+ ):
124
+ if source.get(key) != expected.get(key):
125
+ raise SystemExit(
126
+ "error: report changed after translation source publication"
127
+ )
128
+ if supplied_digest != expected["sourceDataSha256"]:
129
+ raise SystemExit(
130
+ "error: report changed after translation source publication"
131
+ )
72
132
 
73
133
 
74
134
  def _english_source_failure(data_path: Path, data: dict) -> str | None:
@@ -99,18 +159,10 @@ def cmd_extract(args: argparse.Namespace) -> int:
99
159
  strings = extract(data)
100
160
  out_path = translation_source_path(data_path)
101
161
  lang = str((data.get("meta") or {}).get("reportLanguage") or "")
102
- out_path.write_text(
103
- json.dumps(
104
- {
105
- "lang": lang,
106
- "sourceData": data_path.name,
107
- "strings": strings,
108
- },
109
- ensure_ascii=False,
110
- indent=2,
111
- )
112
- + "\n",
113
- encoding="utf-8",
162
+ write_owned_object_atomic(
163
+ out_path,
164
+ {"lang": lang, "sourceData": data_path.name, "strings": strings},
165
+ artifact="translation source",
114
166
  )
115
167
  print(
116
168
  json.dumps(
@@ -128,8 +180,130 @@ def cmd_extract(args: argparse.Namespace) -> int:
128
180
  return 0
129
181
 
130
182
 
183
+ def cmd_source(args: argparse.Namespace) -> int:
184
+ authority, data_path = _translation_authority(Path(args.run_manifest))
185
+ snapshot = _report_snapshot(data_path)
186
+ data = snapshot.value
187
+ failure = _english_source_failure(data_path, data)
188
+ if failure is not None:
189
+ sys.stderr.write(failure)
190
+ return 1
191
+ payload = _source_payload(authority, data_path, snapshot.raw_bytes, data)
192
+ out_path = translation_source_path(data_path)
193
+ if out_path.is_symlink():
194
+ raise SystemExit("error: translation source path is a symlink")
195
+ write_owned_object_atomic(
196
+ out_path, payload, artifact="translation source"
197
+ )
198
+ strings = payload["strings"]
199
+ print("# Translation work list")
200
+ print(line("Task key", authority.task_key), end="")
201
+ print(line("Report", payload["sourceDataPath"]), end="")
202
+ print(line("Run manifest", authority.manifest_ref), end="")
203
+ print(line("Source digest", payload["sourceDataSha256"]), end="")
204
+ print(line("String count", len(strings)), end="")
205
+ for index, source in enumerate(strings.values(), 1):
206
+ print(f"\n## T-{index:03d}\n{scalar(source)}")
207
+ return 0
208
+
209
+
210
+ def _translation_blocks(path: Path) -> list[str]:
211
+ text = path.read_text(encoding="utf-8")
212
+ blocks: list[str] = []
213
+ current: list[str] | None = None
214
+ for row in text.splitlines():
215
+ if row.startswith("## T-"):
216
+ if current is not None:
217
+ blocks.append("\n".join(current).strip())
218
+ expected = f"## T-{len(blocks) + 1:03d}"
219
+ if row != expected:
220
+ raise SystemExit(f"error: expected translation heading {expected}")
221
+ current = []
222
+ elif current is not None:
223
+ current.append(row)
224
+ if current is not None:
225
+ blocks.append("\n".join(current).strip())
226
+ return blocks
227
+
228
+
229
+ def cmd_write(args: argparse.Namespace) -> int:
230
+ authority, data_path = _translation_authority(Path(args.run_manifest))
231
+ snapshot = _report_snapshot(data_path)
232
+ data = snapshot.value
233
+ expected = _source_payload(
234
+ authority, data_path, snapshot.raw_bytes, data
235
+ )
236
+ source_path = translation_source_path(data_path)
237
+ if source_path.is_symlink():
238
+ raise SystemExit("error: translation source path is a symlink")
239
+ source = _load(source_path)
240
+ _validate_source_payload(source, expected, args.source_digest)
241
+ sources = expected["strings"]
242
+ translated = _translation_blocks(Path(args.translations))
243
+ if len(translated) != len(sources) or any(not value for value in translated):
244
+ raise SystemExit("error: translation blocks must match every T-NNN item")
245
+ lang = expected["lang"]
246
+ sidecar = translation_sidecar_path(data_path, lang)
247
+ if sidecar.is_symlink():
248
+ raise SystemExit("error: translation sidecar path is a symlink")
249
+ strings = dict(zip(sources, translated))
250
+ _, report = overlay(data, strings)
251
+ if report.unresolved or report.applied != len(strings):
252
+ raise SystemExit("error: translations do not validate against report source")
253
+ write_owned_object_atomic(
254
+ sidecar,
255
+ {
256
+ "taskKey": authority.task_key,
257
+ "runManifestPath": authority.manifest_ref,
258
+ "lang": lang,
259
+ "sourceData": data_path.name,
260
+ "sourceDataPath": expected["sourceDataPath"],
261
+ "sourceDataSha256": expected["sourceDataSha256"],
262
+ "strings": strings,
263
+ },
264
+ artifact="report translation sidecar",
265
+ )
266
+ print("Translation sidecar\n" + line("Status", "ready"), end="")
267
+ return 0
268
+
269
+
270
+ def cmd_check_data(args: argparse.Namespace) -> int:
271
+ authority, data_path = _translation_authority(Path(args.run_manifest))
272
+ snapshot = _report_snapshot(data_path)
273
+ expected = _source_payload(
274
+ authority, data_path, snapshot.raw_bytes, snapshot.value
275
+ )
276
+ sidecar_path = translation_sidecar_path(data_path, expected["lang"])
277
+ if sidecar_path.is_symlink():
278
+ raise SystemExit("error: translation sidecar path is a symlink")
279
+ sidecar = _load(sidecar_path)
280
+ _validate_authority_sidecar(sidecar, expected)
281
+ payload, code = _check_sidecar(sidecar_path)
282
+ print("Translation check\n" + line("Status", "ready" if payload["ok"] else "error")
283
+ + line("Applied strings", payload["applied"])
284
+ + line("Unresolved strings", len(payload["unresolved"])), end="")
285
+ return code
286
+
287
+
288
+ def _validate_authority_sidecar(sidecar: dict, expected: dict) -> None:
289
+ for key in (
290
+ "taskKey", "runManifestPath", "lang", "sourceData", "sourceDataPath",
291
+ "sourceDataSha256",
292
+ ):
293
+ if sidecar.get(key) != expected.get(key):
294
+ raise SystemExit(
295
+ "error: translation sidecar does not match run authority"
296
+ )
297
+
298
+
131
299
  def cmd_check(args: argparse.Namespace) -> int:
132
300
  sidecar_file = Path(args.sidecar).resolve()
301
+ payload, code = _check_sidecar(sidecar_file)
302
+ print(json.dumps(payload, ensure_ascii=False))
303
+ return code
304
+
305
+
306
+ def _check_sidecar(sidecar_file: Path) -> tuple[dict, int]:
133
307
  sidecar = _load(sidecar_file)
134
308
  strings = sidecar.get("strings")
135
309
  if not isinstance(strings, dict):
@@ -150,7 +324,6 @@ def cmd_check(args: argparse.Namespace) -> int:
150
324
  "untranslated": list(report.untranslated),
151
325
  "unresolved": list(report.unresolved),
152
326
  }
153
- print(json.dumps(payload, ensure_ascii=False))
154
327
  if report.unresolved:
155
328
  # A pointer that resolves nowhere means the sidecar was written against
156
329
  # a different report. Rendering it would silently drop those strings.
@@ -158,8 +331,8 @@ def cmd_check(args: argparse.Namespace) -> int:
158
331
  f"error: {len(report.unresolved)} pointer(s) do not resolve in "
159
332
  f"{data_path.name}\n"
160
333
  )
161
- return 1
162
- return 0
334
+ return payload, 1
335
+ return payload, 0
163
336
 
164
337
 
165
338
  def cmd_check_source(args: argparse.Namespace) -> int:
@@ -191,6 +364,20 @@ def _parser() -> argparse.ArgumentParser:
191
364
  extract_cmd.add_argument("data", help="path to final-report-<type>-<seq>.data.json")
192
365
  extract_cmd.set_defaults(func=cmd_extract)
193
366
 
367
+ source_view = sub.add_parser("source", help="render a fixed translation work list")
368
+ source_view.add_argument("--run-manifest", required=True)
369
+ source_view.set_defaults(func=cmd_source)
370
+
371
+ write_cmd = sub.add_parser("write", help="publish translations from T-NNN blocks")
372
+ write_cmd.add_argument("--run-manifest", required=True)
373
+ write_cmd.add_argument("--source-digest", required=True)
374
+ write_cmd.add_argument("--translations", required=True)
375
+ write_cmd.set_defaults(func=cmd_write)
376
+
377
+ check_data = sub.add_parser("check-data", help="verify the derived sidecar")
378
+ check_data.add_argument("--run-manifest", required=True)
379
+ check_data.set_defaults(func=cmd_check_data)
380
+
194
381
  check_cmd = sub.add_parser("check", help="verify a filled sidecar against its report")
195
382
  check_cmd.add_argument("sidecar", help="path to final-report-<type>-<seq>.i18n.<lang>.json")
196
383
  check_cmd.set_defaults(func=cmd_check)
@@ -41,7 +41,7 @@ If `render-bundle` stdout carries an `okstra concurrent-run stages: <stages>` la
41
41
 
42
42
  If `render-bundle` fails with a `PrepareError` containing `Recorded stage SHAs no longer match the git history`, the git history changed outside okstra (rebase / squash / review-feedback amend / branch deletion). Never fix the registry/consumers by hand; recover in this order:
43
43
 
44
- 1. Run the `okstra git-reconcile … --check --json` command printed in the error message verbatim to get the stale report. (Items whose content-identity is proven by patch-id were already auto-reconciled by prepare, so only confirm items remain here.)
44
+ 1. Run the `okstra git-reconcile … --check --text` command printed in the error message verbatim to get the stale report. (Items whose content-identity is proven by patch-id were already auto-reconciled by prepare, so only confirm items remain here.)
45
45
  2. For each confirm item, present a 3-option picker to the user:
46
46
  - **Re-record to the `stage-<N>` branch's current tip (recommended)** — when an intended change such as review feedback lives on that branch.
47
47
  - **Enter a different ref directly** — the user names a commit/branch/tag.
@@ -49,6 +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
+ - Plan-body verification state: `{{PLAN_BODY_VERIFICATION_STATE_RELATIVE_PATH}}`
52
53
  - Final report: `{{FINAL_REPORT_RECORD_RELATIVE_PATH}}`
53
54
  - Final status: `{{FINAL_STATUS_RELATIVE_PATH}}`
54
55
  - Validator: `{{RUN_VALIDATOR_RELATIVE_PATH}}`
@@ -57,12 +58,7 @@ For a new `implementation-planning` run, the plan-body sequence is initial verif
57
58
 
58
59
  - Run-level errors log (absolute): `{{RUN_ERRORS_LOG_PATH}}`
59
60
  - Run-level errors log (relative): `{{RUN_ERRORS_LOG_RELATIVE_PATH}}`
60
- - Worker error sidecars (absolute):
61
- - Claude worker: `{{CLAUDE_WORKER_ERRORS_SIDECAR_PATH}}`
62
- - Codex worker: `{{CODEX_WORKER_ERRORS_SIDECAR_PATH}}`
63
- - Antigravity worker: `{{ANTIGRAVITY_WORKER_ERRORS_SIDECAR_PATH}}`
64
- - Report writer worker: `{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_PATH}}`
65
- - The paths above are the values; the wiring duties — injecting the `**Errors log path:**` / `**Errors sidecar path:**` header pair into every dispatch prompt, and dumping each terminated worker's sidecar via `okstra error-log append-from-worker` — are defined once in the lifecycle core contract (`{{OKSTRA_LEAD_CONTRACT_PATH}}` "Errors log path wiring (BLOCKING)"). Follow that section; do not re-derive the rules from this block.
61
+ - The lead injects `**Errors log path:**` into every dispatch prompt. Workers use the typed `okstra error-log append-observed` command in the shared worker error contract; runtime-owned sidecar compatibility is not a worker instruction.
66
62
 
67
63
  ## Executor Worktree
68
64
 
@@ -114,7 +110,7 @@ The **default is full re-verification**. Only narrow this re-run to the impacted
114
110
  - Pass ONLY stage numbers that appear in that Stage Map when you do use `--impacted`. Never invent or guess a stage number — a number absent from the graph must never enter `--impacted` (it would leak into `reverify_stages`).
115
111
  2. **Resolve the two base SHAs (safety condition C1 — code-unchanged, decided by the CLI, not by you).**
116
112
  - Current base SHA: `{{EXECUTOR_WORKTREE_BASE_REF}}` (this run's resolved worktree base commit).
117
- - Prior base SHA: read the prior run's active-run-context at `runs/implementation-planning/state/active-run-context-implementation-planning-<prev-seq>.json`, field `executorWorktree.baseRef` (a resolved commit SHA). The run-manifest does NOT carry this field use the active-run-context. If that file or field cannot be located, treat the run as **full** and skip the rest of this procedure.
113
+ - Prior base SHA: run `okstra model-io active-context-input --project-root <project-root> --run-manifest <run-manifest>` and use its fixed `Executor base ref` field. Python resolves the prior active context only when it belongs to this project and run. If the command fails or the field is empty, treat the run as **full** and skip the rest of this procedure.
118
114
  3. **Call the CLI** (it is pure — same inputs always yield the same decision):
119
115
  ```
120
116
  okstra incremental-scope \
@@ -126,19 +122,20 @@ The **default is full re-verification**. Only narrow this re-run to the impacted
126
122
  --answered-clarifications <csv of answered C-NNN ids, empty when none> \
127
123
  --full-reason "<empty, or what structural change forces full>"
128
124
  ```
129
- The CLI reads the plan's dependency graph from the top-level `## 5.5 Stage Map` (`implementationPlanning.stageMap`), which is authoritative for the impacted stage numbers — there is no per-option stage graph. The CLI prints JSON `{mode, reverify_stages, carry_stages, reason}`. Instruct the report-writer to record this JSON verbatim into this run's data.json as `implementationPlanning.incrementalDecision` (keys `mode`, `reverifyStages`, `carryStages`, `reason`) — the renderer turns it into the `### 0.1 Incremental Re-Verification Scope` audit block, and the validator fails an `incremental`-mode run whose Section 0 omits that block.
125
+ The CLI reads the plan's dependency graph from the prior `implementationPlanning.stageMap`, which is authoritative for the impacted stage numbers. The CLI prints JSON `{mode, reverify_stages, carry_stages, reason}`. Instruct the report writer to record this decision in its narrative as `implementationPlanning.incrementalDecision`, using camel-case array keys `reverifyStages` and `carryStages`. Final report assembly preserves that writer-owned decision.
130
126
  4. **`mode == "full"`** → run the existing full re-verification path unchanged; ignore `reverify_stages` / `carry_stages`.
131
127
  5. **`mode == "incremental"`** → scope every worker dispatch prompt to `reverify_stages` only (the downstream closure of the impacted stages). Do NOT re-analyze `carry_stages` — their prior plan-item verdicts are carried forward verbatim (see `prompts/profiles/implementation-planning.md` "Cross-verification mode" and `prompts/lead/convergence.md` "Convergence scope").
132
- 6. **Merge carried-forward verdicts.** In `incremental` mode, after this run's report-writer authors its data.json, instruct it to merge the prior plan-item verdicts for `carry_stages` into it:
128
+ 6. **Merge carried-forward verdicts.** In `incremental` mode, the report writer includes every `carry_stages` stage row unchanged in its narrative. After `okstra plan-items seed --narrative ... --state ...`, the lead runs:
133
129
  ```
134
130
  okstra incremental-carry \
135
131
  --prev-data runs/implementation-planning/reports/final-report-implementation-planning-<prev-seq>.data.json \
136
- --cur-data <this run's data.json> \
132
+ --cur-narrative <this run's report-writer narrative> \
133
+ --state <this run's plan-body-verification state> \
137
134
  --prev-seq <prev-seq> \
138
135
  --carry-stages <csv from incrementalDecision.carry_stages> \
139
136
  --reverify-stages <csv from incrementalDecision.reverify_stages> \
140
- --out <this run's data.json>
137
+ --out-state <this run's plan-body-verification state>
141
138
  ```
142
- A non-zero exit (`CarryError` schema drift between the two runs) means the carry is unsafe: fall back to **full** — discard the incremental result and re-verify every stage. Note: `verdictCard` / `finalVerdict` are NEVER carried this run re-computes them from the re-verified plus carried plan items.
139
+ A non-zero exit means the writer changed or omitted a carried stage, the item set drifted, or the stage scopes conflict. Fall back to **full** and re-verify every stage. The command writes only the convergence-owned plan state. It never patches the writer narrative or final `data.json`. `verdictCard` / `finalVerdict` are never carried.
143
140
 
144
141
  **Carry completeness (BLOCKING).** In incremental mode, this run's `planItems` MUST contain every plan-item id from the re-verified stages, each carried forward with its updated verdict. If re-verification concludes a plan item should be REMOVED, that is a signal the answer's blast radius is NOT local — abandon incremental and re-route to a FULL re-verification. The carry merge only ever ADDS prior items whose id is absent from this run; it cannot distinguish a legitimate deletion from an untouched carry, so it would resurrect a stale verdict.
@@ -64,7 +64,7 @@ Use the screen to tell "still working" from "stuck", and to see at a glance whic
64
64
  - **v1 entry**: carries `workerId`, `provider`, `promptPath`, and `workerResultPath`, and must carry none of the v2 identity fields.
65
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
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.
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 its narrative Markdown, worker-result pointer, and audit sidecar; they do not include the Phase 7 report record.
68
68
  - After either dispatch, run `okstra team await --project-root <root> --run-manifest <path>` before evaluating terminal status or completion paths.
69
69
 
70
70
  ## Completion, cleanup, and resume
@@ -2,154 +2,54 @@
2
2
 
3
3
  ## When to Use
4
4
 
5
- - When starting okstra lead Phase 1 (Task-bundle intake)
6
- - When the user needs to know the okstra task bundle path
7
- - When you need to derive all artifact paths based on `task-manifest.json`
8
-
9
- ## Step 1: Resolve the Task Bundle Path
10
-
11
- (Resolve which task-root path to use; Step 2 opens `task-manifest.json` at that path.)
12
-
13
- ### Default Location Rules
14
-
15
- - AI documentation root: `.project-docs/`
16
- - Project-level latest-task pointer: `.okstra/discovery/latest-task.json`
17
- - Project-level task catalog: `.okstra/discovery/task-catalog.json`
18
- - okstra task root: `.okstra/tasks/`
19
- - Task path pattern: `.okstra/tasks/<task-group>/<task-id>/`
20
-
21
- ### Task Identification
22
- 1. If the user specifies the `task-manifest.json` path or the task root path, that path is used.
23
- 2. If the user specifies only the task key, the expected task root is calculated by converting the `task-group` and `task-id` to lowercase and applying the slug rule (`[^a-z0-9]+` → `-`), and the corresponding `task-manifest.json` is opened.
24
- 3. If the user attempts to find a task based on `task-group` + `task-id` or `task-id`, `.okstra/discovery/task-catalog.json` is read to find candidates.
25
- 4. If multiple candidates are found based on `task-id` alone, the situation is ambiguous, so `task-group` or the full `taskKey` is required.
26
- 5. If the user has not provided an explicit task key/path, first read `.okstra/discovery/latest-task.json` using the current-task convenience pointer.
27
- 6. If the latest-task pointer is missing or corrupted but the task catalog exists, list candidates from the catalog. Do not use the legacy `CLAUDE.md`, project guide, or task scan fallback.
28
- 7. If **neither** `latest-task.json` **nor** `task-catalog.json` exists, ABORT Phase 1 with `OKSTRA_CONTEXT_NOT_INITIALIZED`. Suggest the user run `/okstra-setup` and `/okstra-brief-gen` to bootstrap the project. Do NOT crawl `.okstra/tasks/` directly — discovery pointers are the only supported entry path.
29
-
30
- ## Step 2: Open and Parse task-manifest.json
31
-
32
- `task-manifest.json` (found at the task-root resolved in Step 1) is the canonical metadata source. Extract the following fields:
33
-
34
- | Field | Description |
35
- |------|------|
36
- | `taskKey` | `<project-id>:<task-group>:<task-id>` |
37
- | `projectId` | Project ID |
38
- | `taskGroup` | Task group |
39
- | `taskId` | Task ID |
40
- | `taskType` | Analysis type (requirements-discovery, error-analysis, implementation-option-selection, implementation-planning, implementation, final-verification, release-handoff, plus the sidetrack improvement-discovery) |
41
- | `workCategory` | bugfix / feature / improvement / refactor / ops / unknown |
42
- | `recommendedWorkers` | List of selected workers |
43
- | `currentStatus` | Current task status |
44
- | `workflow.phaseSequence` | Ordered lifecycle phases for the task |
45
- | `workflow.currentPhase` | Current lifecycle phase |
46
- | `workflow.currentPhaseState` | Current lifecycle phase state |
47
- | `workflow.phaseStates` | Phase-by-phase lifecycle state map |
48
- | `workflow.lastCompletedPhase` | Last completed lifecycle phase |
49
- | `workflow.nextRecommendedPhase` | Next-Phase Pointer — an object carrying the target phase, whether it can be started, and why. A projection of this report's Phase Routing; not authored here. Written per the Artifact Persistence Checklist in [report-writer](./report-writer.md); do not re-derive its rules here. |
50
- | `workflow.awaitingApproval` | Approval wait marker |
51
- | `workflow.lastSafeCheckpoint` | Safe resume checkpoint metadata |
52
- | `instructionSetPath` | Path to the `instruction-set/` **directory** containing `analysis-packet.md`, `analysis-profile.md`, `analysis-material.md`, `reference-expectations.md`, `task-brief.md`, `final-report-template.md`, and — only for task types whose host orchestration carries gates — `host-orchestration-rules.md` (see Step 4). Not a single-file path. |
53
- | `referenceExpectationsPath` | config/deployment expectation artifact path |
54
- | `latestRunPath` | latest run path |
55
- | `latestRunStatus` | latest run status |
56
- | `latestRunPromptsPath` | latest run prompt directory path |
57
- | `latestReportRecordPath` | latest report path |
58
- | `latestResumeCommandPath` | resume helper path |
59
- | `historyTimelinePath` | timeline path |
60
- | `resultContract` | team contract and expected artifact metadata |
61
- | `resultContract.requiredWorkerRoles[*].promptPath` | worker prompt history path by role |
62
- | `convergence` | convergence loop settings (`enabled`, `maxRounds`, `verificationMode`). See [convergence](./convergence.md) for the authoritative defaults — do not re-document the `maxRounds` value here. |
63
-
64
- ## Step 3: Directory Structure Rules
65
-
66
- After identifying the task root in `task-manifest.json`, derive all paths according to the following rules:
67
-
68
- ```
69
- <task-root>/
70
- ├── task-manifest.json (canonical metadata)
71
- ├── task-index.md (human-readable summary, non-canonical)
72
- ├── instruction-set/
73
- │ ├── analysis-profile.md (analysis guide by task type)
74
- │ ├── analysis-packet.md (primary compact input for analysis workers)
75
- │ ├── analysis-material.md (analysis materials)
76
- │ ├── reference-expectations.md (config/deployment expected values)
77
- │ ├── task-brief.md (task brief)
78
- │ ├── final-report-template.md (Final Report Template)
79
- │ └── host-orchestration-rules.md (host gate rules; present only for task types whose host orchestration carries gates)
80
- ├── runs/
81
- │ └── <task-type>/ (Run scope isolated per task-type, e.g. error-analysis)
82
- │ ├── manifests/ (run-manifest-<task-type>-<seq>.json)
83
- │ ├── state/ (team-state-<task-type>-<seq>.json, convergence-<task-type>-<seq>.json)
84
- │ ├── prompts/ (run prompt path and worker prompt history paths recorded in the manifest)
85
- │ ├── reports/ (final-report-<task-type>-<seq>.data.json + .html;
86
- │ the .md reading copy is rendered on demand)
87
- │ ├── status/ (final-<task-type>-<seq>.status)
88
- │ ├── sessions/ (runtime-specific resume artifacts when the selected adapter supports them)
89
- │ ├── logs/ (errors-<task-type>-<seq>.jsonl, optional)
90
- │ └── worker-results/
91
- │ ├── claude-worker-<task-type>-<seq>.md
92
- │ ├── codex-worker-<task-type>-<seq>.md
93
- │ ├── antigravity-worker-<task-type>-<seq>.md
94
- │ └── report-writer-worker-<task-type>-<seq>.md
95
- └── history/
96
- └── timeline.json
97
- ```
98
-
99
- ### File Naming Conventions
100
-
101
- - Run-level artifact files use the `-<task-type>-<seq>` suffix, where `<seq>` is a 3-digit zero-padded sequence number (`001`, `002`, …) scanned **per-category directory** (`manifests/`, `prompts/`, `reports/`, `status/`, `state/`, `sessions/`, `worker-results/`). Re-runs of the same task-type never overwrite each other's artifacts.
102
- - Because each category directory has its own counter, the `<seq>` for `manifests/` may differ from the `<seq>` for `reports/` if a previous run only wrote some categories. The shared run identifier across categories is the manifest `runDateTimeSegment` field (still ISO timestamp), not a filename infix.
103
- - Worker result files use the same `-<task-type>-<seq>` filename suffix as other run-level artifacts (counter scoped to `worker-results/`).
104
- - Worker prompt history files use role + suffix names such as `claude-worker-prompt-<task-type>-<seq>.md`.
105
- - A single `runs/<task-type>/` directory is reused for every run of that task type; the per-run separation comes entirely from the per-category sequence-number infix inside each typed subdirectory.
106
-
107
- ### Finding the Latest Run
108
-
109
- 1. If a `latest-task` pointer exists, prioritize the path to the latest run, latest run manifest, or latest team-state recorded there.
110
- 2. If entering via an explicit task key/path, locate the current run based on the latest run metadata in `task-manifest.json`.
111
- 3. Only if necessary, directly check `manifests/`, `state/`, `prompts/`, `reports/`, and `sessions/` within the relevant run directory.
112
-
113
- ## Step 4: Instruction Set Reading Order
114
-
115
- After verifying `task-manifest.json`, read only the compact intake files needed for the current action. Do not bulk-read the whole instruction-set directory.
116
-
117
- 1. `instruction-set/analysis-profile.md` (analysis guide by task type)
118
- 2. `instruction-set/analysis-packet.md` (primary compact input for analysis workers)
119
- 3. `runs/<task-type>/state/active-run-context-<task-type>-<seq>.json` if present (compact current-run path/worker snapshot)
120
- 4. `instruction-set/host-orchestration-rules.md` when the launch prompt's Host Orchestration Rules path is set. Read it here even though no gate has fired yet — the gates it governs fire around `render-bundle`, so waiting for one means reading it too late, and this is the copy that survives compaction. **Enforced:** Phase 7 conformance fails an `implementation` run whose evidence source records no read of it.
121
-
122
- Read source files lazily:
123
-
124
- - `instruction-set/task-brief.md` only for reporter-confirmation checks, source verification, or report-writer synthesis.
125
- - `instruction-set/analysis-material.md` only when packet content is insufficient or a source citation needs verification.
126
- - `instruction-set/reference-expectations.md` for report-writer synthesis or when packet expectation extract is insufficient.
127
- - `instruction-set/final-report-template.md` only for report-writer authoring.
128
- - `instruction-set/host-orchestration-rules.md` — already read at Step 4; re-read it whenever you are about to act on a host gate — the conformance waiver offer, the concurrent-run branch, stale-SHA recovery, the chaining queue — and again after any compaction. Absent file means this task type carries no host gates.
5
+ - When starting okstra lead Phase 1 task-bundle intake.
6
+ - When the user needs the task bundle or current run paths.
7
+
8
+ ## JSON Boundary
9
+
10
+ Do not open, parse, or infer Okstra-owned task, run, discovery, or active-context JSON. This contract uses the fixed text views below before the lead contract is loaded, so it cannot bypass that boundary.
11
+
12
+ - `okstra model-io project-context --project-root <project-root> --task-ref <task-ref>` provides project identity and the pointer for one explicit task reference. `task-ref` accepts a bare task ID, full task key, or that task's manifest path.
13
+ - `okstra model-io run-input --run-manifest <run-manifest-path>` provides the current run identity, worker roster, model assignments, and permitted artifact paths.
14
+
15
+ ## Step 1: Resolve the Task and Run Paths
16
+
17
+ 1. Determine `<project-root>` from the user request or the current project.
18
+ 2. If the user supplies a task ID, full task key, or task-manifest path, run `okstra model-io project-context --project-root <project-root> --task-ref <task-ref>`. Otherwise run `okstra model-io project-context --project-root <project-root>`.
19
+ 3. If the supplied bare task ID is ambiguous, require the full task key or task-manifest path.
20
+ 4. Use the `Task key`, `Task manifest`, and `Latest run manifest` labels from the same Project Context result.
21
+ 5. Use `Latest run manifest` as `<run-manifest-path>`. If it is `-`, request an explicit run-manifest path from the launch context or stop with `OKSTRA_CONTEXT_NOT_INITIALIZED`. Do not scan task directories or reconstruct a run path.
22
+ 6. Run `okstra model-io run-input --run-manifest <run-manifest-path>`.
23
+
24
+ ## Step 2: Read the Compact Intake Files
25
+
26
+ Use the `Instruction set` and `Reference expectations` labels in Run Input as paths for Markdown resources. Read only the resources needed for the current action.
27
+
28
+ 1. `<instruction-set>/analysis-profile.md` for the task-type rules and required worker block.
29
+ 2. `<instruction-set>/analysis-packet.md` for the Phase 1 compact input.
30
+ 3. `<instruction-set>/host-orchestration-rules.md` when the launch prompt supplies that path.
31
+
32
+ Read `task-brief.md`, `analysis-material.md`, `reference-expectations.md`, and `final-report-template.md` only at their phase-specific need. The report writer owns the final-report template.
129
33
 
130
34
  ### Brief Reporter-Confirmation Precondition (BLOCKING)
131
35
 
132
- After reading `task-brief.md`, extract the frontmatter `reporter-confirmations` field (`complete | partial | pending | skipped`) when present and apply the shared handling matrix in `prompts/profiles/_common-contract.md` "Brief handoff contract" → "Reporter confirmation precondition" that block is the single authority on per-value semantics; do not re-derive them here.
36
+ After reading `task-brief.md`, extract the frontmatter `reporter-confirmations` field when present and apply the shared handling matrix in `prompts/profiles/_common-contract.md` "Brief handoff contract" → "Reporter confirmation precondition". That block is the authority for the value semantics.
133
37
 
134
- Loader-level defensive flow control only: on `pending`, emit `REPORTER_CONFIRMATION_PENDING` and STOP do not invoke `team-contract` or any analyser, and do not write a final report. Regenerate the brief with `okstra-brief-gen` Step 6.5 and prepare a fresh run. A missing field is a legacy brief, not `pending`, and proceeds to Step 5. Current-format missing or invalid values are rejected during preparation, before a fresh run can reach this loader. Every other value proceeds to Step 5 with the matrix's flags carried forward for the phase profile.
38
+ On `pending`, emit `REPORTER_CONFIRMATION_PENDING` and stop. Do not invoke `team-contract` or an analyser, and do not write a final report. Regenerate the brief with `okstra-brief-gen` Step 6.5 and prepare a fresh run. A missing field is a legacy brief and proceeds with the matrix's carried flags. Current-format invalid values are rejected during preparation.
135
39
 
136
- ## Step 5: Read Run Manifest and Team State
40
+ ## Step 3: Use Run Input as the Run-State View
137
41
 
138
- 1. Identify the active run by reading `runDateTimeSegment` from the latest `runs/<task-type>/manifests/run-manifest-*.json` (mtime order). That segment is the shared run identifier across all category subdirectories (`state/`, `prompts/`, `reports/`, `status/`, `sessions/`, `worker-results/`).
139
- 2. Resolve sibling artifacts for this run by matching the same `runDateTimeSegment`. Do NOT re-scan `<seq>` counters per category — they may diverge if an earlier run only wrote some categories.
140
- 3. Current team state: the team-state file whose `runDateTimeSegment` matches the active run manifest.
141
- 4. Extract the worker prompt directory path and per-worker prompt history paths from the current run manifest and team-state.
142
- 5. If an existing run report is available, use it solely as historical context.
42
+ Run Input is the only Phase 1 source for task identity, work category, workflow state, selected workers, model assignments, worker prompt paths, result paths, validator path, resume command, and configuration references.
43
+
44
+ Use the `Worker Roster` section for dispatch and model assignment. Use `Artifact Paths` for prompt, result, report, status, validator, and resume locations. Use `Configuration References` for the task-level Markdown resources. Do not derive sibling paths from file names or sequence counters.
45
+
46
+ Use the `Current phase` labels as the workflow view. Rules for `nextRecommendedPhase` are maintained by [report-writer](./report-writer.md); do not re-derive phase routing here.
143
47
 
144
48
  ## Output
145
49
 
146
50
  Information produced after completing this contract:
147
- - task key, task type, work category, workflow lifecycle snapshot, analysis profile
148
- - List of selected workers and model assignments by role
149
- - Absolute/relative paths of all artifacts (including latest-task pointer, task catalog, prompt directory, per-worker prompt history files, resume command, and timeline)
150
- - Reference list of config files/deployment manifests and task-level expected values
151
- - Current run status and presence of existing worker results
152
- - Current run prompt history contract for attempted workers
153
- - Candidate `teamName` for the manifest/team-state audit identity: `okstra-<task-key>` (with task-key slugified per Step 1's slug rule); implementation stage runs append `-s<N>`. The selected runtime adapter owns whether an adapter-specific audit label is exposed in the launch prompt's "Runtime Adapter Dispatch Gate (BLOCKING)" setup facts; this common contract does not guarantee that label for every runtime.
154
- - Current lead session/checkpoint identity, when supplied by the selected adapter
155
- - Resume artifact path from `task-manifest.json.artifacts.resumeCommandPath`; never reconstruct a runtime-specific filename
51
+
52
+ - Project ID, task key, task type, work category, and workflow snapshot.
53
+ - Selected workers and assigned models.
54
+ - Prompt, result, report, status, validator, resume, and configuration-reference paths.
55
+ - The compact Markdown intake paths required for Phase 1.