okstra 0.167.0 → 0.169.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +6 -5
  2. package/docs/architecture/storage-model.md +57 -1
  3. package/docs/architecture.md +70 -2
  4. package/docs/cli.md +8 -4
  5. package/docs/for-ai/skills/okstra-code-review.md +3 -2
  6. package/docs/for-ai/skills/okstra-schedule-gen.md +3 -1
  7. package/docs/pr-template-usage.md +10 -6
  8. package/docs/project-structure-overview.md +14 -11
  9. package/package.json +1 -1
  10. package/runtime/BUILD.json +2 -2
  11. package/runtime/agents/workers/claude-worker.md +6 -5
  12. package/runtime/agents/workers/report-writer-worker.md +9 -4
  13. package/runtime/agents/workers/translator-worker.md +6 -4
  14. package/runtime/prompts/coding-preflight/architectures/hexagonal.md +27 -1
  15. package/runtime/prompts/coding-preflight/clean-code.md +13 -0
  16. package/runtime/prompts/duties/acceptance-critic.md +24 -0
  17. package/runtime/prompts/duties/acceptance-verifier.md +24 -0
  18. package/runtime/prompts/duties/analysis-worker.md +24 -0
  19. package/runtime/prompts/duties/code-reviewer.md +24 -0
  20. package/runtime/prompts/duties/common.md +35 -0
  21. package/runtime/prompts/duties/implementation-executor.md +24 -0
  22. package/runtime/prompts/duties/implementation-verifier.md +24 -0
  23. package/runtime/prompts/duties/lead.md +24 -0
  24. package/runtime/prompts/duties/report-writer.md +24 -0
  25. package/runtime/prompts/duties/reverification-worker.md +24 -0
  26. package/runtime/prompts/duties/schedule-verifier.md +24 -0
  27. package/runtime/prompts/duties/scope-critic.md +24 -0
  28. package/runtime/prompts/duties/translator.md +24 -0
  29. package/runtime/prompts/lead/convergence.md +51 -7
  30. package/runtime/prompts/lead/okstra-lead-contract.md +10 -20
  31. package/runtime/prompts/lead/plan-body-verification.md +16 -1
  32. package/runtime/prompts/lead/report-writer.md +20 -5
  33. package/runtime/prompts/lead/team-contract.md +13 -13
  34. package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
  35. package/runtime/prompts/profiles/_implementation-diff-review.md +3 -1
  36. package/runtime/prompts/profiles/_implementation-executor.md +1 -1
  37. package/runtime/prompts/profiles/_implementation-verifier.md +3 -1
  38. package/runtime/prompts/profiles/implementation.md +4 -2
  39. package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +6 -0
  40. package/runtime/python/okstra_ctl/adapters/hosts/antigravity/relay.md +3 -2
  41. package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +8 -0
  42. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +33 -0
  43. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +13 -12
  44. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +6 -0
  45. package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +3 -2
  46. package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +2 -0
  47. package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +3 -3
  48. package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +6 -0
  49. package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +2 -1
  50. package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +6 -0
  51. package/runtime/python/okstra_ctl/adapters/hosts/kimi/relay.md +2 -1
  52. package/runtime/python/okstra_ctl/agent_invocation.py +1502 -0
  53. package/runtime/python/okstra_ctl/agent_prompt_cli.py +788 -0
  54. package/runtime/python/okstra_ctl/codex_dispatch.py +2 -107
  55. package/runtime/python/okstra_ctl/context_cost.py +46 -5
  56. package/runtime/python/okstra_ctl/dispatch_core.py +312 -37
  57. package/runtime/python/okstra_ctl/dispatch_state.py +461 -36
  58. package/runtime/python/okstra_ctl/doctor.py +150 -16
  59. package/runtime/python/okstra_ctl/entrypoints/hosts.py +87 -9
  60. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +214 -23
  61. package/runtime/python/okstra_ctl/path_hints.py +26 -0
  62. package/runtime/python/okstra_ctl/paths.py +20 -0
  63. package/runtime/python/okstra_ctl/ports/__init__.py +8 -0
  64. package/runtime/python/okstra_ctl/ports/host.py +3 -0
  65. package/runtime/python/okstra_ctl/ports/host_model.py +60 -0
  66. package/runtime/python/okstra_ctl/pr_template.py +3 -6
  67. package/runtime/python/okstra_ctl/registry/host_registry.py +5 -0
  68. package/runtime/python/okstra_ctl/render.py +217 -12
  69. package/runtime/python/okstra_ctl/report_finalize.py +44 -0
  70. package/runtime/python/okstra_ctl/run.py +368 -51
  71. package/runtime/python/okstra_ctl/session.py +16 -12
  72. package/runtime/python/okstra_ctl/team.py +11 -11
  73. package/runtime/python/okstra_ctl/worker_dispatch.py +104 -0
  74. package/runtime/python/okstra_ctl/worker_prompt_body.py +5 -38
  75. package/runtime/python/okstra_ctl/worker_prompt_contract.py +32 -2
  76. package/runtime/python/okstra_ctl/worker_prompt_policy.py +38 -1
  77. package/runtime/skills/okstra-code-review/SKILL.md +22 -3
  78. package/runtime/skills/okstra-run/SKILL.md +16 -1
  79. package/runtime/skills/okstra-schedule-gen/SKILL.md +15 -1
  80. package/runtime/templates/implementation-worker-preamble.md +0 -10
  81. package/runtime/templates/report-writer-prompt-preamble.md +0 -9
  82. package/runtime/templates/reports/settings.template.json +0 -11
  83. package/runtime/templates/worker-prompt-preamble.md +0 -10
  84. package/runtime/validators/lib/fixtures.sh +93 -0
  85. package/runtime/validators/lib/validate-assets.sh +0 -8
  86. package/runtime/validators/validate-run.py +182 -0
  87. package/src/cli-registry.mjs +14 -0
  88. package/src/commands/execute/agent-prompt.mjs +25 -0
  89. package/src/commands/execute/codex-dispatch.mjs +6 -63
  90. package/src/commands/execute/worker-dispatch.mjs +76 -0
  91. package/src/commands/lifecycle/doctor.mjs +18 -3
  92. package/src/commands/lifecycle/install.mjs +33 -15
  93. package/src/commands/lifecycle/uninstall.mjs +4 -3
  94. package/src/lib/install-assets.mjs +9 -0
  95. package/runtime/agents/workers/antigravity-worker.md +0 -259
  96. package/runtime/agents/workers/codex-worker.md +0 -259
  97. package/runtime/agents/workers/grok-worker.md +0 -259
  98. package/runtime/agents/workers/kimi-worker.md +0 -259
  99. package/runtime/prompts/coding-preflight/scripts/preedit-check.sh +0 -79
  100. package/runtime/templates/operating-standard.md +0 -22
  101. package/src/lib/worker-agent-render.mjs +0 -50
  102. /package/runtime/templates/{prd → pr}/pr-body.template.md +0 -0
package/README.md CHANGED
@@ -61,7 +61,7 @@ okstra/ npm package = repo root
61
61
  ├── runtime/ gitignored install payload copied to ~/.okstra
62
62
  ├── scripts/ Python + Bash runtime sources
63
63
  ├── skills/ public skill Markdown sources (8 user-facing skills)
64
- ├── agents/ worker agent Markdown sources
64
+ ├── agents/ Claude host execution-adapter definitions
65
65
  ├── prompts/, schemas/, templates/, validators/
66
66
  ├── docs/ English manuals (architecture, CLI, storage, container, performance)
67
67
  ├── tests/, tests-e2e/
@@ -98,8 +98,8 @@ During `prepack`, `tools/build.mjs` rebuilds `runtime/` from `scripts/`, `skills
98
98
  └── okstra-*/SKILL.md 8 user-facing skills only (setup/brief/run/memory/inspect/schedule/container/manager)
99
99
 
100
100
  ~/.claude/agents/ automatically discovered by Claude Code (when `~/.claude` exists)
101
- └── {claude,codex,antigravity,grok,kimi,report-writer}-worker.md worker definitions
102
- (required for Claude Code multi-agent dispatch)
101
+ └── {claude,report-writer,translator}-worker.md native Claude execution adapters
102
+ (provider CLI workers use deterministic dispatch)
103
103
 
104
104
  <project-root>/.okstra/
105
105
  ├── project.json {projectId, projectRoot, ...} (written by `/okstra-setup`)
@@ -225,9 +225,10 @@ Major workflow changes added to `main` after 0.8.0:
225
225
 
226
226
  - **Automatic isolated worktrees for every task type** — During preparation, `okstra-ctl` runs `git worktree add ~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>` once per task key to create an isolated working tree and a `<work-category-namespace>/<task-id-segment>` branch (for example, `feature/dev-9436` or `fix/dev-7311`). The user chooses the base ref with `--base-ref`, using the same choices as the release-handoff PR base picker: `main`, `dev`, `staging`, `preprod`, `prod`, or a custom value. It is required in the first phase; the okstra-run skill collects it through `AskUserQuestion`, while non-interactive callers must pass `--base-ref` explicitly. Later **non-`implementation`** phases for the same task key (`requirements-discovery` → `error-analysis` → `implementation-planning` → `final-verification` → `release-handoff`) reuse the same path and branch. `implementation` runs are **stage-isolated**: each run executes one stage in its own `.../<task>/stage-<N>/` worktree on a `<work-category-namespace>/<task>-s<N>` branch, so independent stages with `depends-on (none)` can run concurrently without sharing a tree. The registry reserves both task keys and **stage keys** with flock. Provisioning is skipped when the caller is already in another worktree or project_root is not a Git repository; stage isolation degrades to a flat path in those cases. Manual cleanup: `git worktree remove <path>` → `git branch -D <branch>` plus release/removal of the registry entry. Details: [`docs/architecture.md`](docs/architecture.md), in the *Task type* section, and [`docs/cli.md#--executor`](docs/cli.md#--executor).
