okstra 0.111.0 → 0.113.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.kr.md +2 -2
- package/README.md +2 -2
- package/bin/okstra +7 -1
- package/docs/for-ai/README.md +2 -2
- package/docs/for-ai/skills/okstra-brief.md +2 -3
- package/docs/for-ai/skills/okstra-container-build.md +2 -3
- package/docs/for-ai/skills/okstra-inspect.md +5 -12
- package/docs/for-ai/skills/okstra-rollup.md +3 -4
- package/docs/for-ai/skills/okstra-run.md +3 -5
- package/docs/for-ai/skills/okstra-schedule.md +2 -7
- package/docs/for-ai/skills/okstra-setup.md +1 -1
- package/docs/kr/architecture/storage-model.md +1 -2
- package/docs/kr/architecture.md +2 -2
- package/docs/kr/cli.md +4 -2
- package/docs/project-structure-overview.md +5 -3
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +1 -1
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/agents/workers/codex-worker.md +1 -1
- package/runtime/prompts/coding-preflight/overview.md +3 -1
- package/runtime/prompts/launch.template.md +1 -5
- package/runtime/prompts/lead/context-loader.md +2 -4
- package/runtime/prompts/lead/convergence.md +11 -240
- package/runtime/prompts/lead/okstra-lead-contract.md +70 -34
- package/runtime/prompts/lead/plan-body-verification.md +240 -0
- package/runtime/prompts/lead/report-writer.md +7 -7
- package/runtime/prompts/lead/team-contract.md +15 -17
- package/runtime/prompts/profiles/_common-contract.md +1 -37
- package/runtime/prompts/profiles/_implementation-executor.md +2 -2
- package/runtime/prompts/profiles/_implementation-self-check.md +1 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- package/runtime/prompts/profiles/final-verification.md +6 -5
- package/runtime/prompts/profiles/implementation-planning.md +6 -4
- package/runtime/prompts/profiles/implementation.md +1 -1
- package/runtime/prompts/profiles/improvement-discovery.md +1 -0
- package/runtime/prompts/profiles/requirements-discovery.md +1 -0
- package/runtime/python/okstra_ctl/path_hints.py +1 -0
- package/runtime/python/okstra_ctl/paths.py +3 -0
- package/runtime/python/okstra_ctl/render.py +8 -0
- package/runtime/python/okstra_ctl/set_work_status.py +147 -0
- package/runtime/python/okstra_ctl/team_reconcile.py +1 -1
- package/runtime/schemas/final-report-v1.0.schema.json +6 -4
- package/runtime/skills/okstra-brief/SKILL.md +32 -176
- package/runtime/skills/okstra-brief/references/reporter-confirmations.md +71 -0
- package/runtime/skills/okstra-brief/references/tracker-recursion.md +90 -0
- package/runtime/skills/okstra-container-build/SKILL.md +7 -20
- package/runtime/skills/okstra-graphify/SKILL.md +8 -8
- package/runtime/skills/okstra-inspect/SKILL.md +27 -43
- package/runtime/skills/okstra-rollup/SKILL.md +6 -6
- package/runtime/skills/okstra-run/SKILL.md +7 -16
- package/runtime/skills/okstra-schedule/SKILL.md +64 -419
- package/runtime/skills/okstra-setup/SKILL.md +25 -223
- package/runtime/skills/okstra-setup/references/project-config.md +188 -0
- package/runtime/templates/reports/final-report.template.md +24 -1
- package/runtime/templates/reports/improvement-discovery-input.template.md +3 -2
- package/runtime/templates/reports/schedule.template.md +2 -2
- package/runtime/templates/worker-prompt-preamble.md +5 -1
- package/runtime/validators/validate-run.py +8 -7
- package/src/cli-registry.mjs +14 -0
- package/src/commands/inspect/set-work-status.mjs +31 -0
- package/src/commands/lifecycle/check-project.mjs +68 -56
- package/src/commands/lifecycle/install.mjs +1 -0
- package/src/commands/lifecycle/preflight.mjs +82 -0
|
@@ -41,11 +41,11 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
|
|
|
41
41
|
| Setting | Default | Description |
|
|
42
42
|
|------|--------|------|
|
|
43
43
|
| `enabled` | `true` | If `false`, skip the convergence loop and use the existing consensus/divergence method |
|
|
44
|
-
| `maxRounds` | phase-aware: `1` for `requirements-discovery`, `2` otherwise (range 1–3) | Maximum number of re-verification rounds. Discovery's routing/missing-input outputs gain little from a second round; other phases (especially `error-analysis`) keep `2`. Lead resolves the effective value when the manifest omits the key and records it in `config.
|
|
44
|
+
| `maxRounds` | phase-aware: `1` for `requirements-discovery`, `2` otherwise (range 1–3) | Maximum number of re-verification rounds. Discovery's routing/missing-input outputs gain little from a second round; other phases (especially `error-analysis`) keep `2`. Lead resolves the effective value when the manifest omits the key and records it in `config.effectiveMaxRounds` of the convergence state artifact. |
|
|
45
45
|
| `verificationMode` | `"lightweight"` | `"lightweight"` or `"full-reanalysis"` |
|
|
46
46
|
| `adversarial` | phase-aware: `true` for `requirements-discovery` / `error-analysis` / `implementation-planning`, `false` otherwise | When `true`, Phase 5.5 runs in **adversarial mode** (see §"Adversarial Verification Mode"): verifiers actively try to refute each finding, the burden of proof sits on the claim, and `verificationMode` is forced to `"full-reanalysis"` scoped to the finding's cited evidence. Resolved by `scripts/okstra_ctl/render.py` `_build_convergence_block` and recorded in `config.adversarial` of the convergence state artifact. |
|
|
47
47
|
|
|
48
|
-
**Auto-disable rule (BLOCKING).** Convergence requires ≥2 analyser workers to produce a meaningful consensus tally. When the active profile's `Required workers:` block (see `prompts/profiles/*.md`) resolves to fewer than 2 analyser workers — e.g. `release-handoff` (zero analyser workers, lead-only) — the lead MUST treat `convergence.enabled` as `false` for that run regardless of manifest configuration, skip Phases 5.5 and the plan-body verification round, and record `finalState: "converged"` with `totalRounds: 0` and an explanatory note in `config` (e.g. `"autoDisabled": "fewer-than-two-analysers"`). The plan-body round inherits the same rule via its `gating=false` advisory path.
|
|
48
|
+
**Auto-disable rule (BLOCKING).** Convergence requires ≥2 analyser workers to produce a meaningful consensus tally. When the active profile's `Required workers:` block (see `prompts/profiles/*.md`) resolves to fewer than 2 analyser workers — e.g. `release-handoff` (zero analyser workers, lead-only) — the lead MUST treat `convergence.enabled` as `false` for that run regardless of manifest configuration, skip Phases 5.5 and the plan-body verification round ([plan-body-verification](./plan-body-verification.md)), and record `finalState: "converged"` with `totalRounds: 0` and an explanatory note in `config` (e.g. `"autoDisabled": "fewer-than-two-analysers"`). The plan-body round inherits the same rule via its `gating=false` advisory path.
|
|
49
49
|
|
|
50
50
|
## Finding Category
|
|
51
51
|
|
|
@@ -58,7 +58,7 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
|
|
|
58
58
|
|
|
59
59
|
## Convergence Algorithm
|
|
60
60
|
|
|
61
|
-
**Majority definition (BLOCKING).** "Majority" means *strictly greater than half* of the non-error votes for that finding (`verification-error` votes are excluded from both numerator and denominator). Ties — including the 1-AGREE / 1-DISAGREE case in a two-analyser roster — are NOT a majority: in intermediate rounds the finding is **carried forward**; in the final executed round the finding is classified `contested`. This rule applies identically to the plan-body verification round (
|
|
61
|
+
**Majority definition (BLOCKING).** "Majority" means *strictly greater than half* of the non-error votes for that finding (`verification-error` votes are excluded from both numerator and denominator). Ties — including the 1-AGREE / 1-DISAGREE case in a two-analyser roster — are NOT a majority: in intermediate rounds the finding is **carried forward**; in the final executed round the finding is classified `contested`. This rule applies identically to the plan-body verification round ([plan-body-verification](./plan-body-verification.md)) where the same verdict tokens are reused.
|
|
62
62
|
|
|
63
63
|
### Round 0: Parse worker results
|
|
64
64
|
|
|
@@ -196,7 +196,7 @@ Active only when `config.adversarial == true` (default for `requirements-discove
|
|
|
196
196
|
|
|
197
197
|
In adversarial mode the verifier's job inverts: instead of confirming a peer's finding, the verifier **tries to break it**, and the burden of proof sits on the claim — a finding survives only if refutation attempts fail.
|
|
198
198
|
|
|
199
|
-
### Scoped full-reanalysis
|
|
199
|
+
### Scoped full-reanalysis
|
|
200
200
|
|
|
201
201
|
Adversarial mode forces `verificationMode = "full-reanalysis"`, but the re-analysis is **scoped to the evidence the finding under attack cites** (the file paths / line ranges / log lines in its `originEvidence`), plus the immediately surrounding context. The verifier MUST NOT re-read the whole task brief, instruction-set, or `final-report-template.md`. This keeps the documented "single largest avoidable cost in requirements-discovery, error-analysis, and implementation-planning" (see §"Reverify prompt: required-reading suppression") bounded while making the refutation real rather than a text-only argument.
|
|
202
202
|
|
|
@@ -258,19 +258,19 @@ Agent(
|
|
|
258
258
|
prompt: "<re-verification prompt with findings batch>",
|
|
259
259
|
name: "<role-slug>-reverify-r<N>",
|
|
260
260
|
subagent_type: "<same as initial execution>",
|
|
261
|
-
run_in_background: true, # no team_name —
|
|
261
|
+
run_in_background: true, # no team_name — implicit team; see team-contract Operating Rule 0
|
|
262
262
|
model: "<same as initial execution>",
|
|
263
263
|
mode: "auto"
|
|
264
264
|
)
|
|
265
265
|
```
|
|
266
266
|
|
|
267
|
-
- Claude / in-process worker: spawn with `Agent(name: ..., run_in_background: true)`
|
|
267
|
+
- Claude / in-process worker: spawn with `Agent(name: ..., run_in_background: true)` per [team-contract](./team-contract.md) rule 0 (split-pane when `$TMUX` is set)
|
|
268
268
|
- CLI (Codex / Antigravity) worker: inject `**Pane role:** worker-reverify-r<N>` (the Agent `name` minus the `<cli>-` prefix) into the reverify prompt body so the tmux trace pane reads `<cli>-worker-reverify-r<N>` per [okstra-lead-contract](./okstra-lead-contract.md) "Agent `name` on dispatch".
|
|
269
269
|
|
|
270
270
|
For `tmux-pane` backend runs, do not use the Agent snippet. For each reverify round, write a jobs file at `runs/<task-type>/state/reverify-jobs-r<N>-<task-type>-<seq>.json` with `dispatchKind: "reverify-r<N>"` and worker entries containing `workerId`, `provider`, `role` (set to `worker-reverify-r<N>` for the pane title), `modelExecutionValue`, `promptPath`, `resultPath`, `workerResultPath`, and `completionPaths`; then run `okstra team dispatch --project-root <dir> --run-manifest <path> --dispatch-kind reverify-r<N> --jobs-file <jobs-file>` followed by `okstra team await --project-root <dir> --run-manifest <path>`.
|
|
271
271
|
|
|
272
272
|
|
|
273
|
-
**Completion detection per round (BLOCKING).**
|
|
273
|
+
**Completion detection per round (BLOCKING).** The spawn acks are NOT completion — detect each round's completion via the SSOT protocol in [team-contract](./team-contract.md) "Worker-completion detection (self-scheduled polling)", with the pending set reconstructed from that round's dispatched workers' Result Paths. Do NOT end the turn with a prose "waiting" statement.
|
|
274
274
|
|
|
275
275
|
### Required reverify-prompt anchor headers (BLOCKING)
|
|
276
276
|
|
|
@@ -290,18 +290,18 @@ Assigned worker prompt history path: <Project Root>/<Prompt History Path>
|
|
|
290
290
|
2. `team-state-<task-type>-<seq>.json` → `workers[].usage.cliModel` for that role (initial run's actual execution value)
|
|
291
291
|
3. The `**Model:**` line of the initial Phase 4 prompt for that role (read from its persisted prompt-history file)
|
|
292
292
|
|
|
293
|
-
If none of the three is available, **abort the reverify dispatch for that role** and record a `contract-violation` event via `okstra error-log append-observed`. Do NOT guess, do NOT fall back to training-data defaults — for codex this
|
|
293
|
+
If none of the three is available, **abort the reverify dispatch for that role** and record a `contract-violation` event via `okstra error-log append-observed`. Do NOT guess, do NOT fall back to training-data defaults — for codex this silently produces `o4-mini` instead of the assigned `gpt-5.5`-class model.
|
|
294
294
|
|
|
295
295
|
For Codex/Antigravity wrapper subagents, the `**Model:** <role>, <modelExecutionValue>` line is what their wrapper extracts to pass into the underlying CLI's `--model` flag. Omitting it forces the wrapper to fall back to its own training-data knowledge of the CLI's historical default.
|
|
296
296
|
|
|
297
|
-
### Reverify prompt: required-reading suppression
|
|
297
|
+
### Reverify prompt: required-reading suppression
|
|
298
298
|
|
|
299
299
|
Reverify prompts MUST NOT inject the Phase 2 `[Required reading]` clause:
|
|
300
300
|
|
|
301
301
|
- **Lightweight mode**: the clause directly contradicts the "Do NOT re-analyze the original source materials" instruction below. Including it forces workers to re-read the entire instruction-set per round per worker (3 workers × 2 rounds × 5+ files in the worst case) for no quality gain.
|
|
302
302
|
- **Full-reanalysis mode**: workers DO need to re-read source materials, but only the analysis-worker file list (no `final-report-template.md`). If lead chooses to inject a reading clause here, it MUST mirror the audience-scoped enumeration in [okstra-lead-contract](./okstra-lead-contract.md) Phase 2 (no template).
|
|
303
303
|
|
|
304
|
-
This is the single largest avoidable cost in `requirements-discovery`, `error-analysis`, and `implementation-planning` runs. Treat as
|
|
304
|
+
This is the single largest avoidable cost in `requirements-discovery`, `error-analysis`, and `implementation-planning` runs. Treat as mandatory.
|
|
305
305
|
|
|
306
306
|
### Lightweight Re-verification Prompt
|
|
307
307
|
|
|
@@ -591,233 +591,4 @@ If `convergence.enabled: false`, this contract is skipped. Phase 6 operates usin
|
|
|
591
591
|
|
|
592
592
|
## Plan-body verification mode (implementation-planning only)
|
|
593
593
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
### Lifecycle position (BLOCKING)
|
|
597
|
-
|
|
598
|
-
Plan-body verification runs **after** finding convergence and **after** the report-writer draft is written. Sequence inside a single implementation-planning run:
|
|
599
|
-
|
|
600
|
-
```
|
|
601
|
-
Phase 4 workers produce independent analyses (Findings F-001…)
|
|
602
|
-
→ Phase 5.5 FINDING convergence (this contract, sections "Convergence Algorithm" through "Convergence State Artifact")
|
|
603
|
-
→ Phase 6 report-writer authors final-report draft (consolidated Option Candidates / Stepwise Execution Order / Dependency / Validation Checklist / Rollback)
|
|
604
|
-
→ PLAN-BODY VERIFICATION ROUND ← new — described below
|
|
605
|
-
→ User Approval gate (top-of-report `- [ ] Approved` marker is rendered only when this round's Gate result is `passed` or `passed-with-dissent`)
|
|
606
|
-
→ implementation phase (separate run)
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
Plan-body verification MUST NOT replace, precede, or be conflated with the Phase 5.5 finding convergence above. They are two distinct rounds with different inputs (findings vs. consolidated plan body), different ID schemes (`F-*` vs. `P-*`), and different state files.
|
|
610
|
-
|
|
611
|
-
### MUTUAL EXCLUSION (BLOCKING)
|
|
612
|
-
|
|
613
|
-
The finding queue (Phase 5.5) and the plan-item queue (this section) are **disjoint**:
|
|
614
|
-
|
|
615
|
-
- A finding-convergence reverify prompt MUST NOT contain any `P-*` item.
|
|
616
|
-
- A plan-body verification prompt MUST NOT contain any `F-*` finding.
|
|
617
|
-
- The two rounds write to **different state files**: `runs/<task-type>/state/convergence-<task-type>-<seq>.json` (findings, see §"Convergence State Artifact") vs. `runs/<task-type>/state/plan-body-verification-<task-type>-<seq>.json` (plan items, see §"`plan-body-verification.json` schema").
|
|
618
|
-
- Aggregation logic (verdict counting, classification) MUST NOT carry votes from one queue into the other.
|
|
619
|
-
|
|
620
|
-
Mixing the two queues — for example, parsing a Phase 6 draft's Stepwise Execution Order step as if it were an `F-*` finding — is a contract violation. Future Claude reading this contract: if you find yourself tempted to "just reuse the finding queue for plan items, they're similar enough", stop. They are not similar enough; the verdict semantics differ (see §"Plan-body verdict semantics" below).
|
|
621
|
-
|
|
622
|
-
### Configuration
|
|
623
|
-
|
|
624
|
-
Plan-body verification is configured under `convergence.planBodyVerification` in `task-manifest.json`:
|
|
625
|
-
|
|
626
|
-
| Setting | Default | Description |
|
|
627
|
-
|---------|---------|-------------|
|
|
628
|
-
| `enabled` | `true` | If `false`, the round is skipped and the top-of-report Approval marker is rendered unconditionally (legacy behaviour). |
|
|
629
|
-
| `maxRounds` | `1` | Upper bound. Plan-body verification is consistency / completeness checking, not fact checking — additional rounds rarely help. Range 1–3. |
|
|
630
|
-
| `gating` | `true` | If `true` (default), `majority-disagree` blocks the Approval marker. If `false`, the round is advisory-only and the marker always renders. |
|
|
631
|
-
|
|
632
|
-
Default values are emitted into the manifest by `scripts/okstra_ctl/render.py` (`_build_convergence_block`). The ctx knob `OKSTRA_PLAN_VERIFICATION=false` flips `planBodyVerification.enabled` to false.
|
|
633
|
-
|
|
634
|
-
### Plan-item extraction (Round 0 equivalent)
|
|
635
|
-
|
|
636
|
-
From the report-writer's draft of `## 5.4 Implementation Plan Deliverables`, lead extracts plan items with the following prefixes (see also `templates/reports/final-report.template.md` §5.5.9):
|
|
637
|
-
|
|
638
|
-
| Prefix | Source sub-section | One row per |
|
|
639
|
-
|--------|--------------------|-------------|
|
|
640
|
-
| `P-Opt-<N>` | `4.5.1 Option Candidates` | one Option (its File Structure list + interfaces + blast radius) |
|
|
641
|
-
| `P-Step-<N>` | `4.5.4 Stepwise Execution Order` | one step (path + command + success signal) |
|
|
642
|
-
| `P-Dep-<N>` | `4.5.5 Dependency / Migration Risk` | one dependency row |
|
|
643
|
-
| `P-Val-<N>` | `4.5.6 Validation Checklist` | one checklist item |
|
|
644
|
-
| `P-Rb-<N>` | `4.5.7 Rollback Strategy` | one rollback path |
|
|
645
|
-
| `P-Req-<N>` | `4.5.8 Requirement Coverage` | one requirement coverage row |
|
|
646
|
-
|
|
647
|
-
`4.5.2 Trade-off Matrix` and `4.5.3 Recommended Option` are NOT extracted as standalone plan items — the trade-off matrix is evaluated implicitly through each option's `P-Opt-*` verification, and the recommended option is one of those `P-Opt-*` rows.
|
|
648
|
-
|
|
649
|
-
Each plan item inherits the `[TICKETID: ...]` tag of its source section (per the standard ticket-tagging contract).
|
|
650
|
-
|
|
651
|
-
When extracting each item, lead also captures a **`subject`** — a plain one-line label (≤12 words) describing *what that item is* in the reader's terms, e.g. `P-Opt-1` → "Option A: upload v2 를 신규 모듈로 분리", `P-Step-1.1` → "Stage 1 Step 2: `npm run test:v2` 로 회귀 검증". This is a label-capture, not new analysis. The `subject` is what §5.5.9 renders as the per-item heading so the reader knows *what* each AGREE/DISAGREE is about without cross-referencing §4.5; a bare `P-*` ID with no subject is a contract violation. **Enforced:** `validators/validate-run.py` `_validate_plan_item_subject_substance` fails a subject that is a placeholder — under 3 chars, equal to the item id, or shaped like a bare `P-*` id.
|
|
652
|
-
|
|
653
|
-
### Plan-body verdict semantics
|
|
654
|
-
|
|
655
|
-
The verdict tokens `AGREE` / `DISAGREE` / `SUPPLEMENT` are reused, but their meaning is plan-specific:
|
|
656
|
-
|
|
657
|
-
- **AGREE**: the item is executable as written *and* internally consistent with other items in the plan.
|
|
658
|
-
- **DISAGREE(<kind>)**: the item is broken. `<kind>` MUST be one of:
|
|
659
|
-
- `a` — a concrete referenced file path / symbol **contradicts** a different concrete path / symbol for the same artifact in another step or option's File Structure list (a genuine mismatch between two spelled-out references). An abbreviated / ellipsis (`…`) / under-specified path is NOT kind `a` — it contradicts nothing, it is merely imprecise notation; classify it as `b`.
|
|
660
|
-
- `b` — a command **or a referenced path** is not executable or is ambiguous — including an abbreviated, ellipsis, or under-specified path that does not resolve as written
|
|
661
|
-
- `c` — validation signal is not observable
|
|
662
|
-
- `d` — rollback violates commit / dependency order
|
|
663
|
-
- `e` — item contradicts the trade-off matrix
|
|
664
|
-
- `f` — requirement coverage row cites no concrete option / stage / step, cites a non-existent option / stage / step, or marks a requirement `covered` while the cited plan item does not satisfy the row's stated requirement. A row that cites an existing option / stage / step is concrete for this purpose even if that option's File Structure paths are abbreviated — path imprecision inside the cited option is kind `b` on that option's own item, not `f` on the coverage row.
|
|
665
|
-
- **SUPPLEMENT**: the item is sound but is missing a dependency / edge case / precondition.
|
|
666
|
-
|
|
667
|
-
Worker non-result handling (`timeout`, `error`, no result file, wrapper `cli-failure`) is identical to finding convergence: do NOT aggregate as DISAGREE, record `contract-violation`, and apply the round-level abort rule below.
|
|
668
|
-
|
|
669
|
-
### Mode constraint
|
|
670
|
-
|
|
671
|
-
Plan-body verification only supports **lightweight mode** (defined in §"Verification Mode" above). `full-reanalysis` is not meaningful here because the "original source materials" for a plan item are the worker's own analysis plus the lead-mediated synthesis — there is no independent ground truth to re-read. The manifest's top-level `verificationMode` is ignored for this round; lightweight is always used.
|
|
672
|
-
|
|
673
|
-
Exception for `P-Req-*`: verifiers still MUST NOT re-open the original task brief for this round, but they MUST compare the requirement text embedded in the `Requirement Coverage` row with the cited Option / Stage / Step in the draft plan. A row is not sound merely because it says `covered`; the cited plan item must actually satisfy the stated requirement.
|
|
674
|
-
|
|
675
|
-
### Adversarial plan-body posture
|
|
676
|
-
|
|
677
|
-
When `config.adversarial == true` (the default for `implementation-planning`; see the top-level §"Configuration" table), the plan-body round runs with an **adversarial posture**. The classification rules and gate arithmetic in §"Round protocol" are UNCHANGED — `majority-disagree` (a *majority* of analysers DISAGREE) remains the only classification that blocks the Approval marker, and `dissent-isolated` still passes the gate. Adversarial mode changes only *how each verifier evaluates an item*:
|
|
678
|
-
|
|
679
|
-
- The burden of proof sits on the plan: an item earns `AGREE` only if the verifier actively tried to break it and could not.
|
|
680
|
-
- The verifier MUST open the file paths / symbols / commands the item cites and confirm they exist and are executable as written. This is the one allowed widening of the lightweight "judge from internal consistency and stated commands / paths" rule — confirming the existence of cited paths is not "re-analyzing the original requirements".
|
|
681
|
-
- If a cited path / command / validation signal cannot be confirmed, the verifier responds `DISAGREE(<kind>)` with the applicable breakage kind (a–e); uncertainty resolves toward DISAGREE, not AGREE.
|
|
682
|
-
- **Single-vote-blocking kinds.** A single `DISAGREE` is approval-blocking on its own — no majority needed — when the breakage kind is `a` (cited path/symbol mismatch) or `d` (rollback violates commit/dependency order) on *any* plan item, or `f` (requirement-coverage mismatch) on a `P-Req-*` item. These defects are concrete, safety-critical, and adversarially verifiable (the verifier confirmed the cited path / order / requirement), so one correct dissent must not be outvoted. Each creates a `majority-disagree` classification and MUST become a `Blocks=approval` clarification row. Kinds `b` / `c` / `e` still need a majority — `b` especially is prone to planning-vs-implementation environment false positives. Because `a` is reserved for a concrete contradiction between two spelled-out references (see §"Plan-body verdict semantics"), an abbreviated / ellipsis / under-specified path is raised as `b` (majority-gated), never `a` — a lone "this path is abbreviated" dissent must not single-vote-block on notation alone, and it is especially not blocking on a *rejected* option that will never be implemented. **Enforced:** `validators/validate-run.py` `_classify_plan_item_gate` (`_SINGLE_VOTE_BLOCKING_KINDS = {a, d}` + the P-Req `f` rule).
|
|
683
|
-
|
|
684
|
-
Plan-body verification stays **lightweight** even under this posture — the `verificationMode = "full-reanalysis"` forcing in §"Adversarial Verification Mode" applies to finding convergence only (see §"Mode constraint"); the adversarial posture here only changes verifier behaviour, not the mode. This raises verification *quality* (active refutation, plan-side burden). The gate *threshold* stays majority-based for the majority-gated kinds (`b`/`c`/`e`), with the single-vote-blocking exception above for the concrete, safety-critical kinds (`a`/`d`, and `f` on P-Req). A majority requires at least two participating (non-error) votes, so a lone surviving `DISAGREE` whose peer returned a non-result does NOT block on a majority-gated kind — a worker failure must not make the gate stricter than a healthy roster would.
|
|
685
|
-
|
|
686
|
-
### Round protocol (single round at default `maxRounds=1`)
|
|
687
|
-
|
|
688
|
-
1. Lead parses the report-writer draft and extracts the `P-*` plan items.
|
|
689
|
-
2. For each analyser worker in the roster (`claude`, `codex`, and `antigravity` if opted in), lead constructs a reverify prompt using the template in §"Plan-body reverify prompt" below.
|
|
690
|
-
3. Dispatch uses the same wrapper infrastructure as finding convergence. The `--role-slug` is `<role>-plan-verify-r<N>`. Result file path: `runs/<task-type>/worker-results/<role-slug>-plan-verify-r<N>-implementation-planning-<seq>.md`.
|
|
691
|
-
4. After all dispatches return, lead aggregates verdicts per `P-*` item across workers and classifies each:
|
|
692
|
-
- `full-consensus` — all participating analysers `AGREE` (SUPPLEMENT counts as agree on the item itself).
|
|
693
|
-
- `partial-consensus` — majority `AGREE`, dissenting `DISAGREE` recorded.
|
|
694
|
-
- `dissent-isolated` — only one worker `DISAGREE`s, others `AGREE` — treat as `partial-consensus` for gate purposes; record dissent. (Distinct from finding-convergence `worker-unique`, which means the *opposite*: only one worker AGREEs. Plan-body classifications use this dedicated label to avoid the collision.)
|
|
695
|
-
- `majority-disagree` — a *majority* of analysers `DISAGREE` (majority needs ≥2 participating non-error votes), OR any single-vote-blocking kind fires: one `DISAGREE(a)` / `DISAGREE(d)` on any item, or one `DISAGREE(f)` on a `P-Req-*` item (see §"Single-vote-blocking kinds"). This classification **blocks the Approval marker**.
|
|
696
|
-
- `contested` only meaningful when `maxRounds > 1`; at default `maxRounds=1`, fold any unresolved item into `partial-consensus`.
|
|
697
|
-
5. Gate result resolution:
|
|
698
|
-
- any `majority-disagree` item present AND `gating=true` → `blocked-by-disagreement`
|
|
699
|
-
- all dispatches non-result → `aborted-non-result`
|
|
700
|
-
- any `partial-consensus` / `dissent-isolated` present, no `majority-disagree` → `passed-with-dissent`
|
|
701
|
-
- all items `full-consensus` → `passed`
|
|
702
|
-
6. Lead writes `runs/<task-type>/state/plan-body-verification-<task-type>-<seq>.json` (schema below) and populates `### 5.5.9 Plan Body Verification` in the final report's data.json (`implementationPlanning.planBodyVerification`, schema `schemas/final-report-v1.0.schema.json`; template at `templates/reports/final-report.template.md`). The §5.5.9 body is **grouped by plan item**: `planItems[]`, each carrying its `id`, its plain-language `subject` (rendered as the item heading), an optional `sourceSection`, an optional `clarificationId` (the `C-<N>` this item blocks on when `majority-disagree`), and a `verdicts[]` list (`worker / verdict / breakageKind / note`) — one verdict row per worker under that item. The renderer prints three fixed legends (gate values, verdict tokens, breakage kinds a–f) so the reader can decode every cell without opening this spec. The older flat `#### Verdict details` table (`Plan item / Worker / …`, one row per plan-item × worker pair) is superseded by the grouped layout — it hid *what* each vote was about behind a bare `P-*` ID; the subject heading is the fix. The validator's `Plan Body Verification` + `Gate result:` substring checks still gate this section.
|
|
703
|
-
7. For every `majority-disagree` item, lead adds a row to `## 1. Clarification Items` with:
|
|
704
|
-
- new `C-<N>` ID (numbering continues from any existing rows)
|
|
705
|
-
- `Statement` summarising the disagreement and the worker breakage `<kind>`
|
|
706
|
-
- `Kind` chosen per the standard policy (usually `decision` for option-level conflicts, `data-point` for path/symbol mismatches)
|
|
707
|
-
- `Blocks=approval`
|
|
708
|
-
- the item's `planItems[].clarificationId` set to that `C-<N>` (1:1 link). `validators/validate-run.py` `_validate_plan_body_clarification_matching` recomputes each item's class and fails when a majority-disagree item's `clarificationId` is missing, dangling, or points at a non-`approval` row.
|
|
709
|
-
8. The top-of-report `- [ ] Approved` marker line is rendered if and only if the Gate result is `passed` or `passed-with-dissent`. `validators/validate-run.py` `validate_phase_boundary` enforces this correspondence; manually adding the marker line when the gate did not pass is a contract violation.
|
|
710
|
-
|
|
711
|
-
### `plan-body-verification-<task-type>-<seq>.json` schema
|
|
712
|
-
|
|
713
|
-
```json
|
|
714
|
-
{
|
|
715
|
-
"schemaVersion": "1.0",
|
|
716
|
-
"phase": "implementation-planning",
|
|
717
|
-
"round": 1,
|
|
718
|
-
"effectiveMaxRounds": 1,
|
|
719
|
-
"gating": true,
|
|
720
|
-
"verificationMode": "lightweight",
|
|
721
|
-
"gateResult": "passed | passed-with-dissent | blocked-by-disagreement | aborted-non-result",
|
|
722
|
-
"planItems": [
|
|
723
|
-
{
|
|
724
|
-
"id": "P-Opt-1",
|
|
725
|
-
"subject": "Option A: upload v2 를 신규 모듈로 분리",
|
|
726
|
-
"sourceSection": "4.5.1",
|
|
727
|
-
"ticketId": "<id-or-unknown>",
|
|
728
|
-
"votes": {"claude-worker": "AGREE", "codex-worker": "AGREE"},
|
|
729
|
-
"classification": "full-consensus",
|
|
730
|
-
"clarificationId": null
|
|
731
|
-
},
|
|
732
|
-
{
|
|
733
|
-
"id": "P-Step-3",
|
|
734
|
-
"subject": "Stage 2 Step 3: 마이그레이션 실행 후 회귀 테스트",
|
|
735
|
-
"sourceSection": "4.5.4",
|
|
736
|
-
"ticketId": "TICKET-123",
|
|
737
|
-
"votes": {"claude-worker": "DISAGREE(a)", "codex-worker": "DISAGREE(a)"},
|
|
738
|
-
"classification": "majority-disagree",
|
|
739
|
-
"clarificationId": "C-7"
|
|
740
|
-
}
|
|
741
|
-
],
|
|
742
|
-
"dispatches": [
|
|
743
|
-
{"role": "claude-worker", "resultPath": "...", "terminalStatus": "completed"}
|
|
744
|
-
]
|
|
745
|
-
}
|
|
746
|
-
```
|
|
747
|
-
|
|
748
|
-
`dispatches[].terminalStatus` mirrors finding convergence (`completed | timeout | error | not-run | cli-failure`).
|
|
749
|
-
|
|
750
|
-
`planItems[].classification` enum: `full-consensus | partial-consensus | dissent-isolated | majority-disagree | contested`. `contested` only appears when `maxRounds > 1`; at default `maxRounds=1` any otherwise-unresolved item folds into `partial-consensus` per the round protocol above.
|
|
751
|
-
|
|
752
|
-
`planItems[].votes.<worker>` is the verbatim verdict token emitted by the worker — `AGREE | DISAGREE(<a|b|c|d|e|f>) | SUPPLEMENT` — or `verification-error` for terminal non-result dispatches. The `DISAGREE` token retains its `<kind>` suffix so the breakage class is recoverable from the state file alone.
|
|
753
|
-
|
|
754
|
-
### Plan-body reverify prompt
|
|
755
|
-
|
|
756
|
-
Required prompt anchor headers are identical to finding convergence (see §"Required reverify-prompt anchor headers"). The prompt body changes from F-* listing to P-* listing:
|
|
757
|
-
|
|
758
|
-
```
|
|
759
|
-
You are <worker-role> performing plan-body verification for <task-key> (round 1).
|
|
760
|
-
|
|
761
|
-
## Instructions
|
|
762
|
-
|
|
763
|
-
Review the following items extracted from the consolidated implementation plan
|
|
764
|
-
authored after your initial analysis. For EACH item, respond with exactly one
|
|
765
|
-
verdict:
|
|
766
|
-
|
|
767
|
-
- **AGREE**: The item is executable as written and internally consistent with
|
|
768
|
-
other items in the plan.
|
|
769
|
-
- **DISAGREE(<kind>)**: The item is broken. Cite which kind:
|
|
770
|
-
(a) a concrete referenced file path / symbol contradicts a different concrete path / symbol for the same artifact elsewhere — a genuine mismatch; an abbreviated / ellipsis path is NOT (a), use (b),
|
|
771
|
-
(b) command or referenced path is not executable or is ambiguous — including an abbreviated / ellipsis / under-specified path that does not resolve as written,
|
|
772
|
-
(c) validation signal is not observable,
|
|
773
|
-
(d) rollback violates commit / dependency order,
|
|
774
|
-
(e) item contradicts the trade-off matrix,
|
|
775
|
-
(f) requirement coverage row does not map the stated requirement to a concrete satisfying option / stage / step — citing an existing option counts as concrete even if that option's paths are abbreviated (that is (b) on the option's item, not (f)).
|
|
776
|
-
- **SUPPLEMENT**: The item is sound but a dependency / edge case / precondition
|
|
777
|
-
is missing.
|
|
778
|
-
|
|
779
|
-
Do NOT re-analyze the original requirements. Judge solely from plan internal
|
|
780
|
-
consistency and stated commands / paths. Do NOT inspect the original task brief
|
|
781
|
-
or worker analyses for this round.
|
|
782
|
-
|
|
783
|
-
For `P-Req-*` items, compare only the requirement text embedded in the row
|
|
784
|
-
against the cited plan item(s). Do not open the original brief, but do reject
|
|
785
|
-
coverage rows that cite no concrete option/stage/step or cite a plan item that
|
|
786
|
-
does not satisfy the row's own requirement.
|
|
787
|
-
|
|
788
|
-
## Plan items to verify
|
|
789
|
-
|
|
790
|
-
### P-Step-3 [TICKETID: <id>]: <one-line summary>
|
|
791
|
-
**From section**: 4.5.4 Stepwise Execution Order
|
|
792
|
-
**Original text**:
|
|
793
|
-
> <verbatim quote of the step>
|
|
794
|
-
|
|
795
|
-
**Check**:
|
|
796
|
-
- Are referenced file paths consistent with the option's File Structure list?
|
|
797
|
-
- Is the named command executable as written?
|
|
798
|
-
- Does the success criterion produce an observable signal?
|
|
799
|
-
|
|
800
|
-
### P-Opt-2 [TICKETID: <id>]: <one-line summary>
|
|
801
|
-
...
|
|
802
|
-
|
|
803
|
-
## Response format
|
|
804
|
-
|
|
805
|
-
### P-Step-3
|
|
806
|
-
**Verdict**: AGREE | DISAGREE(<a|b|c|d|e>) | SUPPLEMENT
|
|
807
|
-
**Explanation**: <2-3 sentences>
|
|
808
|
-
|
|
809
|
-
### P-Opt-2
|
|
810
|
-
...
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
When `config.adversarial == true`, the lead prepends the adversarial framing from §"Adversarial plan-body posture" to the `## Instructions` block: the burden of proof is on the plan, the verifier opens and confirms every cited path / command, and an item whose cited references cannot be confirmed is answered `DISAGREE(<kind>)` rather than `AGREE`. The verdict tokens, breakage kinds (a–e), classification, and the majority gate threshold are unchanged. This prepended framing supersedes the template's "Judge solely from plan internal consistency" instruction for the adversarial round.
|
|
814
|
-
|
|
815
|
-
The "Reverify prompt: required-reading suppression (BLOCKING)" rule (lightweight mode does NOT inject a `[Required reading]` clause) applies here as well.
|
|
816
|
-
|
|
817
|
-
### Worker non-result handling in plan-body round (BLOCKING)
|
|
818
|
-
|
|
819
|
-
Mirrors finding convergence (§"Worker failure handling in reverify"). Concretely:
|
|
820
|
-
|
|
821
|
-
- A dispatch that returns terminal non-result MUST NOT be aggregated as `DISAGREE`.
|
|
822
|
-
- If at least one dispatch was issued AND **all** plan-body dispatches return non-result, the Gate result is `aborted-non-result`. Record one `contract-violation` event per non-result dispatch.
|
|
823
|
-
- When the gate is `aborted-non-result`, report-writer MUST keep the frontmatter `approved: false` (publishing `approved: true` under this gate result is a validator failure). A single row is added to `## 1. Clarification Items` with `Statement="plan-body verification could not run — all workers returned non-result"`, `Kind=decision`, `Blocks=approval`, allowing the user to either retry the phase or override by manually flipping the frontmatter to `approved: true` (or running `--approve` on the resume command).
|
|
594
|
+
Moved to its own contract: [plan-body-verification](./plan-body-verification.md). It fires only for `task-type = implementation-planning`, as a Phase 6 sub-step after the report-writer draft — read that file at that sub-step, NOT during the Phase 5.5 finding convergence this contract governs. The finding queue (`F-*`, this contract) and the plan-item queue (`P-*`, that contract) are disjoint — see its "MUTUAL EXCLUSION" section.
|