okstra 0.147.0 → 0.148.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 (116) hide show
  1. package/README.md +21 -7
  2. package/docs/architecture/storage-model.md +34 -61
  3. package/docs/architecture.md +51 -49
  4. package/docs/cli.md +38 -21
  5. package/docs/for-ai/skills/okstra-run.md +13 -34
  6. package/docs/performance-improvement-plan-v2.md +2 -2
  7. package/docs/pr-template-usage.md +1 -1
  8. package/docs/project-structure-overview.md +10 -8
  9. package/docs/task-process/README.md +4 -4
  10. package/docs/task-process/common-flow.md +12 -12
  11. package/docs/task-process/final-verification.md +2 -2
  12. package/docs/task-process/implementation.md +1 -1
  13. package/docs/task-process/release-handoff.md +1 -1
  14. package/package.json +2 -2
  15. package/runtime/BUILD.json +2 -2
  16. package/runtime/agents/workers/antigravity-worker.md +2 -2
  17. package/runtime/agents/workers/claude-worker.md +1 -1
  18. package/runtime/agents/workers/codex-worker.md +2 -2
  19. package/runtime/agents/workers/grok-worker.md +256 -0
  20. package/runtime/agents/workers/kimi-worker.md +256 -0
  21. package/runtime/agents/workers/report-writer-worker.md +2 -2
  22. package/runtime/bin/lib/okstra/cli.sh +13 -1
  23. package/runtime/bin/lib/okstra/globals.sh +3 -0
  24. package/runtime/bin/lib/okstra/usage.sh +17 -12
  25. package/runtime/bin/okstra-grok-exec.sh +5 -0
  26. package/runtime/bin/okstra-kimi-exec.sh +5 -0
  27. package/runtime/bin/okstra-provider-exec.py +235 -0
  28. package/runtime/bin/okstra.sh +3 -0
  29. package/runtime/prompts/lead/adapters/antigravity.md +48 -0
  30. package/runtime/prompts/lead/adapters/claude-code.md +13 -11
  31. package/runtime/prompts/lead/adapters/codex.md +7 -7
  32. package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
  33. package/runtime/prompts/lead/report-writer.md +1 -1
  34. package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
  35. package/runtime/prompts/profiles/_common-contract.md +4 -4
  36. package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
  37. package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
  38. package/runtime/prompts/profiles/_implementation-executor.md +12 -12
  39. package/runtime/prompts/profiles/_implementation-self-check.md +4 -4
  40. package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
  41. package/runtime/prompts/profiles/change-impact-analysis.md +2 -0
  42. package/runtime/prompts/profiles/error-analysis.md +2 -0
  43. package/runtime/prompts/profiles/feature-analysis.md +2 -0
  44. package/runtime/prompts/profiles/final-verification.md +3 -1
  45. package/runtime/prompts/profiles/forbidden-actions.json +4 -4
  46. package/runtime/prompts/profiles/implementation-planning.md +3 -1
  47. package/runtime/prompts/profiles/implementation.md +2 -2
  48. package/runtime/prompts/profiles/improvement-discovery.md +3 -1
  49. package/runtime/prompts/profiles/project-analysis.md +2 -0
  50. package/runtime/prompts/profiles/release-handoff.md +7 -7
  51. package/runtime/prompts/profiles/requirements-discovery.md +2 -0
  52. package/runtime/prompts/wizard/prompts.ko.json +9 -1
  53. package/runtime/python/okstra_ctl/codex_dispatch.py +68 -87
  54. package/runtime/python/okstra_ctl/dispatch_core.py +4 -22
  55. package/runtime/python/okstra_ctl/lead_events.py +1 -1
  56. package/runtime/python/okstra_ctl/lead_runtime.py +13 -2
  57. package/runtime/python/okstra_ctl/models.py +156 -8
  58. package/runtime/python/okstra_ctl/path_hints.py +9 -25
  59. package/runtime/python/okstra_ctl/paths.py +1 -1
  60. package/runtime/python/okstra_ctl/render.py +172 -74
  61. package/runtime/python/okstra_ctl/report_html/common.py +38 -2
  62. package/runtime/python/okstra_ctl/report_html/filters.py +104 -0
  63. package/runtime/python/okstra_ctl/report_html/render.py +7 -0
  64. package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +2 -1
  65. package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +2 -1
  66. package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +2 -1
  67. package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +2 -1
  68. package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +2 -1
  69. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -1
  70. package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +2 -1
  71. package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +2 -1
  72. package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +2 -1
  73. package/runtime/python/okstra_ctl/report_html/visualizations.py +32 -6
  74. package/runtime/python/okstra_ctl/run.py +264 -45
  75. package/runtime/python/okstra_ctl/runner_resolution.py +103 -0
  76. package/runtime/python/okstra_ctl/team.py +2 -7
  77. package/runtime/python/okstra_ctl/wizard.py +194 -21
  78. package/runtime/python/okstra_ctl/worker_artifacts.py +46 -0
  79. package/runtime/python/okstra_ctl/workers.py +3 -1
  80. package/runtime/python/okstra_ctl/workflow.py +4 -2
  81. package/runtime/python/okstra_token_usage/__init__.py +1 -0
  82. package/runtime/python/okstra_token_usage/collect.py +32 -23
  83. package/runtime/python/okstra_token_usage/pricing.py +35 -3
  84. package/runtime/schemas/final-report-v2.0.schema.json +2 -2
  85. package/runtime/skills/okstra-run/SKILL.md +31 -42
  86. package/runtime/templates/prd/pr-body.template.md +1 -1
  87. package/runtime/templates/reports/html/assets/base.css +6 -3
  88. package/runtime/templates/reports/html/base.template.html +22 -8
  89. package/runtime/templates/reports/html/macros/forms.html +2 -2
  90. package/runtime/templates/reports/html/macros/layout.html +5 -5
  91. package/runtime/templates/reports/html/macros/visualizations.html +11 -1
  92. package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +11 -11
  93. package/runtime/templates/reports/html/tasks/error-analysis.template.html +12 -12
  94. package/runtime/templates/reports/html/tasks/feature-analysis.template.html +9 -9
  95. package/runtime/templates/reports/html/tasks/final-verification.template.html +7 -7
  96. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +12 -12
  97. package/runtime/templates/reports/html/tasks/implementation.template.html +7 -7
  98. package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +6 -6
  99. package/runtime/templates/reports/html/tasks/project-analysis.template.html +11 -11
  100. package/runtime/templates/reports/html/tasks/release-handoff.template.html +5 -5
  101. package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +8 -8
  102. package/runtime/templates/reports/report.js +21 -4
  103. package/runtime/templates/reports/settings.template.json +4 -0
  104. package/runtime/templates/reports/task-brief.template.md +7 -7
  105. package/runtime/validators/validate-run.py +11 -6
  106. package/runtime/validators/validate_session_conformance.py +2 -1
  107. package/src/cli-registry.mjs +4 -4
  108. package/src/commands/execute/codex-dispatch.mjs +7 -10
  109. package/src/commands/execute/render-bundle.mjs +3 -3
  110. package/src/commands/execute/run.mjs +17 -52
  111. package/src/commands/execute/wizard.mjs +4 -1
  112. package/src/commands/lifecycle/doctor.mjs +6 -3
  113. package/src/commands/lifecycle/install.mjs +31 -8
  114. package/src/lib/runtime-manifest.mjs +1 -1
  115. package/src/lib/runtime-resolver.mjs +2 -2
  116. package/src/lib/worker-agent-render.mjs +50 -0