227
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).
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/pr/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.
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 installed default at `~/.okstra/templates/pr/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.
229
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.
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.
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 runs through the deterministic `okstra worker-dispatch` process boundary. `leadAssignment` and every `workerAssignments[]` row record provider, model, and `runner`. `okstra codex-dispatch` remains a compatibility alias for the provider-neutral dispatcher.
231
+ - **Per-invocation duty contracts** — every Okstra-owned LLM call composes three independently managed inputs: a persisted model assignment, a functional duty contract from `prompts/duties/`, and call-specific task instructions. The final prompt and adjacent metadata carry exactly five SHA-256 digests (catalog, assignment, duty, instruction, prompt). Code-owned process launches fail before execution when verification fails; host-native calls record a verified-specification link without claiming that Okstra observed the bytes delivered by the host. Standalone code-review and schedule-verification calls use the same contract under `.okstra/agent-invocations/`.
231
232
  - **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.
232
233
  - **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).
233
234
  - **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).
@@ -24,7 +24,8 @@ The task manifest, task index, instruction set, runs, and history are collected
24
24
  - `runs/<task-type>/`
25
25
  - `manifests/run-manifest-<task-type>-<seq>.json`
26
26
  - `state/team-state-<task-type>-<seq>.json`
27
- - `prompts/lead-execution-prompt-<task-type>-<seq>.md` and selected `<worker>-worker-prompt-<task-type>-<seq>.md` files
27
+ - `prompts/lead-execution-prompt-<task-type>-<seq>.md` and selected `<worker>-worker-prompt-<task-type>-<seq>.md` files, each with adjacent `.meta.json` invocation metadata
28
+ - `prompts/duty-contracts-<task-type>-<seq>/`, the immutable run-scoped duty catalog snapshot
28
29
  - `reports/final-report-<task-type>-<seq>.{data.json,md,html}`
29
30
  - implementation-planning-only `design-prep-requests/` and `design-prep-inputs/`
30
31
  - `status/final-<task-type>-<seq>.status`
@@ -53,6 +54,8 @@ The representative files below are all relative to the resolved run directory (`
53
54
  - `state/team-state-<task-type>-<seq>.json`
54
55
  - `prompts/lead-execution-prompt-<task-type>-<seq>.md` *(canonical; the Claude-named snapshot remains a compatibility alias)*
55
56
  - `prompts/<worker>-worker-prompt-<task-type>-<seq>.md`
57
+ - `prompts/<prompt>.md.meta.json` *(invocation identity, assignment, duty identity, source paths, and the five catalog/assignment/duty/instruction/prompt digests)*
58
+ - `prompts/duty-contracts-<task-type>-<seq>/` *(run-scoped snapshot; dispatch verification rejects drift from its catalog digest)*
56
59
 
57
60
  After the host-native lead takes over, the lead and its assigned workers add the following result files to the current run.
58
61
  - `sessions/claude-resume-<task-type>-<seq>.sh`
