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.
- package/README.md +21 -7
- package/docs/architecture/storage-model.md +34 -61
- package/docs/architecture.md +51 -49
- package/docs/cli.md +38 -21
- package/docs/for-ai/skills/okstra-run.md +13 -34
- package/docs/performance-improvement-plan-v2.md +2 -2
- package/docs/pr-template-usage.md +1 -1
- package/docs/project-structure-overview.md +10 -8
- package/docs/task-process/README.md +4 -4
- package/docs/task-process/common-flow.md +12 -12
- package/docs/task-process/final-verification.md +2 -2
- package/docs/task-process/implementation.md +1 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +2 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/agents/workers/codex-worker.md +2 -2
- package/runtime/agents/workers/grok-worker.md +256 -0
- package/runtime/agents/workers/kimi-worker.md +256 -0
- package/runtime/agents/workers/report-writer-worker.md +2 -2
- package/runtime/bin/lib/okstra/cli.sh +13 -1
- package/runtime/bin/lib/okstra/globals.sh +3 -0
- package/runtime/bin/lib/okstra/usage.sh +17 -12
- package/runtime/bin/okstra-grok-exec.sh +5 -0
- package/runtime/bin/okstra-kimi-exec.sh +5 -0
- package/runtime/bin/okstra-provider-exec.py +235 -0
- package/runtime/bin/okstra.sh +3 -0
- package/runtime/prompts/lead/adapters/antigravity.md +48 -0
- package/runtime/prompts/lead/adapters/claude-code.md +13 -11
- package/runtime/prompts/lead/adapters/codex.md +7 -7
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +4 -4
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
- package/runtime/prompts/profiles/_implementation-executor.md +12 -12
- package/runtime/prompts/profiles/_implementation-self-check.md +4 -4
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
- package/runtime/prompts/profiles/change-impact-analysis.md +2 -0
- package/runtime/prompts/profiles/error-analysis.md +2 -0
- package/runtime/prompts/profiles/feature-analysis.md +2 -0
- package/runtime/prompts/profiles/final-verification.md +3 -1
- package/runtime/prompts/profiles/forbidden-actions.json +4 -4
- package/runtime/prompts/profiles/implementation-planning.md +3 -1
- package/runtime/prompts/profiles/implementation.md +2 -2
- package/runtime/prompts/profiles/improvement-discovery.md +3 -1
- package/runtime/prompts/profiles/project-analysis.md +2 -0
- package/runtime/prompts/profiles/release-handoff.md +7 -7
- package/runtime/prompts/profiles/requirements-discovery.md +2 -0
- package/runtime/prompts/wizard/prompts.ko.json +9 -1
- package/runtime/python/okstra_ctl/codex_dispatch.py +68 -87
- package/runtime/python/okstra_ctl/dispatch_core.py +4 -22
- package/runtime/python/okstra_ctl/lead_events.py +1 -1
- package/runtime/python/okstra_ctl/lead_runtime.py +13 -2
- package/runtime/python/okstra_ctl/models.py +156 -8
- package/runtime/python/okstra_ctl/path_hints.py +9 -25
- package/runtime/python/okstra_ctl/paths.py +1 -1
- package/runtime/python/okstra_ctl/render.py +172 -74
- package/runtime/python/okstra_ctl/report_html/common.py +38 -2
- package/runtime/python/okstra_ctl/report_html/filters.py +104 -0
- package/runtime/python/okstra_ctl/report_html/render.py +7 -0
- package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +2 -1
- package/runtime/python/okstra_ctl/report_html/visualizations.py +32 -6
- package/runtime/python/okstra_ctl/run.py +264 -45
- package/runtime/python/okstra_ctl/runner_resolution.py +103 -0
- package/runtime/python/okstra_ctl/team.py +2 -7
- package/runtime/python/okstra_ctl/wizard.py +194 -21
- package/runtime/python/okstra_ctl/worker_artifacts.py +46 -0
- package/runtime/python/okstra_ctl/workers.py +3 -1
- package/runtime/python/okstra_ctl/workflow.py +4 -2
- package/runtime/python/okstra_token_usage/__init__.py +1 -0
- package/runtime/python/okstra_token_usage/collect.py +32 -23
- package/runtime/python/okstra_token_usage/pricing.py +35 -3
- package/runtime/schemas/final-report-v2.0.schema.json +2 -2
- package/runtime/skills/okstra-run/SKILL.md +31 -42
- package/runtime/templates/prd/pr-body.template.md +1 -1
- package/runtime/templates/reports/html/assets/base.css +6 -3
- package/runtime/templates/reports/html/base.template.html +22 -8
- package/runtime/templates/reports/html/macros/forms.html +2 -2
- package/runtime/templates/reports/html/macros/layout.html +5 -5
- package/runtime/templates/reports/html/macros/visualizations.html +11 -1
- package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +11 -11
- package/runtime/templates/reports/html/tasks/error-analysis.template.html +12 -12
- package/runtime/templates/reports/html/tasks/feature-analysis.template.html +9 -9
- package/runtime/templates/reports/html/tasks/final-verification.template.html +7 -7
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +12 -12
- package/runtime/templates/reports/html/tasks/implementation.template.html +7 -7
- package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +6 -6
- package/runtime/templates/reports/html/tasks/project-analysis.template.html +11 -11
- package/runtime/templates/reports/html/tasks/release-handoff.template.html +5 -5
- package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +8 -8
- package/runtime/templates/reports/report.js +21 -4
- package/runtime/templates/reports/settings.template.json +4 -0
- package/runtime/templates/reports/task-brief.template.md +7 -7
- package/runtime/validators/validate-run.py +11 -6
- package/runtime/validators/validate_session_conformance.py +2 -1
- package/src/cli-registry.mjs +4 -4
- package/src/commands/execute/codex-dispatch.mjs +7 -10
- package/src/commands/execute/render-bundle.mjs +3 -3
- package/src/commands/execute/run.mjs +17 -52
- package/src/commands/execute/wizard.mjs +4 -1
- package/src/commands/lifecycle/doctor.mjs +6 -3
- package/src/commands/lifecycle/install.mjs +31 -8
- package/src/lib/runtime-manifest.mjs +1 -1
- package/src/lib/runtime-resolver.mjs +2 -2
- 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-
|
|
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
|
|
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
|
|
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
|
-
|
|
91
|
-
--lead-
|
|
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
|
-
|
|
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
|
-
|
|
125
|
-
Report writer worker:
|
|
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,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))
|
package/runtime/bin/okstra.sh
CHANGED
|
@@ -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` | `
|
|
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`;
|
|
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` |
|
|
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
|
|
40
|
-
- For
|
|
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
|
-
-
|
|
46
|
-
-
|
|
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"
|
|
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`
|
|
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
|
|
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
|
|
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` | `
|
|
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` |
|
|
29
|
-
| `await_workers` |
|
|
30
|
-
| `redispatch_worker` |
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
-
|
|
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:
|
|
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
|
|
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
|
|
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`, `
|
|
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):
|