@@ -3,10 +3,10 @@
3
3
  usage() {
4
4
  cat >&2 <<USAGE_EOF
5
5
  usage:
6
- $DISPLAY_COMMAND_NAME [--render-only] [--yes] [--no-plan-verification] --task-type <task-type> [--workers worker1,worker2] [--lead-model <model>] [--claude-model <model>] [--codex-model <model>] [--antigravity-model <model>] [--report-writer-model <model>] [--lead-runtime claude-code|codex] [--executor claude|codex|antigravity] [--critic off|claude|codex|antigravity] [--related-tasks taskA,taskB] --project-id <project-id> [--project-root <path>] --task-group <task-group> --task-id <task-id> --task-brief <brief-path> [--directive <directive>] [--fix-cycle <yes|no>]
6
+ $DISPLAY_COMMAND_NAME [--render-only] [--yes] [--no-plan-verification] --task-type <task-type> [--workers worker1,worker2] [--lead-provider <provider>] [--lead-model <model>] [--worker-model provider=model,...] [--report-writer-provider <provider>] [--report-writer-model <model>] [--lead-runtime claude-code|codex|antigravity|external] [--executor claude|codex|antigravity] [--critic off|claude|codex|antigravity|grok|kimi] [--related-tasks taskA,taskB] --project-id <project-id> [--project-root <path>] --task-group <task-group> --task-id <task-id> --task-brief <brief-path> [--directive <directive>] [--fix-cycle <yes|no>]
7
7
 
8
8
  summary:
9
- $DISPLAY_TOOL_NAME prepares a task-keyed instruction bundle for Claude Code and launches an interactive Claude session by default.
9
+ $DISPLAY_TOOL_NAME prepares a task-keyed instruction bundle. The standalone launcher defaults to an interactive Claude session; supported in-host skills keep the current Claude Code, Codex, or Antigravity session as the native lead.
10
10
  The stable task identifier is composed of project-id + task-group + task-id.
11
11
 
12
12
  Skills, worker agents, and the codex wrapper are installed once per user under
@@ -75,7 +75,7 @@ optional arguments:
75
75
  workflow.nextRecommendedPhase). Explicit flags always win.
76
76
 
77
77
  options:
78
- --render-only Render the Claude handoff prompt only. Do not launch Claude.
78
+ --render-only Render the host-neutral lead handoff prompt only. Do not launch a session.
79
79
  --resume-clarification
80
80
  Interactive convenience mode that wraps --clarification-response.
81
81
  Locates the latest requirements-discovery or error-analysis
@@ -86,24 +86,27 @@ options:
86
86
  (--project-id/--task-group/--task-id or --task-key). Mutually
87
87
  exclusive with --clarification-response and --approved-plan.
88
88
  --yes Skip interactive prompting and confirmation. Requires all required arguments.