@@ -82,6 +85,59 @@ For the standalone Claude launcher, `sessions/claude-resume-<task-type>-<seq>.sh
82
85
  The resolved run directory collects execution history. It divides its contents into type-specific subdirectories such as `manifests/`, `state/`, `prompts/`, `reports/`, `status/`, `sessions/`, and `worker-results/`, then distinguishes each run-level artifact and result file with a `-<task-type>-<seq>` suffix (a three-digit, zero-padded per-category counter, such as `001` or `002`).
83
86
  Worker prompt history is retained not under `/tmp`, but always as a canonical artifact under `prompts/` for the current run.
84
87
 
88
+ `state/team-state-<task-type>-<seq>.json` stores invocation-aware dispatch
89
+ records. Code-owned worker launches record the invocation fields directly in
90
+ `workerDispatches[]`. Host-native calls use `agentDispatches[]`, and
91
+ `agentResultLinks[]` associates an accepted result path with exactly one
92
+ dispatch ID. The inverse is also exclusive: one dispatch ID cannot authorize
93
+ multiple accepted paths. State mutation uses a run-state lock and unique
94
+ temporary files so parallel dispatch and result-link writers do not lose each
95
+ other's updates. `host-native-spec-link-gate` never means that the host attested the
96
+ delivered prompt bytes; `promptDeliveryVerified` remains false.
97
+
98
+ The complete run manifest is published before any run-backed prompt. Its
99
+ `agentContract`, `invocationAssignments`, run-scoped duty root, catalog digest,
100
+ and invocation reservation root are immutable for the lifetime of the run.
101
+ Every invocation metadata file refers back to that manifest and to one
102
+ `assignmentRef`. A reservation binds `invocationId`, `workerId`, assignment,
103
+ audience, dispatch kind, prompt path, and adjacent metadata path before the
104
+ prompt can be reused. This run-scoped state belongs under `runs/.../prompts/`,
105
+ not the task-wide `instruction-set/`, because consecutive runs and parallel
106
+ implementation stages must not share mutable invocation identity.
107
+
108
+ The manifest's `authorizedPaths` allowlists instruction, prompt, and result
109
+ roots. Materialization resolves each requested path to its real path, rejects a
110
+ path outside the corresponding root, rejects a symbolic-link escape, and
111
+ rejects an undeclared result path before writing. Persisted instruction
112
+ provenance is a non-empty list of `{kind: project|runtime, path: <relative POSIX
113
+ path>}` objects. The path is normalized POSIX text relative to its named
114
+ authority; installed runtime absolute paths are not durable metadata.
115
+
116
+ Prompt publication is recoverable rather than pretending that two independent
117
+ files can be replaced atomically. A create-only prompt is written first and its
118
+ adjacent metadata is written last as the completion marker. Under the
119
+ per-invocation publication lock, a retry recovers a byte-identical orphan
120
+ prompt, but rejects different existing bytes. A reservation lock prevents two
121
+ concurrent writers from mixing prompt and metadata identities.
122
+
123
+ LLM calls outside a task run use
124
+ `<project>/.okstra/agent-invocations/<purpose>/`. Identity is scoped by the
125
+ `(purpose, invocationId)` pair, and both values must be canonical slugs. For an
126
+ invocation ID `<invocation-id>`, the exact immutable paths are
127
+ `<invocation-id>.instructions.md`, `<invocation-id>.prompt.md`, adjacent
128
+ `<invocation-id>.prompt.md.meta.json`, `<invocation-id>.result.json`, and
129
+ `<invocation-id>.prompt.md.completion.json`, plus a private `.tmp/` capture
130
+ area. The result envelope binds purpose, invocation ID, prompt metadata path,
131
+ and `returnedBody`; the completion marker is published last and binds the
132
+ canonical result envelope digest. The same raw returned body may be used by two
133
+ independent invocations because identity comes from the envelope, while a
134
+ cross-invocation envelope link is rejected. Code review uses purpose
135
+ `code-review`; schedule narrative verification uses `schedule-verification`.
136
+ Consumers parse only the `returnedBody` emitted by
137
+ `agent-prompt verify-completion`. A branch review's final report may use the
138
+ documented `.project-docs/code-reviews/<branch>/` exception, but its invocation
139
+ specification, result envelope, and completion record remain under `.okstra/`.
140
+
85
141
  The persisted prompt's `**Worker Preamble Path:**` records the selected functional audience contract: analysis uses `templates/worker-prompt-preamble.md`, implementation executor/verifier uses `templates/implementation-worker-preamble.md`, and report writing uses `templates/report-writer-prompt-preamble.md`. Every initial prompt also persists `**Worker Error Contract Path:** templates/worker-error-contract.md`. Active-run and run-context runtime resources expose the same audience map plus the shared error-contract path, so dispatch replay and context-cost accounting do not infer an audience from a provider/model name.
86
142
  Unlike before, `analysis-profile.md`, `analysis-material.md`, `reference-expectations.md`, `task-brief.md`, skill copies, and `final-report-template.md` are not duplicated for every run.
87
143
  These materials retain canonical copies in `instruction-set/` under the stable task root.
@@ -615,6 +615,74 @@ Skill flow:
615
615
  2. It calls `okstra_ctl.run.prepare_task_bundle(render_only=True)` with the user's input—directly invoking the same Python function without passing through `okstra.sh`.
616
616
  3. After the same instruction-set artifacts are written to disk, the current host reads the canonical lead prompt and assumes the lead role.
617
617
 
618
+ ### Agent invocation contract
619
+
620
+ An agent identity is functional, not provider-specific. Each LLM invocation is
621
+ composed from three independent inputs: the selected model assignment, a duty
622
+ contract from `prompts/duties/`, and call-specific task instructions. The
623
+ composer publishes the final prompt and adjacent metadata only after the
624
+ complete run manifest and its immutable `agentContract` and
625
+ `invocationAssignments` maps are on disk. Later manifest rewrites reject any
626
+ change to those fields or to the canonical lead-prompt path. This prevents a
627
+ prompt from being verified against state that appeared only after dispatch.
628
+
629
+ `invocationAssignments` is the assignment source of truth for the lead,
630
+ initial workers, critics, translators, reverification workers, and report
631
+ writer. Each `assignmentRef` resolves to exactly six fields: `provider`,
632
+ `model`, `modelExecutionValue`, `runner`, `hostRuntime`, and `hostModelValue`.
633
+ Invocation metadata repeats that resolved value under `modelAssignment`; the
634
+ verifier requires byte-equivalent canonical data rather than resolving the
635
+ model again. Initial and dynamic calls also reserve `invocationId`, `workerId`,
636
+ `assignmentRef`, audience, dispatch kind, prompt path, and metadata path under
637
+ the run's invocation-reservation root. Reusing an invocation ID with a
638
+ different identity fails as a conflict.
639
+
640
+ The adjacent metadata has one digest location, `digests`, containing exactly
641
+ `catalogDigest`, `assignmentDigest`, `dutyDigest`, `instructionDigest`, and
642
+ `promptDigest`. Assignment and instruction values use UTF-8 JSON with sorted
643
+ keys, compact separators, and no non-finite numbers before SHA-256 hashing.
644
+ Catalog and duty hashes use a versioned frame of sorted relative file names and
645
+ raw bytes. The prompt hash covers the final UTF-8 bytes. Instruction provenance
646
+ uses only `{kind: project|runtime, path: <relative POSIX path>}` entries under
647
+ `instruction.sourcePaths`; installed runtime absolute paths are never persisted.
648
+
649
+ Prompt publication uses an exclusive per-invocation lock and create-only file
650
+ publication. The prompt is published first and its metadata is the completion
651
+ marker published last. A retry may recover a matching prompt that has no
652
+ metadata; any differing prompt or metadata is an immutable conflict. The
653
+ reservation root has its own lock, so concurrent writers either converge on
654
+ the same specification or one fails without mixing artifacts from two writers.
655
+
656
+ `modelExecutionValue` is the provider-process value. `hostModelValue` is the
657
+ model argument understood by a host-native invocation primitive. They are
658
+ separate because a host may use a family token while the provider CLI requires
659
+ a concrete execution identifier. A code-owned provider launch goes through
660
+ `okstra worker-dispatch`, verifies the invocation immediately before process
661
+ creation, starts all selected analysis assignments before collecting any one
662
+ of them, and records `core-pre-dispatch`. Report writer is a Phase 6 dependency
663
+ and must run in its own explicit dispatch after convergence; the dispatcher
664
+ excludes it from default selection and rejects any mixed analysis/report batch.
665
+ Each completed result path must link to
666
+ exactly one dispatch, and one dispatch cannot authorize multiple accepted
667
+ results. A host-native launch records
668
+ `host-native-spec-link-gate` and links its accepted result through
669
+ `okstra agent-prompt link-result`; this proves association with the verified
670
+ specification, not observation of the prompt bytes delivered by the host.
671
+ Accordingly, host-native records retain `promptDeliveryVerified=false` unless
672
+ a future host supplies a verifiable receipt or transcript. The process-owned
673
+ lead launch performs the same pre-dispatch verification and records its lead
674
+ dispatch before process creation; an in-session lead can only apply the weaker
675
+ specification-link gate because Okstra cannot intercept the host primitive.
676
+
677
+ The same boundary covers the lead, initial and dynamic workers, critics,
678
+ report writing, translation, manager child leads, and the standalone code-review
679
+ and schedule-verification agents. `okstra codex-dispatch` is retained only as a
680
+ compatibility alias. Provider-specific LLM transport agents are not installed.
681
+ The implementation executor manifest has no `workerAgent` field: it records the
682
+ provider, both model values, runner, and dispatch mode, while `doctor` checks
683
+ either the native host adapter or deterministic dispatcher required by that
684
+ actual execution mode.
685
+
618
686
  See [`skills/okstra-run/SKILL.md`](../skills/okstra-run/SKILL.md) for the detailed procedure.
619
687
 
620
688
  ### 5. Analyze errors if needed
@@ -870,8 +938,8 @@ Each validator blocks the phase with a `contract-violated` exit code when a cont
870
938
  - The host adapter creates workers and collects results according to persisted runner assignments.
871
939
  - The standard policy uses Claude and Codex analysers plus a report writer. Antigravity, Grok, and Kimi are optional and included only when the selected profile allows them.
872
940
  - Worker models can be overridden through the legacy provider flags or generic `--worker-model provider=model`; lead and report-writer provider choices are explicit. Defaults are centrally managed through the provider registry and `OKSTRA_DEFAULT_*` environment variables.
873
- - For `--task-type implementation`, select the provider that takes the Executor role with `--executor <claude|codex|antigravity>` (or `OKSTRA_DEFAULT_EXECUTOR`, fallback `claude`). Only the Executor may mutate project files. The other two providers and the Executor's own provider are each dispatched as verifiers in separate CLI sessions (session isolation preserves the self-review safeguard). The Executor's model reuses the selected provider's worker-model flag (`--claude-model` / `--codex-model` / `--antigravity-model`). Provider / displayName / workerAgent / model are recorded in the run-manifest `teamContract.executor` block.
874
- - Worktree targeting is runner-based. A native-session executor uses its host adapter's edit and command primitives against `EXECUTOR_WORKTREE_PATH`; a CLI-wrapper executor receives the worktree through its provider wrapper. Prefer explicit working-directory flags such as `git -C` or `cargo --manifest-path` when available.
941
+ - For `--task-type implementation`, select the provider that takes the Executor role with `--executor <claude|codex|antigravity>` (or `OKSTRA_DEFAULT_EXECUTOR`, fallback `claude`). Only the Executor may mutate project files. The other two providers and the Executor's own provider are each dispatched as verifiers in separate sessions (session isolation preserves the self-review safeguard). The Executor's model reuses the selected provider's worker-model flag (`--claude-model` / `--codex-model` / `--antigravity-model`). Provider, display name, model, `modelExecutionValue`, `hostModelValue`, runner, and dispatch mode are recorded in `teamContract.executor`; there is no provider-specific `workerAgent` field.
942
+ - Worktree targeting is runner-based. A native-session executor uses its host adapter's edit and command primitives against `EXECUTOR_WORKTREE_PATH`; a CLI-backed executor receives the worktree path through the deterministic provider process. Prefer explicit working-directory flags such as `git -C` or `cargo --manifest-path` when available.
875
943
  - The project-level current-task convenience pointer is `.okstra/discovery/latest-task.json`.
876
944
  - The project-level canonical task inventory is `.okstra/discovery/task-catalog.json`.
877
945
  - At `okstra install` time, okstra skill assets are seeded to `~/.agents/skills/` by default. If `~/.claude` exists, `~/.claude/skills/` + `~/.claude/agents/` are seeded as well (per-project seeding is no longer performed).
package/docs/cli.md CHANGED
@@ -388,7 +388,7 @@ 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
- - `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.
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 the deterministic provider-process dispatcher.
392
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.
393
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.
394
394
 
@@ -422,7 +422,7 @@ worker roster contains Claude, Codex, or Antigravity.
422
422
 
423
423
  For a Codex lead dry run, use `okstra codex-run <args...>`. It adds `--render-only --lead-runtime codex` itself and prints the prepared task bundle and lead prompt without dispatching workers.
424
424
  The generated team-state, run manifest, and task manifest point `leadEventsPath` to `runs/<task-type>/state/lead-events-<task-type>-<seq>.jsonl`; rendering records a `bundle-prepared` event.
