okstra 0.128.0 → 0.129.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/docs/architecture/storage-model.md +15 -2
  2. package/docs/architecture.md +19 -3
  3. package/docs/cli.md +8 -0
  4. package/docs/project-structure-overview.md +14 -5
  5. package/package.json +1 -1
  6. package/runtime/BUILD.json +2 -2
  7. package/runtime/agents/workers/antigravity-worker.md +3 -3
  8. package/runtime/agents/workers/claude-worker.md +4 -4
  9. package/runtime/agents/workers/codex-worker.md +3 -3
  10. package/runtime/agents/workers/report-writer-worker.md +5 -5
  11. package/runtime/prompts/lead/adapters/claude-code.md +2 -1
  12. package/runtime/prompts/lead/adapters/codex.md +1 -0
  13. package/runtime/prompts/lead/adapters/external.md +1 -0
  14. package/runtime/prompts/lead/convergence.md +42 -84
  15. package/runtime/prompts/lead/okstra-lead-contract.md +10 -8
  16. package/runtime/prompts/lead/report-writer.md +3 -2
  17. package/runtime/prompts/lead/team-contract.md +11 -9
  18. package/runtime/prompts/profiles/_common-contract.md +1 -1
  19. package/runtime/prompts/profiles/error-analysis.md +1 -1
  20. package/runtime/prompts/profiles/implementation-planning.md +5 -0
  21. package/runtime/prompts/profiles/improvement-discovery.md +11 -1
  22. package/runtime/prompts/profiles/requirements-discovery.md +8 -1
  23. package/runtime/python/okstra_ctl/analysis_packet.py +7 -13
  24. package/runtime/python/okstra_ctl/codex_dispatch.py +42 -97
  25. package/runtime/python/okstra_ctl/context_cost.py +82 -7
  26. package/runtime/python/okstra_ctl/convergence.py +223 -0
  27. package/runtime/python/okstra_ctl/convergence_engine.py +1152 -0
  28. package/runtime/python/okstra_ctl/convergence_migration.py +184 -0
  29. package/runtime/python/okstra_ctl/convergence_store.py +85 -0
  30. package/runtime/python/okstra_ctl/dispatch_core.py +53 -14
  31. package/runtime/python/okstra_ctl/improvement_assignment.py +61 -0
  32. package/runtime/python/okstra_ctl/path_hints.py +23 -1
  33. package/runtime/python/okstra_ctl/paths.py +10 -1
  34. package/runtime/python/okstra_ctl/render.py +56 -11
  35. package/runtime/python/okstra_ctl/worker_prompt_body.py +107 -0
  36. package/runtime/python/okstra_ctl/worker_prompt_contract.py +102 -9
  37. package/runtime/python/okstra_ctl/worker_prompt_headers.py +79 -9
  38. package/runtime/python/okstra_ctl/worker_prompt_policy.py +158 -0
  39. package/runtime/templates/implementation-worker-preamble.md +51 -0
  40. package/runtime/templates/operating-standard.md +4 -4
  41. package/runtime/templates/report-writer-prompt-preamble.md +37 -0
  42. package/runtime/templates/worker-error-contract.md +46 -0
  43. package/runtime/templates/worker-prompt-preamble.md +28 -234
  44. package/runtime/validators/lib/fixtures.sh +27 -12
  45. package/runtime/validators/validate-run.py +201 -72
  46. package/src/cli-registry.mjs +7 -0
  47. package/src/commands/execute/convergence.mjs +34 -0