89
- --workers Comma-separated worker list for this run. Default: claude,codex,report-writer
90
- (Antigravity worker is optional; add \`antigravity\` explicitly, e.g. --workers claude,codex,antigravity,report-writer)
91
- --lead-model Model for Claude lead. Default: OKSTRA_DEFAULT_LEAD_MODEL or opus
89
+ --workers Comma-separated worker list for this run. Default: claude,codex,report-writer.
90
+ Optional read-only providers: antigravity, grok, kimi.
91
+ --lead-provider Compatibility assertion for the lead assignment. Must match the native Claude Code, Codex, or Antigravity host.
92
+ --lead-model Model for the host-native lead. Default: the selected provider's lead policy.
92
93
  --claude-model Model for Claude worker. Default: OKSTRA_DEFAULT_CLAUDE_MODEL or opus
93
94
  --codex-model Model for Codex worker. Default: OKSTRA_DEFAULT_CODEX_MODEL or gpt-5.6-sol
94
95
  --antigravity-model Model for Antigravity worker. Default: OKSTRA_DEFAULT_ANTIGRAVITY_MODEL or gemini-3.1-pro
96
+ --worker-model Provider-qualified worker override CSV, e.g. grok=grok-4.5,kimi=kimi-k3.
97
+ --report-writer-provider
98
+ Provider for report writer. Supported: claude, codex. Default: claude.
95
99
  --report-writer-model
96
100
  Model for report writer worker. Default: OKSTRA_DEFAULT_REPORT_WRITER_MODEL or sonnet
97
- --lead-runtime Lead runtime adapter. Default: claude-code.
98
- codex is currently render-only and records Codex adapter
99
- metadata in prepared artifacts without dispatching workers.
101
+ --lead-runtime Lead runtime adapter. Default: claude-code. In-host runs use the
102
+ matching native lead; non-host providers use CLI wrappers.
100
103
  --executor Provider that performs the Executor role during --task-type=implementation.
101
104
  One of: claude | codex | antigravity. Default: OKSTRA_DEFAULT_EXECUTOR or claude.
102
105
  The Executor is the only worker allowed to mutate project files; the other two
103
106
  providers are dispatched as read-only verifiers regardless of this selection.
104
107
  Has no effect on other task types.
105
108
  --critic Provider for the opt-in Phase 5.6 critic pass (coverage gaps /
106
- acceptance devil's-advocate). One of: off | claude | codex | antigravity.
109
+ acceptance devil's-advocate). One of: off | claude | codex | antigravity | grok | kimi.
107
110
  Default: off.
108
111
  --related-tasks Optional comma-separated related task identifiers. Example: auth-token-refresh,frontend-login-ui
109
112
  --work-category Work-category classification for this task. One of:
@@ -121,11 +124,13 @@ options:
121
124
  -h, --help Show this help.
122
125
 
123
126
  model defaults:
124
- Claude lead: OKSTRA_DEFAULT_LEAD_MODEL or opus
125
- Report writer worker: OKSTRA_DEFAULT_REPORT_WRITER_MODEL or Claude lead default
127
+ Host-native lead: provider policy (Claude default: opus; Codex default: gpt-5.6-sol)
128
+ Report writer worker: selected provider policy (Claude default: sonnet)
126
129
  Claude worker: OKSTRA_DEFAULT_CLAUDE_MODEL or opus
127
130
  Codex worker: OKSTRA_DEFAULT_CODEX_MODEL or gpt-5.6-sol
128
131
  Antigravity worker: OKSTRA_DEFAULT_ANTIGRAVITY_MODEL or gemini-3.1-pro
132
+ Grok worker: grok-build-0.1 (analyser) or grok-4.5 (critic)
133
+ Kimi worker: kimi-k2.7-code (analyser) or kimi-k3 (critic)
129
134
  Implementation executor: OKSTRA_DEFAULT_EXECUTOR or claude (one of: claude | codex | antigravity)
130
135
 
131
136
  output:
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
5
+ exec python3 "$script_dir/okstra-provider-exec.py" grok "$@"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
5
+ exec python3 "$script_dir/okstra-provider-exec.py" kimi "$@"
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env python3
2
+ """Run an external LLM CLI with the shared okstra wrapper contract."""
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import selectors
8
+ import shutil
9
+ import signal
10
+ import subprocess
11
+ import sys
12
+ import time
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+ from typing import Callable
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class ProviderCommand:
20
+ binary: str
21
+ wrapper: str
22
+ build_args: Callable[[str, str, str], list[str]]
23
+
24
+
25
+ def _grok_args(prompt: str, model: str, cwd: str) -> list[str]:
26
+ return [
27
+ "grok",
28
+ "-p",
29
+ prompt,
30
+ "-m",
31
+ model,
32
+ "--output-format",
33
+ "streaming-json",
34
+ "--cwd",
35
+ cwd,
36
+ ]
37
+
38
+
39
+ def _kimi_args(prompt: str, model: str, _cwd: str) -> list[str]:
40
+ return ["kimi", "-p", prompt, "-m", model, "--output-format", "stream-json"]
41
+
42
+
43
+ PROVIDERS = {
44
+ "grok": ProviderCommand("grok", "okstra-grok-exec.sh", _grok_args),
45
+ "kimi": ProviderCommand("kimi", "okstra-kimi-exec.sh", _kimi_args),
46
+ }
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class Invocation:
51
+ provider: ProviderCommand
52
+ project_root: Path
53
+ model: str
54
+ prompt_path: Path
55
+ execution_root: Path
56
+ role: str
57
+ idle_timeout_seconds: int
58
+
59
+
60
+ class PreflightError(Exception):
61
+ def __init__(self, exit_code: int, message: str) -> None:
62
+ super().__init__(message)
63
+ self.exit_code = exit_code
64
+
65
+
66
+ def _parse_invocation(argv: list[str]) -> Invocation:
67
+ if len(argv) < 4 or len(argv) > 7:
68
+ raise PreflightError(
69
+ 64,
70
+ "usage: okstra-provider-exec.py <provider> <project-root> <model-execution-value> "
71
+ "<prompt-path> [worktree-path] [role] [idle-timeout-seconds]",
72
+ )
73
+ provider_id, project_root_raw, model, prompt_raw = argv[:4]
74
+ provider = PROVIDERS.get(provider_id)
75
+ if provider is None:
76
+ raise PreflightError(64, f"unsupported provider: {provider_id}")
77
+ worktree_raw = argv[4] if len(argv) >= 5 else ""
78
+ role = argv[5] if len(argv) >= 6 and argv[5] else "worker"
79
+ default_timeout = 1500 if role in {"executor", "verifier"} else 600
80
+ timeout_raw = argv[6] if len(argv) >= 7 else str(default_timeout)
81
+ return _validate_invocation(
82
+ provider, project_root_raw, model, prompt_raw, worktree_raw, role, timeout_raw
83
+ )
84
+
85
+
86
+ def _validate_invocation(
87
+ provider: ProviderCommand,
88
+ project_root_raw: str,
89
+ model: str,
90
+ prompt_raw: str,
91
+ worktree_raw: str,
92
+ role: str,
93
+ timeout_raw: str,
94
+ ) -> Invocation:
95
+ project_root = Path(project_root_raw)
96
+ prompt_path = Path(prompt_raw)
97
+ if not project_root_raw or not project_root.is_dir():
98
+ raise PreflightError(65, f"project-root is missing or not a directory: {project_root_raw!r}")
99
+ if not model:
100
+ raise PreflightError(66, "model-execution-value is empty")
101
+ if not prompt_raw or not prompt_path.is_file():
102
+ raise PreflightError(67, f"prompt-path is missing or not a file: {prompt_raw!r}")
103
+ if not timeout_raw.isdigit():
104
+ raise PreflightError(69, f"idle-timeout-seconds must be a non-negative integer: {timeout_raw!r}")
105
+ execution_root = Path(worktree_raw) if worktree_raw else project_root
106
+ if worktree_raw and not execution_root.is_dir():
107
+ raise PreflightError(68, f"worktree-path was provided but is not a directory: {worktree_raw!r}")
108
+ if shutil.which(provider.binary) is None:
109
+ raise PreflightError(127, f"{provider.binary} CLI is not installed on PATH")
110
+ return Invocation(
111
+ provider=provider,
112
+ project_root=project_root.resolve(),
113
+ model=model,
114
+ prompt_path=prompt_path.resolve(),
115
+ execution_root=execution_root.resolve(),
116
+ role=role,
117
+ idle_timeout_seconds=int(timeout_raw),
118
+ )
119
+
120
+
121
+ def _log_path(prompt_path: Path) -> Path:
122
+ if prompt_path.name.endswith(".md"):
123
+ return prompt_path.with_name(f"{prompt_path.name[:-3]}.log")
124
+ return Path(f"{prompt_path}.log")
125
+
126
+
127
+ def _write_status(path: Path, status: dict[str, object]) -> None:
128
+ temporary = Path(f"{path}.tmp")
129
+ temporary.write_text(json.dumps(status, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
130
+ os.replace(temporary, path)
131
+
132
+
133
+ def _terminate_process(process: subprocess.Popen[bytes]) -> None:
134
+ try:
135
+ os.killpg(process.pid, signal.SIGTERM)
136
+ except ProcessLookupError:
137
+ return
138
+ try:
139
+ process.wait(timeout=5)
140
+ except subprocess.TimeoutExpired:
141
+ try:
142
+ os.killpg(process.pid, signal.SIGKILL)
143
+ except ProcessLookupError:
144
+ pass
145
+ process.wait()
146
+
147
+
148
+ def _stream_process(
149
+ process: subprocess.Popen[bytes], log_file, idle_timeout_seconds: int
150
+ ) -> tuple[int, bool, int]:
151
+ selector = selectors.DefaultSelector()
152
+ assert process.stdout is not None
153
+ selector.register(process.stdout, selectors.EVENT_READ)
154
+ last_output = time.monotonic()
155
+ timed_out = False
156
+ idle_seconds = 0
157
+ while selector.get_map():
158
+ for key, _ in selector.select(timeout=0.25):
159
+ chunk = os.read(key.fd, 8192)
160
+ if not chunk:
161
+ selector.unregister(key.fileobj)
162
+ continue
163
+ last_output = time.monotonic()
164
+ sys.stdout.buffer.write(chunk)
165
+ sys.stdout.buffer.flush()
166
+ log_file.write(chunk)
167
+ log_file.flush()
168
+ idle_seconds = int(time.monotonic() - last_output)
169
+ if idle_timeout_seconds and idle_seconds >= idle_timeout_seconds and process.poll() is None:
170
+ timed_out = True
171
+ _terminate_process(process)
172
+ exit_code = process.wait()
173
+ return (124 if timed_out else exit_code), timed_out, idle_seconds
174
+
175
+
176
+ def _run(invocation: Invocation) -> int:
177
+ prompt = invocation.prompt_path.read_text(encoding="utf-8")
178
+ command = invocation.provider.build_args(prompt, invocation.model, str(invocation.execution_root))
179
+ status_path = Path(f"{invocation.prompt_path}.status.json")
180
+ log_path = _log_path(invocation.prompt_path)
181
+ started_ts = int(time.time())
182
+ started_monotonic = time.monotonic()
183
+ status: dict[str, object] = {
184
+ "schemaVersion": 1,
185
+ "wrapper": invocation.provider.wrapper,
186
+ "role": invocation.role,
187
+ "pid": os.getpid(),
188
+ "started_ts": started_ts,
189
+ "log_path": str(log_path),
190
+ "stage": "started",
191
+ }
192
+ _write_status(status_path, status)
193
+ with log_path.open("wb") as log_file:
194
+ process = subprocess.Popen(
195
+ command,
196
+ cwd=invocation.execution_root,
197
+ stdout=subprocess.PIPE,
198
+ stderr=subprocess.STDOUT,
199
+ start_new_session=True,
200
+ )
201
+ exit_code, timed_out, idle_seconds = _stream_process(
202
+ process, log_file, invocation.idle_timeout_seconds
203
+ )
204
+ ended_ts = int(time.time())
205
+ status.update(
206
+ stage="exited",
207
+ exit_code=exit_code,
208
+ ended_ts=ended_ts,
209
+ duration_ms=int((time.monotonic() - started_monotonic) * 1000),
210
+ )
211
+ if timed_out:
212
+ status.update(
213
+ timeout=True,
214
+ idle_at_ts=ended_ts,
215
+ idle_seconds=idle_seconds,
216
+ terminated_by="idle-watchdog",
217
+ )
218
+ _write_status(status_path, status)
219
+ return exit_code
220
+
221
+
222
+ def main(argv: list[str]) -> int:
223
+ try:
224
+ invocation = _parse_invocation(argv[1:])
225
+ return _run(invocation)
226
+ except PreflightError as exc:
227
+ print(f"okstra-provider-exec: {exc}", file=sys.stderr)
228
+ return exc.exit_code
229
+ except OSError as exc:
230
+ print(f"okstra-provider-exec: execution failed: {exc}", file=sys.stderr)
231
+ return 127 if isinstance(exc, FileNotFoundError) else 1
232
+
233
+
234
+ if __name__ == "__main__":
235
+ sys.exit(main(sys.argv))
@@ -110,10 +110,13 @@ PY_ARGS=(
110
110
  [[ -n "${DIRECTIVE-}" ]] && PY_ARGS+=(--directive "$DIRECTIVE")
111
111
  [[ -n "${FIX_CYCLE-}" ]] && PY_ARGS+=(--fix-cycle "$FIX_CYCLE")
112
112
  [[ -n "${WORKERS_OVERRIDE-}" ]] && PY_ARGS+=(--workers "$WORKERS_OVERRIDE")
113
+ [[ -n "${LEAD_PROVIDER_OVERRIDE-}" ]] && PY_ARGS+=(--lead-provider "$LEAD_PROVIDER_OVERRIDE")
113
114
  [[ -n "${LEAD_MODEL_OVERRIDE-}" ]] && PY_ARGS+=(--lead-model "$LEAD_MODEL_OVERRIDE")
114
115
  [[ -n "${CLAUDE_MODEL_OVERRIDE-}" ]] && PY_ARGS+=(--claude-model "$CLAUDE_MODEL_OVERRIDE")
115
116
  [[ -n "${CODEX_MODEL_OVERRIDE-}" ]] && PY_ARGS+=(--codex-model "$CODEX_MODEL_OVERRIDE")
116
117
  [[ -n "${ANTIGRAVITY_MODEL_OVERRIDE-}" ]] && PY_ARGS+=(--antigravity-model "$ANTIGRAVITY_MODEL_OVERRIDE")
118
+ [[ -n "${WORKER_MODELS_OVERRIDE-}" ]] && PY_ARGS+=(--worker-model "$WORKER_MODELS_OVERRIDE")
119
+ [[ -n "${REPORT_WRITER_PROVIDER_OVERRIDE-}" ]] && PY_ARGS+=(--report-writer-provider "$REPORT_WRITER_PROVIDER_OVERRIDE")
117
120
  [[ -n "${REPORT_WRITER_MODEL_OVERRIDE-}" ]] && PY_ARGS+=(--report-writer-model "$REPORT_WRITER_MODEL_OVERRIDE")
118
121
  [[ -n "${LEAD_RUNTIME-}" ]] && PY_ARGS+=(--lead-runtime "$LEAD_RUNTIME")
119
122
  [[ -n "${EXECUTOR_OVERRIDE-}" ]] && PY_ARGS+=(--executor "$EXECUTOR_OVERRIDE")
@@ -0,0 +1,48 @@
1
+ # Antigravity Lead Runtime Adapter
2
+
3
+ ## Scope
4
+
5
+ This adapter maps the neutral Okstra lead operations to the current Antigravity CLI host. Read it only when the rendered launch prompt selects `leadRuntime=antigravity`.
6
+
7
+ ## Capability declaration
8
+
9
+ | Field | Value |
10
+ |---|---|
11
+ | `runtime` | `antigravity` |
12
+ | `leadRoleLabel` | `Antigravity lead` |
13
+ | `userPromptMode` | `host-text` |
14
+ | `workerDispatchBackend` | `mixed` |
15
+ | `initialPromptDeliveryMode` | `eager-include` |
16
+ | `sessionAccounting` | `artifact-only` |
17
+ | `resumeMode` | `artifact-checkpoint` |
18
+ | `teardownMode` | `process-cleanup` |
19
+ | `leadEventSource` | `lead-events-jsonl` |
20
+
21
+ ## Semantic operation mapping
22
+
23
+ | Operation | Mapping |
24
+ |---|---|
25
+ | `read_artifacts` | Read the manifest-provided paths through the current Antigravity host file interface. |
26
+ | `write_artifact` | Write only core-authorized `.okstra/` artifacts and preserve their schemas. |
27
+ | `prompt_user` | Ask through the current host text/question interface and stop at approval gates until an explicit answer arrives. |
28
+ | `dispatch_worker` | Dispatch every `runner=native-session` Antigravity assignment through the current host. Dispatch every `runner=cli-wrapper` assignment through its registered provider wrapper. |
29
+ | `await_workers` | Await native host workers through the host primitive and CLI workers through their status sidecars, then verify terminal state and Result Paths. |
30
+ | `redispatch_worker` | Start a fresh native worker or CLI wrapper attempt according to the persisted assignment and record the supplied dispatch kind. |
31
+ | `shutdown_workers` | Perform host or process cleanup only for resources owned by this run. |
32
+ | `record_lead_event` | Append the required structured event to the manifest-provided `leadEventsPath`; emit the matching user-facing `PROGRESS:` line. |
33
+ | `collect_usage` | Collect host- or artifact-backed usage through the existing Okstra token-usage path; do not substitute another runtime's session log. |
34
+
35
+ ## Antigravity dispatch details
36
+
37
+ - For convergence reverify, consume the persisted round plan exactly. This adapter may map and transport each returned batch, but it cannot change batch membership and does not classify findings or branch on task type, provider, or model identity.
38
+ - The current Antigravity session is the lead. Never launch another provider as a replacement lead.
39
+ - The prepared run manifest and team-state are the assignment authority. Keep `runner=native-session` assignments in the current host and route `runner=cli-wrapper` assignments through the registered wrapper.
40
+ - Do not infer the current host from an installed `agy` binary. The `antigravity` runtime must come from the active host skill or an explicit runtime flag.
41
+ - Unsupported workers or unavailable models fail before dispatch; do not change the provider, model, or runner silently.
42
+ - Reverify and critic retries use fresh attempts and persist the core-supplied `dispatchKind`.
43
+ - Report-writer completion requires both the data Result Path and the worker-results audit path.
44
+
45
+ ## Completion, cleanup, and resume
46
+
47
+ - A native host completion or successful wrapper return alone is insufficient. Verify terminal state, every required completion path, and the corresponding dispatch audit record.
48
+ - Resume from run artifacts and lead-event checkpoints. Do not invent Claude or Codex session identifiers.
@@ -11,7 +11,7 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
11
11
  | `runtime` | `claude-code` |
12
12
  | `leadRoleLabel` | `Claude lead` |
13
13
  | `userPromptMode` | `native-question` |
14
- | `workerDispatchBackend` | `team` |
14
+ | `workerDispatchBackend` | `mixed` |
15
15
  | `initialPromptDeliveryMode` | `lazy-path-reference` |
16
16
  | `sessionAccounting` | `claude-jsonl` |
17
17
  | `resumeMode` | `session-id` |
@@ -25,10 +25,10 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
25
25
  | `read_artifacts` | Use the host file-read primitive and preserve the core contract's read order. |
26
26
  | `write_artifact` | Use the host file-write primitive only for paths authorized by the active lifecycle phase. |
27
27
  | `prompt_user` | Use the native question tool for approvals and clarifications; do not infer an answer from silence. |
28
- | `dispatch_worker` | Dispatch `Agent(name: "<role>", run_in_background: true)` without `team_name`; apply the assigned model as specified below. |
28
+ | `dispatch_worker` | Dispatch each assignment through `Agent(name: "<role>", run_in_background: true)` without `team_name`; use an in-process worker for `runner=native-session` and the assigned provider's wrapper worker for `runner=cli-wrapper`. |
29
29
  | `await_workers` | Arm one background shell poll for the pending Result Paths; the spawn acknowledgement is not completion. |
30
30
  | `redispatch_worker` | Dispatch a fresh `Agent(...)` session with the same prompt plus the core reverify/retry reason. |
31
- | `shutdown_workers` | Send `SendMessage(to: <name>, message: { type: "shutdown_request" })` only to confirmed-complete teammates selected for cleanup. |
31
+ | `shutdown_workers` | For each confirmed-complete worker selected for cleanup, send `SendMessage(to: <name>, message: { type: "shutdown_request" })` to idle the roster member **and** call `TaskStop(task_id: "<name>")` to stop its background task. Both are required; neither subsumes the other. |
32
32
  | `record_lead_event` | Emit the required `PROGRESS:` line as assistant text and persist core-required state/artifact updates. |
33
33
  | `collect_usage` | Run `okstra token-usage` against the team-state; it reads the run-scoped `~/.claude/projects` session JSONL evidence. |
34
34
 
@@ -36,14 +36,14 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
36
36
 
37
37
  - The session owns one implicit team. `TeamCreate` and `TeamDelete` are absent on current Claude Code builds; never probe for them and never pass `team_name`.
38
38
  - Set `name` to the core-assigned functional role label so token attribution can match `agentName`.
39
- - Map the core assignment key to the native `subagent_type` field: `claude-worker`, `codex-worker`, `antigravity-worker`, or `report-writer-worker`. Never substitute `general-purpose` for a rostered Report writer worker.
40
- - For in-process Claude and report-writer roles, map `modelExecutionValue` to the supported family token and pass it as the `model` argument. CLI-wrapper roles apply their model in the wrapper and remain `inherit` at the Agent layer.
39
+ - Map a `runner=native-session` Claude assignment to `claude-worker`. Map a `runner=cli-wrapper` assignment to `<provider>-worker`; the registered providers currently resolve to `claude-worker`, `codex-worker`, `antigravity-worker`, `grok-worker`, or `kimi-worker`. The functional `report-writer` worker ID does not override its provider assignment. Never substitute `general-purpose` for a rostered worker.
40
+ - For `runner=native-session`, map `modelExecutionValue` to the supported Claude family token and pass it as the `model` argument. CLI-wrapper roles apply their exact model in the provider wrapper and remain `inherit` at the Agent layer.
41
41
  - A resumed lead can dispatch a fresh worker; resume is not a valid reason to omit a rostered role.
42
42
 
43
43
  ### Dispatch-time model enforcement
44
44
 
45
- - `Claude worker` and `Report writer worker` definitions declare `model: inherit`; the lead MUST override that default by passing the assigned family token (`fable`, `opus`, `sonnet`, or `haiku`) as the `Agent(...)` `model` argument.
46
- - Codex and Antigravity wrapper agents remain `inherit` at the Agent layer because their exact `modelExecutionValue` is applied by the wrapper CLI's own model argument.
45
+ - A native Claude worker definition declares `model: inherit`; the lead MUST override that default by passing the assigned family token (`fable`, `opus`, `sonnet`, or `haiku`) as the `Agent(...)` `model` argument.
46
+ - Every CLI-wrapper agent remains `inherit` at the Agent layer because its exact `modelExecutionValue` is applied by `okstra-claude-exec.sh`, `okstra-codex-exec.sh`, `okstra-antigravity-exec.sh`, `okstra-grok-exec.sh`, or `okstra-kimi-exec.sh` according to the assignment provider.
47
47
  - Missing or unsupported family-token mapping is a pre-dispatch contract failure. Never inherit the lead model, choose a nearby alias, or switch provider silently.
48
48
  - Every analysis dispatch sets `name: "<workerId>-worker"`; convergence retries append `-reverify-r<N>`, implementation uses the functional `-executor` / `-verifier` suffix, and report writing uses `report-writer`. These values are retained as `agentName` in session JSONL for usage attribution.
49
49
  - Every Codex / Antigravity prompt includes `**Pane role:** <functional-role>` so the wrapper's optional fifth argument names both its caller pane and trace pane.
@@ -56,7 +56,7 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
56
56
  - For convergence reverify, consume the persisted round plan exactly. This adapter may map and transport each returned batch, but it cannot change batch membership and does not classify findings or branch on task type, provider, or model identity.
57
57
  - Reverify dispatch uses a fresh one-shot `Agent(...)` call named `<workerId>-worker-reverify-r<N>`. Preserve the initial worker's definition and map an in-process Claude assignment's `modelExecutionValue` to its exact family token; CLI-wrapper assignments remain `inherit` at the Agent layer and apply the exact model in their wrapper.
58
58
  - Critic dispatch uses `name: "<provider>-worker-critic"`, `dispatchKind: "critic"`, and the exact mapped model from `config.critic.modelExecutionValue`. If that value cannot be mapped, record `critic-skipped: model-unresolved` and do not dispatch.
59
- - Report-writer dispatch uses `name: "report-writer"` and maps the roster assignment's `modelExecutionValue` to the supported family token. The prompt's `**Model:**` header must carry the same execution value.
59
+ - Report-writer dispatch uses `name: "report-writer"`. A native Claude assignment maps `modelExecutionValue` to the supported family token; a CLI-wrapper assignment remains `inherit` at the Agent layer and applies the exact value in its provider wrapper. The prompt's `**Model:**` header must carry the same execution value.
60
60
  - Each variant persists its prompt path, Result Path, worker-results path, error paths, and `dispatchKind` before dispatch. Completion uses the shared background Result Path poll; an Agent acknowledgement never completes the variant.
61
61
 
62
62
  ## Completion, cleanup, and resume
@@ -71,7 +71,7 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
71
71
 
72
72
  ### CLI-wrapper polling
73
73
 
74
- - Start `okstra-codex-exec.sh` / `okstra-antigravity-exec.sh` with `Bash(run_in_background: true)` and poll `BashOutput(bash_id)` back-to-back until terminal completion. Never add a foreground sleep.
74
+ - Start the assignment's registered wrapper (`okstra-claude-exec.sh`, `okstra-codex-exec.sh`, `okstra-antigravity-exec.sh`, `okstra-grok-exec.sh`, or `okstra-kimi-exec.sh`) with `Bash(run_in_background: true)` and poll `BashOutput(bash_id)` back-to-back until terminal completion. Never add a foreground sleep.
75
75
  - Return accumulated stdout on success. On a non-zero `exit_code`, record the real code and observed duration.
76
76
  - At the 1800-second cap, inspect the live log mtime once. Recent output grants one extension to 2100 seconds; otherwise call `KillShell(shell_id)`, record exit code 124, and return the wrapper timeout sentinel.
77
77
  - Keep the wrapper subagent alive throughout polling so its JSONL timestamp window covers the underlying CLI rollout.
@@ -81,7 +81,7 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
81
81
  - At the start of Phase 7, run `okstra token-usage /abs/path/to/run/state/team-state-<task-type>-<seq>.json --write --summary` with the literal team-state path.
82
82
  - Read the lead and Claude-side wrapper evidence from `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl`; attribute workers by the dispatch `agentName` recorded above.
83
83
  - Resolve `teamName` from `state.teamName` or `state.team.teamName` and use the full manifest-provided value as the team needle. If it is missing, the collector's short-form fallback cannot match worker JSONLs and records those workers as `source: "unavailable"`.
84
- - Keep underlying Codex and Antigravity CLI usage separate from the Claude wrapper-session usage. Persist `leadUsage`, per-worker usage, and `usageSummary` before report substitution and cleanup.
84
+ - Keep every underlying provider CLI's usage separate from the Claude wrapper-session usage. Persist `leadUsage`, per-worker usage, and `usageSummary` before report substitution and cleanup.
85
85
 
86
86
  ## Run-scoped resource lifecycle
87
87
 
@@ -89,6 +89,7 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
89
89
  - Record the lead pane once with `mkdir -p "<RUN_DIR>/state" && { . "$HOME/.okstra/bin/lib/okstra/tmux-pane.sh" 2>/dev/null && okstra_resolve_caller_pane; } > "<RUN_DIR>/state/lead-pane.id" 2>/dev/null || true`. This is silent setup and must not gate cleanup; the cleanup script protects the lead pane itself.
90
90
  - Collect and persist token usage before any live-roster cleanup, including cleanup between batches and the run-end shutdown sequence.
91
91
  - Before each new worker batch (and before the next phase's render-bundle), reclaim the prior round's completed teammate panes in two passes, adding `--keep report-writer-worker` to **both** passes while the report writer is in flight. First source the count: `$HOME/.okstra/bin/okstra-trace-cleanup.sh --list --run-dir "<RUN_DIR>" [--keep report-writer-worker]` never kills and prints one `<pane_id>\t<pane_title>` line per pane it would reclaim — count those lines as `<n>`. Then perform the reclaim by running the same command **without** `--list`, and emit the neutral contract's `PROGRESS: phase-batch-cleanup panes=<n>` checkpoint with that count. Call both passes after collecting that round's results and token usage and before the next dispatch, so no in-flight worker pane is caught. This `tmux kill-pane`s the harness teammate panes; `shutdown_request` only idles the agent and never frees the pane, so it stays part of the run-end sequence for roster/token hygiene. In a non-tmux session there are no panes, both passes no-op, and `<n>` is `0` — still emit the checkpoint. The lead pane (read from `<RUN_DIR>/state/lead-pane.id`) is always preserved.
92
+ - Reclaiming a pane does not stop the worker's background task. Every `dispatch_worker` Agent runs with `run_in_background: true`, so a worker whose result is already collected stays a live background task for the rest of the session — that residue is what fills the harness's exit-time `Background work is running` list. At the same batch boundary, right after the pane reclaim, call `TaskStop(task_id: "<name>")` once per worker of the completed batch, passing the exact `name` used at dispatch (`<workerId>-worker`, `<workerId>-worker-reverify-r<N>`, `<provider>-worker-critic`, `report-writer`). Stop only workers whose results were already collected — never an in-flight worker, never the lead, and keep `report-writer` while it is in flight, matching the pane pass's `--keep report-writer-worker`. `TaskStop` on an already-finished task is a no-op; treat a failure as benign, record nothing, and continue the boundary. This runs in a non-tmux session too, where the pane passes no-op but the background tasks still exist.
92
93
  - After batch cleanup, record the current live session generation with `okstra token-usage "<TEAM_STATE_PATH>" --record-observed-session --project-root "<PROJECT_ROOT>"`. This protects usage accounting when Claude Code re-issues the session id after resume or compaction.
93
94
  - Claude Code cannot delete the implicit team or surgically remove an idle roster entry. Explain that teammates may remain visible until session end and, when needed, give the manual action `Delete team <teamName> in Teams/FleetView`.
94
95
  - The `SessionEnd` hook runs `$HOME/.okstra/bin/okstra-team-reconcile.sh --session-end` as the safety net for the current live session.
@@ -104,4 +105,5 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
104
105
  > (Yes) Close everything and clean up teammates / (No) Keep everything
105
106
  5. On `keep`, preserve every residual resource and show `$HOME/.okstra/bin/okstra-trace-cleanup.sh --run-dir "<RUN_DIR>"` plus the manual Teams/FleetView action. Tell the user that `keep` holds only until the next boundary: if this session goes on to another phase/batch, that transition's round-boundary cleanup reclaims the kept **completed** panes unattended (in-flight resources and the lead pane are never touched).
106
107
  6. On approved `clean`, emit the teardown checkpoint, run `$HOME/.okstra/bin/okstra-trace-cleanup.sh --run-dir "<RUN_DIR>"`, then run `$HOME/.okstra/bin/okstra-team-reconcile.sh --project-root "<PROJECT_ROOT>" --fallback-team "session-<lead.sessionId-prefix>"` exactly once. The resolver reads the current live session's `~/.claude/teams/session-<live>/config.json`, falling back to the snapshot directory only when the live directory is absent, and prints `dismissible-member: <name>` records.
107
- 7. Send `SendMessage(to: <name>, message: { type: "shutdown_request" })` to each printed, confirmed-complete non-lead member. The `message` MUST be the object literal shown, NEVER a JSON string in a text field. Never target the lead or use `TaskStop`; teammates are not background tasks.
108
+ 7. Send `SendMessage(to: <name>, message: { type: "shutdown_request" })` to each printed, confirmed-complete non-lead member. The `message` MUST be the object literal shown, NEVER a JSON string in a text field. Never target the lead.
109
+ 8. Call `TaskStop(task_id: "<name>")` for every worker this run dispatched, reusing the step-7 names plus any batch worker already reclaimed earlier. `shutdown_request` only idles the roster member and the step-6 pane reclaim only closes the pane — neither ends the background task, so this step is the one that empties the harness's exit-time `Background work is running` list. Never target the lead; a `TaskStop` on an already-finished task is a benign no-op.
@@ -11,7 +11,7 @@ This adapter maps the neutral Okstra lead operations to the Codex artifact-first
11
11
  | `runtime` | `codex` |
12
12
  | `leadRoleLabel` | `Codex lead` |
13
13
  | `userPromptMode` | `host-text` |
14
- | `workerDispatchBackend` | `cli-wrapper` |
14
+ | `workerDispatchBackend` | `mixed` |
15
15
  | `initialPromptDeliveryMode` | `eager-include` |
16
16
  | `sessionAccounting` | `artifact-only` |
17
17
  | `resumeMode` | `artifact-checkpoint` |
@@ -25,9 +25,9 @@ This adapter maps the neutral Okstra lead operations to the Codex artifact-first
25
25
  | `read_artifacts` | Read the manifest-provided paths through the current host's file interface. |
26
26
  | `write_artifact` | Write only core-authorized `.okstra/` artifacts and preserve their schemas. |
27
27
  | `prompt_user` | Ask through the host text/question interface and stop at approval gates until an explicit answer arrives. |
28
- | `dispatch_worker` | Run `okstra codex-dispatch --project-root <root> --run-manifest <path>`; use `--dry-run` first when the core requires a dispatch preview. |
29
- | `await_workers` | Treat the synchronous dispatch return plus team-state terminal records and Result Paths as completion evidence. |
30
- | `redispatch_worker` | Invoke a fresh `okstra codex-dispatch` worker attempt for the selected role and record the retry/reverify dispatch kind. |
28
+ | `dispatch_worker` | Dispatch every `runner=native-session` assignment with the current Codex host's native worker/session primitive. Pass only `runner=cli-wrapper` assignments to `okstra codex-dispatch --project-root <root> --run-manifest <path> --workers <ids>`; use `--dry-run` first when the core requires a dispatch preview. |
29
+ | `await_workers` | Await native host workers through the host primitive and CLI workers through synchronous dispatch, then verify team-state terminal records and Result Paths for both. |
30
+ | `redispatch_worker` | Start a fresh native worker or `okstra codex-dispatch` attempt according to the persisted assignment's `runner`, and record the retry/reverify dispatch kind. |
31
31
  | `shutdown_workers` | Perform process cleanup when a wrapper remains live; otherwise this operation is a no-op recorded in state. |
32
32
  | `record_lead_event` | Append the required structured event to the manifest-provided `leadEventsPath`; emit the matching user-facing `PROGRESS:` line. |
33
33
  | `collect_usage` | Collect artifact/rollout-backed usage through the existing Okstra token-usage path; never read Claude session JSONL as a substitute. |
@@ -36,12 +36,12 @@ This adapter maps the neutral Okstra lead operations to the Codex artifact-first
36
36
 
37
37
  - For convergence reverify, consume the persisted round plan exactly. This adapter may map and transport each returned batch, but it cannot change batch membership and does not classify findings or branch on task type, provider, or model identity.
38
38
  - Do not invoke Claude Code team or subagent tools.
39
- - The prepared run manifest and team-state are the dispatch authority. Unsupported explicitly requested workers fail; an adapter must not silently change the roster.
40
- - Report-writer execution keeps the existing explicit opt-in policy in this milestone: dispatch requires `--enable-codex-report-writer` and an explicit `--report-writer-codex-model` value.
39
+ - The prepared run manifest and team-state are the dispatch authority. A `runner=native-session` assignment stays in the current Codex host; a `runner=cli-wrapper` assignment uses the registered provider wrapper. Unsupported explicitly requested workers fail; an adapter must not silently change the roster.
40
+ - The report-writer follows its persisted provider, model, and runner assignment exactly. It has no Codex-only provider override or separate opt-in gate.
41
41
  - Reverify and critic retries invoke a fresh worker attempt and persist the core-supplied `dispatchKind` (`reverify-r<N>` or `critic`) in the dispatch record; never reuse a prior rollout as a new vote.
42
42
  - Report-writer completion requires both the data.json Result Path and the worker-results audit path, even when the synchronous dispatch command exits successfully.
43
43
 
44
44
  ## Completion, cleanup, and resume
45
45
 
46
- - A successful synchronous dispatch return alone is insufficient; verify terminal state, every required completion path, and the corresponding worker-dispatch audit record before counting the worker as complete.
46
+ - A native host completion or successful synchronous CLI return alone is insufficient; verify terminal state, every required completion path, and the corresponding worker-dispatch audit record before counting the worker as complete.
47
47
  - Resume from run artifacts and lead-events checkpoints. Do not invent a Claude session id.
@@ -397,7 +397,7 @@ After persistence, reply briefly in the resolved Report Language with: completio
397
397
  ## Run-scoped worker-resource lifecycle
398
398
 
399
399
  - At run start, call the selected adapter's setup required to distinguish lead-owned resources from worker-owned resources.
400
- - Before every new worker batch, and between worker rounds within a phase, close the prior round's completed teammate resources before the next dispatch never the lead and never an in-flight worker; call `record_lead_event` for the batch-cleanup checkpoint. The round-boundary teammate reclaim primitive is the selected adapter's.
400
+ - Before every new worker batch, and between worker rounds within a phase, close **every** resource the prior round's completed workers still hold — display surfaces, roster entries, and live execution handles alike — before the next dispatch; never the lead and never an in-flight worker. Call `record_lead_event` for the batch-cleanup checkpoint. Which resources exist and how each one is released is the selected adapter's mapping.
401
401
  - After Phase 7 persistence and `collect_usage`, enumerate residual adapter-owned resources. If none remain, skip the question.
402
402
  - If resources remain, call `prompt_user` once with a binary keep-or-clean choice. The answer controls the entire residual set; do not ask a second backend-specific cleanup question.
403
403
  - On keep, preserve all resources and provide the selected adapter's manual-cleanup instruction.
@@ -138,7 +138,7 @@ The remaining numbered-section guide exists only for rendering or diagnosing his
138
138
 
139
139
  ### Report Header
140
140
 
141
- Milestone 1 keeps the final-report schema unchanged. Read the exact permitted values for `header.reportOwner` and `header.reportAuthor` from the task bundle's `instruction-set/final-report-schema.json` excerpt of `schemas/final-report-v1.0.schema.json`, then write those schema v1 compatibility values according to the actual authorship path. Do not derive either header field from the selected runtime's lead role. Runtime identity remains visible in the execution-status row and team-state audit fields.
141
+ Read the exact permitted values for `header.reportOwner` and `header.reportAuthor` from the task bundle's `instruction-set/final-report-schema.json` excerpt, then write those values according to the actual authorship path. The current v2 contract uses the provider-neutral `Okstra lead`; a legacy v1 excerpt may retain historical compatibility values. Do not derive either header field from the selected runtime's provider-specific lead label. Runtime identity remains visible in the execution-status row and team-state audit fields.
142
142
 
143
143
  ```markdown
144
144
  # <task-key> - Multi-Agent Cross Verification Final Report
@@ -2,7 +2,7 @@
2
2
  Single source for the executor's Coding-conventions preflight gate. Two delivery
3
3
  paths converge here (see _implementation-executor.md "Pre-implementation context
4
4
  exploration"):
5
- - Claude executor reads this file directly before its first Edit / Write.
5
+ - The native-session executor reads this file directly before its first edit.
6
6
  - codex / antigravity executor cannot read this path (it sits outside the CLI
7
7
  sandbox and the CLI only sees its stdin prompt), so the lead appends this
8
8
  file's body into the persisted executor prompt at dispatch time.
@@ -5,13 +5,13 @@ Edit here once; every profile picks the change up at next render. Do NOT
5
5
  add phase-specific rules to this file — phase rules stay in the per-
6
6
  profile document.
7
7
  -->
8
- - Team contract (shared): roster roles, model-assignment rules, dispatch invariants, and required-worker attempt rules are canonical in the team contract (`prompts/lead/team-contract.md`). Two consequences every phase honours: `Claude lead` is synthesis-only (in `implementation`, distinct from the `Executor` and verifiers), and unnamed generic parallel workers never replace or extend the per-profile `Required workers:` roster. Prep-time model recommendations come from the catalog defaults in `okstra_ctl.models` (e.g. `Codex worker` → `gpt-5.6-sol`); at dispatch time the task-manifest's materialized assignment is the only source — there is no dispatch-time fallback.
8
+ - Team contract (shared): roster roles, model-assignment rules, dispatch invariants, and required-worker attempt rules are canonical in the team contract (`prompts/lead/team-contract.md`). Two consequences every phase honours: the host-native Okstra lead is synthesis-only (in `implementation`, distinct from the `Executor` and verifiers), and unnamed generic parallel workers never replace or extend the per-profile `Required workers:` roster. Prep-time model recommendations come from the catalog defaults in `okstra_ctl.models` (for example, `Codex worker` → `gpt-5.6-sol`); at dispatch time the task-manifest's materialized assignment is the only source — there is no dispatch-time fallback.
9
9
  - Worker interaction model (shared — read before inferring behaviour from the roster):
10
10
  - the per-profile `Required workers:` block is a **roster**, not a behaviour contract. Each role's interaction mode changes across operating phases of the same run.
11
- - **Phase 4 / 5 (independent analysis)**: analyser workers (`claude`, `codex`, `antigravity` when opted in) produce findings independently and have no access to one another's outputs. `report-writer` does not analyse.
11
+ - **Phase 4 / 5 (independent analysis)**: every analyser in the resolved provider assignment roster produces findings independently and has no access to another worker's output. `report-writer` does not analyse.
12
12
  - **Phase 5.5 (convergence — peer review by workers)**: workers peer-review each other's findings across up to `effectiveMaxRounds` rounds; the lead mediates but does not vote. See `prompts/lead/convergence.md` for the round protocol (replay of findings, `AGREE` / `DISAGREE` / `SUPPLEMENT` verdicts), queue invariants, and final classification (`full-consensus` / `partial-consensus` / `contested` / `worker-unique`). For `requirements-discovery`, `error-analysis`, `implementation-planning`, `project-analysis`, `feature-analysis`, and `change-impact-analysis` this phase runs in **adversarial mode** (`convergence.adversarial=true`): verifiers try to refute each finding against its cited evidence and the burden of proof sits on the claim — see that skill's §"Adversarial Verification Mode".
13
13
  - Do NOT conclude "no peer review happens" from the roster alone — every profile that lists ≥2 analyser workers runs convergence by default (`convergence.enabled=true` in `task-manifest.json`).
14
- - **provider-unavailable fallback (tolerance).** A worker dispatch can fail to produce a result for two distinct reasons, and both take the same recovery path. (1) **Pane budget:** the dispatch is rejected with `no room for another tmux split` (or an equivalent teammate-pane creation failure). (2) **Sandbox CLI-start failure (non-tmux path):** an external CLI worker wrapper exits non-zero within seconds with empty stdout and its live-log shows `operation not permitted` (e.g. `agy`'s `listen tcp 127.0.0.1:0: bind` under a seatbelt sandbox around a non-tmux subagent's Bash tool). In either case the lead retries that worker in-process without `run_in_background`; if it was an external CLI worker, the lead instead **substitutes** an in-process `claude` analysis. Either way the lead records the substitution as provider unavailable in the run log and the convergence notes. Completed external-CLI worker trace panes are reclaimed automatically by the `SubagentStop` hook, but okstra cannot directly reclaim the teammate panes the harness creates, so this fallback is the last line of defence against pane-budget exhaustion and sandbox-blocked CLI workers. (This is a prompt instruction, not a code-enforced gate.)
14
+ - **provider-unavailable fallback (tolerance).** A worker dispatch can fail to produce a result for two distinct reasons, and both take the same recovery path. (1) **Pane budget:** the dispatch is rejected with `no room for another tmux split` (or an equivalent teammate-pane creation failure). (2) **Sandbox CLI-start failure (non-tmux path):** an external CLI worker wrapper exits non-zero within seconds with empty stdout and its live-log shows `operation not permitted`. In either case the lead spends the one shared retry budget through the assignment's recorded runner. If the provider is still unavailable, record that terminal status and continue only under the convergence quorum rules; never replace it silently with a fixed provider or count a substitute as the original provider's vote. Completed external-CLI trace panes are reclaimed by the selected runtime adapter's resource lifecycle. (This is a prompt instruction, not a code-enforced gate.)
15
15
  - Dual-audience final-report contract (shared):
16
16
  - data.json is the sole authored report artifact. AI handoff Markdown and human HTML are independently derived from it; neither derived artifact is the other's source.
17
17
  - User-facing information belongs in `humanSummary` and the selected task block's `userNarrative`; it must not exist only in Markdown. The HTML human main body explains the result with those fields plus task facts.
@@ -82,7 +82,7 @@ profile document.
82
82
  - `verdictCard.verdictToken` and `.direction` MUST byte-match `finalVerdict`; next-step routing must agree with `recommendedNextSteps[0]`. Schema-v1 Markdown renders the visible `## Verdict Card` / `## 7. Final Verdict` pair. Schema-v2 derives the AI handoff and human summary from the data fields without repeating both visible sections. **Enforced:** `validators/validate-run.py` `_validate_verdict_card_consistency` and `_validate_verdict_card_fields`.
83
83
  - Cross-worker traceability (shared — applies to every analysis worker output and to the lead's `## 6.` / `## 2.` tables in the final-report):
84
84
  - **Worker-side item IDs (free-form but unique within the worker).** Every row item in sections 1–5 (and any optional section 6) of an analysis worker's output MUST carry an item ID that is unique within that one worker's result file. The ID convention is the worker's choice — `F-001` / `F-002` per the suggested schema, `1.1` / `1.2` / `1.3` as Codex tends to use, or any other shape — but it MUST appear as the leading column of the row (for table-form items) or as a `[<ID>]` prefix (for bullet/numbered items). Workers that emit findings without IDs make cross-worker reconciliation impossible.
85
- - **Lead-side ID assignment + source preservation.** When the lead (or `report-writer-worker`) synthesises consensus, difference, or primary-evidence rows from worker outputs, the lead assigns a fresh `C-NNN` / `D-NNN` / `E-NNN` row ID. Each `sourceItems` field MUST list every contributing worker:item pair (e.g. `claude:F-001`, `codex:1.1`, `antigravity:F-3`) so an agent can trace the synthesised row to the worker result. Bare worker names are rejected. **Enforced:** `schemas/final-report-v2.0.schema.json` `$defs.SourceItem` pins each entry to `^[a-z][a-z-]*:[A-Za-z0-9._-]+$`, and `ConsensusRow` / `PrimaryEvidenceRow` require non-empty `sourceItems`.
85
+ - **Lead-side ID assignment + source preservation.** When the lead (or `report-writer-worker`) synthesises consensus, difference, or primary-evidence rows from worker outputs, the lead assigns a fresh `C-NNN` / `D-NNN` / `E-NNN` row ID. Each `sourceItems` field MUST list every contributing worker:item pair (e.g. `claude:F-001`, `codex:1.1`, `grok:F-3`, `kimi:2.4`) so an agent can trace the synthesised row to the worker result. Bare worker names are rejected. **Enforced:** `schemas/final-report-v2.0.schema.json` `$defs.SourceItem` pins each entry to `^[a-z][a-z-]*:[A-Za-z0-9._-]+$`, and `ConsensusRow` / `PrimaryEvidenceRow` require non-empty `sourceItems`.
86
86
  - Audit sidecar (shared): Reading Confirmation placement follows the audience-selected preamble named by `**Worker Preamble Path:**`. Profiles do not restate it; the main worker-results body starts at section 1.
87
87
 
88
88
  - Markdown authoring (shared — applies to markdown documents not already governed by an okstra template/schema):