okstra 0.163.2 → 0.164.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 +7 -5
- package/docs/architecture.md +12 -7
- package/docs/cli.md +8 -5
- package/docs/project-structure-overview.md +7 -5
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -0
- package/runtime/bin/lib/okstra/cli.sh +5 -0
- package/runtime/bin/lib/okstra/globals.sh +2 -0
- package/runtime/bin/lib/okstra/usage.sh +5 -5
- package/runtime/bin/okstra.sh +87 -91
- package/runtime/prompts/lead/adapters/cmux.md +1 -1
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -0
- package/runtime/prompts/profiles/implementation-planning.md +1 -1
- package/runtime/python/okstra_ctl/adapters/accounting/__init__.py +11 -0
- package/runtime/python/okstra_ctl/adapters/accounting/claude_jsonl.py +17 -0
- package/runtime/python/okstra_ctl/adapters/accounting/cli_artifact.py +17 -0
- package/runtime/python/okstra_ctl/adapters/accounting/unavailable.py +19 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/__init__.py +92 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/cli_wrapper.py +54 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/cmux.py +68 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/native_team.py +13 -0
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +60 -0
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/manifest.json +1 -0
- package/runtime/{prompts/lead/adapters/antigravity.md → python/okstra_ctl/adapters/hosts/antigravity/relay.md} +52 -0
- package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +292 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +120 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/manifest.json +1 -0
- package/runtime/{prompts/lead/adapters/claude-code.md → python/okstra_ctl/adapters/hosts/claude-code/relay.md} +111 -0
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +60 -0
- package/runtime/python/okstra_ctl/adapters/hosts/codex/manifest.json +1 -0
- package/runtime/{prompts/lead/adapters/codex.md → python/okstra_ctl/adapters/hosts/codex/relay.md} +52 -0
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +72 -0
- package/runtime/python/okstra_ctl/adapters/hosts/external/manifest.json +1 -0
- package/runtime/{prompts/lead/adapters/external.md → python/okstra_ctl/adapters/hosts/external/relay.md} +52 -0
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +63 -0
- package/runtime/python/okstra_ctl/adapters/hosts/grok/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +90 -0
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +63 -0
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/relay.md +90 -0
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +35 -0
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +55 -0
- package/runtime/python/okstra_ctl/adapters/providers/claude/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +43 -0
- package/runtime/python/okstra_ctl/adapters/providers/codex/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +32 -0
- package/runtime/python/okstra_ctl/adapters/providers/grok/manifest.json +1 -0
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +38 -0
- package/runtime/python/okstra_ctl/adapters/providers/kimi/manifest.json +1 -0
- package/runtime/python/okstra_ctl/application/__init__.py +1 -0
- package/runtime/python/okstra_ctl/application/advance_wizard.py +25 -0
- package/runtime/python/okstra_ctl/application/collect_usage.py +15 -0
- package/runtime/python/okstra_ctl/application/dispatch_assignments.py +15 -0
- package/runtime/python/okstra_ctl/application/resolve_assignment.py +93 -0
- package/runtime/python/okstra_ctl/application/resume_run.py +21 -0
- package/runtime/python/okstra_ctl/application/start_run.py +21 -0
- package/runtime/python/okstra_ctl/codex_dispatch.py +49 -826
- package/runtime/python/okstra_ctl/dispatch_core.py +240 -29
- package/runtime/python/okstra_ctl/dispatch_state.py +17 -0
- package/runtime/python/okstra_ctl/domain/__init__.py +34 -0
- package/runtime/python/okstra_ctl/domain/host.py +100 -0
- package/runtime/python/okstra_ctl/domain/provider.py +66 -0
- package/runtime/python/okstra_ctl/domain/wizard/__init__.py +19 -0
- package/runtime/python/okstra_ctl/domain/wizard/interaction.py +140 -0
- package/runtime/python/okstra_ctl/entrypoints/__init__.py +1 -0
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +334 -0
- package/runtime/python/okstra_ctl/models.py +54 -269
- package/runtime/python/okstra_ctl/ports/__init__.py +15 -0
- package/runtime/python/okstra_ctl/ports/host.py +32 -0
- package/runtime/python/okstra_ctl/ports/interaction.py +15 -0
- package/runtime/python/okstra_ctl/ports/lead_session.py +25 -0
- package/runtime/python/okstra_ctl/ports/usage_accounting.py +23 -0
- package/runtime/python/okstra_ctl/ports/worker_dispatch.py +32 -0
- package/runtime/python/okstra_ctl/registry/__init__.py +13 -0
- package/runtime/python/okstra_ctl/registry/factory_loader.py +32 -0
- package/runtime/python/okstra_ctl/registry/host_discovery.py +124 -0
- package/runtime/python/okstra_ctl/registry/host_registry.py +365 -0
- package/runtime/python/okstra_ctl/registry/provider_registry.py +149 -0
- package/runtime/python/okstra_ctl/render.py +145 -47
- package/runtime/python/okstra_ctl/run.py +80 -58
- package/runtime/python/okstra_ctl/session.py +1 -1
- package/runtime/python/okstra_ctl/team.py +44 -32
- package/runtime/python/okstra_ctl/wizard.py +162 -64
- package/runtime/python/okstra_ctl/worker_audit_ledger.py +29 -4
- package/runtime/python/okstra_token_usage/collect.py +34 -6
- package/runtime/schemas/final-report-v1.0.schema.json +3989 -1085
- package/runtime/schemas/final-report-v2.0.schema.json +5622 -1451
- package/runtime/skills/okstra-run/SKILL.md +73 -28
- package/runtime/templates/implementation-worker-preamble.md +1 -1
- package/runtime/templates/reports/final-report.template.md +3 -3
- package/runtime/templates/reports/html/i18n/en.json +3 -1
- package/runtime/templates/reports/html/i18n/ko.json +3 -1
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +1 -1
- package/runtime/templates/worker-prompt-preamble.md +1 -1
- package/runtime/validators/validate-implementation-plan-stages.py +17 -22
- package/runtime/validators/validate-report-views.py +0 -39
- package/runtime/validators/validate-run.py +8 -7
- package/runtime/validators/validate_session_conformance.py +7 -2
- package/src/commands/execute/render-bundle.mjs +4 -4
- package/src/commands/execute/run.mjs +8 -25
- package/src/commands/execute/wizard.mjs +33 -13
- package/src/commands/lifecycle/doctor.mjs +10 -10
- package/src/commands/lifecycle/install.mjs +53 -30
- package/src/commands/lifecycle/preflight.mjs +14 -4
- package/src/lib/host-registry-client.mjs +176 -0
- package/src/lib/runtime-manifest.mjs +6 -8
- package/runtime/python/okstra_ctl/lead_runtime.py +0 -115
- package/runtime/python/okstra_ctl/runner_resolution.py +0 -103
- package/src/lib/runtime-readiness.mjs +0 -90
- package/src/lib/runtime-resolver.mjs +0 -123
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<a id="purpose"></a>
|
|
21
21
|
## 1. Purpose
|
|
22
22
|
|
|
23
|
-
`okstra` is a **host-aware, multi-provider task runner that cross-verifies work with a lead + worker model**.
|
|
23
|
+
`okstra` is a **host-aware, multi-provider task runner that cross-verifies work with a lead + worker model**. A registered host adapter supplies the lead session, while a separate provider adapter supplies models and worker execution. Claude remains the compatibility default policy, not the owner of the lifecycle.
|
|
24
24
|
|
|
25
25
|
The design rests on three principles:
|
|
26
26
|
|
|
@@ -35,10 +35,12 @@ Role assignments are persisted separately from the host runtime, but the lead pr
|
|
|
35
35
|
| Claude | Claude Code | `okstra-claude-exec.sh` | lead, analysis, implementation, report writer |
|
|
36
36
|
| Codex | Codex | `okstra-codex-exec.sh` | lead, analysis, implementation, report writer |
|
|
37
37
|
| Antigravity | Antigravity CLI | `okstra-antigravity-exec.sh` | lead, analysis, and implementation |
|
|
38
|
-
| Grok |
|
|
39
|
-
| Kimi |
|
|
38
|
+
| Grok | Grok CLI | `okstra-grok-exec.sh` | lead, read-only analyser, and critic |
|
|
39
|
+
| Kimi | Kimi CLI | `okstra-kimi-exec.sh` | lead, read-only analyser, and critic |
|
|
40
40
|
|
|
41
|
-
Grok and Kimi
|
|
41
|
+
Grok and Kimi can now lead through their registered host adapters. Their worker roles remain read-only analyser and critic; executor, verifier, and report-writer roles were not added. A real authenticated local invocation still depends on the corresponding CLI being installed and configured.
|
|
42
|
+
|
|
43
|
+
`okstra run <host-id-or-alias>` resolves the requested adapter from the host registry and checks its `spawn-process` readiness before starting it. The installed `okstra-run` skill instead uses `current-session`: it declares only the semantic functions the live harness can actually perform (`plain_text_input`, plus any available native single-select, multi-select, or grouped-question function) and reuses the current lead session. User-installed adapters are discovered only from `~/.okstra/adapters/hosts/<id>/` and `~/.okstra/adapters/providers/<id>/`; project-local executable adapter code is ignored.
|
|
42
44
|
|
|
43
45
|
The next direct-CLI candidates are Mistral Vibe and Qwen Code. DeepSeek V4, GLM-5.2, and MiniMax M2.7 remain API-adapter candidates because their current official coding-agent execution surfaces do not fit Okstra's wrapper contract as directly. Newly published provider models are exposed only after the corresponding local CLI reports them; this keeps model discovery separate from marketing availability.
|
|
44
46
|
|
|
@@ -225,7 +227,7 @@ Major workflow changes added to `main` after 0.8.0:
|
|
|
225
227
|
- **`release-handoff` lifecycle phase** — runs immediately after `final-verification` returns `verdict=accepted`. The current Okstra lead drafts the candidate messages and PR body inline, then uses the selected host adapter's user-prompt operation for the delivery choices. Only the Git/GitHub CLI commands selected through those menus are run. Force pushes, direct pushes to the base branch, hook bypasses (`--no-verify`), and release publication (`gh release`, `npm publish`, and similar commands) are prohibited. This phase does not edit source code. Profile: [`prompts/profiles/release-handoff.md`](prompts/profiles/release-handoff.md).
|
|
226
228
|
- **Configurable PR body template** (release-handoff) — PR bodies are populated from a Markdown template selected in this order: one-time override (`--pr-template-path` or the okstra-run Step 6 prompt) → `prTemplatePath` in `<project_root>/.okstra/project.json` → `prTemplatePath` in `~/.okstra/config.json` → the skill default at `~/.claude/skills/templates/prd/pr-body.template.md`. Register a template with `okstra config set pr-template-path <path> [--scope project|global]`; project scope accepts a path relative to the project root, while global scope requires an absolute path or a path beginning with `~/`. `okstra config get pr-template-path --scope all` prints every scoped value and the effective winner. The default template contains `## Summary`, `## Changes`, `## Test plan`, and `## Linked issues`, plus HTML comment guidance that the lead removes immediately before PR creation.
|
|
227
229
|
- **Profile worker-roster validation** — `--workers <csv>` and the okstra-run Step 6 worker prompt accept only the worker IDs declared in the selected profile's `Required workers:` block. Requesting a worker absent from the profile—for example, `codex` or `antigravity` for `release-handoff`—fails with a clear error, and the interactive prompt shows only workers accepted by that profile.
|
|
228
|
-
- **Host-aware lead adapters** —
|
|
230
|
+
- **Host-aware lead adapters** — `okstra-run` resolves the current harness through the same dynamic host registry used by the terminal front door. Claude Code, Codex, Antigravity, Grok, and Kimi keep their matching provider assignment native; `external` remains the explicit all-CLI host. Host and provider are separate axes, and every non-native worker assignment uses its provider's registered CLI wrapper. `leadAssignment` and every `workerAssignments[]` row record provider, model, and `runner`. `okstra codex-run` and `okstra codex-dispatch` remain low-level artifact/dispatch commands.
|
|
229
231
|
- **Multi-stage `implementation-planning` / `implementation`** — `implementation-planning` always produces a Stage Map and N stage sections. Each stage has no more than six steps, and stages with `depends-on (none)` can be implemented concurrently in separate `implementation` runs. Each `implementation` invocation runs a single stage, selected with `--stage <auto|N>`, and creates an evidence sidecar at `carry/stage-<N>.json` for automatic carry-in to the next stage. The `implementation-planning` run directory accumulates `consumers.jsonl` reverse links that record which run consumed each stage.
|
|
230
232
|
- **AI-prepared design preparation (implementation-planning → implementation)** — `implementation-planning` detects which stages need design input (domain contract, DB/table schema, external interface, transaction/consistency, transformation mapping, lifecycle, rollout/observability, manual user test) and has the AI draft a concrete proposal first, instead of handing the user an empty design document. Each item is assessed as `ready`, `provisional`, `blocked`, or `not-applicable`; a simple task may declare `no-design-inputs`. Phase 7 materializes an Okstra-owned request under `design-prep-requests/`, and `okstra design-prep <list|show|write>` or the okstra-run wizard records the confirmed answer as an **append-only** revision under `design-prep-inputs/`—neither path ever edits the approved planning snapshot. Before creating its worktree, `implementation` resolves only the items its selected stage cites in `stageRefs`: safe `provisional` assumptions are injected into the executor prompt so work proceeds, while an unsafe open decision makes only that stage wait or replan. A markerless legacy plan continues with a `legacy-unassessed` warning. Storage authorities: [`docs/architecture/storage-model.md`](docs/architecture/storage-model.md). CLI: [`docs/cli.md#okstra-design-prep`](docs/cli.md#okstra-design-prep).
|
|
231
233
|
- **Phase 6 plan-body verification (implementation-planning only)** — Immediately after the report-writer worker drafts the final report and before the user approval gate, the lead performs one post-verification round. It extracts `P-Opt-*`, `P-Step-*`, `P-Dep-*`, `P-Val-*`, and `P-Rb-*` plan items from the synthesized `## 5.5` implementation plan deliverables and asks every analyzer worker for an `AGREE`, `DISAGREE(a-e)`, or `SUPPLEMENT` verdict. The aggregate result is `passed`, `passed-with-dissent`, `blocked-by-disagreement`, or `aborted-non-result`. The frontmatter `approved` field is always published as `false`; a blocking result keeps it false and becomes a row in `## 1. Clarification Items`. For fast iteration, opt out with `--no-plan-verification`. Contract details: the "Plan-body verification mode" section of [`prompts/lead/convergence.md`](prompts/lead/convergence.md) and [`docs/cli.md#--no-plan-verification`](docs/cli.md#--no-plan-verification).
|
package/docs/architecture.md
CHANGED
|
@@ -16,7 +16,7 @@ Its core capabilities at a glance are:
|
|
|
16
16
|
- **Profiles by task type**: Loads standard task-type profiles such as `requirements-discovery`, `error-analysis`, `implementation-planning`, `implementation`, `final-verification`, and `release-handoff` to render the instruction set.
|
|
17
17
|
- **Run lifecycle**: Non-stage runs use `runs/<task-type>/` as the run directory, while `implementation` and single-stage `final-verification` use `runs/<task-type>/stage-<N>/`. Manifests, prompts, state, reports, sessions, worker results, and logs accumulate beneath the resolved run directory, and the filename suffix `-<task-type>-<seq>` separates reruns of the same phase.
|
|
18
18
|
- **Single python authority**: All prepare wiring—resolving profiles/workers/models, computing paths, rendering, and central record_start—is concentrated in a single function, [`okstra_ctl.run.prepare_task_bundle()`](../scripts/okstra_ctl/run.py). `okstra.sh` and the `okstra-run` skill are thin callers of that same function and do not pass state through environment variables. Task identity, paths, and workflow state are recalculated from authoritative on-disk files every time.
|
|
19
|
-
- **Host-aware handoff**: Claude Code, Codex, and
|
|
19
|
+
- **Host-aware handoff**: Claude Code, Codex, Antigravity, Grok, and Kimi can keep their current native session as the lead. The standalone compatibility launcher still starts a new `claude` process by default, while the external adapter uses registered CLI wrappers. Every path consumes the same `prepare_task_bundle` outputs.
|
|
20
20
|
- **Required team contract**: The `Required workers:` block in each phase profile is authoritative for the roster. General analysis phases use Claude/Codex analysers plus a report writer by default, while Antigravity, Grok, and Kimi are included only when allowed by both the profile and `--workers`. Lead-oriented phases such as `release-handoff` have separate rosters.
|
|
21
21
|
- **User-home install + project-local task bundles**: One `npx okstra@latest install` command installs the runtime (`~/.okstra/{lib/python, bin, templates, prompts}`) and installs public skills to `~/.agents/skills/` by default. If `~/.claude` exists, it also installs Claude skills and six worker agent definitions (`~/.claude/agents/*-worker.md`). Only user entry-point skills are exposed in the skill list; lead/support operating contracts are installed as runtime resources under `~/.okstra/prompts/` and are not discoverable as skills. Global conversation memory is stored separately from projects under `~/.okstra/memory-book/`. Task bundles and discovery metadata are stored under `.okstra/` in the target project. **In addition, `<PROJECT_ROOT>/.claude/settings.local.json` is provisioned as a symlink to `~/.okstra/templates/settings.local.json`** (`okstra setup` or `okstra-ctl` prepare manages it idempotently; if a regular file already existed, it is preserved as `.bak.<timestamp>` before replacement).
|
|
22
22
|
- **Resume and clarification**: Supports resuming the same task and responding to follow-up questions from the lead through `--task-key`, `--resume-clarification`, and `--clarification-response`.
|
|
@@ -149,9 +149,9 @@ Runtime entry points are consolidated in Python packages. Bash and skills only c
|
|
|
149
149
|
- `prompts/lead/adapters/claude-code.md`, `prompts/lead/adapters/codex.md`, `prompts/lead/adapters/antigravity.md`, and `prompts/lead/adapters/external.md` map the same semantic operations to one selected host runtime. The generated launch prompt exposes the core path plus exactly one adapter path.
|
|
150
150
|
- `prompts/lead/adapters/cmux.md` is selected by environment rather than by runtime: when the run manifest's `terminalBackend` is `cmux-pane`, every lead runtime resolves to it and dispatches through `okstra team`, because okstra owns the worker panes on that path instead of the host. It overrides only the adapter and the dispatch mode; the lead's agent, role, and session accounting still come from its own runtime.
|
|
151
151
|
- Runtime metadata and role assignments are persisted separately, but the lead provider is derived from the host: Claude Code maps to Claude, Codex maps to Codex, and Antigravity CLI maps to Antigravity. New runs persist `hostRuntime`, `leadAssignment`, and `workerAssignments[]`; each assignment records its provider, model, execution value, and resolved `native-session` or `cli-wrapper` runner. `lead-execution-prompt.md` is canonical, while `claude-execution-prompt.md` is a byte-identical compatibility alias for historical consumers.
|
|
152
|
-
-
|
|
152
|
+
- Host and provider registries remain separate: the active registered host owns the native lead session, while non-native providers run through their registered CLI wrappers.
|
|
153
153
|
- [`skills/okstra-setup/SKILL.md`](../skills/okstra-setup/SKILL.md) — **first-run bootstrap**. Runs `okstra install` and creates `project.json`.
|
|
154
|
-
- [`skills/okstra-run/SKILL.md`](../skills/okstra-run/SKILL.md) — host-aware in-session entry point that starts an okstra task in the current
|
|
154
|
+
- [`skills/okstra-run/SKILL.md`](../skills/okstra-run/SKILL.md) — host-aware in-session entry point that starts an okstra task in the current registered host session and calls `prepare_task_bundle` directly.
|
|
155
155
|
- Thirteen skills are user-invocable: `skills/okstra-setup/SKILL.md`, `skills/okstra-brief-gen/SKILL.md`, `skills/okstra-run/SKILL.md`, `skills/okstra-manager/SKILL.md`, `skills/okstra-memory/SKILL.md`, `skills/okstra-inspect/SKILL.md`, `skills/okstra-rollup/SKILL.md`, `skills/okstra-usage/SKILL.md`, `skills/okstra-schedule-gen/SKILL.md`, `skills/okstra-container-build/SKILL.md`, `skills/okstra-pr-gen/SKILL.md`, `skills/okstra-user-response/SKILL.md`, and `skills/okstra-code-review/SKILL.md`. Only these are copied into the agent skill home. They cover brief authoring, phase execution, cross-project manager task coordination, global Memory Book storage/search, read-side status/history/report/time/logs/cost/errors/recap, task-group-level aggregation of run results (rollup), project-wide historical resource usage, schedule support, local container deployment, PR description generation, clarification-response submission, and census-based code review of a stage or branch diff. `okstra-manager` uses `okstra manager` CLI JSON/launch packets as the source of truth, and stores manager-owned plans, assignments, directives, snapshots, and events under `~/.okstra/managers/<manager-id>/`. `okstra-rollup` is a read-side layer that fans the single-task aggregators from `okstra-inspect` (time/errors/recap) out to a task group or the whole project catalog. The `okstra rollup` CLI owns deterministic aggregation, while the skill (LLM) writes only the synthesized report summary. `okstra-usage` is a separate read-only resource snapshot grouped by lifecycle task type; it does not replace single-task `okstra-inspect` detail or the status/report digest from `okstra-rollup`. The canonical definition of `okstra-inspect` read-side facets is the subcommand table in `skills/okstra-inspect/SKILL.md`. `okstra-inspect logs` provides a read-only inventory and cleanup guidance for the live-log sidecars that the Codex/Antigravity wrappers write on every dispatch at the resolved `<run-dir>/prompts/<worker>-prompt-<phase>-<seq>.log`; for stage executions, the stage-qualified `run_dir` includes `stage-<N>/`. `okstra-inspect cost` summarizes `okstra context-cost`; `okstra-inspect errors` collects a task's okstra-run error logs into a timestamped Markdown error report and prints a summary; and `okstra-inspect recap` answers free-form questions about `.okstra` artifacts in addition to summarizing phases before and after each task run.
|
|
156
156
|
- Internal operating contracts—`context-loader` / `team-contract` / `convergence` / `report-writer` and the lead contract—have moved to `prompts/lead/*.md`. Language-specific coding preflight for implementation/verification workers has moved to `prompts/coding-preflight/*` (overview router + clean-code + three-stage language/framework/architecture selection). All are runtime resources installed under `~/.okstra/prompts/` and are not discoverable as skills. The generated launch prompt provides the lead with absolute paths, and reinstalling prunes the legacy exact-name skill directories `okstra-context-loader` / `okstra-team-contract` / `okstra-convergence` / `okstra-report-writer` / `okstra-coding-preflight` / `okstra`.
|
|
157
157
|
- Plugin manifest: [`../../.claude-plugin/plugin.json`](../.claude-plugin/plugin.json) — referenced by the supplementary `npx skills@latest add Devonshin/okstra` channel. Use `npx okstra@latest install` for normal setup. The plugin manifest exposes only the thirteen user entry points (`okstra-setup`, `okstra-brief-gen`, `okstra-run`, `okstra-manager`, `okstra-memory`, `okstra-inspect`, `okstra-rollup`, `okstra-usage`, `okstra-schedule-gen`, `okstra-container-build`, `okstra-pr-gen`, `okstra-user-response`, `okstra-code-review`).
|
|
@@ -263,7 +263,9 @@ Other variables such as `PROJECT_ID`, `TASK_GROUP`, `RUN_*`, `FINAL_*`, and `CLA
|
|
|
263
263
|
|
|
264
264
|
## Host runtime execution behavior
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
Host adapters and model providers are independent axes. The host registry discovers bundled adapters and explicit user installs under `~/.okstra/adapters/hosts/<id>/`; the provider registry uses the parallel `~/.okstra/adapters/providers/<id>/` root. Neither registry executes adapter code found in a project repository. A host descriptor names its native provider, while every non-native worker assignment resolves through the provider's CLI wrapper.
|
|
267
|
+
|
|
268
|
+
The terminal front door accepts `okstra run <host-id-or-alias>`, resolves it through `HostAdapterRegistry`, and probes `entry_mode="spawn-process"` readiness before starting that adapter's CLI. It does not infer the host from installed provider binaries and does not fall back to another host when the selected adapter is unavailable.
|
|
267
269
|
|
|
268
270
|
The legacy standalone shell path remains Claude-specific:
|
|
269
271
|
|
|
@@ -274,10 +276,13 @@ The legacy standalone shell path remains Claude-specific:
|
|
|
274
276
|
- `okstra.sh` performs only this compatibility handoff; the launched session continues under the same Okstra lead contract. Codex-hosted runs do not pass through this shell path.
|
|
275
277
|
|
|
276
278
|
**Mode B — the `okstra-run` skill hands off within the current host session**
|
|
277
|
-
- Use this when the user is already in
|
|
278
|
-
- The skill
|
|
279
|
+
- Use this when the user is already in a registered host and wants to start a new okstra task there.
|
|
280
|
+
- The skill probes `entry_mode="current-session"`, reads the selected relay contract, and declares only the semantic functions the live harness actually exposes: `plain_text_input`, `native_single_select`, `native_multi_select`, and `native_question_group` as available. The wizard receives their intersection with the adapter contract instead of inferring capabilities from a host name.
|
|
281
|
+
- The skill relays the wizard through that semantic interaction plan and calls `prepare_task_bundle(render_only=True)` with the explicit host runtime.
|
|
279
282
|
- It does not launch another lead process. The current host-native session reads `lead-execution-prompt.md` and assumes the Okstra lead role.
|
|
280
283
|
|
|
284
|
+
Kimi and Grok implement this same lead contract. Their provider roles remain `lead`, `analyser`, and `critic`; they do not gain executor, verifier, or report-writer roles from being lead-capable hosts.
|
|
285
|
+
|
|
281
286
|
Both modes create identical artifacts (task-manifest, run-manifest, timeline, instruction set, and central-index registration), so subsequent `okstra-ctl` commands (list / show / rerun / reconcile) operate consistently without distinguishing between them.
|
|
282
287
|
- The handed-off native host acts as the Okstra lead, responsible for orchestration and final synthesis.
|
|
283
288
|
- The default worker policy remains Claude + Codex analysis and a Claude report writer. Antigravity, Grok, and Kimi are profile-gated optional assignments; Grok and Kimi are read-only analyser/critic providers.
|
|
@@ -602,7 +607,7 @@ On this standalone compatibility path, `okstra.sh` finishes the prepare stage an
|
|
|
602
607
|
|
|
603
608
|
**Option B — hand off within the current host session (`okstra-run` skill)**
|
|
604
609
|
|
|
605
|
-
|
|
610
|
+
When using an installed host adapter, the current native session assumes the Okstra lead role without launching a replacement lead process. Example triggers: `"run okstra here"`, `"start error-analysis on this project"`.
|
|
606
611
|
|
|
607
612
|
Skill flow:
|
|
608
613
|
|
package/docs/cli.md
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
Base command for initial entry with full arguments:
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
scripts/okstra.sh [--render-only] [--yes] [--no-plan-verification] --task-type <task-type> [--workers worker1,worker2] [--lead-runtime
|
|
62
|
+
scripts/okstra.sh [--render-only] [--yes] [--no-plan-verification] --task-type <task-type> [--workers worker1,worker2] [--lead-runtime <host-id-or-alias>] [--lead-provider <provider>] [--lead-model <model>] [--worker-model provider=model,...] [--report-writer-provider <provider>] [--report-writer-model <model>] [--executor claude|codex|antigravity] [--critic off|claude|codex|antigravity|grok|kimi] [--related-tasks taskA,taskB] [--work-category bugfix|feature|refactor|ops|improvement|unknown] [--base-ref <branch|tag|sha>] [--clarification-response <previous-final-report>] [--approved-plan <plan-path>] [--approve] --project-id <project-id> --task-group <task-group> --task-id <task-id> --task-brief <brief-path> [--directive <directive>] [--fix-cycle <yes|no>]
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
Analysis input ownership is narrower than the base shell command. The `/okstra-run` wizard collects `--analysis-target` and `--evidence-inputs` values and passes them internally to `node bin/okstra render-bundle`. `scripts/okstra.sh` does not accept either flag. Because `feature-analysis` requires a target, start that task type with the in-host skill; the two option sections below document the internal Node render inputs, not standalone shell options.
|
|
@@ -382,15 +382,18 @@ scripts/okstra.sh --task-type implementation-planning --workers claude,codex --p
|
|
|
382
382
|
|
|
383
383
|
### `--lead-runtime`
|
|
384
384
|
|
|
385
|
-
Selects
|
|
385
|
+
Selects a lead adapter by registered host ID or alias. The default is `claude-code`. Terminal `okstra run <host-id-or-alias>` requests the same registry entry and verifies its `spawn-process` readiness before launch; an unavailable selected host is never replaced silently.
|
|
386
386
|
|
|
387
387
|
Lead runtime independence boundary:
|
|
388
388
|
|
|
389
389
|
- `claude-code`: the current default execution path. Claude Code v2.1.178 removed `TeamCreate`, and the session owns an implicit team. Workers are dispatched with `Agent(name: ..., run_in_background: true)` without `team_name`. `teamName` is audit/display metadata; Claude session JSONL is used for usage accounting.
|
|
390
390
|
- `codex`: the runtime marker for the Codex lead adapter. `okstra codex-run` owns `--render-only --lead-runtime codex` to prepare a task bundle, and the prepared run manifest can be passed to `okstra codex-dispatch` for CLI-backed worker execution.
|
|
391
391
|
- `antigravity`: the runtime marker for the Antigravity CLI lead adapter. The current Antigravity session owns the native lead, keeps Antigravity assignments native, and routes every other provider through its registered CLI wrapper.
|
|
392
|
+
- `grok` and `kimi`: registered native lead adapters for their corresponding CLIs. They can lead, analyse, and criticise, but they do not add executor, verifier, or report-writer worker roles.
|
|
392
393
|
- `external`: without Claude Code Teams, `--render-only --lead-runtime external` prepares the task bundle and external lead prompt. The lead manages the tmux-pane worker lifecycle with `okstra team dispatch`, `okstra team await`, and `okstra team teardown`. This path does not use `TeamCreate` / `Agent(...)` and uses artifact-only accounting.
|
|
393
394
|
|
|
395
|
+
Host IDs and providers are separate axes. Bundled and user-installed host adapters resolve from the registry; user adapters must be installed under `~/.okstra/adapters/hosts/<id>/` or `~/.okstra/adapters/providers/<id>/`. Project-local adapter code is not discovered.
|
|
396
|
+
|
|
394
397
|
The current Claude Code independence boundary covers the external lead prompt and `okstra team *` worker dispatch. non-render `okstra_ctl.run --lead-runtime external` remains blocked; a complete external lead driver remains separate future work. `--runtime external` only selects the runtime adapter. `okstra install` creates `~/.agents/skills/` by default and also installs Claude skills and agents when `~/.claude` exists. Selecting the `claude` worker still requires the local Claude CLI wrapper.
|
|
395
398
|
|
|
396
399
|
Host-runtime readiness is independent of worker selection. When `/okstra-setup`
|
|
@@ -405,10 +408,10 @@ worker roster contains Claude, Codex, or Antigravity.
|
|
|
405
408
|
|
|
406
409
|
### Runtime auto-detection (`auto`)
|
|
407
410
|
|
|
408
|
-
`okstra run` defaults to `auto`. `
|
|
411
|
+
`okstra run` defaults to `auto`. `src/lib/host-registry-client.mjs` asks the Python host registry to resolve explicit IDs and aliases, the `OKSTRA_RUNTIME_HOST` environment declaration, Claude Code skill handoff, or the external tmux claim. It fails fast when no adapter claims the session. Installed CLI presence alone never selects a host.
|
|
409
412
|
|
|
410
|
-
- Inside a
|
|
411
|
-
- From a terminal, `okstra run <
|
|
413
|
+
- Inside a registered host, the installed `okstra-run` skill uses `current-session`, declares the live semantic function list, and reuses the session you are already in.
|
|
414
|
+
- From a terminal, `okstra run <host-id-or-alias>` uses `spawn-process` and starts the selected host CLI. The leading word is an alias for `--lead-runtime`.
|
|
412
415
|
- `okstra run external` does not start a lead — it orchestrates an all-CLI run through `okstra team`.
|
|
413
416
|
- Starting a lead whose CLI sandboxes itself prints what is given up and waits for a `y`; `--yes` answers it. Today only Codex asks, because a sandboxed lead can reach neither cmux nor the worker CLIs' own configuration.
|
|
414
417
|
- In a generic terminal with tmux, `okstra run` orchestrates `render-bundle --lead-runtime external` -> `okstra team dispatch` -> `okstra team await`.
|
|
@@ -145,7 +145,7 @@ Runtime/install asset changes follow this checklist:
|
|
|
145
145
|
|
|
146
146
|
`--link <repo>` mode is for development and symlinks installed files back to repo sources.
|
|
147
147
|
|
|
148
|
-
`src/lib/
|
|
148
|
+
`src/lib/host-registry-client.mjs` is the single Node boundary for host catalog, ID/alias resolution, and readiness probes. It delegates host policy to `okstra_ctl.entrypoints.hosts`; the Claude Code adapter checks project workspace trust, while other adapters own their own checks. `okstra install` defaults to `--runtime auto`, records the request and any successful resolution in `installed-runtimes.json` schemaVersion 2, and still copies the shared runtime payload from the installed package `runtime/` tree even when host detection is unavailable. Skill targets always include the default Agent-compatible `~/.agents/skills` target, with `~/.claude/skills` also populated when `~/.claude` exists. Dynamic capabilities are probed by the selected adapter rather than frozen into the install manifest.
|
|
149
149
|
|
|
150
150
|
---
|
|
151
151
|
|
|
@@ -303,7 +303,9 @@ Important modules:
|
|
|
303
303
|
| `schema_excerpt.py` | generates a task-type-scoped excerpt of the final-report schema — a schema reduction to inject into the worker/lead prompt |
|
|
304
304
|
| `work_categories.py` | requirements-discovery work-category (domain) **SSOT** (`is_valid_category`) — the work-category allowlist is defined only here |
|
|
305
305
|
| `model_discovery.py` | pre-dispatch model-identity normalization for CLI workers — roster-gated label correction + a per-role reasoning-effort policy (deterministic, no per-run improvisation) for CLIs (agy) that bake effort into the model name |
|
|
306
|
-
| `
|
|
306
|
+
| `domain/`, `application/`, `ports/` | Host-neutral values and errors, wizard/run use cases, and the interaction/session/dispatch/accounting port contracts |
|
|
307
|
+
| `registry/host_registry.py`, `registry/provider_registry.py` | Discover bundled adapters plus explicit user installs under `~/.okstra/adapters/{hosts,providers}/<id>/`; project-local adapter code is outside the discovery roots |
|
|
308
|
+
| `adapters/hosts/`, `adapters/providers/` | Six bundled host strategies and the independent provider catalogs; host manifests select a native provider without merging the two axes |
|
|
307
309
|
| `lead_events.py` | structured JSONL events emitted by artifact-accounted lead runtimes |
|
|
308
310
|
| `team_reconcile.py` | stale team-member reconciliation at run-end teardown |
|
|
309
311
|
| `worker_prompt_headers.py` | shared rendering of phase-aware worker prompt anchors (`worker_prompt_headers`): coding-preflight only for implementation and compact target identity for final-verification |
|
|
@@ -406,7 +408,7 @@ Boilerplate shared by several skills (bash invocation rule, outdated-CLI preflig
|
|
|
406
408
|
| Skill | User-invocable | Role |
|
|
407
409
|
|---|---:|---|
|
|
408
410
|
| `okstra-brief-gen` | yes | Produce task brief from ticket/doc/link/conversation |
|
|
409
|
-
| `okstra-run` | yes | Start/resume an okstra task in the current
|
|
411
|
+
| `okstra-run` | yes | Start/resume an okstra task in the current registered host session |
|
|
410
412
|
| `okstra-memory` | yes | Store/search/archive global conversation memory under `~/.okstra/memory-book` |
|
|
411
413
|
| `okstra-inspect` | yes | Unified read-side — sub-commands `status` (lifecycle + workStatus), `history` (past runs / re-run / resume), `report` (find final-report), `time` (elapsed-time breakdown), `logs` (wrapper log inventory + cleanup), `cost` (task bundle context/read cost), `errors` (error-log aggregation), `error-zip` (anonymized cross-project error bundle), `run-audit` (progress-invariant audit over run artifacts), `error-issue` (anomaly → GitHub issue candidates, filed only after explicit approval), `recap` (cross-run phase recap). `SKILL.md` is a thin core (preflight + dispatch table + shared rules) and each sub-command body lives in `skills/okstra-inspect/facets/<sub-command>.md`, lazily read only after dispatch resolves; the 1:1 match between dispatch rows and facet files is enforced by `tests/contract/test_okstra_inspect_facets.py` |
|
|
412
414
|
| `okstra-rollup` | yes | Cross-task roll-up — aggregate runs/time/errors across a task-group (or whole project) and synthesize a digest from the report files |
|
|
@@ -432,11 +434,11 @@ Boilerplate shared by several skills (bash invocation rule, outdated-CLI preflig
|
|
|
432
434
|
| `agents/workers/kimi-worker.params.json` | Kimi read-only analyser/critic wrapper params |
|
|
433
435
|
| `agents/workers/report-writer-worker.md` | data.json SSOT author and audit sidecar writer |
|
|
434
436
|
|
|
435
|
-
The neutral lead lifecycle contract lives at `prompts/lead/okstra-lead-contract.md`.
|
|
437
|
+
The neutral lead lifecycle contract lives at `prompts/lead/okstra-lead-contract.md`. Executable host strategies and their relay contracts live together under `scripts/okstra_ctl/adapters/hosts/<host-id>/`; `prompts/lead/adapters/cmux.md` remains the environment-selected cmux worker-backend contract. Lead resources are installed under `~/.okstra/prompts/lead/`, while executable host adapters are installed under `~/.okstra/lib/python/okstra_ctl/adapters/hosts/`. They are runtime resources, not agent skills.
|
|
436
438
|
|
|
437
439
|
### 4.12 `tests/` and `tests-e2e/`
|
|
438
440
|
|
|
439
|
-
- `tests/`: pytest modules
|
|
441
|
+
- `tests/`: pytest modules organized by production boundary. `domain/wizard/` owns wizard state and answer behavior, `application/` owns use-case and render orchestration tests, and `adapters/{hosts,host_contract,providers,dispatch,accounting}/` owns external strategy contracts. Existing `run/`, `contract/`, `report/`, `inspect/`, `worktree/`, and `handoff/` folders retain their narrower responsibilities. The shared path SSOT is `tests/_paths.py` (`REPO_ROOT`/`TESTS_DIR`/`FIXTURES`), and the repo-root `pytest.ini` adds `tests/` to the import path. Fixtures live in `tests/fixtures/`.
|
|
440
442
|
- `tests-e2e/`: `scenario-<id>-<name>.sh` shell scenarios (record-start/reconcile, rerun, task lock, agent install, report view, etc.).
|
|
441
443
|
- Each behavior branch has one owning test at the lowest practical layer.
|
|
442
444
|
- An end-to-end scenario must cross the public CLI or installed-runtime boundary.
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -48,6 +48,7 @@ Unlike the Codex / Antigravity workers, you are an in-process Claude subagent
|
|
|
48
48
|
- **Executor coding-conventions preflight (BLOCKING, before your first `Edit` / `Write`):** when dispatched as the `Executor`, you MUST run the coding-conventions preflight defined in the executor sidecar (`prompts/profiles/_implementation-executor.md` → "Pre-implementation context exploration") before writing any code. Use this worker prompt's `**Coding preflight pack:**` anchor header; read that pack's `overview.md` and `clean-code.md`, then follow the routed pack's language → framework → architecture stages, iterating every rule and loading every matching resource (for example `frameworks/node-server.md` and `architectures/hexagonal.md` when their conditions match). The preflight pack is a runtime resource, not an auto-invoked skill; read the files via the Read tool by absolute path.
|
|
49
49
|
- **Executor post-write gates (BLOCKING, before your final commit / before claiming done):** the same dispatch prompt carries two gate blocks the lead appends after the preflight — `Pre-commit diff review sweep` (`prompts/profiles/_implementation-diff-review.md`) and `Implementation self-check` (`prompts/profiles/_implementation-self-check.md`). Execute both and record their coverage lines in your worker result exactly as the blocks specify. The codex/antigravity wrappers refuse to launch when an executor prompt lacks these blocks (`*_POSTWRITE_GATE_MISSING`); this worker runs in-process with no wrapper gate, so the contract lands on you directly — if either block is missing from your dispatch prompt, record a `tool-failure` in the errors sidecar and tell the lead to re-dispatch with the blocks included instead of skipping the gates.
|
|
50
50
|
- **Verifier QA-gate exception:** verifier roles MAY use the same `cd <WORKTREE> && <cmd>` shape when executing project-declared `qaCommands` (lint / format / typecheck / test) from `project.json`, since those commands are cwd-sensitive by nature. Outside the QA gate, verifiers still read with absolute paths only — do NOT use `cd` for file inspection.
|
|
51
|
+
- **Shell commands must not be able to prompt:** this worker runs inside the host session, so its Bash calls see the user's own shell, where `cp`, `mv`, and `rm` are commonly aliased to their `-i` form. The confirmation that alias raises has nobody to answer it and the dispatch hangs until it is killed. Invoke these as `command cp` / `command mv` / `command rm` — alias expansion is skipped and the tool behaves exactly as written. Do not reach for `-f` instead; it also changes what the tool does on failure (`rm -f` reports success on a path that never existed).
|
|
51
52
|
- **No extra chaining beyond `cd && cmd`:** the permission matcher only allows the exact two-segment shape `cd <PATH> && <single-command>`. Do NOT append additional pipes, semicolons, redirects, or `&&` chains — e.g. `cd ... && cargo test ... 2>&1 | tail -20; echo "exit:$?"` will trigger a permission prompt every dispatch because the trailing `| tail`, `; echo`, and `2>&1` tokens disqualify the prefix match against `Bash(cargo:*)`. Let Claude Code capture the full stdout/stderr and exit code natively — do not post-process with `tail`, `head`, or `echo "exit:$?"`. If output truncation is genuinely needed, run the command first and read the result in a separate tool call.
|
|
52
53
|
|
|
53
54
|
5. **MCP usage**: The canonical list of MCP servers and tools available for this run lives in the analysis packet's `Available MCP Servers` section. If the section is absent or says none, treat MCP as unavailable for this run; never infer tools from host configuration. When the task requires inspection of an external system covered by a listed server, call the tool directly by name (e.g. `mcp__<server>__<tool>`). Do NOT shell out via `claude --mcp-cli call ...` or run the tool name as a Bash command — those are not valid invocation paths. If a server you need is not listed, record `MCP not available for this run` in your worker output rather than guessing a tool name.
|
|
@@ -54,6 +54,11 @@ while [[ $# -gt 0 ]]; do
|
|
|
54
54
|
RESUME_CLARIFICATION_MODE="true"
|
|
55
55
|
shift
|
|
56
56
|
;;
|
|
57
|
+
--resume-session)
|
|
58
|
+
RESUME_SESSION_MODE="true"
|
|
59
|
+
RESUME_SESSION_ID="$(require_option_value --resume-session "${2-}")"
|
|
60
|
+
shift 2
|
|
61
|
+
;;
|
|
57
62
|
--yes)
|
|
58
63
|
ASSUME_YES="true"
|
|
59
64
|
shift
|
|
@@ -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-provider <provider>] [--lead-model <model>] [--worker-model provider=model,...] [--report-writer-provider <provider>] [--report-writer-model <model>] [--lead-runtime
|
|
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 <host-id-or-alias>] [--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. The standalone launcher defaults to an interactive Claude session;
|
|
9
|
+
$DISPLAY_TOOL_NAME prepares a task-keyed instruction bundle. The standalone launcher defaults to an interactive Claude session; in-host skills keep the current registered host 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
|
|
@@ -88,7 +88,7 @@ options:
|
|
|
88
88
|
--yes Skip interactive prompting and confirmation. Requires all required arguments.
|
|
89
89
|
--workers Comma-separated worker list for this run. Default: claude,codex,report-writer.
|
|
90
90
|
Optional read-only providers: antigravity, grok, kimi.
|
|
91
|
-
--lead-provider Compatibility assertion for the lead assignment. Must match the
|
|
91
|
+
--lead-provider Compatibility assertion for the lead assignment. Must match the selected host adapter's native provider.
|
|
92
92
|
--lead-model Model for the host-native lead. Default: the selected provider's lead policy.
|
|
93
93
|
--claude-model Model for Claude worker. Default: OKSTRA_DEFAULT_CLAUDE_MODEL or opus
|
|
94
94
|
--codex-model Model for Codex worker. Default: OKSTRA_DEFAULT_CODEX_MODEL or gpt-5.6-sol
|
|
@@ -98,8 +98,8 @@ options:
|
|
|
98
98
|
Provider for report writer. Supported: claude, codex. Default: claude.
|
|
99
99
|
--report-writer-model
|
|
100
100
|
Model for report writer worker. Default: OKSTRA_DEFAULT_REPORT_WRITER_MODEL or sonnet
|
|
101
|
-
--lead-runtime
|
|
102
|
-
|
|
101
|
+
--lead-runtime Registered host ID or alias. Default: claude-code. In-host runs
|
|
102
|
+
use current-session; terminal launch uses spawn-process readiness.
|
|
103
103
|
--executor Provider that performs the Executor role during --task-type=implementation.
|
|
104
104
|
One of: claude | codex | antigravity. Default: OKSTRA_DEFAULT_EXECUTOR or claude.
|
|
105
105
|
The Executor is the only worker allowed to mutate project files; the other two
|
package/runtime/bin/okstra.sh
CHANGED
|
@@ -58,10 +58,87 @@ okstra_py() {
|
|
|
58
58
|
PYTHONPATH="$OKSTRA_PYTHONPATH:${PYTHONPATH-}" python3 "$@"
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
confirm_sandbox_waiver() {
|
|
62
|
+
local sandbox_note="$1"
|
|
63
|
+
local lead_executable="$2"
|
|
64
|
+
local cancel_message="$3"
|
|
65
|
+
local assume_yes_message="$4"
|
|
66
|
+
local sandbox_answer
|
|
67
|
+
|
|
68
|
+
[[ -z "$sandbox_note" ]] && return
|
|
69
|
+
printf '\n%s\n' "$sandbox_note" >&2
|
|
70
|
+
if [[ "$ASSUME_YES" == "true" ]]; then
|
|
71
|
+
[[ -n "$assume_yes_message" ]] && printf '%s\n\n' "$assume_yes_message" >&2
|
|
72
|
+
return 0
|
|
73
|
+
fi
|
|
74
|
+
printf 'Start %s without its sandbox? [y/N] ' "$lead_executable" >&2
|
|
75
|
+
read -r sandbox_answer < /dev/tty || sandbox_answer=""
|
|
76
|
+
if [[ "$sandbox_answer" != "y" && "$sandbox_answer" != "Y" ]]; then
|
|
77
|
+
printf '%s\n' "$cancel_message" >&2
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
execute_launch_plan() {
|
|
83
|
+
local cancel_message="$1"
|
|
84
|
+
local assume_yes_message="$2"
|
|
85
|
+
shift 2
|
|
86
|
+
local launch_plan_file
|
|
87
|
+
local sandbox_note
|
|
88
|
+
local env_count
|
|
89
|
+
local argv_offset
|
|
90
|
+
local lead_executable
|
|
91
|
+
local field
|
|
92
|
+
local -a launch_fields
|
|
93
|
+
local -a launch_env
|
|
94
|
+
local -a launch_argv
|
|
95
|
+
|
|
96
|
+
launch_plan_file="$(mktemp "${TMPDIR:-/tmp}/okstra-launch-plan.XXXXXX")"
|
|
97
|
+
if ! okstra_py -m okstra_ctl.entrypoints.hosts "$@" \
|
|
98
|
+
--format null > "$launch_plan_file"; then
|
|
99
|
+
rm -f "$launch_plan_file"
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
launch_fields=()
|
|
103
|
+
while IFS= read -r -d '' field; do
|
|
104
|
+
launch_fields+=("$field")
|
|
105
|
+
done < "$launch_plan_file"
|
|
106
|
+
rm -f "$launch_plan_file"
|
|
107
|
+
if [[ ${#launch_fields[@]} -lt 4 ]] || \
|
|
108
|
+
[[ ! "${launch_fields[2]}" =~ ^[0-9]+$ ]]; then
|
|
109
|
+
printf 'okstra: invalid launch plan\n' >&2
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
sandbox_note="${launch_fields[1]}"
|
|
113
|
+
env_count="${launch_fields[2]}"
|
|
114
|
+
argv_offset=$((env_count + 3))
|
|
115
|
+
if [[ ${#launch_fields[@]} -le $argv_offset ]]; then
|
|
116
|
+
printf 'okstra: invalid launch plan\n' >&2
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
launch_env=("${launch_fields[@]:3:env_count}")
|
|
120
|
+
launch_argv=("${launch_fields[@]:argv_offset}")
|
|
121
|
+
lead_executable="${launch_argv[0]}"
|
|
122
|
+
confirm_sandbox_waiver \
|
|
123
|
+
"$sandbox_note" "$lead_executable" "$cancel_message" "$assume_yes_message"
|
|
124
|
+
cd "${launch_fields[0]}"
|
|
125
|
+
exec env "${launch_env[@]}" "${launch_argv[@]}"
|
|
126
|
+
}
|
|
127
|
+
|
|
61
128
|
parse_cli_arguments "$@"
|
|
62
129
|
split_task_key
|
|
63
130
|
|
|
64
131
|
PROJECT_ROOT="$(resolve_project_root_safe "$PROJECT_ROOT_OVERRIDE")"
|
|
132
|
+
if [[ "$RESUME_SESSION_MODE" == "true" ]]; then
|
|
133
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
134
|
+
PROJECT_ROOT="$(resolve_project_root_strict "$PROJECT_ROOT_OVERRIDE")" || exit 1
|
|
135
|
+
fi
|
|
136
|
+
execute_launch_plan \
|
|
137
|
+
"okstra: resume cancelled" \
|
|
138
|
+
"" \
|
|
139
|
+
resume-plan --host "$LEAD_RUNTIME" --entry-mode spawn-process \
|
|
140
|
+
--project-root "$PROJECT_ROOT" --session-id "$RESUME_SESSION_ID"
|
|
141
|
+
fi
|
|
65
142
|
if [[ "$RESUME_CLARIFICATION_MODE" == "true" ]]; then
|
|
66
143
|
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
67
144
|
PROJECT_ROOT="$(resolve_project_root_strict "$PROJECT_ROOT_OVERRIDE")" \
|
|
@@ -80,47 +157,11 @@ if [[ "$LAUNCH_ONLY" == "true" ]]; then
|
|
|
80
157
|
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
81
158
|
PROJECT_ROOT="$(resolve_project_root_strict "$PROJECT_ROOT_OVERRIDE")" || exit 1
|
|
82
159
|
fi
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
provider = lead_runtime_info(sys.argv[1]).agent
|
|
89
|
-
print(bare_lead_launch_argv(provider)[0], lead_launch_spec(provider).sandbox_waiver_note)
|
|
90
|
-
PY
|
|
91
|
-
)
|
|
92
|
-
if ! command -v "$LEAD_EXECUTABLE" >/dev/null 2>&1; then
|
|
93
|
-
printf '%s command not found\n' "$LEAD_EXECUTABLE" >&2
|
|
94
|
-
exit 1
|
|
95
|
-
fi
|
|
96
|
-
if [[ -n "$SANDBOX_NOTE" ]]; then
|
|
97
|
-
okstra_py - "$LEAD_RUNTIME" <<'PY' >&2
|
|
98
|
-
import sys
|
|
99
|
-
from okstra_ctl.lead_runtime import lead_runtime_info
|
|
100
|
-
from okstra_ctl.models import lead_launch_spec
|
|
101
|
-
print("\n" + lead_launch_spec(lead_runtime_info(sys.argv[1]).agent).sandbox_waiver_note)
|
|
102
|
-
PY
|
|
103
|
-
if [[ "$ASSUME_YES" != "true" ]]; then
|
|
104
|
-
printf 'Start %s without its sandbox? [y/N] ' "$LEAD_EXECUTABLE" >&2
|
|
105
|
-
read -r _sandbox_answer < /dev/tty || _sandbox_answer=""
|
|
106
|
-
if [[ "$_sandbox_answer" != "y" && "$_sandbox_answer" != "Y" ]]; then
|
|
107
|
-
printf 'okstra: cancelled\n' >&2
|
|
108
|
-
exit 1
|
|
109
|
-
fi
|
|
110
|
-
fi
|
|
111
|
-
fi
|
|
112
|
-
LAUNCH_ARGV=()
|
|
113
|
-
while IFS= read -r -d '' _arg; do LAUNCH_ARGV+=("$_arg"); done < <(
|
|
114
|
-
okstra_py - "$LEAD_RUNTIME" <<'PY'
|
|
115
|
-
import sys
|
|
116
|
-
from okstra_ctl.lead_runtime import lead_runtime_info
|
|
117
|
-
from okstra_ctl.models import bare_lead_launch_argv
|
|
118
|
-
for a in bare_lead_launch_argv(lead_runtime_info(sys.argv[1]).agent):
|
|
119
|
-
sys.stdout.write(a + "\0")
|
|
120
|
-
PY
|
|
121
|
-
)
|
|
122
|
-
cd "$PROJECT_ROOT"
|
|
123
|
-
exec "${LAUNCH_ARGV[@]}"
|
|
160
|
+
execute_launch_plan \
|
|
161
|
+
"okstra: cancelled" \
|
|
162
|
+
"" \
|
|
163
|
+
launch-plan --host "$LEAD_RUNTIME" --entry-mode spawn-process \
|
|
164
|
+
--project-root "$PROJECT_ROOT"
|
|
124
165
|
fi
|
|
125
166
|
|
|
126
167
|
autofill_from_manifest
|
|
@@ -232,57 +273,12 @@ if [[ -z "$LAUNCH_JSON" ]]; then
|
|
|
232
273
|
exit 1
|
|
233
274
|
fi
|
|
234
275
|
|
|
235
|
-
read -r LEAD_EXECUTABLE PROJECT_ROOT_FROM_PY < <(
|
|
236
|
-
okstra_py - "$LAUNCH_JSON" <<'PY'
|
|
237
|
-
import json, sys
|
|
238
|
-
d = json.loads(sys.argv[1])
|
|
239
|
-
print(d["leadExecutable"], d["projectRoot"])
|
|
240
|
-
PY
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
if ! command -v "$LEAD_EXECUTABLE" >/dev/null 2>&1; then
|
|
244
|
-
printf '%s command not found\n' "$LEAD_EXECUTABLE" >&2
|
|
245
|
-
exit 1
|
|
246
|
-
fi
|
|
247
|
-
|
|
248
|
-
# Starting some leads means lowering a protection that CLI applies to itself.
|
|
249
|
-
# okstra never waives it silently: the note says what is given up, and nothing
|
|
250
|
-
# starts without an explicit yes.
|
|
251
|
-
SANDBOX_NOTE="$(
|
|
252
|
-
okstra_py - "$LAUNCH_JSON" <<'PY'
|
|
253
|
-
import json, sys
|
|
254
|
-
print(json.loads(sys.argv[1])["sandboxWaiverNote"])
|
|
255
|
-
PY
|
|
256
|
-
)"
|
|
257
|
-
if [[ -n "$SANDBOX_NOTE" ]]; then
|
|
258
|
-
printf '\n%s\n' "$SANDBOX_NOTE" >&2
|
|
259
|
-
if [[ "$ASSUME_YES" == "true" ]]; then
|
|
260
|
-
printf 'Proceeding without the sandbox (--yes).\n\n' >&2
|
|
261
|
-
else
|
|
262
|
-
printf 'Start %s without its sandbox? [y/N] ' "$LEAD_EXECUTABLE" >&2
|
|
263
|
-
read -r SANDBOX_ANSWER < /dev/tty || SANDBOX_ANSWER=""
|
|
264
|
-
if [[ "$SANDBOX_ANSWER" != "y" && "$SANDBOX_ANSWER" != "Y" ]]; then
|
|
265
|
-
printf 'okstra: cancelled. The task bundle is prepared; rerun to resume it.\n' >&2
|
|
266
|
-
exit 1
|
|
267
|
-
fi
|
|
268
|
-
fi
|
|
269
|
-
fi
|
|
270
|
-
|
|
271
|
-
# NUL-separated so a prompt carrying newlines or quotes survives the handoff.
|
|
272
|
-
LAUNCH_ARGV=()
|
|
273
|
-
while IFS= read -r -d '' LAUNCH_ARG; do
|
|
274
|
-
LAUNCH_ARGV+=("$LAUNCH_ARG")
|
|
275
|
-
done < <(
|
|
276
|
-
okstra_py - "$LAUNCH_JSON" <<'PY'
|
|
277
|
-
import json, sys
|
|
278
|
-
for arg in json.loads(sys.argv[1])["launchArgv"]:
|
|
279
|
-
sys.stdout.write(arg + "\0")
|
|
280
|
-
PY
|
|
281
|
-
)
|
|
282
|
-
|
|
283
276
|
# Note: per-session --settings injection was removed. okstra-ctl prepare
|
|
284
277
|
# provisions <PROJECT_ROOT>/.claude/settings.local.json as a symlink to
|
|
285
278
|
# ~/.okstra/templates/settings.local.json, which Claude Code auto-loads
|
|
286
279
|
# whenever it runs inside that project — no CLI flag required.
|
|
287
|
-
|
|
288
|
-
|
|
280
|
+
execute_launch_plan \
|
|
281
|
+
"okstra: cancelled. The task bundle is prepared; rerun to resume it." \
|
|
282
|
+
"Proceeding without the sandbox (--yes)." \
|
|
283
|
+
launch-plan --host "$LEAD_RUNTIME" --entry-mode spawn-process \
|
|
284
|
+
--prepared-launch-json "$LAUNCH_JSON"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
This adapter maps the neutral Okstra lead operations to a cmux session, where Okstra owns the worker panes regardless of which model is leading. Read it only when the rendered launch prompt selects it; the run manifest's `terminalBackend` is `cmux-pane` for exactly those runs.
|
|
6
6
|
|
|
7
|
-
It
|
|
7
|
+
It overrides only the worker-dispatch portion of the selected host relay, not the host or lead contract. Your own runtime still decides how you read files, ask the user, and record your session — this file only decides how workers are started, awaited, and reclaimed.
|
|
8
8
|
|
|
9
9
|
## Capability declaration
|
|
10
10
|
|
|
@@ -58,6 +58,7 @@ Read-side inspection (`/okstra-inspect`) and scheduling (`/okstra-schedule-gen`)
|
|
|
58
58
|
- Standard worker roles remain `Claude worker`, `Codex worker`, `Antigravity worker`, and `Report writer worker`; these are provider/functional-role identities, not lead-runtime primitives.
|
|
59
59
|
- `Report writer worker`, when in the roster, is the **author** of the final-report file. Lead reviews the draft and may request a revision via a follow-up dispatch, but MUST NOT write the report itself as a "shortcut". The only legal lead-authored fallback is when a Report writer worker dispatch was actually attempted and recorded a terminal status of `error`/`timeout`/`not-run` with an explicit reason in team-state — see [report-writer](./report-writer.md) "Lead-authored fallback".
|
|
60
60
|
- "Session resume", "team is no longer alive", and similar are NOT valid reasons to skip Report writer worker dispatch — see [report-writer](./report-writer.md) "Resume-safe dispatch".
|
|
61
|
+
- A shell command the lead runs must not be able to ask a question. The lead's shell is the user's own, where `cp`, `mv`, and `rm` are commonly aliased to their `-i` form; the confirmation that alias raises has nobody to answer it, so the call hangs until it is killed — observed as a `cp` over an existing state file stalling a whole self-fix round. Invoke these as `command cp` / `command mv` / `command rm`, which skips alias expansion and leaves the tool's own behaviour untouched. `-f` is not a substitute: it changes what the tool does on failure (`rm -f` reports success on a path that never existed).
|
|
61
62
|
- If the brief is incomplete, continue with explicit uncertainty markers rather than fabricating confidence.
|
|
62
63
|
- Required roles must not be replaced by unnamed generic parallel workers. Before the final verdict, every selected worker must have either a saved result file or an explicit terminal status with reason. Any attempted worker with status `completed`, `timeout`, or `error` must also have a saved worker prompt history file at its assigned run-level prompt path.
|
|
63
64
|
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
**Pick the cases by distinct outcome, not by line coverage.** When the stage writes or reconciles state, its meaningfully different outcomes are usually more than three — normal success, target already in the desired state (resume), existing data reused rather than created, a conflicting concurrent state, target absent, and mid-way failure with rollback. Enumerate the ones this stage actually implements and route them across the three lines (the `boundary` line is where resume / already-done / reuse belongs; `failure` carries conflict, absence, and rollback), naming each in the cell rather than collapsing them into "edge input". An implemented outcome with no declared case is a coverage gap the executor will not backfill.
|
|
120
120
|
- **Per-stage subsections** (`## 5.5.<i> Stage <i>: <title>` for each `i`), each containing the four required subsections:
|
|
121
121
|
- `### Carry-In` — for `depends-on (none)`: task-brief only. Otherwise: each depended-on stage's static exit contract + runtime sidecar path `runs/<impl-key>/carry/stage-<i>.json` placeholder.
|
|
122
|
-
- `### Stepwise Execution Order` — bite-sized table with `step | action | files | command | expected`. The `files` cell lists each touched path in full and `<PROJECT_ROOT>`-relative — never ellipsis-abbreviated (`…` / `...`), which does not resolve and is rejected by plan-body verification as a kind-b path mismatch. **Effective row count ≤ 8** (excluding header / divider / blank). Each step is one cohesive, self-contained change (no lower time bound; it may span several files that change together); for code steps include actual code or diff sketch. **TDD ordering is MUST, not a preference:** the **first** effective step's `action` cell MUST start with the literal `RED:` and describe the failing test(s) that capture this stage's `Acceptance` **and the three declared `Test case (success|boundary|failure)` lines** (`
|
|
122
|
+
- `### Stepwise Execution Order` — bite-sized table with `step | action | files | command | outcome | expected`. `outcome` is one word — `PASS` or `FAIL` — and `expected` is the sentence saying what that looks like here; a verdict written inside the sentence is not read as one. The `files` cell lists each touched path in full and `<PROJECT_ROOT>`-relative — never ellipsis-abbreviated (`…` / `...`), which does not resolve and is rejected by plan-body verification as a kind-b path mismatch. **Effective row count ≤ 8** (excluding header / divider / blank). Each step is one cohesive, self-contained change (no lower time bound; it may span several files that change together); for code steps include actual code or diff sketch. **TDD ordering is MUST, not a preference:** the **first** effective step's `action` cell MUST start with the literal `RED:` and describe the failing test(s) that capture this stage's `Acceptance` **and the three declared `Test case (success|boundary|failure)` lines** (`outcome` = `FAIL`) — the RED step encodes the case set, not a single happy-path assertion; at least one later `action` cell MUST start with the literal `GREEN:` and describe the minimal implementation that makes it pass (`outcome` = `PASS`); an optional refactor step starts with `REFACTOR:`. **Exemption:** doc-only / config-only / pure-rename stages with no observable runtime behaviour may omit RED/GREEN by declaring one line `TDD exemption: <reason>` in the stage section (mirrors the executor's per-step exemption in `_implementation-executor.md`). Validator S10c enforces RED-first + GREEN; the `outcome` cell agreeing with its `RED:` / `GREEN:` prefix is a schema conditional (`StageStepRow.allOf`), so a plan whose data.json says otherwise never reaches the validator. S10e rejects a `TDD exemption:` whose reason is not one of doc-only / config-only / pure-rename (both in `validators/validate-implementation-plan-stages.py`).
|
|
123
123
|
- **Per-stage conformance declaration (mandatory one line, in the stage section — same placement freedom as `TDD exemption:`):** the stage MUST carry exactly one of:
|
|
124
124
|
- `Conformance tests: stage-<N> — <task_root>/qa/scripts/stage-<N>.<ext> (requires=[db|io|http|external,...])` — a Tier3 verification script that proves this stage's upstream requirements (brief / requirements-discovery / error-analysis / improvement-discovery → this stage's `Acceptance`) hold against **real** DB rows, real endpoints, or the real external API — NOT mocks. When you emit this line you MUST also (a) write the script to `<task_root>/qa/scripts/stage-<N>.<ext>` and (b) add a matching entry to `<task_root>/qa/conformance-manifest.json` with fields `stageKey` (= `<task-id>-stage-<N>`), `script`, `runCommand`, `requirementIds`, `requires` (subset of `{db, io, http, external}`), `passContract`, `exemption: null`, `waiver: null`. The script's standard interface: a `main` that exits `0`=PASS / non-zero=FAIL, and whose stdout ends with `QA-RESULT: PASS|FAIL` followed by one `REQ <id>: PASS|FAIL: <reason>` line per requirement. When the verification body is a test spec, author it with the project's own test framework (devDependency) invoked via a discovery override at `<task_root>/qa/scripts/` (jest: `--config <project config> --roots <task_root>/qa/scripts`) — never hand-roll `describe`/`expect` and never widen the project's own test config; for TypeScript specs also write `<task_root>/qa/scripts/tsconfig.json` extending the project tsconfig with the runner's `types` entry so editors resolve the file.
|
|
125
125
|
- `Conformance exemption: <reason>` — only for stages that touch no db/io/http/external surface, or where unit tests fully cover the increment. (If the eventual `implementation` diff actually touches one of those surfaces, `validate-run.py`'s diff-surface cross-check is BLOCKING — an exemption cannot hide a real db/io/http/external change.)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Usage-accounting adapters."""
|
|
2
|
+
|
|
3
|
+
from .claude_jsonl import ClaudeJsonlUsageAccountingPort
|
|
4
|
+
from .cli_artifact import CliArtifactUsageAccountingPort
|
|
5
|
+
from .unavailable import UnavailableUsageAccountingPort
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
"ClaudeJsonlUsageAccountingPort",
|
|
9
|
+
"CliArtifactUsageAccountingPort",
|
|
10
|
+
"UnavailableUsageAccountingPort",
|
|
11
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Claude JSONL usage-accounting adapter."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from okstra_ctl.ports.usage_accounting import UsageRequest, UsageSnapshot
|
|
5
|
+
from okstra_token_usage.collect import (
|
|
6
|
+
collect_claude_runtime_usage as collect_runtime_usage,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ClaudeJsonlUsageAccountingPort:
|
|
11
|
+
def collect(self, request: UsageRequest) -> UsageSnapshot:
|
|
12
|
+
payload = collect_runtime_usage(
|
|
13
|
+
request.team_state_path,
|
|
14
|
+
request.project_root,
|
|
15
|
+
incremental=request.incremental,
|
|
16
|
+
)
|
|
17
|
+
return UsageSnapshot(source="claude-jsonl", payload=payload)
|