425
- Then `okstra codex-dispatch --project-root <dir> --run-manifest <run-manifest> [--workers <csv>]` reads each persisted assignment. `runner=native-session` rows stay with the current Codex host; `runner=cli-wrapper` rows run through their registered Claude, Antigravity, Grok, Kimi, or report-writer wrapper. The report-writer provider and model come from the manifest without a Codex-only opt-in flag; on success, postprocessing runs check-source → token-usage substitution → render-views → spawn-followups → validate-run in order.
425
+ Then `okstra worker-dispatch --project-root <dir> --run-manifest <run-manifest> [--workers <csv>]` reads each persisted assignment. `runner=native-session` rows stay with the current host; `runner=cli-wrapper` rows run through the registered provider process only after adjacent invocation metadata verifies. Without `--workers`, only CLI-backed analysis assignments are selected. Report writer is deferred to Phase 6 and must be dispatched explicitly with `--workers report-writer`; mixing it with analysis workers in one invocation is rejected before process creation. `okstra codex-dispatch` is a compatibility alias for this provider-neutral command. The report-writer provider and model still come from the manifest without a Codex-only opt-in flag; on success, postprocessing runs check-source → token-usage substitution → render-views → spawn-followups → validate-run in order.
426
426
 
427
427
  The Codex worker (`--workers codex`, `--codex-model`) and Codex lead runtime are separate. The former creates a worker assignment whose runner depends on the host; the latter selects Codex as the native lead boundary. On Claude Code the Codex worker uses a CLI wrapper, while on Codex it uses the host-native worker/session primitive.
428
428
 
@@ -772,14 +772,18 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
772
772
  | `okstra render-bundle <args…> [--stage <auto\|N>] [--stages <csv>]` | Thin shim over `prepare_task_bundle(render_only=True)` with the same signature as `python3 -m okstra_ctl.run --render-only`. `--stage` is for `implementation` and `final-verification`: for implementation, `auto` (default) selects the earliest incomplete stage with satisfied dependencies, while `<N>` forces a stage; for final-verification, `<N>` verifies one stage with artifacts under `runs/final-verification/stage-<N>/` and a `-fv-s<N>` team suffix, while an empty value performs whole-task verification with the flat layout. The separate `--stages <csv>` channel is for `release-handoff`: stage-group mode bundles the listed stage numbers into one PR, while an empty value selects whole-task mode. Preparation enforces eligibility—`done` + accepted `verified` + not yet `pr`—and automatically creates an input document that cites verification reports |
773
773
  | `okstra profile show <task-type> [--resolved]` | Print a phase profile. `--resolved` expands its `{{INCLUDE:}}` targets and appends the lazy-read sidecars named in the profile body — transitively, because sidecars name sidecars of their own (`_implementation-executor.md` points at the coding-conventions preflight, the diff-review sweep, and the completion self-check). That matters because a profile is assembled from three places, so grepping only the top-level file returns false negatives: `grep clarification prompts/profiles/implementation.md` finds nothing while the assembled profile has many hits. One grep over this output answers whether a task-type covers a rule. The sidecar list is read from the profile body, never hard-coded, so a newly added sidecar is picked up without a code change. Read-only: it writes no manifest and registers no run, which is what separates it from `render-bundle` — `render-bundle` answers the same question but records a run in `recent.jsonl`, so it cannot be used to look something up. Exits 2 for an unknown task-type |
774
774
  | `okstra codex-run <args…>` | Codex lead-adapter dry-run entry point. Accepts the same arguments as `render-bundle` but owns `--render-only --lead-runtime codex`. It prepares the task bundle and prints the prompt for the Codex lead without dispatching workers |
775
- | `okstra codex-dispatch --project-root <dir> --run-manifest <path> [--workers <csv>]` | Read a Codex-host run manifest and dispatch every requested `runner=cli-wrapper` assignment through its registered provider wrapper. Native Codex rows remain in-session. The persisted report-writer assignment needs no Codex-specific opt-in; successful report completion triggers token substitution, HTML rendering, follow-up generation, and validation |
776
- | `okstra team dispatch --project-root <dir> --run-manifest <path> [--workers <csv>] [--jobs-file <path>] [--dry-run]` / `okstra team await --project-root <dir> --run-manifest <path> [--json]` / `okstra team teardown --project-root <dir> --run-manifest <path> [--dry-run] [--json]` | Read a `leadRuntime=external` run manifest and dispatch, await, or tear down tmux-pane workers. If a tmux pane cannot be created, gracefully degrade to the CLI wrapper and record the fallback in `workerDispatches[].degradedFrom` |
775
+ | `okstra worker-dispatch --project-root <dir> --run-manifest <path> [--workers <csv>] [--dry-run]` | Provider-neutral deterministic dispatcher for `runner=cli-wrapper` assignments. It verifies each adjacent invocation specification against the immutable run manifest immediately before process creation and records `core-pre-dispatch`; native-session rows stay with the host. The default selects CLI analysis assignments only. Phase 6 uses explicit `--workers report-writer`, and a mixed analysis/report batch is rejected. `--dry-run` performs the same verification and resolution without starting a provider process. |
776
+ | `okstra codex-dispatch --project-root <dir> --run-manifest <path> [--workers <csv>] [--dry-run]` | Compatibility alias for `okstra worker-dispatch`; it no longer selects a Codex-only transport-agent path. |
777
+ | `okstra agent-prompt materialize\|verify\|record-dispatch\|link-result\|materialize-result\|complete\|verify-completion` | Internal invocation-contract CLI. `materialize` composes model assignment, functional duty, and task instructions; `verify` rejects identity, path, snapshot, assignment, source, or digest drift. Run-backed calls resolve `assignmentRef` from the manifest, enforce `authorizedPaths`, and reject real-path or symbolic-link escape. `record-dispatch` records a verified host-native specification before dispatch and `link-result` binds the accepted result. Standalone calls are identified by `(purpose, invocationId)` under `.okstra/agent-invocations/<purpose>/`; they publish a canonical result envelope and publish the completion marker last. Consumers use only the `returnedBody` from `verify-completion`. Metadata contains exactly `catalogDigest`, `assignmentDigest`, `dutyDigest`, `instructionDigest`, and `promptDigest`; JSON inputs use UTF-8, sorted keys, compact separators, and no non-finite values, while duty files use versioned sorted-name/byte framing. Instruction sources use `{kind: project\|runtime, path: <relative POSIX path>}` and never persist an installed absolute runtime path. |
778
+ | `okstra team dispatch --project-root <dir> --run-manifest <path> [--workers <csv>] [--jobs-file <path>] [--dry-run]` / `okstra team await --project-root <dir> --run-manifest <path> [--json]` / `okstra team teardown --project-root <dir> --run-manifest <path> [--dry-run] [--json]` | Read a `leadRuntime=external` run manifest and dispatch, await, or tear down tmux-pane workers. Default dispatch excludes report writer; Phase 6 selects it explicitly, and mixed analysis/report jobs are rejected. If a tmux pane cannot be created, gracefully degrade to the CLI wrapper and record the fallback in `workerDispatches[].degradedFrom` |
777
779
  | `okstra report-finalize --project-root <dir> --run-manifest <path> --report <final-report.md>` | Run the whole Phase 7 post-report sequence in its contractual order: `token-usage` → `render-views` → `spawn-followups` → `validate-run`. Stops at the first non-zero exit and names the failing step, then prints a per-step `[ok]` / `[FAIL]` / `[skip]` summary on stderr so the outcome is legible without parsing the JSON payload. Every step is idempotent, so re-running after a fix is safe — but `--only <step>` (repeatable) reruns just the named steps in contractual order, which matters because `validate-run` is the step that usually fails and retrying it otherwise repeats the three steps before it at full token and wall-clock cost. This is the same code path (`scripts/okstra_ctl/report_finalize.py`) the Codex lead adapter runs automatically after its report-writer completes, so a Claude-led and a Codex-led run finalize identically. `--workspace-root` is owned by the Node wrapper. Prefer this over invoking the four steps individually |
778
780
  | `okstra render-views <final-report.data.json\|final-report.md>` | The Phase 7 `render-views` step, runnable on its own. Schema v2 data is rendered directly (contract: `schemas/final-report-v2.0.schema.json`) into an always-generated, task-specific human HTML sibling while `templates/reports/final-report-v2.template.md` independently owns the AI handoff Markdown. Passing the Markdown sibling locates the same v2 data.json. Schema v1 and quick reports keep the legacy conditional renderer. The Node wrapper calls `scripts/okstra-render-report-views.py`; `validators/validate-report-views.py` verifies source/schema/template digests, required human fields, form controls, external assets, diagram/table ID parity, and Response ID parity |
779
781
  | `okstra design-prep <list\|show\|write>` | Review AI-prepared implementation design requests, inspect their effective confirmed response, or append a confirmed user/wizard response without editing the planning report |