@@ -0,0 +1,107 @@
1
+ """Shared initial analysis-worker prompt body rendering."""
2
+ from __future__ import annotations
3
+
4
+ from typing import Any, Mapping, Sequence
5
+
6
+ from .worker_prompt_policy import PromptPlan
7
+
8
+
9
+ ANALYSIS_WORKER_LABELS = {
10
+ "claude": "Claude worker",
11
+ "codex": "Codex worker",
12
+ "antigravity": "Antigravity worker",
13
+ }
14
+
15
+
16
+ def analysis_prompt_body(
17
+ manifest: Mapping[str, Any],
18
+ active_context: Mapping[str, Any],
19
+ worker_id: str,
20
+ model: str,
21
+ role: str,
22
+ plan: PromptPlan,
23
+ ) -> list[str]:
24
+ """Render the provider-neutral body for an initial analysis audience."""
25
+ label = ANALYSIS_WORKER_LABELS.get(worker_id, f"{worker_id} worker")
26
+ return [
27
+ f"**Model:** {label}, {model}",
28
+ f"**Pane role:** {role}",
29
+ "",
30
+ f"# {label} Dispatch",
31
+ "",
32
+ "## Role",
33
+ (
34
+ f"You are the {label} for okstra cross-verification. "
35
+ "Produce an independent worker result."
36
+ ),
37
+ "",
38
+ "## Task",
39
+ f"- Task key: `{_require_string(manifest, 'taskKey')}`",
40
+ f"- Task type: `{_require_string(manifest, 'taskType')}`",
41
+ "",
42
+ "## Inputs",
43
+ *analysis_input_lines(manifest, active_context, plan),
44
+ "",
45
+ mcp_pointer_line(),
46
+ "",
47
+ "## Output Contract",
48
+ "- Read the Worker Preamble Path end-to-end before analysis.",
49
+ "- Write the worker result to Result Path and no other canonical result path.",
50
+ "- Write the audit sidecar and any tool-failure entries as the preamble requires.",
51
+ "- Cite evidence with file paths and line numbers whenever you make a claim.",
52
+ ]
53
+
54
+
55
+ def analysis_input_lines(
56
+ manifest: Mapping[str, Any],
57
+ active_context: Mapping[str, Any],
58
+ plan: PromptPlan,
59
+ ) -> list[str]:
60
+ if plan.packet_only:
61
+ packet_path = instruction_path(manifest, active_context, "analysisPacketPath")
62
+ return existing_input_lines((("Primary analysis packet", packet_path),))
63
+ inputs = [
64
+ ("Primary analysis packet", instruction_path(manifest, active_context, "analysisPacketPath")),
65
+ ("Task brief", instruction_path(manifest, active_context, "taskBriefPath")),
66
+ ("Analysis profile", instruction_path(manifest, active_context, "analysisProfilePath")),
67
+ ("Analysis material", instruction_path(manifest, active_context, "analysisMaterialPath")),
68
+ ("Reference expectations", instruction_path(manifest, active_context, "referenceExpectationsPath")),
69
+ ("Clarification response", instruction_path(manifest, active_context, "clarificationResponsePath")),
70
+ ]
71
+ return existing_input_lines(inputs)
72
+
73
+
74
+ def existing_input_lines(inputs: Sequence[tuple[str, str]]) -> list[str]:
75
+ lines = [f"- {label}: `{path}`" for label, path in inputs if path]
76
+ return lines or ["- No input paths were recorded in active-run-context."]
77
+
78
+
79
+ def instruction_path(
80
+ manifest: Mapping[str, Any],
81
+ active_context: Mapping[str, Any],
82
+ key: str,
83
+ ) -> str:
84
+ instruction_set = active_context.get("instructionSet")
85
+ if isinstance(instruction_set, Mapping):
86
+ value = _string_value(instruction_set.get(key))
87
+ if value:
88
+ return value
89
+ return _string_value(manifest.get(key))
90
+
91
+
92
+ def mcp_pointer_line() -> str:
93
+ return (
94
+ '**MCP servers:** follow the task brief\'s "## Available MCP Servers" '
95
+ "section (already in your Required reading)."
96
+ )
97
+
98
+
99
+ def _require_string(payload: Mapping[str, Any], key: str) -> str:
100
+ value = _string_value(payload.get(key))
101
+ if not value:
102
+ raise ValueError(f"missing required string field: {key}")
103
+ return value
104
+
105
+
106
+ def _string_value(value: Any) -> str:
107
+ return value.strip() if isinstance(value, str) else ""
@@ -2,14 +2,18 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import re
5
+ from dataclasses import dataclass
5
6
  from pathlib import Path
6
- from typing import Mapping
7
+ from typing import Any, Mapping, Sequence
8
+
9
+ from .worker_prompt_policy import PromptPlan, resolve_prompt_plan_for_manifest
7
10
 
8
11
 
