okstra 0.184.0 → 0.185.1
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 +2 -1
- package/dist/cli-registry.mjs +9 -0
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/chat/chat.d.mts +1 -0
- package/dist/commands/chat/chat.mjs +386 -0
- package/dist/commands/chat/chat.mjs.map +1 -0
- package/dist/lib/skill-catalog.mjs +1 -0
- package/dist/lib/skill-catalog.mjs.map +1 -1
- package/docs/architecture.md +9 -7
- package/docs/cli.md +3 -2
- package/docs/for-ai/README.md +4 -2
- package/docs/for-ai/skills/okstra-chat.md +34 -0
- package/docs/for-ai/skills/okstra-inspect.md +1 -1
- package/docs/for-ai/skills/okstra-run.md +2 -2
- package/docs/for-ai/skills/okstra-user-response.md +10 -8
- package/docs/project-structure-overview.md +6 -5
- package/docs/task-process/README.md +1 -1
- package/docs/task-process/implementation-planning.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/prompts/lead/okstra-lead-contract.md +6 -5
- package/runtime/prompts/lead/plan-body-verification.md +8 -7
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/_clarification-recommendation.md +2 -2
- package/runtime/prompts/profiles/implementation-planning.md +4 -3
- package/runtime/prompts/wizard/prompts.ko.json +2 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +1 -1
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +1 -1
- package/runtime/python/okstra_ctl/incremental_carry.py +149 -5
- package/runtime/python/okstra_ctl/incremental_scope.py +25 -3
- package/runtime/python/okstra_ctl/next_phase.py +67 -4
- package/runtime/python/okstra_ctl/plan_items.py +40 -4
- package/runtime/python/okstra_ctl/user_response.py +147 -37
- package/runtime/python/okstra_ctl/wizard.py +13 -0
- package/runtime/skills/okstra-chat/SKILL.md +108 -0
- package/runtime/skills/okstra-inspect/facets/status.md +6 -5
- package/runtime/skills/okstra-run/SKILL.md +2 -2
- package/runtime/skills/okstra-user-response/SKILL.md +50 -16
- package/runtime/validators/validate-run.py +109 -34
package/docs/cli.md
CHANGED
|
@@ -825,6 +825,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
|
|
|
825
825
|
| `okstra error-log append-observed --out <errors.jsonl> --task-key <key> --phase <phase> --agent <assigned-worker-id> --agent-role worker --model <model> --error-type tool-failure --command-file <markdown-file> --command-kind <kind> --message-file <markdown-file> [--cause <cause> --evidence-file <kind=file>]…` | Worker-facing typed error recording surface. Python validates and serializes the JSONL record; a worker supplies scalar identity fields plus Markdown files for free-form command, message, and probe content, never a JSON sidecar or JSON argument. `sandbox-denied` requires both `targetProbe` and `controlProbe` evidence files. |
|
|
826
826
|
| `okstra config <get\|set\|unset\|show> [key] [value] [--scope project\|global\|all]` | Manage persistent settings such as `pr-template-path` with atomic JSON writes |
|
|
827
827
|
| `okstra memory <add\|list\|search\|show\|archive>` | Manage global conversation memory in `~/.okstra/memory-book`, a user-home store separate from project `.okstra/` and the CLI basis of the `save this in okstra` natural-language skill |
|
|
828
|
+
| `okstra chat <rooms\|create\|join\|members\|send\|unread\|inbox\|log\|ack>` | Global rooms under `~/.okstra/chat`. Join takes an explicit `--name`. Send requires `--to` (`all` or a member); `--to` may equal `--as`. Unread is the inbox after the read cursor minus messages the participant sent; it does not move the cursor. `inbox` and `log` keep those arrivals, including after ack. Rows are `id:time:@from:to:body`. CLI basis of the `okstra-chat` skill. |
|
|
828
829
|
| `okstra model-io active-context-input --project-root <dir> --run-manifest <path>` | Resolve the prior implementation-planning active context only through the current project and run authority, then emit fixed fields such as the executor base ref. Caller-supplied active-context paths are not accepted. |
|
|
829
830
|
| `okstra report-translate <source\|write\|check-data> --run-manifest <path> …` | Resolve the report data and translation sidecar from the run manifest. `source` emits a source digest with the fixed translation queue; `write` requires that digest and rejects a stale report; `check-data` verifies the derived sidecar. Models never choose the data or sidecar path. |
|
|
830
831
|
| `okstra convergence prepare-groups --run-manifest <path> --input <grouping.md>` | Parse fixed grouping Markdown, validate distinct per-worker evidence and group semantics against the run authority, then publish the schema-valid groups artifact at the run-owned path. |
|
|
@@ -836,14 +837,14 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
|
|
|
836
837
|
| `okstra worker-audit-check --run-dir <runs/<task-type>/> --task-type <type> --seq <nnn> [--worker <id>]` | Apply the Phase 7 worker audit-sidecar rules mid-run, while the worker session is still alive. For each of this run's `worker-results/<worker>-<task-type>-<seq>.md` it checks that the file carries no `## 0. Reading Confirmation` heading, that the matching audit sidecar exists, and — for prompts carrying the required-v1 evidence-ledger marker — that every backticked `path:line` citation has an Evidence read row in that sidecar. `--worker` scopes it to the role that just returned. Emits `{ok, failures[]}` and exits 2 when `failures[]` is non-empty. The rules come from the `okstra_ctl.worker_audit_ledger` SSOT shared with `validate-run.py`, so an early pass and the Phase 7 pass cannot disagree. Run it right after collecting a result: the same failure at Phase 7 leaves only a retroactive edit, which breaks the audit chain, or a failed run |
|
|
837
838
|
| `okstra log-report [--project-root <dir>] [--cwd <dir>] [--top <N>] [--json]` | Read-only inventory of wrapper transcript `.log` files and their sibling prompt `.md` files. Each ranked entry preserves `path` / `sizeBytes` for compatibility and also reports `transcriptPath`, `transcriptBytes`, `promptPath`, `promptBytes`, and `transcriptToPromptRatio`; totals distinguish prompt bytes from transcript bytes and count paired files. Ranking remains transcript-size descending |
|
|
838
839
|
| `okstra recap <assemble\|record\|note> <task-root\|task-key> …` | Backend for the okstra-inspect `recap` facet. `assemble` is read-only and prints a JSON summary of phase transitions across a task's runs. `record --kind <summary\|qa> --mode <artifact\|code> --answer <text> [--question <text>] [--citation <path:line> …]` appends one line to `<task-root>/recap/recap-log.jsonl` and never mutates other artifacts. `note --kind <verification-evidence\|decision-draft\|analysis-note> --slug <topic> --purpose <text> --scope-note <text> (--body <markdown>\|--body-file <path>)` writes an agent-authored note to `<task-root>/notes/` and prints its path plus the `--clarification-response` argument for feeding it into a later run |
|
|
839
|
-
| `okstra user-response <list-view\|show-view\|begin\|answer\|plan-decision\|legacy-report-authoring\|finalize> …` | Backend for the `/okstra-user-response` skill. `list-view` and `show-view --report <md\|data.json> --project-root <dir>` are fixed-text model views; `show-view` validates that the report belongs to the explicit project root. The legacy `list` and `show` JSON reads retain their automation-compatible fields. `begin --report <md\|data.json> --task-key <key>` returns an opaque transaction id. A predefined clarification choice uses `answer --transaction <id> --clarification-id <C-NNN> --kind <kind> --option-number <N>`; Python resolves the answer, disposition, reach, and scope effects from the validated report. Direct input instead uses `--disposition <answer\|reframe> --value-file <md> [--rationale-file <md>]`. Every value, rationale, and reason file must be a regular file under `<PROJECT_ROOT>/.okstra/tmp/user-response/`; external paths and symbolic links are rejected. `plan-decision` accepts `approved`, `revision-requested`, or `rejected`, validates any `--implementation-option` against the report candidates, and requires `--reason-file` for the latter two statuses. `legacy-report-authoring` is restricted to report contract 2.0. `finalize` validates the complete existing sidecar before a lossless merge, uses compare-and-swap under a run-local lock, and atomically publishes only the user-owned sidecar; exit 0 ok / 1 error. |
|
|
840
|
+
| `okstra user-response <list-view\|show-view\|begin\|answer\|plan-decision\|legacy-report-authoring\|finalize> …` | Backend for the `/okstra-user-response` skill. `list-view` and `show-view --report <md\|data.json> --project-root <dir>` are fixed-text model views; `show-view` validates that the report belongs to the explicit project root and prints each open row's why-asked line, linked plan items, and cited `path:line` artifacts so the skill can read them before asking. The legacy `list` and `show` JSON reads retain their automation-compatible fields. `begin --report <md\|data.json> --task-key <key>` returns an opaque transaction id. A predefined clarification choice uses `answer --transaction <id> --clarification-id <C-NNN> --kind <kind> --option-number <N>`; Python resolves the answer, disposition, reach, and scope effects from the validated report. Direct input instead uses `--disposition <answer\|reframe> --value-file <md> [--rationale-file <md>]`. Every value, rationale, and reason file must be a regular file under `<PROJECT_ROOT>/.okstra/tmp/user-response/`; external paths and symbolic links are rejected. `plan-decision` accepts `approved`, `revision-requested`, or `rejected`, validates any `--implementation-option` against the report candidates, and requires `--reason-file` for the latter two statuses. `legacy-report-authoring` is restricted to report contract 2.0. `finalize` validates the complete existing sidecar before a lossless merge, uses compare-and-swap under a run-local lock, and atomically publishes only the user-owned sidecar; exit 0 ok / 1 error. |
|
|
840
841
|
| `okstra pr <template\|branches\|gen> … [--json]` | Backend for the okstra-pr-gen skill. Git-only—no project registration required. `template list\|show <name\|default>\|add --name <name> (--content <text>\|--file <path>)\|path` manages PR body templates under `~/.okstra/template/pr/` (bundled fallback `src/commands/pr/default.md`); `branches` recommends a base branch; `gen --base <ref> [--template <name\|default>]` emits fixed `Base`, `Current branch`, `Template name`, `Commits`, `Diff stat`, and `Template` sections by default. `--json` preserves the machine bundle for automation. |
|
|
841
842
|
| `okstra migrate [--apply] [--cwd <dir>] [--quiet]` | One-time migration of the project artifact root from `.project-docs/okstra/` to `.okstra/`. It is a dry run by default; `--apply` performs the move with `git mv` in a Git worktree, removes an empty `.project-docs/`, and synchronizes the `<PROJECT>/CLAUDE.md` import line, `.gitignore`, the project's rows in `~/.okstra/{recent,active}.jsonl`, and `~/.okstra/worktrees/registry.json`. It exits 1 if `.okstra/` already exists or the legacy directory is absent. Scheduled for removal by the end of v0.x |
|
|
842
843
|
| `okstra task-list [--project-root <path>]` | Combine `list_project_tasks` and `read_latest_task` into JSON containing the task catalog and latest task |
|
|
843
844
|
| `okstra task-show <task-key> [--project-root <path>]` | Summarize workflow, phase, status, and artifacts from the Task Read-Side Snapshot |
|
|
844
845
|
| `okstra stage-map <task-key> [--cwd <dir>\|--project <dir>]` | Dump the task's implementation-planning Stage Map as JSON: `{ ok, taskKey, taskRoot, state, sourcePlanPath, stages:[{stage_number,title,depends_on,step_count}], doneStages:[int] }`. `state` is `ready` for one resolved source and `missing` when no Stage Map exists; corrupt or conflicting sources return structured non-zero errors instead of silently selecting another report. `doneStages` is read from the implementation-planning stage consumer state (with carry recovery). This is the read-side source `/okstra-schedule-gen [task-group]` uses to derive selectable unfinished stages and their completed dependency closure |
|
|
845
846
|
| `okstra incremental-scope <args…>` | Decide re-verify vs carry-forward scope for an `implementation-planning` clarification re-run. Thin shim into `scripts/okstra_ctl/incremental_scope.py` (deterministic pure function): it reads the dependency graph from the prior run `data.json`'s `implementationPlanning.stageMap` and returns `mode:"incremental"` only when the base-ref SHA is unchanged and the affected stages' `downstream_stage_closure` covers at most half of all stages; `--full-reason` (selected option / Stage Map / approach) still forces `mode:"full"`. An answered `C-NNN` that traces to no stage returns `mode:"unresolved"` rather than full — pass `--impacted` with the stage numbers or `--full-reason`. `--preview --prev-data <path> --answered-clarifications <csv>` runs the link half alone — no base SHA, no side effects — and prints `{wouldForceFull, unlinkedIds, reason}`; unlinked ids set `wouldForceFull: false` and fill `unlinkedIds` |
|
|
846
|
-
| `okstra incremental-carry <args…>` | Merge carried-forward plan-item verdicts into an incremental re-run. Contract v3 takes `--prev-data`, `--cur-narrative`, and the convergence-owned `--state`; it verifies carried stage rows and writes only `--out-state`, tagging copied verdicts with `carriedForwardFromSeq`. The historical v2 `--cur-data --out` form remains readable. Ownership, scope, item, or schema drift raises `CarryError` and forces a full fallback. |
|
|
847
|
+
| `okstra incremental-carry <args…>` | Merge carried-forward plan-item verdicts into an incremental re-run. Contract v3 takes `--prev-data`, `--cur-narrative`, and the convergence-owned `--state`; it verifies carried stage rows and writes only `--out-state`, tagging copied verdicts with `carriedForwardFromSeq`. Unchanged `P-Val-*` / `P-Req-*` / `P-Rb-*` rows whose extract hash still matches are carried the same way, and the sibling `plan-items-*.json` `dispatchQueue` is rewritten to match. The historical v2 `--cur-data --out` form remains readable. Ownership, scope, item, or schema drift raises `CarryError` and forces a full fallback. |
|
|
847
848
|
| `okstra code-review target --task-key <k> --stage <N> [--project-root <dir>] [--cwd <dir>] [--json]` / `okstra code-review target --branch <name> [--base <ref>] [--date <YYYY-MM-DD>] [--project-root <dir>] [--cwd <dir>] [--json]` | Resolve what a code review reads and where its result file goes. Output is always JSON, so `--json` only makes that explicit. `--project-root` and `--cwd` are shared pre-dispatch arguments and apply to both modes; `--cwd` is only consulted when `--project-root` is absent. Both modes return `{ ok, projectRoot, mode, worktreePath, branch, baseCommit, headCommit, reviewPath, round }`; stage mode additionally returns `taskKey`, `taskRoot`, and `stage`. Stage mode takes the diff base from the `base_ref` recorded on that stage's worktree-registry row when it was provisioned — not from a rule re-applied at review time — and names the result `.okstra/tasks/<task-group>/<task-id>/code-reviews/stage-<NN>.md`, where a re-review of the same stage becomes `-r2`, `-r3`, … (the `round` field). Only a legacy row provisioned before `base_ref` was recorded falls back to re-deriving the base through `stage_targets`, and a failure there is reported as `stage_base_unresolved`. `worktreePath` comes back empty whenever the stage worktree is not usable as a live checkout — the registry row is no longer `active` (whole-task final-verification released it), the row never carried a path, or the recorded directory is gone — and the review then reads the `branch` ref instead. Branch mode uses `--base` when given, otherwise the merge-base with the default branch (`refs/remotes/origin/HEAD`, else `main`/`master`), and names the result `.project-docs/code-reviews/<branch>/<YYYY-MM-DD>-<NN>.md`, where `<NN>` (the `round` field) is the next sequence number for that date — the highest already on disk plus one. Read-only: it resolves paths and creates no directory and no file, so the review directory does not exist until the caller writes the report. Backend for the okstra-code-review skill |
|
|
848
849
|
| `okstra set-work-status <token> <todo\|in-progress\|blocked\|done> [--note <text>] [--task-group <g>] [--project-root <dir>]` | Update user-managed `workStatus` in task-manifest.json, along with `workStatusUpdatedAt` and, when `--note` is supplied, `workStatusNote`. `<token>` is a full task key or bare task ID. It uses the manifest renderer's serialization rules and returns `stage:"ambiguous"` plus `matches[]` when ambiguous |
|
|
849
850
|
| `okstra worktree-lookup <task-key>` | Return the `worktree_registry.lookup` result: reserved path, branch, base ref, and current status |
|
package/docs/for-ai/README.md
CHANGED
|
@@ -25,6 +25,7 @@ This directory is a compressed manual for an AI to quickly select and precisely
|
|
|
25
25
|
| Project-wide recent run coverage, tokens, known cost, CPU, and wall-clock usage by task type | `okstra-usage` | [`skills/okstra-usage.md`](skills/okstra-usage.md) |
|
|
26
26
|
| Generate a client-facing work schedule for a whole task-group | `okstra-schedule-gen` | [`skills/okstra-schedule-gen.md`](skills/okstra-schedule-gen.md) |
|
|
27
27
|
| Store or search conversations/decisions/preferences/requirements in the global Memory Book | `okstra-memory` | [`skills/okstra-memory.md`](skills/okstra-memory.md) |
|
|
28
|
+
| Create or join a global room and send or read addressed messages across host sessions | `okstra-chat` | [`skills/okstra-chat.md`](skills/okstra-chat.md) |
|
|
28
29
|
| Manage the implementation-task worktree-based docker compose user-test environment | `okstra-container-build` | [`skills/okstra-container-build.md`](skills/okstra-container-build.md) |
|
|
29
30
|
| Answer the unresolved clarification questions an okstra run left behind in-session and record the approval gate | `okstra-user-response` | [`skills/okstra-user-response.md`](skills/okstra-user-response.md) |
|
|
30
31
|
| Register a PR body template or generate a PR description from a branch diff (global, git repository) | `okstra-pr-gen` | [`skills/okstra-pr-gen.md`](skills/okstra-pr-gen.md) |
|
|
@@ -35,7 +36,7 @@ This directory is a compressed manual for an AI to quickly select and precisely
|
|
|
35
36
|
1. Run commands as separate Bash calls whenever the source skill requires it. In particular, do not wrap `okstra preflight --runtime claude-code`, `okstra wizard ...`, or `okstra container ...` calls in `&&`, `||`, `$(...)`, a leading variable assignment, `eval`, or `export`.
|
|
36
37
|
2. An `okstra <subcmd>` call bootstraps its own Python path. Unless a skill states otherwise, do not build `okstra paths --shell` or `export PYTHONPATH=...`.
|
|
37
38
|
3. Most skills except `okstra-setup` do not use an `npx` fallback. If the runtime is missing, tell the user to run `/okstra-setup` and stop. But if it fails with `unknown command: <cmd>`, the `okstra` binary on PATH is older than the skill — point the user to `npm i -g okstra@latest` rather than `/okstra-setup`, and stop.
|
|
38
|
-
4. Project artifacts go under `<PROJECT_ROOT>/.okstra/` by default. The
|
|
39
|
+
4. Project artifacts go under `<PROJECT_ROOT>/.okstra/` by default. The exceptions are `okstra-memory` (`~/.okstra/memory-book/`) and `okstra-chat` (`~/.okstra/chat/`).
|
|
39
40
|
5. `runtime/` is build output. When fixing a source skill or template, edit the source under `skills/`, `templates/`, `validators/`, `scripts/`, `src/` and apply it via a build.
|
|
40
41
|
6. Do not guess the contents of a tracker, URL, file, report, log, zip, template, or validator. Use only what you have confirmed by reading or running with a tool.
|
|
41
42
|
7. Read-side skills also produce some artifacts. `okstra-inspect errors` produces an error report Markdown and `okstra-inspect error-zip` produces an anonymized zip. Even in these cases, keep the purpose-specific fixed CLI fields as the source of truth.
|
|
@@ -49,13 +50,14 @@ This directory is a compressed manual for an AI to quickly select and precisely
|
|
|
49
50
|
|
|
50
51
|
## Public Skill List
|
|
51
52
|
|
|
52
|
-
The public skills listed in this AI manual are the following
|
|
53
|
+
The public skills listed in this AI manual are the following 14:
|
|
53
54
|
|
|
54
55
|
- `okstra-setup`
|
|
55
56
|
- `okstra-brief-gen`
|
|
56
57
|
- `okstra-run`
|
|
57
58
|
- `okstra-manager`
|
|
58
59
|
- `okstra-memory`
|
|
60
|
+
- `okstra-chat`
|
|
59
61
|
- `okstra-inspect`
|
|
60
62
|
- `okstra-rollup`
|
|
61
63
|
- `okstra-usage`
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# okstra-chat AI Manual
|
|
2
|
+
|
|
3
|
+
## Source
|
|
4
|
+
|
|
5
|
+
- Skill source: [`skills/okstra-chat/SKILL.md`](../../../skills/okstra-chat/SKILL.md)
|
|
6
|
+
- CLI: [`src/commands/chat/chat.mts`](../../../src/commands/chat/chat.mts)
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
Global rooms under the okstra home so host sessions from different providers can send and read messages. Not a project `.okstra/` artifact.
|
|
11
|
+
|
|
12
|
+
## CLI
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
okstra chat rooms
|
|
16
|
+
okstra chat create --room <name>
|
|
17
|
+
okstra chat join --room <name> --name <display>
|
|
18
|
+
okstra chat members --room <name>
|
|
19
|
+
okstra chat send --room <name> --as <display> --to <all|name> --body <text>
|
|
20
|
+
okstra chat unread --room <name> --as <display>
|
|
21
|
+
okstra chat inbox --room <name> --as <display>
|
|
22
|
+
okstra chat log --room <name> --as <display>
|
|
23
|
+
okstra chat ack --room <name> --as <display> --through <id>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Display names are typed. The CLI does not generate them.
|
|
27
|
+
|
|
28
|
+
Unread is the inbox after the read cursor minus messages whose `from` equals `--as`. It does not move the cursor. `inbox` and `log` keep those messages. Rows are `id:time:@from:to:body`; recipient `all` stays `all`.
|
|
29
|
+
|
|
30
|
+
`send --to` may equal `--as`. `members` is the full roster.
|
|
31
|
+
|
|
32
|
+
The skill picker is `all` plus `members` minus the current display name. After showing unread rows, the skill runs `ack --through` with the last unread id unless the output is `no unread`.
|
|
33
|
+
|
|
34
|
+
Read the fixed text rows. Do not parse JSON.
|
|
@@ -87,7 +87,7 @@ Keep the table narrow.
|
|
|
87
87
|
- workStatus
|
|
88
88
|
- Next
|
|
89
89
|
|
|
90
|
-
`nextRecommendedPhase` is an object `{phase, status, rationale}`. The Next cell is its `phase`, or `--` when that is empty. If `awaitingApproval`, or `nextRecommendedPhase.status` is anything but `ready`, add a marker to the Next cell and explain it.
|
|
90
|
+
`nextRecommendedPhase` is an object `{phase, status, rationale}`. The Next cell is its `phase`, or `--` when that is empty. If `awaitingApproval`, or `nextRecommendedPhase.status` is anything but `ready`, add a marker to the Next cell and explain it. When `awaitingApproval` is true, the next human action is to approve the plan (`okstra-run` → `implementation`, or `--approve`); do not re-run `implementation-planning`. When the pointer is `blocked` after planning, send the user to `okstra-user-response` on the named `C-NNN` ids first.
|
|
91
91
|
|
|
92
92
|
### Specific task
|
|
93
93
|
|
|
@@ -213,7 +213,7 @@ Read the scope and path from the persist action of `okstra wizard outcome`, not
|
|
|
213
213
|
|
|
214
214
|
## Okstra lead takeover
|
|
215
215
|
|
|
216
|
-
After render-bundle, read `<INSTRUCTION_SET_PATH>/lead-execution-prompt.md` verbatim and proceed from Phase 1 in that prompt's order.
|
|
216
|
+
After render-bundle, read `<INSTRUCTION_SET_PATH>/lead-execution-prompt.md` verbatim and proceed from Phase 1 in that prompt's order. Before any in-run approval or clarification question, follow the lead contract "User confirmation before an approval blocker": read cited plan items, worker findings, and files, then ask in the user's language with each option's outcome.
|
|
217
217
|
|
|
218
218
|
Inform the user on one line.
|
|
219
219
|
|
|
@@ -221,7 +221,7 @@ Inform the user on one line.
|
|
|
221
221
|
Took over as Okstra lead (`<host-runtime>`) for `<taskKey>` (`<task-type>`). Run dir: `<RUN_DIR_RELATIVE_PATH>`. Beginning Phase 1 (context loading).
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
For a single-element chain, the end of Step 6 is the end of the run. Step 7 below applies only when the `chain-stages` CSV has 2 or more elements.
|
|
224
|
+
For a single-element chain, the end of Step 6 is the end of the run. Step 7 below applies only when the `chain-stages` CSV has 2 or more elements. After an `implementation-planning` run, if `workflow.awaitingApproval` is true, tell the user to approve the plan; do not start another planning run. If the pointer is `blocked`, send the user to `okstra-user-response` first.
|
|
225
225
|
|
|
226
226
|
## implementation unattended chaining (chain-stages)
|
|
227
227
|
|
|
@@ -15,7 +15,7 @@ The model-facing reads are fixed text. Do not use the automation-compatible JSON
|
|
|
15
15
|
| Command | Purpose |
|
|
16
16
|
|---|---|
|
|
17
17
|
| `okstra user-response list-view --home <home> --project <projectId> --limit 3` | List tasks that still need clarification answers or a plan decision. |
|
|
18
|
-
| `okstra user-response show-view --report <reportPath> --project-root <projectRoot>` | Show open questions, choices, impact, approval context, plan candidates, and current state after validating project ownership. |
|
|
18
|
+
| `okstra user-response show-view --report <reportPath> --project-root <projectRoot>` | Show open questions, choices, impact, why asked, linked plan items, cited artifacts, approval context, plan candidates, and current state after validating project ownership. |
|
|
19
19
|
| `okstra user-response begin --report <reportPath> --task-key <taskKey>` | Open a typed transaction and return an opaque id. |
|
|
20
20
|
| `okstra user-response answer ...` | Add one validated answer to the draft. |
|
|
21
21
|
| `okstra user-response plan-decision ...` | Record an explicit plan decision. |
|
|
@@ -26,14 +26,16 @@ The legacy `list` and `show` JSON commands remain for automation compatibility.
|
|
|
26
26
|
|
|
27
27
|
## Flow
|
|
28
28
|
|
|
29
|
-
1. Run `okstra preflight --runtime
|
|
30
|
-
2.
|
|
31
|
-
3.
|
|
32
|
-
4.
|
|
33
|
-
5.
|
|
34
|
-
6.
|
|
29
|
+
1. Run `okstra preflight --runtime <host-runtime>` for the current harness. On `Okstra preflight: failed`, show `Reason` and `Recovery`, then stop. On `Okstra preflight: ready`, carry `Project root`, `Project ID`, `Runtime`, and `Relay contract`, then run `okstra paths --field home`.
|
|
30
|
+
2. Read the relay `Wizard interaction relay` JSON. When `native-single` is available and the option count fits `nativeLimits`, call `interactions.native-single.function` (`AskUserQuestion` / `ask_user_question` / `request_user_input` from that field). Do not print a numbered list in chat while the native tool is available. Otherwise render a numbered Markdown list. Do not substitute one host function name for another.
|
|
31
|
+
3. Select a task from `list-view` through that host picker.
|
|
32
|
+
4. Read only `show-view --report <reportPath> --project-root <projectRoot>` for report facts. The view also prints `Why asked`, `Linked plan items`, and `Cited artifacts`.
|
|
33
|
+
5. Read every cited `path:line` under the project root and every linked plan-item definition before asking. Do not search beyond that list. Investigation explains; it never changes `options[]`.
|
|
34
|
+
6. Ask one open clarification at a time in the user's language through the host picker. The question body is why, what is already decided, the fork, and what stays blocked. Do not lead with `Kind`, `Blocks`, `Expected form`, or `C-NNN`. Keep the row id in parentheses at the end.
|
|
35
|
+
7. Echo the complete response and confirm through the host picker (`Record as shown` / `Change an answer`). Do not ask them to type `confirmed`.
|
|
36
|
+
8. Begin the transaction, add answers and decisions, then finalize it.
|
|
35
37
|
|
|
36
|
-
Each `options[]` row displays `{role, answer, rationale, scopeImpact, addedWork, directionChange, disposition}`. Contract 3.0 additionally displays `reach`, `scopeEffects`, and row-level `approvalContext`.
|
|
38
|
+
Each `options[]` row displays `{role, answer, rationale, scopeImpact, addedWork, directionChange, disposition}`. Contract 3.0 additionally displays `reach`, `scopeEffects`, and row-level `approvalContext`. Option descriptions use this order: If you pick this (`addedWork`). What it reverses (`directionChange`). Scope (`reach` or `scopeImpact`). Why it is on the board (`rationale`). If the view says `not stated in the report`, repeat that text and never invent a value. A quote from a cited file may follow those axes; it does not replace them.
|
|
37
39
|
|
|
38
40
|
When the user selects a predefined option, pass only the fixed view's one-based option number. Python resolves that option's `disposition`, answer, reach, and scope effects from the validated report:
|
|
39
41
|
|
|
@@ -137,7 +137,7 @@ Runtime/install asset changes follow this checklist:
|
|
|
137
137
|
- `runtime/python/*` → `~/.okstra/lib/python/`
|
|
138
138
|
- `runtime/bin/*` → `~/.okstra/bin/`
|
|
139
139
|
- `runtime/templates/*` → `~/.okstra/templates/`
|
|
140
|
-
- `runtime/skills/<name>` (the
|
|
140
|
+
- `runtime/skills/<name>` (the fourteen 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
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`
|
|
@@ -250,7 +250,7 @@ Important modules:
|
|
|
250
250
|
| `design_surfaces.py` | deterministic detection of an `implementation-planning` stage's design surface — matches the stage's file-path tokens/suffixes/patterns and action wording via `SurfaceRule` to derive which design input the stage needs among domain contract, DB/table schema, external interface, transaction/consistency, transformation mapping, lifecycle, rollout/observability, and manual user test, plus its evidence (`TriggerEvidence`). An unmappable structure raises `DesignSurfaceError` |
|
|
251
251
|
| `design_prep.py` | fingerprint / materialize / resolve backend for design-preparation requests (CLI: `okstra design-prep <list\|show\|write>`) — computes an assessment fingerprint from the approved planning snapshot's `ASSESSMENT_FIELDS`, idempotently writes an Okstra-owned request under `design-prep-requests/`, and resolves the highest-revision append-only user response under `design-prep-inputs/` whose fingerprint matches as the effective response. Keeps the three authorities (report snapshot / Okstra request / user input) separate and never modifies the report or existing revisions. Sidecar I/O is protected by a directory-fd anchor + flock |
|
|
252
252
|
| `incremental_scope.py` | incremental re-verification decision for an `implementation-planning` clarification re-run (deterministic pure function) — reads the dependency graph from the previous run data.json's `implementationPlanning.stageMap` and returns `mode="incremental"` only when the base-ref SHA is unchanged and the affected stages' `downstream_stage_closure` is at most half of all stages; an unlinked `C-NNN` is `mode="unresolved"` (needs `--impacted`), not full. CLI: `okstra incremental-scope` |
|
|
253
|
-
| `incremental_carry.py` | carry merge for an incremental re-run — verifies unchanged carried stage rows and merges their previous
|
|
253
|
+
| `incremental_carry.py` | carry merge for an incremental re-run — verifies unchanged carried stage rows and merges their previous `P-Step-*` / `P-Prep-*` verdicts, plus unchanged `P-Val-*` / `P-Req-*` / `P-Rb-*` rows whose extract hash still matches, into the convergence-owned v3 plan state with a `carriedForwardFromSeq` tag. Rewrites `dispatchQueue` and the sibling `plan-items-*.json` so the next prompt does not re-score a carried checklist row. The historical v2 data.json form remains readable. Ownership, scope, or schema drift exits non-zero with `CarryError`. CLI: `okstra incremental-carry` |
|
|
254
254
|
| `build_tools.py` | allowlist SSOT for deciding whether a plan's command cell invokes the project build toolchain (`npm`/`pytest`/`cargo`/`gradle`/… behind transparent leaders like `sudo`/`env`). The planning worktree has no dependencies installed, so `validators/validate-run.py` uses this to warn (advisory) when a toolchain stage declares no install precondition. Intentionally an allowlist, not a denylist, so unknown tokens go undetected rather than firing on `grep`/`sed` in every plan |
|
|
255
255
|
| `stage_citations.py` | shared grammar SSOT for reading the Stage Map stage numbers a prose cell cites (`Stages 1, 2, and 3`, ranges, etc.). One definition serves two readers that must not drift — the coverage check in `validators/validate-run.py` proving every stage traces to a requirement, and `incremental_scope.py`'s back-trace resolving which stages an answered clarification touches |
|
|
256
256
|
| `self_mock_signals.py` | self-mock signal SSOT — language-keyed regexes (`SIGNALS`), the `EXT_TO_LANG` extension map, and the waiver-matching mechanics both gates share — `selfmock_path_key` (the one path-normalization), `waiver_entry_key` (the `(file, line, <discriminator>)` triple, with the hand-typed line coerced to `int`) and `partition_waived_entries` (the split into still-failing vs waived). Gate A passes the discriminator `signal`, gate B `mutant`; one definition means the two cannot disagree about whether a waiver matches a finding. The signals are each ported from a `prompts/coding-preflight/languages/<lang>.md` "Self-mock signals to refuse" bullet with the source `doc_keyword` retained so a drift guard fails when doc and module diverge. Patterns stay deliberately narrow (only the "stub the subject's own method, then assert the stub" shape and reaching into the subject's privates; subject identity is never inferred beyond the literal `sut` token). The static detector `validators/detect_self_mock.py`, the drift guard and `mutation_probe.py` MUST import from here; four documented shapes needing subject identity no regex has are left to the mutation gate (`mutation_probe.py`) |
|
|
@@ -318,7 +318,7 @@ Important modules:
|
|
|
318
318
|
| `codex_dispatch.py` | Compatibility adapter delegating `okstra codex-dispatch` to the provider-neutral `worker_dispatch` path |
|
|
319
319
|
| `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 |
|
|
320
320
|
| `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 |
|
|
321
|
-
| `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 |
|
|
321
|
+
| `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; `format_show_view` prints why-asked, linked plan items, and cited artifacts for the in-session picker |
|
|
322
322
|
| `context_cost.py` | read-side context-cost estimator for a prepared okstra task bundle (the `okstra context-cost` backend) |
|
|
323
323
|
| `schema_excerpt.py` | generates a task-type-scoped excerpt of the final-report schema — a schema reduction to inject into the worker/lead prompt |
|
|
324
324
|
| `work_categories.py` | requirements-discovery work-category (domain) **SSOT** (`is_valid_category`) — the work-category allowlist is defined only here |
|
|
@@ -436,7 +436,7 @@ Optional (v1.0 backward-compatible) top-level keys:
|
|
|
436
436
|
|
|
437
437
|
### 4.10 `skills/`
|
|
438
438
|
|
|
439
|
-
|
|
439
|
+
14 user-facing skills (the only skills `okstra install` copies). The list SSOT is `USER_SKILL_NAMES` in `src/lib/skill-catalog.mts`; the Claude plugin manifest and the installer both derive from it.
|
|
440
440
|
|
|
441
441
|
Boilerplate shared by several skills (bash invocation rule, outdated-CLI preflight, python bootstrap note) is kept canonical in `skills/_fragments/*.md` and expanded in place inside each `SKILL.md` between `<!-- BEGIN FRAGMENT: <name> -->` / `<!-- END FRAGMENT: <name> -->` markers by `tools/sync-skill-fragments.mjs` (`--check` fails on drift). Sources stay fully expanded, so `runtime/` and installed copies remain self-contained; the guards are `tests-js/skill-fragments.test.mjs` and `tests/contract/test_prompt_fragment_ownership.py`.
|
|
442
442
|
|
|
@@ -445,6 +445,7 @@ Boilerplate shared by several skills (bash invocation rule, outdated-CLI preflig
|
|
|
445
445
|
| `okstra-brief-gen` | yes | Produce task brief from ticket/doc/link/conversation |
|
|
446
446
|
| `okstra-run` | yes | Start/resume an okstra task in the current registered host session |
|
|
447
447
|
| `okstra-memory` | yes | Store/search/archive global conversation memory under `~/.okstra/memory-book` |
|
|
448
|
+
| `okstra-chat` | yes | Global rooms under `~/.okstra/chat` so host sessions from different providers can send and read addressed messages |
|
|
448
449
|
| `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), `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` |
|
|
449
450
|
| `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 |
|
|
450
451
|
| `okstra-usage` | yes | Read-only project usage snapshot — aggregate recent run coverage, tokens, known cost, CPU, and wall-clock time by task type (default: 30 days) |
|
|
@@ -453,7 +454,7 @@ Boilerplate shared by several skills (bash invocation rule, outdated-CLI preflig
|
|
|
453
454
|
| `okstra-pr-gen` | yes | Register PR body templates under `~/.okstra/template/pr/` and generate a PR description from a branch diff (drives `okstra pr template` / `branches` / `gen`). **Global skill** — needs a Git repo, not `<PROJECT_ROOT>/.okstra/project.json` |
|
|
454
455
|
| `okstra-manager` | yes | Coordinate cross-project okstra tasks through manager-owned plans, assignments, sync snapshots, status, and child launch context packets |
|
|
455
456
|
| `okstra-setup` | yes | Install/check runtime and register project |
|
|
456
|
-
| `okstra-user-response` | yes | Submit answers to a run's open clarification items through the installed response helper without hand-editing artifacts |
|
|
457
|
+
| `okstra-user-response` | yes | Submit answers to a run's open clarification items through the installed response helper without hand-editing artifacts. The skill reads cited context from `show-view` before asking, uses the host native selection UI, and each option names the outcome of picking it |
|
|
457
458
|
| `okstra-code-review` | yes | Census-based code review of a diff — one okstra `implementation` stage, or any branch — against this project's coding-preflight rules. The result file goes under the task bundle (`code-reviews/stage-<NN>.md`) in stage mode and under `.project-docs/code-reviews/<branch>/` in branch mode; `okstra code-review target` resolves both |
|
|
458
459
|
|
|
459
460
|
> The former internal skills — `context-loader`, `team-contract`, `convergence`, `report-writer`, `coding-preflight` — and the lead contract are no longer skills. They ship as runtime resources under `prompts/lead/*` and `prompts/coding-preflight/*` (installed to `~/.okstra/prompts/`); see §4.11.
|
|
@@ -76,7 +76,7 @@ The last column is the `workflow.nextRecommendedPhase` pointer Phase 7 leaves be
|
|
|
76
76
|
| `requirements-discovery` | common questions only | profile/brief/base-ref exist | multi-worker analysis, convergence 1 round default | `ready` at `error-analysis` or `implementation-option-selection`; `pending` when neither is settled |
|
|
77
77
|
| `error-analysis` | common questions only | profile/brief/base-ref exist | multi-worker analysis, convergence 2 rounds default | `ready` at `implementation-option-selection`, or at `error-analysis` while the investigation continues |
|
|
78
78
|
| `implementation-option-selection` | comparison or preselected-validation context | stable brief IDs and at least three analysers | read-only candidate validation, exact coverage, separate direction confirmation | `ready` at `implementation-planning`, `pending` on `pending-direction-selection`, or `blocked` |
|
|
79
|
-
| `implementation-planning` | selected-direction report for a new plan | selection report/sidecar/digest or same-task planning rerun | one-direction realization + Phase 6 plan-body verification | `ready` at `implementation` on `plan-ready` (the
|
|
79
|
+
| `implementation-planning` | selected-direction report for a new plan | selection report/sidecar/digest or same-task planning rerun | one-direction realization + Phase 6 plan-body verification | `ready` at `implementation` on approvable `plan-ready` (`awaitingApproval` until the user flips `approved`); `blocked` when the gate is blocking or a `Blocks=approval` row is open; `ready` at `implementation-option-selection` on `direction-invalidated` |
|
|
80
80
|
| `implementation` | approved plan, stage multi-pick, executor | approved marker, Stage Lifecycle Snapshot, stage-key reservation, QA command deny-list | one run = one stage; executor writes in isolated stage worktree, verifiers read-only | `ready` at the stage report's `routingRecommendation.target` — `final-verification` on a clean stage |
|
|
81
81
|
| `final-verification` | approved plan, stage pick (whole-task or single-stage) | `VERIFICATION_TARGET` resolved; whole-task auto integration/teardown or single-stage worktree reuse | whole-task may integrate stages first; analyser verification itself is read-only | `ready` at `release-handoff` on an `accepted` verdict, otherwise at the phase owning the defect; `terminal` on `done` |
|
|
82
82
|
| `release-handoff` | handoff scope (stage-group or whole-task), PR template override/scope | Stage Lifecycle Snapshot eligibility, generated `release-handoff-input.md`, empty worker roster | single-lead; whole-task PR or stage-group collector branch/PR | always `terminal` |
|
|
@@ -62,7 +62,7 @@ sequenceDiagram
|
|
|
62
62
|
|
|
63
63
|
Prepare rejects a new plan without a selected-direction report. Comparison mode requires a valid `DIRECTION SELECTION` sidecar, while preselected-validation mode uses the confirmed upstream direction without one. The normalized snapshot binds the source report, source-data digest, option ID, direction body, requirements, and invariants.
|
|
64
64
|
|
|
65
|
-
Prepare does not name the next phase. It carries the inherited `workflow.nextRecommendedPhase` forward and lowers a `ready` pointer to `pending`, because this run has not finished and a `ready` pointer would read as an invitation to start the following phase. The pointer becomes `ready` at `implementation`
|
|
65
|
+
Prepare does not name the next phase. It carries the inherited `workflow.nextRecommendedPhase` forward and lowers a `ready` pointer to `pending`, because this run has not finished and a `ready` pointer would read as an invitation to start the following phase. The pointer becomes `ready` at `implementation` when Phase 7 projects an approvable `plan-ready` outcome; `workflow.awaitingApproval` is then true until the user flips `frontmatter.approved`. A blocking plan-body gate or an open `Blocks=approval` row projects `blocked` instead, so inspect and the wizard ask the user to answer those rows rather than start implementation or loop planning.
|
|
66
66
|
|
|
67
67
|
## 4. lead execution flow
|
|
68
68
|
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -133,10 +133,11 @@ This is not a phase. It fires wherever the blocker surfaces — during intake wh
|
|
|
133
133
|
|
|
134
134
|
The sequence is fixed:
|
|
135
135
|
|
|
136
|
-
1.
|
|
137
|
-
2.
|
|
138
|
-
3.
|
|
139
|
-
4.
|
|
136
|
+
1. Investigate before asking. Read every cited plan item, worker finding, and `path:line` the question depends on. You are not ready to ask while any option's outcome cannot be named as a concrete change: the extra work it creates, the already-decided thing it reverses, and which files or stages it touches. If a citation cannot be read, say so in the question; do not invent the missing fact.
|
|
137
|
+
2. Emit `PROGRESS: user-confirm <C-NNN> <the question, one line>` with the id the row would carry.
|
|
138
|
+
3. Ask in the user's language through the selected adapter's `prompt_user` mapping. Do not lead with `C-NNN`, `Kind`, `Blocks`, or `expectedForm`. The question body is why this is being asked, what is already decided, the fork, and each option as "if you pick this, then …". Keep the report-owned impact axes (reach or scope, added work, direction change). One question at a time. When that mapping names a native question function and the option count fits the relay `nativeLimits`, call that function with `{label, description}` options. Do not print a numbered list in chat while the native tool is available. Numbered text is only for a text-only relay or a prompt that does not fit `nativeLimits`.
|
|
139
|
+
4. On an answer — record the raw text in the row's `userInput`, set `status: answered` and `userConfirmation: asked-and-answered`, and apply the selected disposition in this run.
|
|
140
|
+
5. Only when asking fails does the row stay open: `asked-awaiting` when the user has not answered, `deferred-no-interactive-session` when this run has no user to ask.
|
|
140
141
|
|
|
141
142
|
For report contract v3 `implementation-planning`, record active approval decisions only through `okstra approval-decision`; report assembly derives each report row's status, resolution, and backtraces from that lead-owned ledger plus the activity ledger. Classify a user-owned selection as `user-decision`, a surviving non-correctness majority disagreement as `noncritical-dissent`, and a cited path/symbol mismatch, `P-Req-*` coverage mismatch, or independent Requirement Coverage blocker as `correctness-critical`. `select` is limited to `user-decision`, `accept-risk` is limited to `noncritical-dissent`, and `request-revision` / `reject` are available to all three classifications. `correctness-critical` never offers or records `accept-risk`. Contract v2 remains read-only compatible; do not create a new v2 report. **Enforced:** `scripts/okstra_ctl/approval_decisions.py` rejects invalid option/disposition combinations, and `validators/validate-run.py` `_validate_v3_approval_context` recomputes report backtraces.
|
|
142
143
|
|
|
@@ -424,7 +425,7 @@ jq -s 'group_by(.errorType) | map({type: .[0].errorType, count: length})' <runDi
|
|
|
424
425
|
|
|
425
426
|
The errors log is informational. Its presence/absence does not affect the final verdict. Do not block report writing on it.
|
|
426
427
|
|
|
427
|
-
After persistence, reply briefly in the resolved Report Language with: completion status, the human report path, the report record path, team-state path, validator result, resume command path, any remaining blocker. **Lead this reply with the run's task identity** — state `<task-group>/<task-id>` (or the full `taskKey`) first, so the reader knows which task the reply is about. **Every run-artifact path in this reply MUST be task-qualified** — report the human report as `.okstra/tasks/<task-group>/<task-id>/runs/<task-type>/reports/final-report-<task-type>-<seq>.html` rooted at the task bundle, NOT the bare `runs/<task-type>/reports/...` form (byte-for-byte identical across every task of the same task-type, so it cannot identify the task). Under that, cite the report record (`.data.json`) and one line to render the full reading copy: `okstra render-final-report <task-qualified data.json>`. The same task-qualified rule applies to the team-state path, resume command path, and any other run-artifact path this reply cites.
|
|
428
|
+
After persistence, reply briefly in the resolved Report Language with: completion status, the human report path, the report record path, team-state path, validator result, resume command path, any remaining blocker. For `implementation-planning` that left `workflow.awaitingApproval` true, the first sentence after the task identity tells the user to approve this plan (`okstra-run` → `implementation`, which asks `approve_plan_confirm`, or `--approve`) and does not propose another planning run. For a `blocked` pointer after planning, the first sentence names the open `C-NNN` ids and sends the user to `okstra-user-response`; do not start implementation and do not re-run planning until those answers exist. **Lead this reply with the run's task identity** — state `<task-group>/<task-id>` (or the full `taskKey`) first, so the reader knows which task the reply is about. **Every run-artifact path in this reply MUST be task-qualified** — report the human report as `.okstra/tasks/<task-group>/<task-id>/runs/<task-type>/reports/final-report-<task-type>-<seq>.html` rooted at the task bundle, NOT the bare `runs/<task-type>/reports/...` form (byte-for-byte identical across every task of the same task-type, so it cannot identify the task). Under that, cite the report record (`.data.json`) and one line to render the full reading copy: `okstra render-final-report <task-qualified data.json>`. The same task-qualified rule applies to the team-state path, resume command path, and any other run-artifact path this reply cites.
|
|
428
429
|
|
|
429
430
|
## Run-scoped worker-resource lifecycle
|
|
430
431
|
|
|
@@ -210,11 +210,11 @@ approval only when it has standing over the stage that is about to start:
|
|
|
210
210
|
- **`deferred`** — the item's stages are all still `blocked`. It does not block;
|
|
211
211
|
the stage it judges has not been reached.
|
|
212
212
|
|
|
213
|
-
An item with no `stageScope` is `in-scope`
|
|
214
|
-
`P-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
An item with no `stageScope` is `in-scope` when it is plan-wide (`P-Opt-*`,
|
|
214
|
+
`P-Var-*`, `P-Dep-*`, `P-Dir-1`) or when no stage is `done` yet. Once a stage is
|
|
215
|
+
`done`, an unscoped `P-Val-*` / `P-Req-*` / `P-Rb-*` is `deferred`: omitting
|
|
216
|
+
`stageRefs` must not re-score the whole checklist on every re-plan. Scoping a
|
|
217
|
+
plan-wide item out would stop an unrequested-work verdict from blocking a start.
|
|
218
218
|
|
|
219
219
|
**Nothing is dropped.** An out-of-scope blocker becomes `has-dissent`, so the
|
|
220
220
|
gate reads `passed-with-dissent` rather than `passed` and the reader can see that
|
|
@@ -346,7 +346,7 @@ CLI-wrapper calls follow the planned execution surface after
|
|
|
346
346
|
consume only `modelExecutionValue`. A missing or invalid invocation contract blocks the
|
|
347
347
|
round before any host or provider process starts.
|
|
348
348
|
|
|
349
|
-
1. Lead runs `okstra plan-items prepare --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`, places the fixed output of `okstra plan-items prompt --run-manifest <run-manifest>` verbatim in every verifier prompt, then runs `okstra plan-items validate-prepared --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`. After a self-fix rewrite, pass `--state <plan-body-verification.json>` on prepare and validate-prepared so the dispatch queue is the changed items plus their stage closure, not the full extract. Python resolves the one convergence-owned state path from that run identity. Dispatch only after that exact-match validation succeeds. The prompt is the dispatch queue: `observed` / `deferred` stages are omitted; plan-wide items (`P-Dir-1`, `P-Var-*`, `P-Dep-*`,
|
|
349
|
+
1. Lead runs `okstra plan-items prepare --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`, places the fixed output of `okstra plan-items prompt --run-manifest <run-manifest>` verbatim in every verifier prompt, then runs `okstra plan-items validate-prepared --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`. After a self-fix rewrite, pass `--state <plan-body-verification.json>` on prepare and validate-prepared so the dispatch queue is the changed items plus their stage closure, not the full extract. Python resolves the one convergence-owned state path from that run identity. Dispatch only after that exact-match validation succeeds. The prompt is the dispatch queue: `observed` / `deferred` stages are omitted; plan-wide items (`P-Dir-1`, `P-Var-*`, `P-Dep-*`, `P-Opt-*`) stay. Unscoped `P-Val-*` / `P-Req-*` / `P-Rb-*` stay on the first round, then only when their own `contentHash` changed — a neighbour rewrite must not resweep them. **Enforced:** `okstra_ctl.plan_items.dispatch_item_ids` / `reverify_item_ids`. After `okstra incremental-carry`, the carry copies unchanged checklist verdicts, rewrites `dispatchQueue`, and updates the sibling `plan-items-*.json` so the next `okstra plan-items prompt` does not re-score a carried row.
|
|
350
350
|
|
|
351
351
|
**Then seed the landing table (BLOCKING):** `okstra plan-items seed --narrative <report-writer-narrative.md> --state <plan-body-verification.json> --run-manifest <run-manifest.json>`. `apply-verdicts` in step 8 refuses a verdict whose item has no `planBodyVerification.planItems[]` row. The report writer never owns that state, so the deterministic seed is the only creator of its rows. The seed is idempotent by id and never touches existing verdicts, so it is safe to re-run between rounds and after a self-fix re-extraction. It does refresh `contentHash` from the current extract. Skipping it makes step 8 fail with `plan-body state has no row for [...]`.
|
|
352
352
|
|
|
@@ -422,7 +422,8 @@ round before any host or provider process starts.
|
|
|
422
422
|
- `not-attempted` — the loop never ran because no item qualified.
|
|
423
423
|
The `no-progress` and `max-rounds-reached` exits are what make the loop terminate; `selfFixMaxRounds` alone is the backstop.
|
|
424
424
|
- a `majority-disagree` item with a majority of its deciding `DISAGREE` votes at `needs-user-input` is NOT a self-fix target — after correctness-critical precedence, it goes straight to the next step as `user-decision` rather than generic `noncritical-dissent`. **Enforced:** `validators/validate-run.py` `_expected_approval_classification`.
|
|
425
|
-
8. For every `majority-disagree` item **that remains after the self-fix loop** (items not resolved by self-fix, or with a `needs-user-input` majority from the start), lead adds a row to `## 1. Clarification Items` with:
|
|
425
|
+
8. For every **in-scope execution** `majority-disagree` item **that remains after the self-fix loop** (items not resolved by self-fix, or with a `needs-user-input` majority from the start), lead adds a row to `## 1. Clarification Items` with:
|
|
426
|
+
- do **not** promote an `observed` / `deferred` / `record` item. Those belong in `setAside`. A `Blocks=approval` C row for a frozen or unreached stage is how the clarification list grew while the next stage was already executable. **Enforced:** `validators/validate-run.py` `_validate_plan_body_clarification_matching` uses `_plan_item_gate_class` (after stage scope), not the raw vote class.
|
|
426
427
|
- new `C-<N>` ID (numbering continues from any existing rows)
|
|
427
428
|
- `Statement` summarising the disagreement and the worker breakage `<kind>`
|
|
428
429
|
- `Kind` chosen per the standard policy (usually `decision` for option-level conflicts, `data-point` for path/symbol mismatches)
|
|
@@ -112,7 +112,7 @@ Never dispatch the translator before report assembly and `check-source`. For a n
|
|
|
112
112
|
|
|
113
113
|
`workflow.nextRecommendedPhase` has `phase`, `status`, and `rationale`. The status vocabulary is `ready`, `pending`, `blocked`, and `terminal`.
|
|
114
114
|
|
|
115
|
-
`phase` is non-empty only for an authored `ready` pointer. `prepare` may lower `ready` to `pending` while retaining `phase` in `scripts/okstra_ctl/render.py::_derive_next_recommended_phase`; readers must use `status` for launchability. When `finalVerification.routingRecommendation.target` is `release-handoff(stage-group)`, write `phase` as `release-handoff`.
|
|
115
|
+
`phase` is non-empty only for an authored `ready` pointer. `prepare` may lower `ready` to `pending` while retaining `phase` in `scripts/okstra_ctl/render.py::_derive_next_recommended_phase`; readers must use `status` for launchability. When `finalVerification.routingRecommendation.target` is `release-handoff(stage-group)`, write `phase` as `release-handoff`. When `implementationPlanning.outcome` is `plan-ready` and the plan-body gate is `blocked-by-disagreement` or `aborted-non-result`, or a `Blocks=approval` row is still `open`/`answered`, write `status` `blocked` (empty `phase`) — do not write `ready` at `implementation`. When that outcome is `plan-ready` and the plan is approvable, write `ready` at `implementation`; the user still has to approve it separately.
|
|
116
116
|
|
|
117
117
|
## Compatibility
|
|
118
118
|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
- `rationale` — one sentence on why this option is on the board.
|
|
5
5
|
- `reach` — exactly one of `in-repo` or `cross-repo`.
|
|
6
6
|
- `scopeEffects` — optional tokens drawn from `{new-schema, deferrable}`.
|
|
7
|
-
- `addedWork` — one sentence naming the work this choice creates that the other choices do not. Name the
|
|
8
|
-
- `directionChange` — one sentence naming what this choice reverses: an approved plan item, a recorded decision, an earlier answer. When it reverses nothing, say so.
|
|
7
|
+
- `addedWork` — one sentence naming the work this choice creates that the other choices do not. Name the files, stages, or commands; do not substitute a cost adjective.
|
|
8
|
+
- `directionChange` — one sentence naming what this choice reverses: an approved plan item, a recorded decision, an earlier answer. Name that item. When it reverses nothing, say so.
|
|
9
9
|
- `disposition` — the effect of selecting the option. Use `select` for `user-decision`, `accept-risk` for `noncritical-dissent`, and `request-revision` or `reject` when the option sends the plan back. `correctness-critical` never offers `accept-risk`.
|
|
10
10
|
- report assembly derives `approvalContext`, status, and resolution. `approvalContext` contains only `classification`, `unblockCondition`, and `recommendedDisposition`; it never copies plan or activity identifiers.
|
|
11
11
|
- the three impact fields answer three different questions — how far the change reaches, what new work it creates, and what it overturns. Someone choosing between options needs all three, so never fold them into one sentence: whichever axis is easiest to write would silently stand in for the other two.
|
|
@@ -104,7 +104,7 @@ roles:
|
|
|
104
104
|
- §5.5.9 plan-body verification runs with an **adversarial posture** (`prompts/lead/plan-body-verification.md` §"Adversarial plan-body posture"): verifiers open and confirm every cited path / command and put the burden of proof on the plan. The gate threshold is majority-based for kinds `b`/`c`/`e`, but a single `DISAGREE` blocks on its own for the concrete, safety-critical kind `a` (path/symbol mismatch) — and `f` on `P-Req-*` items. `P-Var-*` items are excepted from the kind-`a` exception: a variation-point defect takes a majority. Rollback ordering (`d`) is advisory and never blocks the gate — a rollback is executed by a human, not by okstra's workers or verifiers. A majority also needs ≥2 participating votes, so a lone dissent whose peer returned a non-result does not block on a majority-gated kind (see that contract's §"Adversarial plan-body posture").
|
|
105
105
|
- **Incremental re-verification scope (clarification re-runs):** when the lead's `okstra incremental-scope` decision is `mode == "incremental"` (procedure in `prompts/launch.template.md` §"Clarification Response Carried In"), workers re-analyze ONLY the stages listed in `reverify_stages` (the downstream closure of the impacted stages). Workers MUST NOT re-open, re-score, or re-judge any stage in `carry_stages` — those stages' prior plan-item verdicts are carried forward verbatim, and a worker never overwrites a carried verdict with its own judgement. When the decision is `mode == "full"` (the default), every stage is re-analyzed as usual.
|
|
106
106
|
- **Single incremental-scope decision:** the lead calls `okstra incremental-scope` once the inputs are complete, passing the answered `C-NNN` ids through `--answered-clarifications`, changed design-preparation IDs through `--prep-items`, and any lead-resolved stage numbers through `--impacted`; the CLI unions all three before applying the existing dependency closure and cutoff. The clarification ids are resolved to stages by the CLI from the prior report's own `planItems[].clarificationId` and `blocked C-NNN` coverage links — the lead does not map answers to stage numbers. An answer that changes the selected planning payload, Stage Map, or execution approach is not a local impact: pass `--full-reason`, which is the only structural path that still forces `mode == "full"`. A clarification id that traces to no stage returns `mode == "unresolved"` — ask the user for stage numbers and call again with `--impacted`; do not treat it as full and do not silently drop the id. Unknown PREP IDs or invalid `stageRefs` still return an explicit full decision instead of being guessed. When the wizard pin is `auto` and the CLI returns `mode == "incremental"`, keep it — do not upgrade to full. When the user pinned a scope at the wizard (`REVERIFY_SCOPE_MODE` / `REVERIFY_SCOPE_STAGES` in `prompts/launch.template.md` §"Clarification Response Carried In" step 0), that pin is an input to this same call — `full` supplies the `--full-reason`, and pinned stage numbers join `--impacted` — never a bypass of the CLI's closure and cutoff.
|
|
107
|
-
- **Stage-aware carry:** for an incremental decision, the report writer copies each `carry_stages` stage row unchanged into its narrative. After plan-item seeding, pass the decision's `carry_stages` and `reverify_stages` CSVs unchanged to `okstra incremental-carry --cur-narrative ... --state ... --out-state ...`. The helper rejects a changed or missing carried stage and copies
|
|
107
|
+
- **Stage-aware carry:** for an incremental decision, the report writer copies each `carry_stages` stage row unchanged into its narrative. After plan-item seeding, pass the decision's `carry_stages` and `reverify_stages` CSVs unchanged to `okstra incremental-carry --cur-narrative ... --state ... --out-state ...`. The helper rejects a changed or missing carried stage and copies prior `P-Step-*` / `P-Prep-*` verdicts for `carry_stages`, plus unchanged `P-Val-*` / `P-Req-*` / `P-Rb-*` rows whose extract `contentHash` still matches. It rewrites `dispatchQueue` and the sibling `plan-items-*.json` so the next prompt does not re-score a carried checklist row. Overlap, omissions, and canonical conflicts return `CarryError`. On that error, discard the partial state and run full re-verification.
|
|
108
108
|
{{INCLUDE:_coverage-critic.md}}
|
|
109
109
|
- Non-goals:
|
|
110
110
|
- code-level micro-optimization unless it changes the implementation approach
|
|
@@ -195,7 +195,8 @@ roles:
|
|
|
195
195
|
- dependency / migration risk assessment (ordering constraints, data backfills, feature-flag prerequisites, repo-internal sequencing)
|
|
196
196
|
- **Cross-Project Dependencies (conditionally required):** when the plan depends on work in another project / repo / published package, add (a) a `kind: cross-project` DM row to `dependencyMigrationRisk`, and (b) a matching `XP-NNN` row to `crossProjectDependencies`. An upstream-precondition row must have concrete `requiredWork` / `verificationSignal` / `howToStart` — `validators/validate-run.py` enforces that a DM `cross-project` ⇒ at least one `direction: upstream-precondition` XP row, and the schema enforces non-empty row fields. A cross-project dependency is recorded as this structured precondition, not as a soft Recommended Next Step. A single-project plan uses an empty array.
|
|
197
197
|
- **recommendedNextSteps policy:** keep the substance of cross-project preconditions/carries in `crossProjectDependencies`, and put in `§3 Recommended Next Steps` only a pointer to that section (`§5.4 Cross-Project Dependencies`) — no double recording.
|
|
198
|
-
- **Resuming from an approval blocker (BLOCKING).** When this report carries any `blocks: approval` clarification, one `recommendedNextSteps` entry MUST name the `--answered-clarifications` re-run, in its `text` or in one of its `commands`. The reader of the report is the person who has to act, and until now the mechanics of resuming lived only in the lead prompt — which is read after the next run has already started. Point them at `okstra recap assemble`, which prints the answered ids, the exact flag value, the sidecar paths, and whether the re-verification would fall back to full. Do not restate those values here: they are unknown while you write, because the user has not answered yet. **Enforced:** `validators/validate-run.py` `_validate_rerun_guidance`.
|
|
198
|
+
- **Resuming from an approval blocker (BLOCKING).** When this report carries any `blocks: approval` clarification, one `recommendedNextSteps` entry MUST name the `--answered-clarifications` re-run, in its `text` or in one of its `commands`. The reader of the report is the person who has to act, and until now the mechanics of resuming lived only in the lead prompt — which is read after the next run has already started. Point them at `okstra recap assemble`, which prints the answered ids, the exact flag value, the sidecar paths, and whether the re-verification would fall back to full. Do not restate those values here: they are unknown while you write, because the user has not answered yet. The first human action is answering those rows (`okstra-user-response`); do not tell the reader to start another planning run before the answers exist. **Enforced:** `validators/validate-run.py` `_validate_rerun_guidance`.
|
|
199
|
+
- **Asking for approval (BLOCKING).** When `outcome` is `plan-ready`, the gate is `passed` or `passed-with-dissent`, and no `blocks: approval` row is open, one `recommendedNextSteps` entry MUST tell the reader to approve (`--approve` or the in-session wizard). Do not recommend another `implementation-planning` run. **Enforced:** `validators/validate-run.py` `_validate_approval_guidance`.
|
|
199
200
|
- validation checklist (pre / mid / post) — each item is an exact command or observable outcome
|
|
200
201
|
- rollback strategy — exact revert path (commits, flags, migrations) and the signal that triggers rollback
|
|
201
202
|
- **Requirement admissibility (scope boundary):** a brief line becomes a Requirement Coverage row only when **a stage can satisfy it by changing files in this repository** — source, tests, config, or deployment *manifest files*. A line whose satisfaction needs a person's approval, a ticket status change, or an action against live infrastructure (applying a manifest, a cutover, creating a dashboard or alert, validating in staging/production) is NOT a requirement for this phase: it belongs to the brief's `## External Gates`, and this plan neither creates a stage for it nor cites it in coverage. Briefs generated by `okstra-brief-gen` pre-split these into the end-state sections `## Expected Behavior` / `## Preserved Behavior` / `## Expected Outcome` (admissible) and `## External Gates` (not); when reading an older brief that carries a raw Definition-of-Done checklist, apply the same test line by line. The boundary is the *action*, not the topic — "add the flag to `values-prod.yaml`" is admissible, "apply that manifest to prod" is not. Planning an operational stage this phase cannot execute (see the run-scope rule above forbidding deployments) produces steps whose commands never resolve, which the §5.5.9 gate then correctly blocks — the plan must not create that deadlock in the first place.
|
|
@@ -261,6 +262,6 @@ roles:
|
|
|
261
262
|
10. **Decision-draft materialization check** — when `decisionDrafts` is non-empty, confirm as a reviewer which stage's stepwise order contains the matching materialization step (creating `.okstra/decisions/<NNNN>-<slug>.md`) and that the number of drafts corresponds 1:1 with the materialization steps. The validator only checks the *existence* of the step, so the `<NNNN>-<slug>` correctness and count correspondence are the self-review's responsibility.
|
|
262
263
|
11. **Variation-point & seam check** — read `variationPointAnalysis` as a skeptic. Is `hasMultipleImplementations` honest against the brief and the sibling code you inspected during pre-planning, or was `false` chosen because it is the cheaper field to fill? For every point with `extract: true`, confirm the `extractionDecision` names a real interface (a `port` for a hexagonal project, not a shared helper) and a `coveredBy` stage that exists in the Stage Map — an interface no stage builds is a decision nobody executes. Then read the chosen realization's `testSeams`: each `injectedAs` must name a construction or wiring point a test can actually substitute at, not a symbol the test would have to re-implement — a seam nothing can be injected into leaves the executor writing self-mocks. An empty `testSeams` array is only acceptable when you can defend it in one sentence; the validator accepts it either way, so this is the check that catches an unfilled field posing as a decision.
|
|
263
264
|
12. **Approval blast-radius check (BLOCKING).** Every approval clarification must be reachable from `planItems[].clarificationRefs[]` or a requirement-coverage blocker. Report assembly derives plan-item links from activity `clarificationRefs[]` plus `planItemIds[]`; `okstra incremental-scope` reads the resulting reverse links.
|
|
264
|
-
- **The link must resolve to a stage, not merely exist.** `incremental-scope` reads the stage number out of a `P-Step-<stage>.<step>` / `P-Prep-S<stage>-<kind>` plan-item id, or out of a `Stage N` citation in the blocked coverage row's `coveredBy`.
|
|
265
|
+
- **The link must resolve to a stage, not merely exist.** `incremental-scope` reads the stage number out of a `P-Step-<stage>.<step>` / `P-Prep-S<stage>-<kind>` plan-item id, out of `stageScope` / `stageRefs` on the linked plan item or coverage row, or out of a `Stage N` citation in the blocked coverage row's `coveredBy`. A `P-Req-*` / `P-Val-*` id is positional, so a blocker linked only that way MUST carry `stageRefs` or cite the stage in `coveredBy`. Writing the blocked row's `coveredBy` as prose with no `Stage N` in it — `No stage.`, `Partly covered — …` — satisfies nothing unless `stageRefs` is present: the row passes the link check and the next re-run cannot place the answer without asking for stage numbers.
|
|
265
266
|
- What to write when no stage covers the requirement yet: name the stage the answer will change, not the stage that satisfies the requirement today. A `Blocks=approval` row is admissible only when, absent an answer, `implementation` would produce wrong or unsafe code (see the admissibility rule above) — so some stage's code is at stake by construction. If you genuinely cannot name one, the row fails the admissibility test and belongs in `## 5. Missing Information and Risks` with `Blocks=none`, not in the approval gate.
|
|
266
267
|
**Enforced:** `validators/validate-run.py` `_validate_approval_clarification_backtrace` — one failure for a missing link, a separate one for a link that resolves to no stage.
|
|
@@ -81,7 +81,9 @@
|
|
|
81
81
|
"echo_template": "task-type: {value}",
|
|
82
82
|
"options": {
|
|
83
83
|
"_RECOMMENDED_SUFFIX": " (recommended)",
|
|
84
|
+
"_APPROVE_SUFFIX": " (recommended · 계획 승인 후 구현)",
|
|
84
85
|
"_RERUN_SUFFIX": " (현재 phase 재실행)",
|
|
86
|
+
"_BLOCKED_RERUN_SUFFIX": " (현재 phase 재실행 — 열린 명료화에 답한 뒤)",
|
|
85
87
|
"_NEXT_SUFFIX": " (다음 단계)",
|
|
86
88
|
"__free_input__": "직접 입력"
|
|
87
89
|
},
|
|
@@ -140,7 +140,7 @@ For a `host-text` mapping, render each numbered item as its option label followe
|
|
|
140
140
|
|---|---|
|
|
141
141
|
| `read_artifacts` | Use the host file-read primitive and preserve the core contract's read order. |
|
|
142
142
|
| `write_artifact` | Use the host file-write primitive only for paths authorized by the active lifecycle phase. |
|
|
143
|
-
| `prompt_user` | Use
|
|
143
|
+
| `prompt_user` | Use `AskUserQuestion` for approvals and clarifications that fit `nativeLimits`. Do not print a numbered list in chat while that tool is available. Do not infer an answer from silence. |
|
|
144
144
|
| `dispatch_worker` | First verify the materialized invocation metadata. Dispatch `runner=native-session` through `Agent(name: "<role>", run_in_background: true)` without `team_name`, passing the verified final prompt and `hostModelValue`. Dispatch `runner=cli-wrapper` with the deterministic shell command `okstra worker-dispatch --project-root <root> --run-manifest <path> --workers <ids>`; never wrap that process in another `Agent(...)` call. **Not in a cmux run:** when the run manifest's `terminalBackend` is `cmux-pane`, `prompts/lead/adapters/cmux.md` overrides this row. |
|
|
145
145
|
| `await_workers` | Arm one background shell poll for the pending Result Paths; the spawn acknowledgement is not completion. |
|
|
146
146
|
| `redispatch_worker` | Materialize and verify a fresh invocation, then use a fresh native `Agent(...)` session or `okstra worker-dispatch` attempt according to the persisted runner. |
|
|
@@ -207,7 +207,7 @@ For a `host-text` mapping, render each numbered item as its option label followe
|
|
|
207
207
|
- Collect and persist token usage before any live-roster cleanup, including cleanup between batches and the run-end shutdown sequence.
|
|
208
208
|
- Before each new worker batch (and before the next phase's render-bundle), close the panes of the dispatches that finished in the prior round, in two passes. First count: `okstra team reclaim --project-root "<PROJECT_ROOT>" --run-manifest "<RUN_MANIFEST_PATH>" --dry-run` closes nothing and prints one `<paneId>\t<kind>` line per pane it would close — count those lines as `<n>`. Then run the same command **without** `--dry-run` to close them, and emit the neutral contract's `PROGRESS: phase-batch-cleanup panes=<n>` checkpoint with that count. Call both passes after collecting that round's results and token usage and before the next dispatch. The command reads each dispatch's recorded status, so an in-progress worker keeps its pane whichever moment you call it — you do not scope the pass by hand. It closes only the panes okstra opened and recorded; a pane the harness opened for itself carries no recorded id and is not okstra's to close. A `cli-wrapper` run holds no pane at all, so `<n>` is `0` — still emit the checkpoint.
|
|
209
209
|
- Reclaiming a pane does not stop the worker's background task. Every `dispatch_worker` Agent runs with `run_in_background: true`, so a worker whose result is already collected stays a live background task for the rest of the session — that residue is what fills the harness's exit-time `Background work is running` list. At the same batch boundary, right after the pane reclaim, call `TaskStop(task_id: "<name>")` once per worker of the completed batch, passing the exact `name` used at dispatch (`<workerId>-worker`, `<workerId>-worker-reverify-r<N>`, `<provider>-worker-critic`, `report-writer`). Stop only workers whose results were already collected — never an in-flight worker, never the lead, and keep `report-writer` while it is in flight, matching the pane pass's `--keep report-writer-worker`. `TaskStop` on an already-finished task is a no-op; treat a failure as benign, record nothing, and continue the boundary. This runs in a non-tmux session too, where the pane passes no-op but the background tasks still exist.
|
|
210
|
-
- Before any `prompt_user`/`AskUserQuestion` that follows worker dispatch — an approval, clarification, or decision gate — run the same two passes used at a round boundary: `okstra team reclaim … --dry-run` to count `<n>`, then the same command without `--dry-run` to close, and emit `PROGRESS: phase-gate-cleanup panes=<n>`. Then `TaskStop(task_id: "<name>")` each completed worker, exactly as at a batch boundary. A bare `TaskStop` idles the roster task and closes no pane, so it is never cleanup on its own. This keeps the user from being shown a gate while finished worker panes are still open.
|
|
210
|
+
- Before any `prompt_user`/`AskUserQuestion` that follows worker dispatch — an approval, clarification, or decision gate — run the same two passes used at a round boundary: `okstra team reclaim … --dry-run` to count `<n>`, then the same command without `--dry-run` to close, and emit `PROGRESS: phase-gate-cleanup panes=<n>`. Then `TaskStop(task_id: "<name>")` each completed worker, exactly as at a batch boundary. A bare `TaskStop` idles the roster task and closes no pane, so it is never cleanup on its own. This keeps the user from being shown a gate while finished worker panes are still open. After that cleanup, follow the lead contract "User confirmation before an approval blocker": read cited plan items, worker findings, and files before asking, and ask in the user's language with each option's outcome.
|
|
211
211
|
- After batch cleanup, record the current live session generation with `okstra token-usage "<TEAM_STATE_PATH>" --record-observed-session --project-root "<PROJECT_ROOT>"`. This protects usage accounting when Claude Code re-issues the session id after resume or compaction.
|
|
212
212
|
- Claude Code cannot delete the implicit team or surgically remove an idle roster entry. Explain that teammates may remain visible until session end and, when needed, give the manual action `Delete team <teamName> in Teams/FleetView`.
|
|
213
213
|
- The `SessionEnd` hook runs `$HOME/.okstra/bin/okstra-team-reconcile.sh --session-end` as the safety net for the current live session.
|
|
@@ -121,7 +121,7 @@ For a `host-text` mapping, render each numbered item as its option label followe
|
|
|
121
121
|
|---|---|
|
|
122
122
|
| `read_artifacts` | Read the manifest-provided paths through the current host's file interface. |
|
|
123
123
|
| `write_artifact` | Write only core-authorized `.okstra/` artifacts and preserve their schemas. |
|
|
124
|
-
| `prompt_user` | Use `request_user_input` for approvals and clarifications that fit two or three options
|
|
124
|
+
| `prompt_user` | Use `request_user_input` for approvals and clarifications that fit `nativeLimits` (two or three options). Do not print a numbered list in chat while that tool is available. Otherwise ask through host text and stop until an explicit answer arrives. |
|
|
125
125
|
| `dispatch_worker` | Verify each materialized invocation first. Dispatch `runner=native-session` with the current Codex host's primitive, the returned `promptPath`, and `hostModelValue`. Pass `runner=cli-wrapper` assignments to `okstra worker-dispatch --project-root <root> --run-manifest <path> --workers <ids>`; use `--dry-run` first when required. **Not in a cmux run:** when `terminalBackend` is `cmux-pane`, the cmux adapter overrides this row. |
|
|
126
126
|
| `await_workers` | Await native host workers through the host primitive and CLI workers through synchronous dispatch, then verify team-state terminal records and Result Paths for both. |
|
|
127
127
|
| `redispatch_worker` | Materialize and verify a fresh invocation, then start a fresh native worker or `okstra worker-dispatch` attempt according to the persisted runner. |
|
|
@@ -136,7 +136,7 @@ For a `host-text` mapping, render each numbered item as its option label followe
|
|
|
136
136
|
|---|---|
|
|
137
137
|
| `read_artifacts` | Read the manifest-provided paths through the current Grok host file interface. |
|
|
138
138
|
| `write_artifact` | Write only core-authorized `.okstra/` artifacts and preserve their schemas. |
|
|
139
|
-
| `prompt_user` | Use `ask_user_question` for approvals and clarifications
|
|
139
|
+
| `prompt_user` | Use `ask_user_question` for approvals and clarifications that fit `nativeLimits`. Do not print a numbered list in chat while that tool is available. Do not infer an answer from silence. |
|
|
140
140
|
| `dispatch_worker` | Verify the materialized invocation. Use the host primitive with `promptPath` and `hostModelValue` for `native-session`; use deterministic `okstra worker-dispatch` with `modelExecutionValue` for `cli-wrapper`. **Not in a cmux run:** the cmux adapter overrides this row. |
|
|
141
141
|
| `await_workers` | Await through the selected common dispatch backend, then verify terminal state and Result Paths. |
|
|
142
142
|
| `redispatch_worker` | Start a fresh attempt from the persisted assignment and record the supplied dispatch kind. |
|