okstra 0.128.0 → 0.129.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/architecture/storage-model.md +15 -2
- package/docs/architecture.md +19 -3
- package/docs/cli.md +8 -0
- package/docs/project-structure-overview.md +14 -5
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +3 -3
- package/runtime/agents/workers/claude-worker.md +4 -4
- package/runtime/agents/workers/codex-worker.md +3 -3
- package/runtime/agents/workers/report-writer-worker.md +5 -5
- package/runtime/prompts/lead/adapters/claude-code.md +2 -1
- package/runtime/prompts/lead/adapters/codex.md +1 -0
- package/runtime/prompts/lead/adapters/external.md +1 -0
- package/runtime/prompts/lead/convergence.md +42 -84
- package/runtime/prompts/lead/okstra-lead-contract.md +10 -8
- package/runtime/prompts/lead/report-writer.md +3 -2
- package/runtime/prompts/lead/team-contract.md +11 -9
- package/runtime/prompts/profiles/_common-contract.md +1 -1
- package/runtime/prompts/profiles/error-analysis.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +5 -0
- package/runtime/prompts/profiles/improvement-discovery.md +11 -1
- package/runtime/prompts/profiles/requirements-discovery.md +8 -1
- package/runtime/python/okstra_ctl/analysis_packet.py +7 -13
- package/runtime/python/okstra_ctl/codex_dispatch.py +42 -97
- package/runtime/python/okstra_ctl/context_cost.py +82 -7
- package/runtime/python/okstra_ctl/convergence.py +223 -0
- package/runtime/python/okstra_ctl/convergence_engine.py +1152 -0
- package/runtime/python/okstra_ctl/convergence_migration.py +184 -0
- package/runtime/python/okstra_ctl/convergence_store.py +85 -0
- package/runtime/python/okstra_ctl/dispatch_core.py +53 -14
- package/runtime/python/okstra_ctl/improvement_assignment.py +61 -0
- package/runtime/python/okstra_ctl/path_hints.py +23 -1
- package/runtime/python/okstra_ctl/paths.py +10 -1
- package/runtime/python/okstra_ctl/render.py +56 -11
- package/runtime/python/okstra_ctl/worker_prompt_body.py +107 -0
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +102 -9
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +79 -9
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +158 -0
- package/runtime/templates/implementation-worker-preamble.md +51 -0
- package/runtime/templates/operating-standard.md +4 -4
- package/runtime/templates/report-writer-prompt-preamble.md +37 -0
- package/runtime/templates/worker-error-contract.md +46 -0
- package/runtime/templates/worker-prompt-preamble.md +28 -234
- package/runtime/validators/lib/fixtures.sh +27 -12
- package/runtime/validators/validate-run.py +201 -72
- package/src/cli-registry.mjs +7 -0
- package/src/commands/execute/convergence.mjs +34 -0
|
@@ -47,7 +47,7 @@ Read-side inspection (`/okstra-inspect`) and scheduling (`/okstra-schedule-gen`)
|
|
|
47
47
|
| 3. Dispatch setup | Execute the selected adapter's pre-dispatch setup and record its state | selected runtime adapter + this contract |
|
|
48
48
|
| 4. Execution | Call `dispatch_worker` once per selected assignment | selected runtime adapter + `team-contract` |
|
|
49
49
|
| 5. Completion wait | Call `await_workers` and verify terminal state plus required artifacts | selected runtime adapter + `team-contract` |
|
|
50
|
-
| 5.5 Convergence |
|
|
50
|
+
| 5.5 Convergence | Semantically group findings, then drive deterministic state transitions through `ConvergenceEngine` via `okstra convergence` | `convergence` |
|
|
51
51
|
| 5.6 Critic pass | (opt-in) fresh one-shot critic pass through `redispatch_worker`: coverage gaps (discovery/error-analysis/impl-planning) or acceptance devil's-advocate (final-verification). The critic dispatch fires concurrently with the first 5.5 reverify round (its input is fixed at Round 0); gap/blocker verification (one round) completes here | `convergence` "Coverage critic pass" / "Acceptance critic pass" |
|
|
52
52
|
| 6. Synthesis | Dispatch Report writer worker, review draft. **For `implementation-planning`: then run the Phase 6 plan-body verification sub-step (see Phase 6 section below).** | `report-writer` + `plan-body-verification` (sub-step) |
|
|
53
53
|
| 7. Persist | Call `collect_usage`, update manifests, run the cleanup approval gate, then call `shutdown_workers` only on approval | selected runtime adapter + `report-writer` + this contract |
|
|
@@ -210,6 +210,8 @@ These phases are governed by [team-contract](./team-contract.md). It is the cano
|
|
|
210
210
|
|
|
211
211
|
For `final-verification`, Lead persists initial analysis prompts with one shared semantic body. Any genuine run delta appears under exactly one `## Run-specific directive` heading and applies to every selected analysis worker; Lead never shards verification requirements by worker, provider, or model. If the shared directive would exceed 40 nonblank lines, Lead writes it into the instruction set and adds the same reference to `analysis-packet.md` before dispatch. Phase 7 validates the persisted initial analysis prompts through the shared prompt contract before the run can pass.
|
|
212
212
|
|
|
213
|
+
For `improvement-discovery`, Lead records `## Primary Pass Assignments` in the Phase 1.5 grilling log before worker prompt generation. Enumerate selected analyser worker instances in run-manifest `requiredWorkerRoles` order and rotate them over the resolved lenses in log order; provider and model names never determine assignment position. Every analyser still inspects every resolved lens. Phase 7 recomputes this rotation from the persisted roster and fails a missing, extra, duplicate, out-of-order, or out-of-scope assignment.
|
|
214
|
+
|
|
213
215
|
`Report writer worker` is NOT an analysis worker. Do not dispatch it in Phase 4/5 alongside analysis workers. It is invoked only in Phase 6 — see [report-writer](./report-writer.md).
|
|
214
216
|
|
|
215
217
|
### Phase 3 — Runtime adapter setup (BLOCKING)
|
|
@@ -282,15 +284,15 @@ Convergence is enabled by default. Configure via task-manifest.json:
|
|
|
282
284
|
- `convergence.verificationMode`: `"lightweight"` | `"full-reanalysis"` (default: `"lightweight"`; the adversarial phases below force `"full-reanalysis"`)
|
|
283
285
|
- `convergence.adversarial`: true/false — **phase-aware default**: `true` for `requirements-discovery` / `error-analysis` / `implementation-planning`, `false` otherwise. When `true`, Phase 5.5 runs in adversarial mode (verifiers refute findings; burden of proof on the claim). See [convergence](./convergence.md) "Adversarial Verification Mode".
|
|
284
286
|
|
|
285
|
-
When `task-manifest.json` does not set `convergence.maxRounds`, lead MUST resolve the effective value via the phase-aware default above before entering Phase 5.5
|
|
287
|
+
When `task-manifest.json` does not set `convergence.maxRounds`, lead MUST resolve the effective value via the phase-aware default above before entering Phase 5.5 and put it in the grouped input at `config.effectiveMaxRounds`.
|
|
286
288
|
|
|
287
|
-
|
|
289
|
+
The lead judges semantic similarity, ticket-set equality, and evidence. After writing the grouped input, it drives `okstra convergence seed → plan-round → apply-round → finalize → validate` and MUST NOT calculate queue membership, classification, history arithmetic, skip reasons, final state, or counts itself. `ConvergenceEngine` owns all of those deterministic transitions.
|
|
288
290
|
|
|
289
|
-
|
|
291
|
+
For every dispatch plan, create only the worker batches returned by the engine and send them through the selected adapter. Confirmed findings cannot reappear because queue pruning is engine-owned and monotonic. Reverify terminal failures are structured outcomes for `apply-round`, never lead-authored `DISAGREE` votes.
|
|
290
292
|
|
|
291
293
|
If any re-verification batch yields a `verification-error` terminal status, or a worker result fails the contract, Lead MUST record one event per violation via `okstra error-log append-observed --error-type contract-violation --agent <offending-agent> ...`. For an internally detected violation without a specific worker, use the selected adapter's lead-role event identity.
|
|
292
294
|
|
|
293
|
-
If convergence is disabled,
|
|
295
|
+
If convergence is disabled, `seed`/`finalize` produce the auto-disabled final state and Phase 6 uses the raw worker results for synthesis.
|
|
294
296
|
|
|
295
297
|
## Phase 6: Final report assembly
|
|
296
298
|
|
|
@@ -403,10 +405,10 @@ After persistence, reply briefly in the resolved Report Language with: completio
|
|
|
403
405
|
| Selecting a generic worker assignment for Report writer worker | Use the rostered `report-writer-worker` assignment; the selected adapter owns its native field mapping |
|
|
404
406
|
| Including `final-report-template.md` in analysis worker `[Required reading]` | Template belongs only in the report-writer prompt — see [team-contract](./team-contract.md) audience-scoped enumeration |
|
|
405
407
|
| Injecting `[Required reading]` into lightweight reverify prompts | Lightweight reverify forbids re-reading source materials — see [convergence](./convergence.md) "Reverify prompt: required-reading suppression" |
|
|
406
|
-
| Letting `convergence.maxRounds` default to 2 for `requirements-discovery` | Resolve effective default to `1` for discovery and
|
|
408
|
+
| Letting `convergence.maxRounds` default to 2 for `requirements-discovery` | Resolve effective default to `1` for discovery and put it in the grouped input |
|
|
407
409
|
| Issuing serial Read calls in Phase 1 | The intake files are independent — issue all Read calls in a single message (parallel) |
|
|
408
410
|
| Flagging an adapter-shortened dispatch prompt as "incomplete" because it omits host-loaded material | The Worker Preamble pointer and core inputs remain mandatory; the selected adapter may omit duplicate host-loaded definitions |
|
|
409
411
|
| Waiting silently after `dispatch_worker` returns without a completed worker artifact | A dispatch acknowledgement is not completion — call `await_workers` and enforce the selected adapter's liveness policy |
|
|
410
|
-
| Re-sending
|
|
411
|
-
| Aggregating a `timeout`/`error` reverify dispatch as `DISAGREE` |
|
|
412
|
+
| Re-sending a finding absent from the persisted round plan | Dispatch exactly the engine-returned `findingIds`; see [convergence](./convergence.md) "Re-verification Dispatch" |
|
|
413
|
+
| Aggregating a `timeout`/`error` reverify dispatch as `DISAGREE` | Put the terminal outcome in round results; `apply-round` records `verification-error`. See [convergence](./convergence.md) "Worker failure handling in reverify" |
|
|
412
414
|
| Skipping `--substitute-data` in the Phase 7 collector run | Always pass the flag — see [report-writer](./report-writer.md) "Phase 7 token-usage collector" |
|
|
@@ -37,8 +37,9 @@ The prompt MUST include, in this order at the top:
|
|
|
37
37
|
3. `**Result Path:** runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json` — canonical JSON SSOT. The renderer produces the sibling `.md` automatically.
|
|
38
38
|
4. `**Worker Result Path:** runs/<task-type>/worker-results/report-writer-worker-<task-type>-<seq>.md` — mandatory validator-checked worker-results audit file
|
|
39
39
|
5. `Assigned worker prompt history path: <absolute-path>`
|
|
40
|
-
6. The
|
|
41
|
-
- `**Worker Preamble Path:** <absolute-path>` —
|
|
40
|
+
6. The four BLOCKING dispatch anchor headers generated from the report-writer audience (the worker cannot synthesize any of these paths):
|
|
41
|
+
- `**Worker Preamble Path:** <absolute-path>` — selects `templates/report-writer-prompt-preamble.md`.
|
|
42
|
+
- `**Worker Error Contract Path:** <absolute-path>` — selects `templates/worker-error-contract.md`.
|
|
42
43
|
- `**Errors log path:** <absolute-path>` — run-level errors JSONL (`logs/errors-<task-type>-<seq>.jsonl`).
|
|
43
44
|
- `**Errors sidecar path:** <absolute-path>` — this worker's per-run sidecar JSON (`worker-results/report-writer-worker-errors-<task-type>-<seq>.json`).
|
|
44
45
|
7. `**Model:** Report writer worker, <modelExecutionValue>` (resolved per Phase 5.5 anchor-header rules)
|
|
@@ -65,13 +65,15 @@ Only workers selected from `recommendedWorkers` in `task-manifest.json` and `res
|
|
|
65
65
|
|
|
66
66
|
Every worker prompt MUST start with the anchor headers rendered by `okstra_ctl.worker_prompt_headers.worker_prompt_headers()` (the generating SSOT — never hand-author or reorder them). Their meaning and extraction rules for workers are documented in the worker preamble §"Anchor headers". Phase-specific extra headers (implementation worktree, final-verification target snapshot, improvement-discovery grilling log) are listed there too.
|
|
67
67
|
|
|
68
|
-
The `**Coding preflight pack:**` anchor is emitted only
|
|
68
|
+
The `**Coding preflight pack:**` anchor is emitted only for the `implementation-executor` and `implementation-verifier` audiences. An implementation report-writer never receives it. A pane display role named `verifier` during `final-verification` is still an initial analysis worker; it neither receives implementation conventions nor becomes a Phase 5.5 reverify dispatch. Reverify is identified by the `-reverify-r<N>-` prompt/result path contract in [convergence](./convergence.md).
|
|
69
|
+
|
|
70
|
+
For `improvement-discovery`, `worker_prompt_headers()` resolves and validates the Phase 1.5 grilling log before dispatch and emits its absolute path as `**Phase 1.5 Grilling Log:**`. The lead does not hand-author or guess that path.
|
|
69
71
|
|
|
70
72
|
The body must include: role name, task type, task key, assigned model, output contract, evidence handling rules, and `analysis-packet.md` as the single primary analysis input. For `final-verification`, the compact target anchors and that packet path are sufficient; do not copy the full diff stat, source/fallback path inventory, profile sections, report-output sections, or lead self-review into the initial prompt.
|
|
71
73
|
|
|
72
|
-
A `final-verification` prompt may contain one optional `## Run-specific directive` section for a true run delta. It is limited to 40 nonblank lines; the nonblank prompt body excluding common/target anchors is limited to 96 lines. Larger shared material belongs in the instruction set and analysis packet. Before initial dispatch, the selected adapter validates each prompt and requires byte-identical normalized analysis bodies after removing only worker/model/role and worker-specific artifact-path metadata.
|
|
74
|
+
A `final-verification` prompt may contain exactly one optional `## Run-specific directive` section for a true run delta. It is limited to 40 nonblank lines; the nonblank prompt body excluding common/target anchors is limited to 96 lines. Larger shared material belongs in the instruction set and analysis packet. Before initial dispatch, the selected adapter validates each prompt and requires byte-identical normalized analysis bodies after removing only worker/model/role and worker-specific artifact-path metadata.
|
|
73
75
|
|
|
74
|
-
The Phase 7 run validator enforces the same rule against the persisted prompt paths recorded by the run manifest and team-state. `validators/validate-run.py`
|
|
76
|
+
The Phase 7 run validator enforces the same cross-task rule against the persisted prompt paths recorded by the run manifest, dispatch records, and team-state. Both dispatch adapters and `validators/validate-run.py` call `okstra_ctl.worker_prompt_contract.validate_initial_prompt_records()`. The validator resolves each record through `PromptPlan`, applies audience-specific anchors, compares every equality group with at least two prompts, validates report-writer common anchors without adding it to an analysis group, and ignores `-reverify-r<N>-` prompts because they belong to the lightweight convergence protocol. A dispatch-time bypass therefore remains a run-level contract violation.
|
|
75
77
|
|
|
76
78
|
When a worker reads any project-relative path from the prompt, it MUST resolve it against `Project Root` (e.g. `<Project Root>/<Result Path>`) — never use bare relative paths that depend on cwd.
|
|
77
79
|
|
|
@@ -81,13 +83,13 @@ Persist the exact worker prompt before dispatch per Operating Rule 6; never use
|
|
|
81
83
|
|
|
82
84
|
Send byte-identical dispatch prompts to every analysis worker per the "Dispatch-prompt invariant" (Role Definitions). Specialization lives in Section 6 of the worker output, not in the dispatch prompt body.
|
|
83
85
|
|
|
84
|
-
###
|
|
86
|
+
### Audience preambles + shared error contract (SSOT)
|
|
85
87
|
|
|
86
|
-
The lead does
|
|
88
|
+
The lead does not inline reading or error blocks. It resolves `PromptPlan.audience` and injects exactly one `**Worker Preamble Path:**`: analysis → `templates/worker-prompt-preamble.md`, implementation executor/verifier → `templates/implementation-worker-preamble.md`, report-writer → `templates/report-writer-prompt-preamble.md`. Every initial worker also receives `**Worker Error Contract Path:**` pointing to `templates/worker-error-contract.md`, the sole owner of error schema and write rules. Reverify prompts use neither preamble.
|
|
87
89
|
|
|
88
90
|
What the lead MUST still do per dispatch:
|
|
89
91
|
- Inject the input file enumeration into the dispatch prompt body via an `## Inputs` section (or any heading the recipient agent expects), listing the actual project-relative primary inputs derived from the run's `instruction-set/`. For `final-verification` analysis workers, list only `analysis-packet.md` as the primary input; source files are reached on demand through that packet. Other phases may list source/fallback paths when useful. The preamble describes the rules; the lead provides the specific paths for THIS run.
|
|
90
|
-
- Inject the absolute `**Errors log path:**` and `**Errors sidecar path:**`
|
|
92
|
+
- Inject `**Worker Error Contract Path:**` plus the absolute `**Errors log path:**` and `**Errors sidecar path:**` headers — workers cannot synthesize these paths.
|
|
91
93
|
- Omit the preamble pointer for reverify dispatches (Phase 5.5 lightweight mode) — see [convergence](./convergence.md) "Reverify prompt: required-reading suppression".
|
|
92
94
|
|
|
93
95
|
Audience-scoped file enumeration (performance optimization — mandatory):
|
|
@@ -149,7 +151,7 @@ After each worker subagent returns (regardless of role), Lead MUST verify the ca
|
|
|
149
151
|
|
|
150
152
|
## Worker Output Contract
|
|
151
153
|
|
|
152
|
-
The canonical worker output contract —
|
|
154
|
+
The canonical analysis-worker output contract — frontmatter, sections 1–5 plus optional Section 6, item IDs, and ticket tagging — lives in `templates/worker-prompt-preamble.md`. Implementation output behavior remains in its executor/verifier sidecars; report authoring remains in the report-writer preamble and Phase 6 contract.
|
|
153
155
|
|
|
154
156
|
Lead-facing duties that stay here:
|
|
155
157
|
|
|
@@ -223,8 +225,8 @@ without proceeding.
|
|
|
223
225
|
2. Re-verification workers follow a constrained response format (verdict + brief explanation).
|
|
224
226
|
3. Workers cannot vote on their own findings (only verify other workers’ work).
|
|
225
227
|
4. The `report writer worker` does not participate in re-verification voting. It is responsible only for generating the final report.
|
|
226
|
-
5. Division of labor: the lead performs **finding-to-finding matching** (
|
|
227
|
-
6. Batch processing is performed with one spawn per worker
|
|
228
|
+
5. Division of labor: the lead performs **finding-to-finding matching** (semantic similarity plus ticket-set equality); workers cast AGREE / DISAGREE / SUPPLEMENT verdicts; `ConvergenceEngine` owns queue membership, classifications, round arithmetic, pruning, skip reasons, and final counts. The lead does not vote or hand-edit engine state.
|
|
229
|
+
6. Batch processing is performed with one fresh spawn per persisted `plan-round` worker row (not one spawn per finding). The selected adapter transports that exact batch and terminal result without changing membership.
|
|
228
230
|
7. These rules do not apply if Convergence is disabled.
|
|
229
231
|
|
|
230
232
|
## Re-verification Terminal Statuses
|
|
@@ -75,7 +75,7 @@ profile document.
|
|
|
75
75
|
- **Worker-side item IDs (free-form but unique within the worker).** Every row item in sections 1–5 (and any optional section 6) of an analysis worker's output MUST carry an item ID that is unique within that one worker's result file. The ID convention is the worker's choice — `F-001` / `F-002` per the suggested schema, `1.1` / `1.2` / `1.3` as Codex tends to use, or any other shape — but it MUST appear as the leading column of the row (for table-form items) or as a `[<ID>]` prefix (for bullet/numbered items). Workers that emit findings without IDs make cross-worker reconciliation impossible.
|
|
76
76
|
- **Lead-side ID assignment + source preservation.** When the lead (or `report-writer-worker`) synthesises `## 6.1 Consensus` / `## 6.2 Differences` / `## 2.1 Primary Evidence` rows from worker outputs, the lead assigns a fresh `C-NNN` / `D-NNN` / `E-NNN` row ID. The `Source items` column (or, where the template still calls it `Supporting workers` / `Workers (position)` / `Source`, that same column) MUST list every contributing worker:item pair (e.g. `claude:F-001, codex:1.1, antigravity:F-3`) so a reviewer can trace the synthesised row back to each worker's original wording without re-reading every worker-results file. Bare worker names without item IDs (e.g. `claude, codex, antigravity`) are deprecated for these tables; the validator does not yet fail on them but the readability pass treats it as a contract violation.
|
|
77
77
|
- **Why this matters.** A real run had `claude=F-1..F-11`, `codex=1.1..1.8`, `antigravity=F-3..F-9` — three incompatible ID schemes. When the lead synthesised `C-1..C-8`, the link from `C-3` back to "which sentence in which worker file" was lost. Source-item preservation restores that link without forcing every worker to adopt a single ID prefix, which would over-constrain worker output style.
|
|
78
|
-
- Audit sidecar (shared): Reading Confirmation placement
|
|
78
|
+
- Audit sidecar (shared): Reading Confirmation placement follows the audience-selected preamble named by `**Worker Preamble Path:**`. Profiles do not restate it; the main worker-results body starts at section 1.
|
|
79
79
|
|
|
80
80
|
- Markdown authoring (shared — applies to markdown documents not already governed by an okstra template/schema):
|
|
81
81
|
- ad-hoc markdown documents should begin with an `Index` section. Template-governed artifacts such as final-reports, worker-results, and briefs follow their own schema first.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- read `Related Task Graph` before forming root-cause candidates. `depends-on`, `blocks` / `blocked-by`, parent/child, follow-up, and split edges define upstream/downstream boundaries for the symptom: identify whether this task's failure is caused by, blocks, or merely relates to another task before merging causes.
|
|
15
15
|
- any `intent-inference` augmentation that re-characterises the symptom (e.g. classifying a vague reporter phrase like "it sometimes doesn't work" as "intermittent failure on a specific code path") is a **hypothesis**, not a confirmed symptom. If `[CONFIRMED …]` appears on the matching `intent-check:` row, treat that confirmation as the symptom. Otherwise follow the precondition's `skipped` branch above and keep the inference labelled as a hypothesis in the root-cause analysis.
|
|
16
16
|
- `conversion-block:` rows mean the brief could not map a reporter statement to project vocabulary; never invent the missing mapping in this phase.
|
|
17
|
-
-
|
|
17
|
+
- Worker diagnosis procedure:
|
|
18
18
|
- **Symptom lock:** state the reporter's symptom verbatim, then translate it into one observable failure condition. If no observable condition can be derived from the brief, record that gap as the first blocker instead of guessing.
|
|
19
19
|
- **Reproduction status:** classify the run as `reproduced`, `not-reproduced`, or `blocked-before-repro`. Cite the command/log/file evidence used. If no command can be run safely in this phase, explain the read-only evidence path and the exact material needed next.
|
|
20
20
|
- **Falsifiable cause candidates:** every root-cause candidate must include supporting evidence, the strongest falsifying evidence checked, confidence, and the next diagnostic action that would disprove it. A candidate that cannot be falsified is too vague for this phase.
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
- Apply the shared reporter-confirmation precondition exactly as written. In this phase, unresolved `intent-check:` / `conversion-block:` rows carry `Blocks=approval`, so the approval frontmatter stays `approved: false` until they are resolved.
|
|
14
14
|
- never plan around an unconfirmed `intent-inference` augmentation as if it were a settled requirement. Treat the inference as settled ONLY when a `[CONFIRMED …]` marker sits on the matching `intent-check:` row after the precondition runs; absent the marker it stays a `Blocks=approval` clarification item per the precondition's `skipped` branch.
|
|
15
15
|
- `conversion-block:` rows are handled by the precondition; planning around an untranslated reporter phrase is forbidden until it is resolved.
|
|
16
|
+
- Worker planning procedure:
|
|
17
|
+
- identify requirement gaps and affected interfaces with file:line evidence, resolving codebase-answerable ambiguity before returning findings
|
|
18
|
+
- compare at least two feasible options unless the brief carries a confirmed decision; record concrete trade-offs and current-pattern evidence for every option
|
|
19
|
+
- propose stages with real dependency edges, validation signals, rollback order, and change-locality evidence; do not serialize independent work
|
|
20
|
+
- surface migration, deployment, cross-project, and approval risks without drafting final-report headings or schema rows
|
|
16
21
|
- Pre-planning context exploration (mandatory before option drafting):
|
|
17
22
|
- read the task brief, related-task briefs, and any cited spec / design doc end-to-end
|
|
18
23
|
- inspect the current state of every file the task names (or the closest matching files if names are stale) — record current responsibilities, public interfaces, and known coupling points
|
|
@@ -21,8 +21,13 @@
|
|
|
21
21
|
- per-candidate evidence (path:line) and scope mapping
|
|
22
22
|
- per-candidate severity / effort / recommended-next-phase
|
|
23
23
|
- convergence classification (full / partial / contested / worker-unique) across workers
|
|
24
|
+
- Worker candidate procedure:
|
|
25
|
+
- inspect every resolved priority lens inside the resolved scan scope; the assigned primary lens changes only the first pass, never total coverage
|
|
26
|
+
- for the primary pass, return an evidence-backed candidate or a no-candidate rationale citing the highest-signal path:line inspected
|
|
27
|
+
- for every candidate, record lens, scope, severity, effort, recommended next phase, evidence, and a worker-local source item ID
|
|
28
|
+
- identify overlap with other findings or linked tasks as duplicate, broader/narrower, conflicting, blocked-by, or follow-up instead of silently merging it
|
|
24
29
|
- Worker diversity rule:
|
|
25
|
-
- every analyser inspects every priority lens
|
|
30
|
+
- every analyser inspects every resolved priority lens. The Phase 1.5 grilling log assigns only the first pass: enumerate selected analyser worker instances in `requiredWorkerRoles` order and rotate them over resolved priority lenses in log order. Provider/model names never determine the position.
|
|
26
31
|
- each worker, before broadening to the remaining lenses, must do one of: (a) produce at least one candidate from its primary pass, or (b) record a no-candidate rationale citing the highest-signal path:line evidence it inspected.
|
|
27
32
|
- two workers' candidates are the same candidate only when they cite the same underlying design/code problem and the same remediation direction. Shared evidence paths alone are not enough to merge; keep distinct failure modes distinct.
|
|
28
33
|
- when a candidate from one worker overlaps another worker's evidence, convergence must classify the relationship as one of: duplicate, broader/narrower, or conflicting. Do not collapse contested candidates just to meet the candidate cap.
|
|
@@ -33,10 +38,15 @@
|
|
|
33
38
|
- For each open question Lead asks ONE `AskUserQuestion` with a `(Recommended)` answer drawn from a codebase-first inspection. Budget: at most 12 questions in this phase.
|
|
34
39
|
- Stop conditions (OR): all questions resolved / budget exhausted / user signals proceed.
|
|
35
40
|
- Lead persists the round at `<RUN_DIR>/state/phase-1.5-grilling.md` with one section per question (question / recommended / user answer) and a closing `Resolved scope` / `Resolved lenses` block. Worker prompts use this resolved block as the authoritative scope and lens definition.
|
|
41
|
+
- The same log includes `## Primary Pass Assignments` with a `Worker ID | Primary lens` table. It contains every selected analyser exactly once in `requiredWorkerRoles` order; the lead derives it from the resolved roster and lenses rather than provider catalog order.
|
|
36
42
|
- After writing the log and before Phase 4 dispatch, the lead injects its **absolute path** into every analyser prompt as the `**Phase 1.5 Grilling Log:** <absolute-path>` anchor header (see `templates/worker-prompt-preamble.md` §"Anchor headers"). This is the improvement-discovery counterpart to the `**Worktree:**` / `**Verification …:**` anchors that implementation / final-verification inject: workers read the log from this explicit path rather than re-deriving `<RUN_DIR>`. The path is byte-identical across all analysers, so it does not break the dispatch-prompt invariant.
|
|
37
43
|
- Decision-tree walk (bounded):
|
|
38
44
|
- When candidates branch on a structural question (e.g. "is module X meant to own this responsibility?"), resolve via `Read` / `Grep` first. Only escalate to the user inside the Phase 1.5 budget.
|
|
39
45
|
- Expected output emphasis:
|
|
46
|
+
- evidence-backed candidate or explicit no-candidate result for every resolved lens
|
|
47
|
+
- worker-local candidate IDs that convergence can trace back to their source worker
|
|
48
|
+
- uncertainty and overlap relationships kept explicit for downstream consensus classification
|
|
49
|
+
- Report assembly instructions:
|
|
40
50
|
- the `## 5.9 Improvement Candidates` table populated with rows that obey the 10-column schema from `validators/validate_improvement_report.py` (Cand ID `I-NNN`, Lens from whitelist, Title, Scope ⊆ scan-scope, Severity, Effort, Consensus, Source workers `<worker>:<id>` from {claude, codex, antigravity}, Recommended next-phase ∈ {requirements-discovery, implementation-planning, error-analysis}, Evidence as path:line list)
|
|
41
51
|
- `Consensus` cells in `## 5.9 Improvement Candidates` use the table enum exactly: `full`, `partial`, `contested`, `worker-unique`. Map convergence's `full-consensus` / `partial-consensus` labels to `full` / `partial` before writing the table.
|
|
42
52
|
- `## 7. Final Verdict` Verdict Token ∈ {`candidates-ready`, `no-candidates`, `blocked`}; Direction `routing`; Next Step "ask the user to select K candidates (see the ## 5.9 table)"
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
- before deciding whether to fan out, read `Related Task Graph`. Use `depends-on`, `blocks` / `blocked-by`, parent/child, follow-up, and split edges as seed ordering constraints. Do not flatten a directed graph into unrelated tasks.
|
|
15
15
|
- `intent-inference` augmentations whose paired `intent-check:` row carries `[CONFIRMED …]` are treated as **confirmed**; trust the confirmation text in `## Reporter Confirmations` over the original inference if they differ. Unconfirmed `intent-inference` rows under `reporter-confirmations: skipped` follow the precondition's `skipped` branch above.
|
|
16
16
|
- `conversion-block:` rows are explicit "translation failed" signals — never attempt to resolve them by inference here; the precondition above already handled them.
|
|
17
|
+
- Worker discovery procedure:
|
|
18
|
+
- classify the request and cite the evidence that determines both its work category and safest next phase
|
|
19
|
+
- identify independently startable decomposition candidates without publishing or rendering fan-out artifacts; preserve every directed dependency from `Related Task Graph`
|
|
20
|
+
- resolve codebase-answerable ambiguity by inspection and record file:line evidence; return only human-owned decisions as clarification candidates with the evidence already checked
|
|
21
|
+
- state the reporter's rejection criteria, missing routing inputs, and the evidence boundary behind each recommendation
|
|
17
22
|
- Primary focus areas:
|
|
18
23
|
- classify the work as bugfix, feature, improvement, refactor, or ops
|
|
19
24
|
- determine whether `error-analysis` or `implementation-planning` is the next safe step. Direct `implementation` handoff is never a valid routing target — implementation requires an approved `implementation-planning` report
|
|
@@ -49,7 +54,9 @@
|
|
|
49
54
|
- evidence-backed routing decision
|
|
50
55
|
- uncertainty boundaries and missing inputs
|
|
51
56
|
- next recommended phase and safe resume guidance
|
|
52
|
-
- canonical-term resolution for every `terminology:*` brief item
|
|
57
|
+
- canonical-term resolution for every `terminology:*` brief item as `<term> = <definition>`, plus whether `<PROJECT_ROOT>/.okstra/glossary.md` should be updated
|
|
58
|
+
- Report assembly instructions:
|
|
59
|
+
- write canonical-term resolutions in a new `Domain Alignment` subsection of the final report; actual glossary writes happen via `okstra-brief-gen` Step 4.5 on a subsequent run
|
|
53
60
|
- Clarification request policy (phase-specific addenda — shared policy is in `_common-contract.md`):
|
|
54
61
|
- if any blocking input is missing at the time of writing the final report, populate `## 1. Clarification Items` in `final-report-template.md` (a single unified table; `Blocks=next-phase` for items the next run cannot start without)
|
|
55
62
|
- prefer concrete questions whose answers map directly to a routing decision (`bugfix` vs `feature`, `error-analysis` vs `implementation-planning`, etc.). State each option in plain language with one sentence describing what choosing it would mean for the next phase.
|
|
@@ -24,29 +24,23 @@ PROFILE_SECTIONS = (
|
|
|
24
24
|
"Primary focus areas",
|
|
25
25
|
"Expected output emphasis",
|
|
26
26
|
"Non-goals",
|
|
27
|
-
"Clarification request policy",
|
|
28
27
|
)
|
|
29
|
-
|
|
28
|
+
WORKER_PROFILE_SECTIONS_BY_TASK_TYPE = {
|
|
30
29
|
"requirements-discovery": (
|
|
31
|
-
"
|
|
32
|
-
"Decision-tree walk",
|
|
30
|
+
"Worker discovery procedure",
|
|
33
31
|
),
|
|
34
32
|
"error-analysis": (
|
|
35
|
-
"
|
|
33
|
+
"Worker diagnosis procedure",
|
|
36
34
|
),
|
|
37
35
|
"implementation-planning": (
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"No-placeholder rule",
|
|
41
|
-
"Self-review pass before finalising the report",
|
|
36
|
+
"Worker planning procedure",
|
|
37
|
+
"Design principles applied when scoring options",
|
|
42
38
|
),
|
|
43
39
|
"final-verification": (
|
|
44
40
|
"Worker verification procedure",
|
|
45
41
|
),
|
|
46
42
|
"improvement-discovery": (
|
|
47
|
-
"
|
|
48
|
-
"Worker diversity rule",
|
|
49
|
-
"Decision-tree walk",
|
|
43
|
+
"Worker candidate procedure",
|
|
50
44
|
),
|
|
51
45
|
}
|
|
52
46
|
CLARIFICATION_SECTIONS = (
|
|
@@ -156,7 +150,7 @@ def _brief_block(brief_text: str) -> list[str]:
|
|
|
156
150
|
def _profile_block(task_type: str, profile_text: str) -> list[str]:
|
|
157
151
|
section_names = (
|
|
158
152
|
PROFILE_SECTIONS
|
|
159
|
-
+
|
|
153
|
+
+ WORKER_PROFILE_SECTIONS_BY_TASK_TYPE.get(task_type, ())
|
|
160
154
|
)
|
|
161
155
|
profile_sections = _extract_sections(profile_text, section_names)
|
|
162
156
|
profile_bullets = _extract_bullet_sections(profile_text, section_names)
|
|
@@ -27,7 +27,18 @@ from .path_hints import hydrate_active_run_context
|
|
|
27
27
|
from .wrapper_status import status_path_for_prompt
|
|
28
28
|
from .paths import task_dir
|
|
29
29
|
from .worker_prompt_headers import WorkerPromptHeaderError, worker_prompt_headers
|
|
30
|
-
from .worker_prompt_contract import
|
|
30
|
+
from .worker_prompt_contract import PromptRecord, validate_initial_prompt_records
|
|
31
|
+
from .worker_prompt_body import (
|
|
32
|
+
analysis_input_lines as _analysis_input_lines,
|
|
33
|
+
analysis_prompt_body as _analysis_prompt_body,
|
|
34
|
+
existing_input_lines as _existing_input_lines,
|
|
35
|
+
instruction_path as _instruction_path,
|
|
36
|
+
mcp_pointer_line as _mcp_pointer_line,
|
|
37
|
+
)
|
|
38
|
+
from .worker_prompt_policy import (
|
|
39
|
+
PromptPlan,
|
|
40
|
+
resolve_prompt_plan_for_manifest,
|
|
41
|
+
)
|
|
31
42
|
|
|
32
43
|
|
|
33
44
|
SUPPORTED_CLI_WORKERS = {
|
|
@@ -39,10 +50,6 @@ BACKEND_MIXED = "mixed"
|
|
|
39
50
|
MAX_WORKER_ATTEMPTS = 2
|
|
40
51
|
REPORT_WRITER_WORKER_ID = "report-writer"
|
|
41
52
|
REPORT_LANGUAGE_VALUES = {"en", "ko", "auto"}
|
|
42
|
-
ANALYSIS_WORKER_LABELS = {
|
|
43
|
-
"codex": "Codex worker",
|
|
44
|
-
"antigravity": "Antigravity worker",
|
|
45
|
-
}
|
|
46
53
|
|
|
47
54
|
|
|
48
55
|
class DispatchError(Exception):
|
|
@@ -183,7 +190,7 @@ def build_dispatch_plan(
|
|
|
183
190
|
)
|
|
184
191
|
for worker_id in selected_workers
|
|
185
192
|
)
|
|
186
|
-
|
|
193
|
+
_validate_initial_prompts(manifest, workers)
|
|
187
194
|
return DispatchPlan(
|
|
188
195
|
project_root=project_root,
|
|
189
196
|
workspace_root=workspace_root,
|
|
@@ -195,21 +202,18 @@ def build_dispatch_plan(
|
|
|
195
202
|
)
|
|
196
203
|
|
|
197
204
|
|
|
198
|
-
def
|
|
205
|
+
def _validate_initial_prompts(
|
|
199
206
|
manifest: Mapping[str, Any],
|
|
200
207
|
workers: Sequence[WorkerDispatch],
|
|
201
208
|
) -> None:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
prompt_paths = {
|
|
205
|
-
worker.worker_id: worker.prompt_path
|
|
209
|
+
records = [
|
|
210
|
+
PromptRecord(worker.worker_id, "initial", worker.prompt_path)
|
|
206
211
|
for worker in workers
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
errors = validate_final_verification_prompt_paths(prompt_paths)
|
|
212
|
+
]
|
|
213
|
+
errors = validate_initial_prompt_records(manifest=manifest, records=records)
|
|
211
214
|
if errors:
|
|
212
|
-
|
|
215
|
+
task_type = _require_string(manifest, "taskType")
|
|
216
|
+
raise DispatchError(f"{task_type} prompt contract: " + "; ".join(errors))
|
|
213
217
|
|
|
214
218
|
|
|
215
219
|
def dispatch_plan(plan: DispatchPlan) -> int:
|
|
@@ -460,6 +464,7 @@ def _render_missing_analysis_worker_prompt(
|
|
|
460
464
|
prompt_rel = _worker_prompt_path(manifest, worker_id)
|
|
461
465
|
model = _require_string(worker_state, "modelExecutionValue")
|
|
462
466
|
role = _analysis_pane_role(manifest, active_context, worker_id)
|
|
467
|
+
plan = _initial_prompt_plan(manifest, worker_id)
|
|
463
468
|
lines = _base_prompt_headers(
|
|
464
469
|
project_root=project_root,
|
|
465
470
|
manifest=manifest,
|
|
@@ -469,7 +474,16 @@ def _render_missing_analysis_worker_prompt(
|
|
|
469
474
|
result_rel=result_rel,
|
|
470
475
|
)
|
|
471
476
|
lines.extend(_worktree_headers(manifest, active_context, role))
|
|
472
|
-
lines.extend(
|
|
477
|
+
lines.extend(
|
|
478
|
+
_analysis_prompt_body(
|
|
479
|
+
manifest,
|
|
480
|
+
active_context,
|
|
481
|
+
worker_id,
|
|
482
|
+
model,
|
|
483
|
+
role,
|
|
484
|
+
plan,
|
|
485
|
+
)
|
|
486
|
+
)
|
|
473
487
|
executor_tail = _implementation_executor_tail(
|
|
474
488
|
manifest,
|
|
475
489
|
active_context,
|
|
@@ -614,43 +628,6 @@ def _build_report_writer_dispatch(
|
|
|
614
628
|
)
|
|
615
629
|
|
|
616
630
|
|
|
617
|
-
def _analysis_prompt_body(
|
|
618
|
-
manifest: Mapping[str, Any],
|
|
619
|
-
active_context: Mapping[str, Any],
|
|
620
|
-
worker_id: str,
|
|
621
|
-
model: str,
|
|
622
|
-
role: str,
|
|
623
|
-
) -> list[str]:
|
|
624
|
-
label = ANALYSIS_WORKER_LABELS[worker_id]
|
|
625
|
-
return [
|
|
626
|
-
f"**Model:** {label}, {model}",
|
|
627
|
-
f"**Pane role:** {role}",
|
|
628
|
-
"",
|
|
629
|
-
f"# {label} Dispatch",
|
|
630
|
-
"",
|
|
631
|
-
"## Role",
|
|
632
|
-
(
|
|
633
|
-
f"You are the {label} for okstra cross-verification. "
|
|
634
|
-
"Produce an independent worker result."
|
|
635
|
-
),
|
|
636
|
-
"",
|
|
637
|
-
"## Task",
|
|
638
|
-
f"- Task key: `{_require_string(manifest, 'taskKey')}`",
|
|
639
|
-
f"- Task type: `{_require_string(manifest, 'taskType')}`",
|
|
640
|
-
"",
|
|
641
|
-
"## Inputs",
|
|
642
|
-
*_analysis_input_lines(manifest, active_context),
|
|
643
|
-
"",
|
|
644
|
-
_mcp_pointer_line(),
|
|
645
|
-
"",
|
|
646
|
-
"## Output Contract",
|
|
647
|
-
"- Read the Worker Preamble Path end-to-end before analysis.",
|
|
648
|
-
"- Write the worker result to Result Path and no other canonical result path.",
|
|
649
|
-
"- Write the audit sidecar and any tool-failure entries as the preamble requires.",
|
|
650
|
-
"- Cite evidence with file paths and line numbers whenever you make a claim.",
|
|
651
|
-
]
|
|
652
|
-
|
|
653
|
-
|
|
654
631
|
def _report_writer_prompt_body(
|
|
655
632
|
manifest: Mapping[str, Any],
|
|
656
633
|
active_context: Mapping[str, Any],
|
|
@@ -688,13 +665,6 @@ def _report_writer_prompt_body(
|
|
|
688
665
|
]
|
|
689
666
|
|
|
690
667
|
|
|
691
|
-
def _mcp_pointer_line() -> str:
|
|
692
|
-
return (
|
|
693
|
-
'**MCP servers:** follow the task brief\'s "## Available MCP Servers" '
|
|
694
|
-
"section (already in your Required reading)."
|
|
695
|
-
)
|
|
696
|
-
|
|
697
|
-
|
|
698
668
|
def _base_prompt_headers(
|
|
699
669
|
*,
|
|
700
670
|
project_root: Path,
|
|
@@ -710,6 +680,7 @@ def _base_prompt_headers(
|
|
|
710
680
|
prompt_rel=prompt_rel,
|
|
711
681
|
result_rel=result_rel,
|
|
712
682
|
worker_id=worker_id,
|
|
683
|
+
dispatch_kind="initial",
|
|
713
684
|
manifest=manifest,
|
|
714
685
|
active_context=active_context,
|
|
715
686
|
)
|
|
@@ -717,26 +688,18 @@ def _base_prompt_headers(
|
|
|
717
688
|
raise DispatchError(str(exc)) from exc
|
|
718
689
|
|
|
719
690
|
|
|
720
|
-
def
|
|
691
|
+
def _initial_prompt_plan(
|
|
721
692
|
manifest: Mapping[str, Any],
|
|
722
|
-
|
|
723
|
-
) ->
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
manifest,
|
|
727
|
-
|
|
728
|
-
"
|
|
693
|
+
worker_id: str,
|
|
694
|
+
) -> PromptPlan:
|
|
695
|
+
try:
|
|
696
|
+
return resolve_prompt_plan_for_manifest(
|
|
697
|
+
manifest=manifest,
|
|
698
|
+
worker_id=worker_id,
|
|
699
|
+
dispatch_kind="initial",
|
|
729
700
|
)
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
("Primary analysis packet", _instruction_path(manifest, active_context, "analysisPacketPath")),
|
|
733
|
-
("Task brief", _instruction_path(manifest, active_context, "taskBriefPath")),
|
|
734
|
-
("Analysis profile", _instruction_path(manifest, active_context, "analysisProfilePath")),
|
|
735
|
-
("Analysis material", _instruction_path(manifest, active_context, "analysisMaterialPath")),
|
|
736
|
-
("Reference expectations", _instruction_path(manifest, active_context, "referenceExpectationsPath")),
|
|
737
|
-
("Clarification response", _instruction_path(manifest, active_context, "clarificationResponsePath")),
|
|
738
|
-
]
|
|
739
|
-
return _existing_input_lines(inputs)
|
|
701
|
+
except ValueError as exc:
|
|
702
|
+
raise DispatchError(str(exc)) from exc
|
|
740
703
|
|
|
741
704
|
|
|
742
705
|
def _report_writer_input_lines(
|
|
@@ -759,11 +722,6 @@ def _report_writer_input_lines(
|
|
|
759
722
|
return lines or ["- No report-writer inputs were recorded in active-run-context."]
|
|
760
723
|
|
|
761
724
|
|
|
762
|
-
def _existing_input_lines(inputs: Sequence[tuple[str, str]]) -> list[str]:
|
|
763
|
-
lines = [f"- {label}: `{path}`" for label, path in inputs if path]
|
|
764
|
-
return lines or ["- No input paths were recorded in active-run-context."]
|
|
765
|
-
|
|
766
|
-
|
|
767
725
|
def _analysis_worker_result_lines(team_state: Mapping[str, Any]) -> list[str]:
|
|
768
726
|
lines = []
|
|
769
727
|
workers = team_state.get("workers")
|
|
@@ -781,19 +739,6 @@ def _analysis_worker_result_lines(team_state: Mapping[str, Any]) -> list[str]:
|
|
|
781
739
|
return lines
|
|
782
740
|
|
|
783
741
|
|
|
784
|
-
def _instruction_path(
|
|
785
|
-
manifest: Mapping[str, Any],
|
|
786
|
-
active_context: Mapping[str, Any],
|
|
787
|
-
key: str,
|
|
788
|
-
) -> str:
|
|
789
|
-
instruction_set = active_context.get("instructionSet")
|
|
790
|
-
if isinstance(instruction_set, Mapping):
|
|
791
|
-
value = _string_value(instruction_set.get(key))
|
|
792
|
-
if value:
|
|
793
|
-
return value
|
|
794
|
-
return _string_value(manifest.get(key))
|
|
795
|
-
|
|
796
|
-
|
|
797
742
|
def _analysis_pane_role(
|
|
798
743
|
manifest: Mapping[str, Any],
|
|
799
744
|
active_context: Mapping[str, Any],
|