9
12
  MAX_FINAL_VERIFICATION_DIRECTIVE_LINES = 40
10
13
  MAX_FINAL_VERIFICATION_BODY_LINES = 96
11
14
 
12
15
  _DIRECTIVE_HEADING = "## Run-specific directive"
16
+ _WORKER_ERROR_CONTRACT_HEADER = "**Worker Error Contract Path:**"
13
17
  _PRIMARY_PACKET_RE = re.compile(
14
18
  r"(?im)^-\s+Primary analysis packet:\s+`[^`\n]*analysis-packet\.md`\s*$"
15
19
  )
@@ -63,6 +67,13 @@ _WORKER_LABEL_RE = re.compile(
63
67
  )
64
68
 
65
69
 
70
+ @dataclass(frozen=True)
71
+ class PromptRecord:
72
+ worker_id: str
73
+ dispatch_kind: str
74
+ path: Path
75
+
76
+
66
77
  def validate_final_verification_initial_prompt(text: str) -> list[str]:
67
78
  """Return deterministic compact-prompt contract violations."""
68
79
  errors: list[str] = []
@@ -155,23 +166,105 @@ def validate_final_verification_prompt_paths(
155
166
  prompt_paths: Mapping[str, Path],
156
167
  ) -> list[str]:
157
168
  """Validate persisted initial prompts and their normalized equality."""
158
- prompts: dict[str, str] = {}
169
+ records = [
170
+ PromptRecord(worker_id, "initial", path)
171
+ for worker_id, path in sorted(prompt_paths.items())
172
+ ]
173
+ return validate_initial_prompt_records(
174
+ manifest={"taskType": "final-verification"},
175
+ records=records,
176
+ )
177
+
178
+
179
+ def validate_initial_prompt_records(
180
+ *,
181
+ manifest: Mapping[str, Any],
182
+ records: Sequence[PromptRecord],
183
+ ) -> list[str]:
184
+ """Validate prompt audiences and compare their normalized equality groups."""
159
185
  errors: list[str] = []
160
- for worker_id, path in sorted(prompt_paths.items()):
186
+ equality_groups: dict[str, dict[str, str]] = {}
187
+ for record in records:
188
+ plan = _resolve_record_plan(manifest, record, errors)
189
+ if plan is None or plan.audience in {"lead-only", "reverify"}:
190
+ continue
161
191
  try:
162
- text = path.read_text(encoding="utf-8")
192
+ text = record.path.read_text(encoding="utf-8")
163
193
  except OSError as exc:
164
- errors.append(f"{worker_id}: cannot read prompt {path}: {exc}")
194
+ errors.append(
195
+ f"{record.worker_id}: cannot read prompt {record.path}: {exc}"
196
+ )
165
197
  continue
166
- prompts[worker_id] = text
167
198
  errors.extend(
168
- f"{worker_id}: {error}"
169
- for error in validate_final_verification_initial_prompt(text)
199
+ f"{record.worker_id}: {error}"
200
+ for error in _validate_prompt_for_plan(text, plan, manifest)
170
201
  )
171
- errors.extend(validate_analysis_prompt_set(prompts))
202
+ if plan.equality_group:
203
+ group = equality_groups.setdefault(plan.equality_group, {})
204
+ group[record.worker_id] = text
205
+ for prompts in equality_groups.values():
206
+ errors.extend(validate_analysis_prompt_set(prompts))
172
207
  return errors
173
208
 
174
209
 