780
782
  | `okstra wizard <init\|step\|render-args\|confirmation\|outcome> --state-file <path>` | Interactive input state machine for okstra-run, implemented by `okstra_ctl.wizard`. Seed a state file with `init`, then repeatedly call `step --answer <val>` to receive the next `Prompt` JSON. `--answer` is **required**; use `--no-submit` to peek at the next prompt without submitting a response. A `pick` with more choices than the host picker can display keeps `kind: "pick"` but adds `presentation: "numbered-text"`; render every option as a numbered Markdown list and submit the user's 1-based number, exact value, or exact label. Invalid, out-of-range, and ambiguous answers re-prompt without dropping choices. `render-args` returns the final `render-bundle` argument map, and `confirmation` returns the user echo block. On a completed wizard, `outcome` returns `renderArgs`, `persistActions`, and `confirmationText` together; project/global release-handoff PR-template persistence appears as `persistActions[].command == "config.set"`. For an `implementation` task type, `stage_pick` follows `approved_plan_pick` and selects the stage before `executor_pick`. The brief step appears only for entry task types—requirements-discovery, error-analysis, improvement-discovery, project-analysis, feature-analysis, and change-impact-analysis. Analysis inputs use `feature_evidence_pick` / `feature_evidence`, `project_evidence_pick` / `project_evidence`, and `analysis_target_pick` / `analysis_target`; a revision-requested report prioritizes its same-task, same-type rerun. Downstream lifecycle phases automatically carry the manifest brief, with a three-option `brief_carry` fallback when none is registered; `release-handoff` has no brief and enters multi-select `handoff_stage_pick` for eligible stage groups or the whole task |
781
783
  | `okstra token-usage ...` | Wrap the installed `okstra-token-usage.py` to collect and substitute run token usage. Session JSONL is incrementally scanned by default through a byte-cursor cache at `$OKSTRA_HOME/cache/token-usage/`; `--no-cache` bypasses the cache and forces a full rescan as an accuracy fallback |
782
784
 
785
+ In prose, `okstra agent-prompt materialize|verify|record-dispatch|link-result|materialize-result|complete|verify-completion` denotes that internal command family; the vertical bars separate subcommands and are not literal shell arguments.
786
+
783
787
  The convergence state lifecycle is `groups v1.0 → work v1.0 → final v1.3`; round-plan, round-results, and optional critic-results v1.0 artifacts provide the auditable transitions between those endpoints.
784
788
 
785
789
  `okstra convergence apply-critic-gaps` is the only transition that may add verified coverage gaps to terminal main-queue state. `okstra plan-items extract` creates the complete plan queue, and `okstra plan-items validate` rejects any omission or drift before verifier dispatch.
@@ -45,8 +45,8 @@ okstra preflight --runtime claude-code --json
45
45
  2. **Call the target CLI**: `okstra code-review target --task-key <k> --stage <N> --project-root <dir> --json`, or `okstra code-review target --branch <name> [--base <ref>] --project-root <dir> --json`. It returns `{ok, projectRoot, mode, worktreePath, branch, baseCommit, headCommit, reviewPath, round}` (stage mode adds `taskKey`, `taskRoot`, `stage`). **Never derive the base** — the CLI owns it, and `baseCommit` may be a ref rather than a commit id, so pass it through verbatim. Run git in `worktreePath` when non-empty, otherwise in `projectRoot` against `branch`.
46
46
  3. **Show the base and confirm it** with a 3-option picker before censusing anything — the returned `baseCommit` plus `git log -1 --oneline <baseCommit>` first, `Enter directly` last. Only an override calls the target CLI a second time, with `--base <ref>`.
47
47
  4. **Census the diff** per `census-rules.md`: four axes (`structural`, `semantic`, `state-and-tests`, `general`), one cell per target per axis — the axis **is** the rule group, never one cell per individual rule. Membership is mechanical; judgment only ever decides a verdict. Route the coding-preflight packs exactly once here (`okstra paths --field home` → `<okstraHome>/prompts/coding-preflight/overview.md`), and fix the calibration path the briefs carry (`~/.claude/skills/okstra-code-review/references/review-calibration.md`). Print every cell table, every exclusion with its reason, the applied packs, and both completion criteria. Never truncate a large census — report the cell count and confirm.
48
- 5. **Dispatch four reviewers in parallel** all four `Agent` calls in one message, `subagent_type: "general-purpose"`. Each brief carries the diff, the work directory, its own axis's cell list verbatim, its packs' absolute paths, and the absolute calibration path.
49
- 6. **Audit the coverage.** Diff each reviewer's returned cells against the slice it was handed; re-dispatch one gap-fill agent per axis for the missing cells, and repeat until every cell has a verdict. A missing verdict is unfinished work, never an implicit `clean`.
48
+ 5. **Materialize and dispatch four reviewers in parallel.** Each reviewer is a separate standalone invocation under `.okstra/agent-invocations/code-review/`. Write `<invocation-id>.instructions.md`, run `okstra agent-prompt materialize --purpose code-review --audience code-reviewer ...`, and verify the returned `metadataPath` before dispatch. A native host call receives the verified prompt body and `hostModelValue`; a deterministic provider process receives the prompt path and `modelExecutionValue` through `okstra worker-dispatch`. Each brief carries the diff, the work directory, its own axis's cell list verbatim, its packs' absolute paths, and the absolute calibration path.
49
+ 6. **Complete and audit the results.** Capture each raw return under the purpose directory's `.tmp/`, then run `agent-prompt materialize-result`, `complete`, and `verify-completion` in order. Parse only the verified `returnedBody`. Diff those cells against the assigned slice; re-dispatch one gap-fill invocation per axis for missing cells, using a new invocation ID and the same full materialize/verify/result/completion boundary. A missing or unverified verdict is unfinished work, never an implicit `clean`.
50
50
  7. **Merge and write.** Dedupe across axes, never re-grade a severity, and write the report to `reviewPath` with `Write` (frontmatter `mode` / `taskKey` / `branch` / `stage` / `round` / `baseCommit` / `headCommit` / `packs` / `generatedAt`; body `## Coverage`, `## Must-fix`, `## Should-fix`, `## Nits`, `## Score`). An empty diff dispatches no reviewer and still writes the report — Coverage reading zero cells and a Score table totalling 0.
51
51
 
52
52
  ## Output Rules
@@ -55,3 +55,4 @@ okstra preflight --runtime claude-code --json
55
55
  - The report's prose is Korean; paths, identifiers, rule names, and quoted code stay verbatim.
56
56
  - Every finding cites a line **this diff changed**, and carries a concrete fix (a pseudocode sketch for readability, an alternative name for naming, a destination for structural).
57
57
  - Read-only against the repository: `okstra code-review target` creates no directory and no file, and the review never reconciles git history. A rewritten base is reported, not force-fixed.
58
+ - Branch mode keeps the final review at `.project-docs/code-reviews/<branch>/`, but its invocation prompts, result envelopes, and completion markers remain under `.okstra/agent-invocations/code-review/`.
@@ -217,7 +217,9 @@ Run both gates against the same draft and temporary selection contract.
217
217
  python3 ~/.okstra/lib/validators/validate-schedule.py <draft> --selection-json <selection>