210
+ def _resolve_record_plan(
211
+ manifest: Mapping[str, Any],
212
+ record: PromptRecord,
213
+ errors: list[str],
214
+ ) -> PromptPlan | None:
215
+ try:
216
+ return resolve_prompt_plan_for_manifest(
217
+ manifest=manifest,
218
+ worker_id=record.worker_id,
219
+ dispatch_kind=record.dispatch_kind,
220
+ )
221
+ except ValueError as exc:
222
+ errors.append(f"{record.worker_id}: {exc}")
223
+ return None
224
+
225
+
226
+ def _validate_prompt_for_plan(
227
+ text: str,
228
+ plan: PromptPlan,
229
+ manifest: Mapping[str, Any],
230
+ ) -> list[str]:
231
+ errors: list[str] = []
232
+ _require_non_empty_header(text, _WORKER_ERROR_CONTRACT_HEADER, errors)
233
+ if manifest.get("taskType") == "final-verification" and plan.audience == "analysis":
234
+ return [*errors, *validate_final_verification_initial_prompt(text)]
235
+ if not plan.allow_coding_preflight:
236
+ _reject_literal(
237
+ text,
238
+ "**Coding preflight pack:**",
239
+ "Coding preflight pack is forbidden for this prompt audience",
240
+ errors,
241
+ )
242
+ for prefix in plan.required_headers:
243
+ _require_non_empty_header(text, prefix, errors)
244
+ if plan.audience == "analysis":
245
+ packet_count = len(_PRIMARY_PACKET_RE.findall(text))
246
+ if packet_count != 1:
247
+ errors.append(
248
+ "exactly one Primary analysis packet path is required "
249
+ f"(found {packet_count})"
250
+ )
251
+ return errors
252
+
253
+
254
+ def _require_non_empty_header(
255
+ text: str,
256
+ prefix: str,
257
+ errors: list[str],
258
+ ) -> None:
259
+ matches = [
260
+ line.strip()
261
+ for line in text.splitlines()
262
+ if line.strip().startswith(prefix)
263
+ ]
264
+ if len(matches) != 1 or not matches[0][len(prefix):].strip():
265
+ errors.append(f"exactly one non-empty {prefix} header is required")
266
+
267
+
175
268
  def _reject_literal(
176
269
  text: str,
177
270
  literal: str,
@@ -5,6 +5,12 @@ from pathlib import Path
5
5
  from typing import Any, Mapping
6
6
 
7
7
  from .paths import okstra_home
8
+ from .worker_prompt_policy import (
9
+ PromptPlan,
10
+ WORKER_ERROR_CONTRACT_FILENAME,
11
+ WORKER_PREAMBLE_FILENAME_BY_AUDIENCE,
12
+ resolve_prompt_plan_for_manifest,
13
+ )
8
14
 
9
15
 
10
16
  class WorkerPromptHeaderError(Exception):
@@ -36,30 +42,42 @@ def worker_prompt_headers(
36
42
  prompt_rel: str,
37
43
  result_rel: str,
38
44
  worker_id: str,
45
+ dispatch_kind: str,
39
46
  manifest: Mapping[str, Any],
40
47
  active_context: Mapping[str, Any],
41
48
  ) -> list[str]:
42
49
  """Render the team-contract worker prompt anchor headers."""
43
50
  prompt_path = _resolve_project_path(project_root, prompt_rel)
51
+ errors_log_path = _errors_log_path(project_root, manifest, active_context)
52
+ errors_sidecar_path = _worker_errors_sidecar_path(
53
+ project_root,
54
+ manifest,
55
+ active_context,
56
+ worker_id,
57
+ )
58
+ plan = _prompt_plan(manifest, worker_id, dispatch_kind)
44
59
  headers = [
45
60
  f"**Project Root:** {project_root}",
46
61
  f"**Prompt History Path:** {prompt_rel}",
47
62
  f"**Result Path:** {result_rel}",
48
63
  f"Assigned worker prompt history path: {prompt_path}",
49
- f"**Worker Preamble Path:** {_worker_preamble_path()}",
64
+ f"**Worker Preamble Path:** {_worker_preamble_path(plan, active_context)}",
65
+ f"**Worker Error Contract Path:** {_worker_error_contract_path(active_context)}",
50
66
  ]
51
- if _string_value(manifest.get("taskType")) == "implementation":
67
+ if plan.allow_coding_preflight:
52
68
  headers.append(
53
69
  f"**Coding preflight pack:** {_coding_preflight_pack_path(active_context)}"
54
70
  )
55
71
  headers.extend([
56
- f"**Errors log path:** {_errors_log_path(project_root, manifest, active_context)}",
57
- (
58
- f"**Errors sidecar path:** "
59
- f"{_worker_errors_sidecar_path(project_root, manifest, active_context, worker_id)}"
60
- ),
72
+ f"**Errors log path:** {errors_log_path}",
73
+ f"**Errors sidecar path:** {errors_sidecar_path}",
61
74
  READ_SCOPE_HEADER,
62
75
  ])
76
+ if _string_value(manifest.get("taskType")) == "improvement-discovery":
77
+ headers.append(
78
+ f"**Phase 1.5 Grilling Log:** "
79
+ f"{_improvement_grilling_log_path(project_root, manifest)}"
80
+ )
63
81
  if _string_value(manifest.get("taskType")) == "final-verification":
64
82
  headers.extend(_final_verification_target_headers(active_context))
65
83
  return headers
@@ -86,8 +104,31 @@ def _final_verification_target_headers(
86
104
  ]
87
105
 
88
106
 
89
- def _worker_preamble_path() -> Path:
90
- return okstra_home() / "templates" / "worker-prompt-preamble.md"
107
+ def _worker_preamble_path(
108
+ plan: PromptPlan,
109
+ active_context: Mapping[str, Any],
110
+ ) -> Path:
111
+ runtime_resources = active_context.get("runtimeResources")
112
+ if isinstance(runtime_resources, Mapping):
113
+ paths = runtime_resources.get("workerPreamblePathByAudience")
114
+ if isinstance(paths, Mapping):
115
+ value = _string_value(paths.get(plan.audience))
116
+ if value:
117
+ return Path(value)
118
+ filename = WORKER_PREAMBLE_FILENAME_BY_AUDIENCE.get(
119
+ plan.audience,
120
+ WORKER_PREAMBLE_FILENAME_BY_AUDIENCE["analysis"],
121
+ )
122
+ return okstra_home() / "templates" / filename
123
+
124
+
125
+ def _worker_error_contract_path(active_context: Mapping[str, Any]) -> Path:
126
+ runtime_resources = active_context.get("runtimeResources")
127
+ if isinstance(runtime_resources, Mapping):
128
+ value = _string_value(runtime_resources.get("workerErrorContractPath"))
129
+ if value:
130
+ return Path(value)
131
+ return okstra_home() / "templates" / WORKER_ERROR_CONTRACT_FILENAME
91
132
 
92
133
 
93
134
  def _coding_preflight_pack_path(active_context: Mapping[str, Any]) -> Path:
@@ -99,6 +140,35 @@ def _coding_preflight_pack_path(active_context: Mapping[str, Any]) -> Path:
99
140
  return okstra_home() / "prompts" / "coding-preflight"
100
141
 
101
142
 
143
+ def _prompt_plan(
144
+ manifest: Mapping[str, Any],
145
+ worker_id: str,
146
+ dispatch_kind: str,
147
+ ) -> PromptPlan:
148
+ try:
149
+ return resolve_prompt_plan_for_manifest(
150
+ manifest=manifest,
151
+ worker_id=worker_id,
152
+ dispatch_kind=dispatch_kind,
153
+ )
154
+ except ValueError as exc:
155
+ raise WorkerPromptHeaderError(str(exc)) from exc
156
+
157
+
158
+ def _improvement_grilling_log_path(
159
+ project_root: Path,
160
+ manifest: Mapping[str, Any],
161
+ ) -> Path:
162
+ path = (
163
+ _run_directory_path(project_root, manifest)
164
+ / "state"
165
+ / "phase-1.5-grilling.md"
166
+ )
167
+ if not path.is_file():
168
+ raise WorkerPromptHeaderError(f"Phase 1.5 grilling log not found: {path}")
169
+ return path
170
+
171
+
102
172
  def _errors_log_path(
103
173
  project_root: Path,
104
174
  manifest: Mapping[str, Any],
@@ -0,0 +1,158 @@
1
+ """Resolve worker prompt requirements from functional audience."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass
5
+ from typing import Any, Literal, Mapping
6
+
7
+
8
+ PromptAudience = Literal[
9
+ "analysis",
10
+ "implementation-executor",
11
+ "implementation-verifier",
12
+ "report-writer",
13
+ "reverify",
14
+ "lead-only",
15
+ ]
16
+
17
+ FINAL_VERIFICATION_HEADERS = (
18
+ "**Worktree:**",
19
+ "**Verification scope:**",
20
+ "**Verification base ref:**",
21
+ "**Verification head ref:**",
22
+ "**Verification target path:**",
23
+ "**Verification target digest:**",
24
+ )
25
+ SUPPORTED_TASK_TYPES = frozenset({
26
+ "requirements-discovery",
27
+ "error-analysis",
28
+ "implementation-planning",
29
+ "improvement-discovery",
30
+ "implementation",
31
+ "final-verification",
32
+ "release-handoff",
33
+ })
34
+ WORKER_PREAMBLE_FILENAME_BY_AUDIENCE = {
35
+ "analysis": "worker-prompt-preamble.md",
36
+ "implementation-executor": "implementation-worker-preamble.md",
37
+ "implementation-verifier": "implementation-worker-preamble.md",
38
+ "report-writer": "report-writer-prompt-preamble.md",
39
+ }
40
+ WORKER_ERROR_CONTRACT_FILENAME = "worker-error-contract.md"
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class PromptPlan:
45
+ audience: PromptAudience
46
+ equality_group: str | None
47
+ packet_only: bool
48
+ allow_coding_preflight: bool
49
+ required_headers: tuple[str, ...]
50
+ max_body_lines: int | None
51
+ max_directive_lines: int | None
52
+
53
+
54
+ def resolve_prompt_plan(
55
+ *,
56
+ task_type: str,
57
+ worker_id: str,
58
+ executor_worker_id: str | None,
59
+ dispatch_kind: str,
60
+ ) -> PromptPlan:
61
+ """Resolve prompt policy without consulting provider or model identity."""
62
+ if task_type not in SUPPORTED_TASK_TYPES:
63
+ raise ValueError(f"unsupported task type: {task_type}")
64
+ if task_type == "release-handoff":
65
+ return _plan("lead-only")
66
+ if not worker_id.strip():
67
+ raise ValueError("worker ID is required")
68
+ if dispatch_kind.startswith("reverify-r"):
69
+ return _plan("reverify")
70
+ if task_type == "implementation" and not executor_worker_id:
71
+ raise ValueError("implementation executor worker ID is required")
72
+ if worker_id == "report-writer":
73
+ return _plan("report-writer")
74
+ if task_type == "implementation" and worker_id == executor_worker_id:
75
+ return _plan(
76
+ "implementation-executor",
77
+ allow_coding_preflight=True,
78
+ required_headers=("**Worktree:**",),
79
+ )
80
+ if task_type == "implementation":
81
+ return _plan(
82
+ "implementation-verifier",
83
+ equality_group="implementation-verifier-core",
84
+ allow_coding_preflight=True,
85
+ required_headers=("**Worktree:**",),
86
+ )
87
+ if task_type == "final-verification":
88
+ return _plan(
89
+ "analysis",
90
+ equality_group="analysis-core",
91
+ packet_only=True,
92
+ required_headers=FINAL_VERIFICATION_HEADERS,
93
+ max_body_lines=96,
94
+ max_directive_lines=40,
95
+ )
96
+ if task_type == "improvement-discovery":
97
+ return _plan(
98
+ "analysis",
99
+ equality_group="analysis-core",
100
+ packet_only=True,
101
+ required_headers=("**Phase 1.5 Grilling Log:**",),
102
+ )
103
+ return _plan("analysis", equality_group="analysis-core", packet_only=True)
104
+
105
+
106
+ def resolve_prompt_plan_for_manifest(
107
+ *,
108
+ manifest: Mapping[str, Any],
109
+ worker_id: str,
110
+ dispatch_kind: str,
111
+ ) -> PromptPlan:
112
+ """Resolve policy from canonical task and executor manifest fields."""
113
+ task_type = _required_string(manifest, "taskType")
114
+ return resolve_prompt_plan(
115
+ task_type=task_type,
116
+ worker_id=worker_id,
117
+ executor_worker_id=_executor_worker_id(manifest),
118
+ dispatch_kind=dispatch_kind,
119
+ )
120
+
121
+
122
+ def _plan(
123
+ audience: PromptAudience,
124
+ *,
125
+ equality_group: str | None = None,
126
+ packet_only: bool = False,
127
+ allow_coding_preflight: bool = False,
128
+ required_headers: tuple[str, ...] = (),
129
+ max_body_lines: int | None = None,
130
+ max_directive_lines: int | None = None,
131
+ ) -> PromptPlan:
132
+ return PromptPlan(
133
+ audience=audience,
134
+ equality_group=equality_group,
135
+ packet_only=packet_only,
136
+ allow_coding_preflight=allow_coding_preflight,
137
+ required_headers=required_headers,
138
+ max_body_lines=max_body_lines,
139
+ max_directive_lines=max_directive_lines,
140
+ )
141
+
142
+
143
+ def _executor_worker_id(manifest: Mapping[str, Any]) -> str | None:
144
+ team_contract = manifest.get("teamContract")
145
+ if not isinstance(team_contract, Mapping):
146
+ return None
147
+ executor = team_contract.get("executor")
148
+ if not isinstance(executor, Mapping):
149
+ return None
150
+ provider = executor.get("provider")
151
+ return provider.strip() if isinstance(provider, str) and provider.strip() else None
152
+
153
+
154
+ def _required_string(payload: Mapping[str, Any], key: str) -> str:
155
+ value = payload.get(key)
156
+ if not isinstance(value, str) or not value.strip():
157
+ raise ValueError(f"missing required string field: {key}")
158
+ return value.strip()
@@ -0,0 +1,51 @@
1
+ # Implementation Worker Prompt Preamble (canonical)
2
+
3
+ This file is shared by the `implementation-executor` and `implementation-verifier` audiences. Read it end-to-end, then read the role sidecar enumerated by the prompt. Before work, also read the shared file named by `**Worker Error Contract Path:**`; error rules live only there.
4
+
5
+ ## Operating standard (read before any output)
6
+
7
+ Work like a senior engineer who owns this result, not a commentator on it.
8
+ - Evidence over assertion — back every claim with a file:line, or mark it an explicit assumption. Never state the unverified as fact.
9
+ - Read before you reason — read each required input end to end; when you lack basis, write "insufficient evidence" instead of filling the gap plausibly.
10
+ - Shortest sound path — chase the most likely cause first; don't re-verify what is settled or pad with restatement.
11
+ - Decide, don't survey — when options exist, give the trade-off and one recommendation, not an exhaustive list.
12
+ - Fit what's here — match the surrounding code and prose; size the response to the request.
13
+ - Hold your own line — reason from your independent angle; do not drift toward the other workers' likely answers or echo the user. Triangulation fails if your view isn't genuinely yours.
14
+
15
+ ## Required reading
16
+
17
+ - Read the implementation role sidecar named by the prompt end-to-end. The executor sidecar owns mutation behavior; the verifier sidecar owns read-only review behavior.
18
+ - Read `overview.md` and `clean-code.md` under `**Coding preflight pack:**`, then follow every matching language, framework, and architecture route before editing or verification.
19
+ - Read the approved implementation deliverable and any effective design-preparation block enumerated by the prompt.
20
+
21
+ ## Worktree and command discipline
22
+
23
+ - `**Worktree:**` is the canonical checkout. Project commands run with that directory as cwd.
24
+ - `.okstra/**` artifacts remain anchored at `**Project Root:**`; the worktree may not contain them.
25
+ - Only the executor may mutate source files. Verifiers remain read-only except for okstra result/audit artifacts and project-declared QA commands.
26
+ - Execute the role sidecar's pre-write, post-write, QA, and return gates without substituting analysis Sections 1–6 or report-authoring instructions.
27
+
28
+ ## Anchor headers
29
+
30
+ Every initial implementation prompt begins with these generated common anchors in this exact order, before its implementation-specific body:
31
+
32
+ 1. `**Project Root:** <absolute-path>`
33
+ 2. `**Prompt History Path:** <project-relative-path>`
34
+ 3. `**Result Path:** <project-relative-path>`
35
+ 4. `Assigned worker prompt history path: <absolute-path>`
36
+ 5. `**Worker Preamble Path:** <absolute-path>`
37
+ 6. `**Worker Error Contract Path:** <absolute-path>`
38
+ 7. `**Coding preflight pack:** <absolute-path>`
39
+ 8. `**Errors log path:** <absolute-path>`
40
+ 9. `**Errors sidecar path:** <absolute-path>`
41
+ 10. `**Read scope:** <allowlist>`
42
+
43
+ The implementation body additionally carries `**Worktree:**` and its role-sidecar inputs. Do not synthesize any missing path.
44
+
45
+ ## Return message to the lead
46
+
47
+ Begin the inline return with the exact `**Model:** <Role>, <modelExecutionValue>` line from the prompt, followed by the role-sidecar status summary. Never invent or abbreviate the model.
48
+
49
+ ## Writing style
50
+
51
+ Use concise evidence-backed prose. Keep identifiers, paths, symbols, model names, CLI flags, and status tokens in English. Translate meaning rather than dictionary words.
@@ -1,10 +1,10 @@
1
1
  # Operating Standard (canonical)
2
2
 
3
3
  This file is the single source of truth for the operating standard injected into
4
- every okstra agent. The "Common core" block below is inlined verbatim into
5
- `templates/worker-prompt-preamble.md` (workers) and
6
- `prompts/lead/okstra-lead-contract.md` (lead). A unit test asserts both inlined
7
- copies match this core — update all three together.
4
+ every okstra agent. The "Common core" block below is inlined verbatim into the
5
+ three audience preambles under `templates/` and
6
+ `prompts/lead/okstra-lead-contract.md` (lead). A unit test asserts every inlined
7
+ copy matches this core — update all copies together.
8
8
 
9
9
  ## Common core
10
10
 
@@ -0,0 +1,37 @@
1
+ # Report Writer Prompt Preamble (canonical)
2
+
3
+ This file is the audience-specific contract for `report-writer`. Read it end-to-end. Before work, also read the shared file named by `**Worker Error Contract Path:**`; error rules live only there.
4
+
5
+ ## Operating standard (read before any output)
6
+
7
+ Work like a senior engineer who owns this result, not a commentator on it.
8
+ - Evidence over assertion — back every claim with a file:line, or mark it an explicit assumption. Never state the unverified as fact.
9
+ - Read before you reason — read each required input end to end; when you lack basis, write "insufficient evidence" instead of filling the gap plausibly.
10
+ - Shortest sound path — chase the most likely cause first; don't re-verify what is settled or pad with restatement.
11
+ - Decide, don't survey — when options exist, give the trade-off and one recommendation, not an exhaustive list.
12
+ - Fit what's here — match the surrounding code and prose; size the response to the request.
13
+
14
+ ## Required reading
15
+
16
+ Read every input enumerated by the Phase 6 dispatch end-to-end: task/analysis inputs, worker results, convergence state, the instruction-set-local `final-report-template.md`, and the task-type excerpt `final-report-schema.json`. Do not pull the full repository template or schema when the scoped instruction-set copies are provided.
17
+
18
+ Write Reading Confirmation to the report-writer audit sidecar, not the rendered final report. Resolve `.okstra/**` paths against `**Project Root:**`.
19
+
20
+ ## Report authoring handoff
21
+
22
+ - Author the data.json at `**Result Path:**` and the audit file at `**Worker Result Path:**`.
23
+ - Follow the task-type schema excerpt and Phase 6 report-writer contract. Do not perform independent analysis, edit source code, or load implementation coding-preflight resources.
24
+ - Invoke `okstra render-final-report <Result Path>` after writing data.json and verify the markdown sibling exists before returning.
25
+ - Preserve source item IDs, convergence classifications, round history, and unresolved dissent; do not recompute them from intuition.
26
+
27
+ ## Anchor headers
28
+
29
+ The generated prompt selects this file through `**Worker Preamble Path:**` and includes `**Worker Error Contract Path:**`, error paths, and read scope. It never includes `**Coding preflight pack:**`.
30
+
31
+ ## Return message to the lead
32
+
33
+ Begin the inline return with the exact `**Model:** Report writer worker, <modelExecutionValue>` line from the prompt, followed by the artifact status. Never invent or abbreviate the model.
34
+
35
+ ## Writing style
36
+
37
+ Use concise reader-facing prose and honor the report language. Keep identifiers, paths, symbols, model names, CLI flags, and status tokens in English. Translate meaning rather than dictionary words.