218
218
  ```
219
219
 
220
- 2. Only after that command passes, dispatch an independent LLM verifier with the draft and selection JSON, but not the lead's reasoning. It checks narrative coherence, phase rationale, executable order, engineering-only scope, and contradictions with the structured rows.
220
+ 2. Only after that command passes, create a new `.okstra/agent-invocations/schedule-verification/<invocation-id>.instructions.md` with the draft, selection JSON, and checks, but not the lead's reasoning. Run `okstra agent-prompt materialize --purpose schedule-verification --audience schedule-verifier ...` and verify the returned metadata before dispatch. A native host call uses the verified prompt body plus `hostModelValue`; a deterministic provider process uses `okstra worker-dispatch`, the prompt path, and `modelExecutionValue`. The verifier checks narrative coherence, phase rationale, executable order, engineering-only scope, and contradictions with the structured rows.
221
+
222
+ Capture the raw verifier return under the purpose directory's `.tmp/`, then run `okstra agent-prompt materialize-result`, `complete`, and `verify-completion` in order. Parse only the verified `returnedBody`; an inline or unverified response cannot pass the narrative gate.
221
223
 
222
224
  If either gate finds a defect, revise the same draft in place and restart from the deterministic gate. Allow at most two revision rounds across both gates. Never publish a draft that has not passed both gates in that order.
223
225
 
@@ -13,22 +13,26 @@ Higher priority matches first. Once an upper step matches, the lower steps are n
13
13
  | 1 | **per-run override** | `okstra render-bundle --pr-template-path <path>` or the wizard's one-time input | A relative path is resolved against the caller cwd (override) or against `project_root` (using the same function as project scope). |
14
14
  | 2 | **project scope** | the `prTemplatePath` field in `<project_root>/.okstra/project.json` | A relative path is resolved against `project_root`. |
15
15
  | 3 | **global scope** | the `prTemplatePath` field in `~/.okstra/config.json` | **Only an absolute path or a `~/`-prefixed path** is allowed. A relative path is ambiguous and rejected. |
16
- | 4 | **default (skill bundle)** | the first existing file among the candidate paths (§2 below) | The fallback path right after `npx okstra install`. |
16
+ | 4 | **default (installed runtime)** | the first existing file among the candidate paths (§2 below) | The fallback path right after `npx okstra install`. |
17
17
 
18
18
  If the file named in any of the 4 steps does not exist, it fails immediately with `PrTemplateError` (no silent fallback).
19
19
 
20
20
  ## 2. Default candidate paths
21
21
 
22
- 1. `$OKSTRA_SKILLS_DIR/templates/pr/pr-body.template.md` only when the `OKSTRA_SKILLS_DIR` environment variable is set.
23
- 2. `~/.claude/skills/templates/pr/pr-body.template.md` — the standard location `npx okstra install` installs to.
22
+ | # | Path | When it exists |
23
+ |---|------|----------------|
24
+ | 1 | `$OKSTRA_SKILLS_DIR/okstra-run/templates/pr-body.template.md` | Only when the `OKSTRA_SKILLS_DIR` environment variable is set. Nothing in okstra sets it for you — it is an escape hatch for a non-standard skill home. |
25
+ | 2 | `$OKSTRA_HOME/templates/pr/pr-body.template.md` — `~/.okstra/templates/pr/pr-body.template.md` unless `OKSTRA_HOME` overrides the home | The location `npx okstra install` writes to. This is the candidate that normally matches. |
24
26
 
25
- The candidates are tried in priority order, and if all are absent it ends with an explicit error as follows.
27
+ The candidates are tried in priority order, and if all are absent it ends with an explicit error that names every path it searched:
26
28
 
27
- > `no PR template available: default skill template not found. Reinstall okstra (npx okstra install) or set prTemplatePath in project.json / ~/.okstra/config.json.`
29
+ ```text
30
+ no PR template available: default template not found. Searched: <candidate paths>. Reinstall okstra (`npx okstra install`) or set prTemplatePath in project.json / ~/.okstra/config.json.
31
+ ```
28
32
 
29
33
  ## 3. The original inside the source repository
30
34
 
31
- - [`templates/pr/pr-body.template.md`](../templates/pr/pr-body.template.md) — the original that `npx okstra install` copies to the §2 default location. To change the copy, edit this file and install again.
35
+ - [`templates/pr/pr-body.template.md`](../templates/pr/pr-body.template.md) — the original that `npx okstra install` copies to `~/.okstra/templates/pr/pr-body.template.md` (§2 candidate 2). To change the copy, edit this file and install again.
32
36
 
33
37
  ## 4. Configuration commands — persistence
34
38
 
@@ -59,8 +59,8 @@ okstra/
59
59
  ├── .claude/skills/ Claude Code project-only mirror-sync surface
60
60
  ├── .claude/settings.json Claude Code project-only mirror-sync surface
61
61
  ├── .codex/hooks.json Codex project lifecycle hooks
62
- ├── agents/ lead SKILL.md + worker agent specs
63
- ├── prompts/ launch template, phase profiles, wizard prompt JSON
62
+ ├── agents/ native Claude execution-adapter definitions
63
+ ├── prompts/ launch/profile contracts, duty catalog, wizard prompt JSON
64
64
  ├── schemas/ JSON schema for final-report data.json
65
65
  ├── templates/ report, setup, PR, project-doc templates/assets
66
66
  ├── validators/ run / brief / schedule / view validators
@@ -139,7 +139,7 @@ Runtime/install asset changes follow this checklist:
139
139
  - `runtime/templates/*` → `~/.okstra/templates/`
140
140
  - `runtime/skills/<name>` (the thirteen user-facing skills only) → `~/.agents/skills` always, plus `~/.claude/skills` when `~/.claude` exists
141
141
  - `runtime/prompts/*` → `~/.okstra/prompts/` (lead contracts under `prompts/lead/`, coding-preflight pack under `prompts/coding-preflight/`)
142
- - `runtime/agents/workers/*.md` → `~/.claude/agents/*-worker.md` when `~/.claude` exists
142
+ - the native Claude execution adapters in `runtime/agents/workers/` → `~/.claude/agents/` when `~/.claude` exists; retired provider transport-agent files are removed only when the prior install manifest owned them
143
143
  - install manifests → `~/.okstra/installed-skills.json` (target-aware), `~/.okstra/installed-agents.json`
144
144
  - version stamp → `~/.okstra/version`
145
145
 
@@ -184,7 +184,8 @@ Runtime/install asset changes follow this checklist:
184
184
  | `render-bundle` | `src/commands/execute/render-bundle.mjs` | Preview `prepare_task_bundle(render_only=True)` |
185
185
  | `profile` | `src/commands/inspect/profile-show.mjs` | Print a phase profile with `{{INCLUDE:}}` expanded and its lazy-read sidecars appended transitively, so one grep answers whether a task-type covers a rule — a top-level grep alone returns false negatives (Python: `okstra_ctl.profile_show`). Read-only, unlike `render-bundle` |
186
186
  | `run` | `src/commands/execute/run.mjs` | Host-aware execution front door (`auto` → Claude/Codex/Antigravity/external path selection) |
187
- | `codex-run`, `codex-dispatch` | `src/commands/execute/codex-*.mjs` | Codex lead dry-run bundle preparation and CLI-backed worker dispatch |
187
+ | `codex-run`, `codex-dispatch` | `src/commands/execute/codex-*.mjs` | Codex lead dry-run bundle preparation; `codex-dispatch` is the compatibility alias for provider-neutral worker dispatch |
188
+ | `agent-prompt`, `worker-dispatch` | `src/commands/execute/{agent-prompt,worker-dispatch}.mjs` | Materialize/verify invocation prompts, record host-native specification/result links, and launch verified CLI assignments through the provider-neutral dispatcher |
188
189
  | `team` | `src/commands/execute/team.mjs` | External lead tmux-pane worker dispatch / await / teardown |
189
190
  | `convergence` | `src/commands/execute/convergence.mjs` | Internal admin CLI for the deterministic Phase 5.5 convergence engine (`seed`/`plan-round`/`apply-round`/`apply-critic-gaps`/`finalize`/`validate`/`example`; Python: `okstra_ctl.convergence`) |
190
191
  | `plan-items` | `src/commands/execute/plan-items.mjs` | Internal admin CLI for deterministic plan-body item extraction and exact-match validation (`extract`/`validate`; Python: `okstra_ctl.plan_items_cli`) |
@@ -294,9 +295,13 @@ Important modules:
294
295
  | `manager_store.py` | Manager-owned state mutation — project membership, task planning, assignment, directives, event append |
295
296
  | `manager_sync.py` | One-way child project `.okstra` snapshot reader; corrupt child state becomes row-level `error` so other children continue |
296
297
  | `manager_launch.py` | Child launch packet and manager child context renderer; records `prepared` launch metadata/events without changing project-local task state |
297
- | `dispatch_core.py` | Backend-neutral worker dispatch core worker execution/collection logic shared by any lead runtime (Claude/Codex/Antigravity/external); gates selected initial prompts through the shared cross-task contract before launch |
298
+ | `agent_invocation.py` | Deep invocation-contract module composes model assignment, common/functional duty, and task instructions; publishes immutable prompt/metadata pairs; verifies five digests; owns standalone result/completion envelopes |
299
+ | `agent_prompt_cli.py` | CLI boundary for run-backed and standalone materialization/verification plus host-native dispatch and result-link records |
300
+ | `dispatch_state.py` | Provider-neutral `WorkerJob`, invocation metadata validation, immutable host-native dispatch/result-link recording, and shared team-state mutation helpers |
301
+ | `dispatch_core.py` | Backend-neutral worker dispatch core — verifies invocation metadata immediately before worker execution, then records and collects code-owned process/pane attempts shared by every lead runtime |
302
+ | `worker_dispatch.py` | Provider-neutral deterministic dispatcher for every `runner=cli-wrapper` assignment; it never composes or rewrites a prompt |
298
303
  | `cmux.py` | cmux-pane worker backend — mirrors the tmux backend's contract (a worker that gets a pane frees the lead process; anything that stops a pane opening degrades quietly to the blocking wrapper, recording a surface UUID rather than a tmux pane id). Detects a usable cmux session before selecting the backend (CLI resolves + ping answers PONG + the lead's workspace is resolvable), derives placement from the workspace geometry each dispatch, relays lead/worker events to the cmux sidebar, and records the run's terminal backend in the manifest so both phases of a run land on one backend. A sandbox that hides cmux (`PermissionError` on the socket) stops dispatch with the remedy instead of degrading into the same broken fallback; a quit app (`FileNotFoundError`) still degrades |
299
- | `codex_dispatch.py` | Codex lead CLI-worker dispatcher — the `okstra codex-dispatch` backend. Reads the run manifest to run the Codex-side supported worker subset, applies the same cross-task initial-prompt gate, and performs token-usage substitution, view render, follow-up, and validation |
304
+ | `codex_dispatch.py` | Compatibility adapter delegating `okstra codex-dispatch` to the provider-neutral `worker_dispatch` path |
300
305
  | `analysis_packet.py` | assembles the compact analysis-worker input packet for a task run from worker-owned profile sections; report/lead procedure stays outside the packet |
301
306
  | `analysis_inputs.py` | shared input boundary for `project-analysis`, `feature-analysis`, and `change-impact-analysis` — validates evidence-report identity and review status, enforces the type-to-type relation allowlist, computes `exact`/`stale` freshness, and resolves free-text or `PF-NNN` feature targets for both wizard and prepare paths |
302
307
  | `user_response.py` | parses clarification/approval responses and the analysis-review sidecar; `parse_analysis_review` validates accepted, revision-requested, and rejected decisions plus their affected IDs and reason |
@@ -356,6 +361,7 @@ Token/cost accounting:
356
361
  | Path | Role |
357
362
  |---|---|
358
363
  | `launch.template.md` | Lead prompt template rendered for each run |
364
+ | `duties/common.md`, `duties/<audience>.md` | Canonical common and functional duty contracts composed into every Okstra-owned LLM invocation; provider/model identity does not select the duty |
359
365
  | `profiles/_common-contract.md` | Shared phase contract |
360
366
  | `profiles/<task-type>.md` | Phase profiles (single language — runtime always loads from `profiles/`, never a translated mirror) |
361
367
  | `project-analysis.md`, `feature-analysis.md`, `change-impact-analysis.md` | Read-only sidetrack profiles for project mapping, one-feature behavior tracing, and proposed-change impact mapping |
@@ -434,13 +440,10 @@ Boilerplate shared by several skills (bash invocation rule, outdated-CLI preflig
434
440
  | File | Role |
435
441
  |---|---|
436
442
  | `agents/workers/claude-worker.md` | Claude analyzer/verifier/executor spec |
437
- | `agents/workers/codex-worker.params.json` | Codex analyzer/verifier/executor wrapper params (build renders `.md` via `_cli-wrapper-template.md`) |
438
- | `agents/workers/antigravity-worker.params.json` | Antigravity analyzer/verifier/executor wrapper params (build renders `.md` via `_cli-wrapper-template.md`) |
439
- | `agents/workers/grok-worker.params.json` | Grok read-only analyser/critic wrapper params |
440
- | `agents/workers/kimi-worker.params.json` | Kimi read-only analyser/critic wrapper params |
441
443
  | `agents/workers/report-writer-worker.md` | data.json SSOT author and audit sidecar writer |
444
+ | `agents/workers/translator-worker.md` | Claude-native final-report translation execution adapter |
442
445
 
443
- 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.
446
+ These files are native Claude execution adapters, not provider-neutral LLM transport wrappers. Non-native providers execute through the deterministic `worker-dispatch` process boundary. 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.
444
447
 
445
448
  ### 4.12 `tests/` and `tests-e2e/`
446
449
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.167.0",
3
+ "version": "0.169.0",
4
4
  "description": "Host-aware multi-provider cross-verification orchestrator runtime and agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.167.0",
3
- "builtAt": "2026-08-12T03:32:48.888Z",
2
+ "package": "0.169.0",
3
+ "builtAt": "2026-08-13T13:31:24.833Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -21,11 +21,12 @@ color: blue
21
21
  tools: ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "TodoWrite", "WebFetch", "WebSearch"]
22
22
  ---
23
23
 
24
- You share an **identical core responsibility** with the Codex and Antigravity workers: cover every brief question across feasibility, requirement interpretation, hidden assumptions, alternatives, and execution risk in sections 1–5 of the worker output. Cross-verification only triangulates if all three workers answer the same questions against the same brief.
25
-
26
- Your specialization lens — **broad reasoning depth, hidden-assumption surfacing, execution-risk decomposition** — is the only content that belongs in optional Section 6 (additive, not subject to convergence). Do NOT let the lens narrow sections 1–5: a Claude-only "Findings" populated solely with assumption-class items is a contract violation.
27
-
28
- Unlike the Codex / Antigravity workers, you are an in-process Claude subagent — you do NOT shell out to a CLI. Use your native tools (Read / Grep / Glob / MCP) directly.
24
+ This is the Claude host-native execution adapter for a materialized Okstra
25
+ invocation. The final prompt's duty contract owns the role boundary,
26
+ responsibility, and prohibited actions. This file owns Claude tool usage,
27
+ artifact persistence, and liveness procedure only. Do not shell out to another
28
+ model CLI, and refuse a dispatch whose prompt has no adjacent verified
29
+ invocation metadata.
29
30
 
30
31
  ## Execution Rules
31
32
 
@@ -14,7 +14,11 @@ model: inherit
14
14
  tools: ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "TodoWrite", "WebFetch", "WebSearch"]
15
15
  ---
16
16
 
17
- **Author the three report completion artifacts**: the final-report data.json (the JSON SSOT) at the assigned `Result Path`, its rendered Markdown sibling, and the worker-result pointer at `Worker Result Path`. Maintain the separate heartbeat audit sidecar at `Audit sidecar path`. That is the `Report writer worker`'s sole responsibility for okstra cross-verification. You are NOT an analysis worker — you do not produce independent findings, you do not vote in convergence, and you do not re-do the workers' analysis.
17
+ This is the Claude host execution adapter for a materialized Okstra invocation.
18
+ The final prompt's `report-writer` duty contract owns the role boundary,
19
+ required responsibility, and prohibited actions. This file owns only how that
20
+ contract is executed with Claude host tools. Refuse a dispatch whose prompt has
21
+ no adjacent verified invocation metadata.
18
22
 
19
23
  - The `**Report Language:**` header in your dispatch prompt is already
20
24
  resolved to `en` or `ko` by the lead. Copy it verbatim into
@@ -28,9 +32,10 @@ tools: ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "TodoWrite", "WebFetch"
28
32
  when the data.json's authored prose is not English. You can run that check
29
33
  yourself before returning.
30
34
 
31
- ## Authority
35
+ ## Host file procedure
32
36
 
33
- You are the canonical author of `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json` for this run. The host-native Okstra lead has explicitly delegated file-authorship to you. The lead reviews your output but does not write the file.
37
+ Use the assigned `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json`
38
+ path and the renderer commands supplied by the final prompt.
34
39
 
35
40
  The data.json is the **single source of truth** for two audiences. The renderer (`scripts/okstra-render-final-report.py`) produces the AI handoff Markdown (`final-report-<task-type>-<seq>.md`) deterministically from it. Phase 7 produces the human HTML (`final-report-<task-type>-<seq>.html`) through the task-specific HTML renderer. HTML is rendered directly from the data.json; it is not a presentation of the Markdown. You do NOT hand-write either derived artifact. Both are regenerated whenever the data.json changes.
36
41
 
@@ -130,7 +135,7 @@ Rules (the schema enforces most of these — they are listed here so you know *w
130
135
  - For `implementation-planning`, populate `implementationPlanning.variationPointAnalysis` — a `hasMultipleImplementations` judgement synthesized from the analysis workers' output, not a field filled in last. When it is `true`, write one `points[]` row per varying behavior carrying `behavior`, the two or more `implementations` that serve it, `evidence` (a `path:line`, or the sibling task / stage that already implements that behavior), and an `extractionDecision` of `extract` / `interfaceKind` / `coveredBy` (the Stage Map stage that builds the interface) / `rationale`; when it is `false`, write a non-empty `noVariationRationale` and leave `points` empty (the two branches are mutually exclusive). Do NOT pass a boilerplate rationale — `false` is the cheaper field to fill, and a `false` declaration the brief or the sibling code in the workers' evidence contradicts is a `P-Var` DISAGREE, not a saving. Also populate `implementationPlanning.recommendedOption.testSeams`: one row per boundary a test injects at and replaces, each carrying `boundary` / `injectedAs` / `replacedInTest`. An empty list is a conscious "no seam needed" claim, never a default for a field nobody filled. The schema excerpt enumerates both row shapes — author against it. (Maintainer SSOT for these two rules: the `Required deliverable shape` bullet in `prompts/profiles/implementation-planning.md` in the okstra repo; that path is not resolvable here, so it is provenance, not a file to open.) **Enforced:** `schemas/final-report-v2.0.schema.json` `$defs.VariationPointAnalysis` / `$defs.VariationPoint` (the block is in `implementationPlanning.required`) plus `testSeams` in `$defs.RecommendedOption`'s `required`; `validators/validate-run.py` `_validate_variation_point_analysis` rejects a rationale-less `false`, a `false` carrying points, a `true` with no point, an `extract: true` decision leaving `interfaceKind` or `coveredBy` empty, and a hexagonal project extracting as anything but a port; and every point becomes a `P-Var-*` plan item judged in §5.5.9.
131
136
  - When the `Task Type` is `improvement-discovery`, populate `improvementDiscovery.candidates[]`, `improvementDiscovery.lensCoverage[]`, `improvementDiscovery.selectionLimit`, and `improvementDiscovery.userNarrative`. Each candidate carries the 11 logical fields enforced by `validators/validate_improvement_report.py`; each lens-coverage row records candidate IDs or an evidence-backed no-candidate rationale. Source IDs, lens names, and worker prefixes from `scripts/okstra_ctl/improvement_lenses.py`. The standard renderer derives the AI handoff Markdown; never author a free-form improvement report.
132
137
 
133
- Write the three completion artifacts and the separate audit sidecar with your `Write` tool — that is the canonical authoring path, and okstra ships no hook that blocks `.md` writes (its only settings hook is the `SessionEnd` trace-cleanup; the coding-preflight hook emits reminders but never blocks). A Bash heredoc is acceptable ONLY when a specific `Write` call is genuinely rejected by the host environment, and it MUST produce byte-identical content — do not reach for it pre-emptively. After writing data.json, invoke the renderer (`Bash`): `okstra render-final-report <data.json path>`, then write the Worker Result Path pointer. Confirm data.json, rendered Markdown, the pointer, and the audit sidecar exist before responding with a short status line prefixed by your model identity, per the preamble §"Return message to the lead". **Enforced:** dispatch `completionPaths` requires the first three files and `validators/validate_session_conformance.py` validates the audit sidecar.
138
+ Write the three completion artifacts and the separate audit sidecar with your `Write` tool — that is the canonical authoring path, and okstra ships no hook that blocks `.md` writes (its seeded settings carry no `PreToolUse` entry at all — only the session/subagent lifecycle hooks `SessionStart` compact-reminder, `SessionEnd` trace-cleanup, and `SubagentStop` / `TaskCompleted` pane reclaim, none of which can intercept a tool call). A Bash heredoc is acceptable ONLY when a specific `Write` call is genuinely rejected by the host environment, and it MUST produce byte-identical content — do not reach for it pre-emptively. After writing data.json, invoke the renderer (`Bash`): `okstra render-final-report <data.json path>`, then write the Worker Result Path pointer. Confirm data.json, rendered Markdown, the pointer, and the audit sidecar exist before responding with a short status line prefixed by your model identity, per the preamble §"Return message to the lead". **Enforced:** dispatch `completionPaths` requires the first three files and `validators/validate_session_conformance.py` validates the audit sidecar.
134
139
 
135
140
  ```
136
141
  **Model:** Report writer worker, <modelExecutionValue>
@@ -14,9 +14,11 @@ model: inherit
14
14
  tools: ["Bash", "Read", "Write", "Glob", "Grep"]
15
15
  ---
16
16
 
17
- **Write one file**: the translation sidecar at the assigned `Result Path`. That is the `Translator worker`'s sole responsibility. You are NOT an analysis worker — you produce no findings, you vote in nothing, and you never edit the final-report data.json, its Markdown sibling, or its HTML.
18
-
19
- The data.json is the English SSOT that every later phase and validator reads. Your sidecar is presentation: the HTML renderer overlays it onto an in-memory copy so only the human report speaks the reader's language.
17
+ This is the Claude host execution adapter for a materialized Okstra invocation.
18
+ The final prompt's `translator` duty contract owns the role boundary,
19
+ responsibility, and prohibited actions. This file owns only the extraction,
20
+ translation-sidecar, and verification tool procedure. Refuse a dispatch whose
21
+ prompt has no adjacent verified invocation metadata.
20
22
 
21
23
  ## Procedure
22
24
 
@@ -48,7 +50,7 @@ The data.json is the English SSOT that every later phase and validator reads. Yo
48
50
 
49
51
  ## How to translate
50
52
 
51
- You are a translator who reads code. Judge every term on whether the translation or the original carries the meaning faster to a working developer in the target language, and pick that one. The goal is a reader who understands the report sooner, not a document with no English left in it.
53
+ Judge every term on whether the translation or the original carries the meaning faster to a working developer in the target language, and pick that one. The goal is a reader who understands the report sooner, not a document with no English left in it.
52
54
 
53
55
  - **Never touch**: code identifiers, file paths, CLI commands and flags, model names, commit SHAs, URLs, and anything already inside backticks. Reproduce them character for character.
54
56
  - **Keep the English word** when that is what developers in the target language actually say. Forcing a native coinage onto `commit`, `worktree`, `merge`, `lint`, `diff`, `stage`, `rollback` or `PR` makes the sentence *slower* to read, not more local.
@@ -133,7 +133,33 @@ When a change adds or modifies a service's dependency on a concrete adapter —
133
133
 
134
134
  The verdict is mechanical: this change adds or modifies such a dependency → finding; the injection sits entirely on lines this change did not touch → clean.
135
135
 
136
- **An existing convention does not clear this one.** A codebase that injects concrete `*Repository` classes everywhere is precisely the debt this rule pays down, one touched injection at a time — matching the surrounding style is the condition being flagged, not a defence against it. Record it, say so in the note (*"matches existing convention — advisory"*), and propose the port: its name and the two or three method signatures it would declare — unless the project declares `architecture.style = hexagonal` in `.okstra/project.json`, where this item is blocking and that same port sketch is what you write rather than what you record: fix the injection before the write, never record-and-pass. This is the single rule in this overlay where a project-local convention does not override the pack; every other conflict still resolves in the project's favour.
136
+ **An existing convention does not clear this one.** A codebase that injects concrete `*Repository` classes everywhere is precisely the debt this rule pays down, one touched injection at a time — matching the surrounding style is the condition being flagged, not a defence against it. Record it, say so in the note (*"matches existing convention — advisory"*), and propose the port: its name and the two or three method signatures it would declare — unless the project declares `architecture.style = hexagonal` in `.okstra/project.json`, where this item is blocking and that same port sketch is what you write rather than what you record: fix the injection before the write, never record-and-pass.
137
+
138
+ ---
139
+
140
+ ## Rule H6 — Business rules live in the domain, not in application services
141
+
142
+ A service orchestrates: fetch, delegate the decision to a domain function, persist, publish. Flag changed service code that *decides* a business outcome inline instead of calling into the domain.
143
+
144
+ Violations:
145
+
146
+ - An `if` / `else` chain over domain fields that decides an outcome — eligibility, validity, which branch of a business process runs.
147
+ - Arithmetic expressing a business formula: money, ranking, quota, date-based entitlement.
148
+ - A private service method whose name is a domain concept (`isEligible…`, `computeDowngrade…`, `resolve…Status`). The name is telling you where it belongs.
149
+
150
+ Not a violation:
151
+
152
+ - Orchestration control flow — early return on not-found, `try` / `finally` around a transaction, threading a transaction through repository calls.
153
+ - DTO → domain mapping.
154
+ - Calling a domain predicate and acting on its result. That IS the pattern.
155
+
156
+ The test: **if this decision changed, would the change be described as a business rule change or as a plumbing change?** A business rule change that lands in a service is the violation — the rule is now invisible to the domain's own tests, and the next caller that needs it writes its own copy.
157
+
158
+ Severity: `should-fix`, and blocking when the embedded rule is substantial — money, permissions, or a state machine. Under a declared `architecture.style = hexagonal` the substantial case is fixed before the write, never record-and-pass.
159
+
160
+ ---
161
+
162
+ **How far a project convention reaches.** H5 above is the one rule in this overlay a project-local convention cannot clear. Elsewhere here, a documented convention does resolve the conflict in the project's favour — but that latitude stops at okstra's own gates. It never clears a check the implementation verifier's blocking list declares **mechanical**: a changed file under the domain folder importing an ORM / DB layer, a framework or its DI decorators, or anything under adapters / infrastructure / services is decided by reading the import list, and no convention argument reaches that verdict (`prompts/profiles/_implementation-verifier.md` §"Static design & test-quality review" → Hexagonal). Resolving a declared-mechanical finding as "project convention" is the failure this paragraph exists to prevent — a real run did exactly that, returned `clean`, and the team's own PR review then flagged the same import.
137
163
 
138
164
  Severity: advisory in a project that has not declared `architecture.style = hexagonal` — a direction-of-travel rule there, not a correctness gate, so it never blocks on its own. Under that declaration it is blocking: the injection is fixed before the write.
139
165