okstra 0.223.2 → 0.225.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/commands/execute/render-bundle.mjs +7 -5
  2. package/dist/commands/execute/render-bundle.mjs.map +1 -1
  3. package/docs/cli.md +10 -2
  4. package/package.json +1 -1
  5. package/runtime/BUILD.json +2 -2
  6. package/runtime/bin/lib/okstra/cli.sh +5 -1
  7. package/runtime/bin/lib/okstra/globals.sh +1 -0
  8. package/runtime/bin/lib/okstra/usage.sh +4 -1
  9. package/runtime/bin/okstra-provider-exec.py +32 -16
  10. package/runtime/bin/okstra.sh +1 -0
  11. package/runtime/prompts/coding-preflight/clean-code.md +8 -1
  12. package/runtime/prompts/duties/code-reviewer.json +5 -3
  13. package/runtime/prompts/wizard/prompts.ko.json +10 -0
  14. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +2 -0
  15. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +3 -1
  16. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +6 -0
  17. package/runtime/python/okstra_ctl/adapters/providers/zai/adapter.py +5 -1
  18. package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +25 -0
  19. package/runtime/python/okstra_ctl/code_review_coverage.py +213 -0
  20. package/runtime/python/okstra_ctl/code_review_target.py +31 -2
  21. package/runtime/python/okstra_ctl/coverage_census.py +2 -2
  22. package/runtime/python/okstra_ctl/dispatch_core.py +15 -0
  23. package/runtime/python/okstra_ctl/domain/worker_exec.py +6 -0
  24. package/runtime/python/okstra_ctl/incremental_carry.py +10 -16
  25. package/runtime/python/okstra_ctl/phases/final_verification/profile.md +1 -1
  26. package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-diff-review.md +1 -1
  27. package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-verifier.md +2 -2
  28. package/runtime/python/okstra_ctl/phases/implementation_planning/authoring.py +164 -33
  29. package/runtime/python/okstra_ctl/phases/implementation_planning/instructions/plan-body-verification.md +27 -3
  30. package/runtime/python/okstra_ctl/phases/implementation_planning/plan_body.py +33 -0
  31. package/runtime/python/okstra_ctl/phases/implementation_planning/profile.md +1 -0
  32. package/runtime/python/okstra_ctl/phases/implementation_planning/round_completion.py +177 -0
  33. package/runtime/python/okstra_ctl/plan_item_evidence.py +237 -0
  34. package/runtime/python/okstra_ctl/plan_items.py +12 -9
  35. package/runtime/python/okstra_ctl/plan_review_context.py +82 -0
  36. package/runtime/python/okstra_ctl/render.py +2 -0
  37. package/runtime/python/okstra_ctl/run.py +23 -2
  38. package/runtime/python/okstra_ctl/team.py +12 -18
  39. package/runtime/python/okstra_ctl/verdict_blocks.py +17 -1
  40. package/runtime/python/okstra_ctl/wizard/__init__.py +1 -0
  41. package/runtime/python/okstra_ctl/wizard/confirmation.py +2 -0
  42. package/runtime/python/okstra_ctl/wizard/engine.py +26 -5
  43. package/runtime/python/okstra_ctl/wizard/ids.py +3 -0
  44. package/runtime/python/okstra_ctl/wizard/registry.py +13 -0
  45. package/runtime/python/okstra_ctl/wizard/render.py +1 -0
  46. package/runtime/python/okstra_ctl/wizard/state.py +8 -0
  47. package/runtime/python/okstra_ctl/wizard/steps_identity.py +99 -16
  48. package/runtime/python/okstra_ctl/wizard/steps_options.py +52 -1
  49. package/runtime/python/okstra_ctl/wizard/steps_plan.py +14 -0
  50. package/runtime/python/okstra_ctl/worker_dispatch.py +9 -0
  51. package/runtime/python/okstra_ctl/worker_request.py +12 -1
  52. package/runtime/schemas/execution-manifest-v2.schema.json +1 -0
  53. package/runtime/schemas/final-report-v2.0.schema.json +3 -1
  54. package/runtime/schemas/final-report-v3.0.schema.json +3 -1
  55. package/runtime/skills/_fragments/bash-invocation-rule.md +1 -1
  56. package/runtime/skills/_fragments/host-question-interface.md +1 -0
  57. package/runtime/skills/_fragments/host-runtime-selection.md +1 -0
  58. package/runtime/skills/_fragments/wizard-bash-invocation-rule.md +1 -1
  59. package/runtime/skills/_fragments/wizard-preflight.md +1 -1
  60. package/runtime/skills/_fragments/wizard-render-bundle.md +4 -4
  61. package/runtime/skills/okstra-brief-gen/SKILL.md +45 -47
  62. package/runtime/skills/okstra-brief-gen/references/reporter-confirmations.md +3 -3
  63. package/runtime/skills/okstra-brief-gen/references/tracker-recursion.md +1 -1
  64. package/runtime/skills/okstra-chat/SKILL.md +1 -1
  65. package/runtime/skills/okstra-code-review/SKILL.md +26 -15
  66. package/runtime/skills/okstra-code-review/references/census-rules.md +38 -2
  67. package/runtime/skills/okstra-code-review/references/review-calibration.md +39 -15
  68. package/runtime/skills/okstra-container-build/SKILL.md +6 -2
  69. package/runtime/skills/okstra-discussion/SKILL.md +6 -6
  70. package/runtime/skills/okstra-inspect/SKILL.md +5 -3
  71. package/runtime/skills/okstra-manager/SKILL.md +8 -4
  72. package/runtime/skills/okstra-memory/SKILL.md +1 -1
  73. package/runtime/skills/okstra-pr-gen/SKILL.md +10 -6
  74. package/runtime/skills/okstra-rollup/SKILL.md +6 -2
  75. package/runtime/skills/okstra-run/SKILL.md +8 -7
  76. package/runtime/skills/okstra-schedule-gen/SKILL.md +13 -6
  77. package/runtime/skills/okstra-setup/SKILL.md +8 -4
  78. package/runtime/skills/okstra-setup/references/project-config.md +3 -3
  79. package/runtime/skills/okstra-usage/SKILL.md +5 -1
  80. package/runtime/skills/okstra-user-response/SKILL.md +7 -3
@@ -9557,7 +9557,8 @@
9557
9557
  "block": {"type": "string", "enum": ["execution", "record"]},
9558
9558
  "stageScope": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "integer", "minimum": 1}},
9559
9559
  "contentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "SHA-256 of subject+payload+block+stageScope for the current extract."},
9560
- "verifiedContentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "contentHash at the last apply-verdicts. Matching hashes keep a pre-self-fix verdict."},
9560
+ "verifiedContentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "Body hash at the last apply-verdicts; insufficient on its own for runtime verdict reuse."},
9561
+ "verifiedReviewHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "Runtime fingerprint of the judged body, declared evidence closure, contract, roster and detailed votes."},
9561
9562
  "id": {
9562
9563
  "type": "string",
9563
9564
  "minLength": 1
@@ -9597,6 +9598,7 @@
9597
9598
  "additionalProperties": false,
9598
9599
  "properties": {
9599
9600
  "claimKind": {"description": "Set only on DISAGREE. `fact` claims are settled by reproduction, `judgement` by quorum.", "enum": ["fact", "judgement", null]},
9601
+ "dependencies": {"type": "object", "required": ["complete", "itemIds", "paths"], "additionalProperties": false, "properties": {"complete": {"type": "boolean"}, "itemIds": {"type": "array", "items": {"type": "string", "minLength": 1}}, "paths": {"type": "array", "items": {"type": "string", "minLength": 1}}}},
9600
9602
  "reproduction": {"description": "Set only on a `fact` claim. The probe okstra runs; see okstra_ctl/claim_reproduction.py.", "type": ["object", "null"]},
9601
9603
  "reproductionResult": {"description": "Written by okstra, not by the worker.", "enum": ["reproduced", "not-reproduced", "not-runnable", null]},
9602
9604
  "worker": {
@@ -9849,7 +9849,8 @@
9849
9849
  "block": {"type": "string", "enum": ["execution", "record"]},
9850
9850
  "stageScope": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "integer", "minimum": 1}},
9851
9851
  "contentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "SHA-256 of subject+payload+block+stageScope for the current extract."},
9852
- "verifiedContentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "contentHash at the last apply-verdicts. Matching hashes keep a pre-self-fix verdict."},
9852
+ "verifiedContentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "Body hash at the last apply-verdicts; insufficient on its own for runtime verdict reuse."},
9853
+ "verifiedReviewHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "Runtime fingerprint of the judged body, declared evidence closure, contract, roster and detailed votes."},
9853
9854
  "id": {
9854
9855
  "type": "string",
9855
9856
  "minLength": 1
@@ -9896,6 +9897,7 @@
9896
9897
  "additionalProperties": false,
9897
9898
  "properties": {
9898
9899
  "claimKind": {"description": "Set only on DISAGREE. `fact` claims are settled by reproduction, `judgement` by quorum.", "enum": ["fact", "judgement", null]},
9900
+ "dependencies": {"type": "object", "required": ["complete", "itemIds", "paths"], "additionalProperties": false, "properties": {"complete": {"type": "boolean"}, "itemIds": {"type": "array", "items": {"type": "string", "minLength": 1}}, "paths": {"type": "array", "items": {"type": "string", "minLength": 1}}}},
9899
9901
  "reproduction": {"description": "Set only on a `fact` claim. The probe okstra runs; see okstra_ctl/claim_reproduction.py.", "type": ["object", "null"]},
9900
9902
  "reproductionResult": {"description": "Written by okstra, not by the worker.", "enum": ["reproduced", "not-reproduced", "not-runnable", null]},
9901
9903
  "worker": {
@@ -1 +1 @@
1
- Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
1
+ Run one shell-command tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — literal command tokens keep execution permissions reviewable):
@@ -0,0 +1 @@
1
+ For authored questions, `prompt_user` means the current host's available question interface, not a literal tool name. Use the selected host relay when one is provided. Preserve every question, option, description, and answer; use native controls only when the live session permits them and the complete interaction fits its limits. Otherwise show complete numbered choices and accept the user's reply; collect free text through normal conversation. If a native control is refused, follow the relay's recovery mapping, or use text when no relay is provided. A delivery acknowledgement or preselected option is not an answer; wait for the user's actual response.
@@ -0,0 +1 @@
1
+ Resolve `<host-runtime>` from the launcher's `OKSTRA_RUNTIME_HOST` when present; otherwise use the registered host ID declared by the current harness. Do not infer the host from worker models, installed executables, or `PATH`. If neither source identifies the host, report that it is unknown instead of substituting another host. Keep the same resolved host when retrying against another project directory.
@@ -1 +1 @@
1
- **Bash invocation rule (permission-friendly)**: every Bash command in this skill begins with the literal token `okstra` (or another already-allowed binary) and passes literal argument values. Do not introduce shell variables (`$STATE_FILE`, `$ANSWER`, `$projectRoot`, ...), `$(...)` command substitution, or leading `VAR=...` assignments — any of those make the leading token non-literal, defeat the `Bash(okstra:*)` permission match, and force a confirmation prompt on every wizard call. When a prior tool call emitted a path or value, read it from the tool output and paste the literal string into the next command.
1
+ **Shell invocation rule (permission-friendly)**: every shell command in this skill begins with the literal token `okstra` (or another already-allowed binary) and passes literal argument values. Do not introduce shell variables (`$STATE_FILE`, `$ANSWER`, `$projectRoot`, ...), `$(...)` command substitution, or leading `VAR=...` assignments — any of those make the command harder to audit; apply the selected host adapter's execution-permission guidance. When a prior tool call emitted a path or value, read it from the tool output and paste the literal string into the next command.
@@ -6,7 +6,7 @@ Record the semantic functions that the live harness can actually perform. Always
6
6
 
7
7
  The host-native provider runs as the current lead session when its role is supported. Other selected providers run through their registered CLI wrappers. Claude remains only the model-policy default; it is not the process owner.
8
8
 
9
- Run one Bash tool call (Bash invocation rule from the top of this file applies):
9
+ Run one shell-command tool call (Shell invocation rule from the top of this file applies):
10
10
 
11
11
  ```bash
12
12
  okstra preflight --runtime <host-runtime>
@@ -29,9 +29,9 @@ okstra config set pr-template-path "<value>" --scope global
29
29
 
30
30
  If an action has an unknown `command`, `key`, or `scope`, stop and report the wizard output instead of inventing a command.
31
31
 
32
- Before rendering the next phase's bundle — and between worker rounds within a phase (reverify/critic/gapverify batches), after you have collected that round's results and token usage and before you dispatch the next round — close the panes of the dispatches that finished in the prior round so they do not accumulate: `okstra team reclaim --project-root <projectRoot> --run-manifest <RUN_MANIFEST_PATH>` closes them, records `phase-batch-cleanup panes=<n>` with the number it closed, and prints that `PROGRESS:` line last — emit it as printed and do not call `okstra lead-progress append` for it. The command reads each dispatch's recorded status, so an in-progress worker keeps its pane whichever moment you call it. It closes only the panes okstra opened and recorded — a pane the harness opened for its own teammate carries no recorded id and is not okstra's to close. `shutdown_request` alone only idles the agent and frees no pane, so it stays part of the run-end sequence for roster/token hygiene. A `cli-wrapper` run holds no pane at all and `team reclaim` refuses it, so record the checkpoint there with `okstra lead-progress append … --phase phase-batch-cleanup --field panes=0`.
32
+ Before rendering the next phase's bundle — and between worker rounds within a phase (reverify/critic/gapverify batches), after you have collected that round's results and token usage and before you dispatch the next round — close the panes of the dispatches that finished in the prior round so they do not accumulate: `okstra team reclaim --project-root <projectRoot> --run-manifest <RUN_MANIFEST_PATH>` closes them, records `phase-batch-cleanup panes=<n>` with the number it closed, and prints that `PROGRESS:` line last — emit it as printed and do not call `okstra lead-progress append` for it. The command reads each dispatch's recorded status, so an in-progress worker keeps its pane whichever moment you call it. It closes only the panes okstra opened and recorded — a pane the harness opened for its own teammate carries no recorded id and is not okstra's to close. After collecting results and token usage, apply the selected adapter's `shutdown_workers` operation only to confirmed-complete workers; preserve in-flight workers and the lead. A `cli-wrapper` run holds no pane at all and `team reclaim` refuses it, so record the checkpoint there with `okstra lead-progress append … --phase phase-batch-cleanup --field panes=0`.
33
33
 
34
- Before you ask the user for any approval, clarification, or decision after workers have been dispatched, run `okstra team reclaim … --gate` first: it closes the finished panes and prints `PROGRESS: phase-gate-cleanup panes=<n>` for you to emit, without recording a batch cleanup. Then `TaskStop` each completed worker. A `TaskStop` by itself idles the task but leaves the pane open — the `team reclaim` call is what closes it. This keeps a user gate from being shown while finished worker panes remain; in-progress dispatches keep their panes. Then follow `prompts/lead/okstra-lead-contract.md` "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.
34
+ Before you ask the user for any approval, clarification, or decision after workers have been dispatched, run `okstra team reclaim … --gate` first: it closes the finished panes and prints `PROGRESS: phase-gate-cleanup panes=<n>` for you to emit, without recording a batch cleanup. Then apply the selected adapter's `shutdown_workers` operation to confirmed-complete workers after collecting their results and token usage; preserve in-flight workers and the lead. This keeps a user gate from being shown while finished worker panes remain; in-progress dispatches keep their panes. Then follow `prompts/lead/okstra-lead-contract.md` "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.
35
35
 
36
36
  Build the `okstra render-bundle` invocation from `outcome.renderArgv`, passing every token verbatim and in order (including empty strings — they are intentional `use phase default` markers).
37
37
 
@@ -44,13 +44,13 @@ Step 3's empty-answer and escaping rules apply verbatim: every flag in `renderAr
44
44
  `renderArgv` already contains exactly one `--lead-runtime <host-runtime>` pair. Do not add or replace it. Do not enumerate a fixed provider list in this skill because the wizard and role model pool own the ordered tokens.
45
45
 
46
46
  ```bash
47
- okstra render-bundle \
47
+ okstra render-bundle --summary-only \
48
48
  <outcome.renderArgv token 1> \
49
49
  <outcome.renderArgv token 2> \
50
50
  <each remaining outcome.renderArgv token in order>
51
51
  ```
52
52
 
53
- `render-bundle` auto-supplies `--workspace-root` and forces `--render-only`. Stdout prints `okstra task root:`, `okstra instruction-set:`, `okstra run manifest:`, and the full rendered lead prompt. Parse the labelled lines for `TASK_ROOT`, `INSTRUCTION_SET_PATH`, and `RUN_MANIFEST_PATH`. Also watch for an optional `okstra concurrent-run stages:` label line — present only when a concurrent run is detected (see "Concurrent-run detection branch" below).
53
+ `render-bundle` auto-supplies `--workspace-root` and forces `--render-only`. Stdout prints `okstra task root:`, `okstra instruction-set:`, `okstra run manifest:`, and other preparation status. `--summary-only` omits the lead prompt body from stdout; Step 6 reads the verified saved prompt from the manifest path. Parse the labelled lines for `TASK_ROOT`, `INSTRUCTION_SET_PATH`, and `RUN_MANIFEST_PATH`. Also watch for an optional `okstra concurrent-run stages:` label line — present only when a concurrent run is detected (see "Concurrent-run detection branch" below).
54
54
 
55
55
  Before acting as the lead, read `resources.leadPromptMetadataPath` from the run
56
56
  manifest and execute the host-native specification-link gate:
@@ -6,6 +6,10 @@ disable-model-invocation: true
6
6
 
7
7
  # okstra-brief-gen
8
8
 
9
+ <!-- BEGIN FRAGMENT: host-question-interface -->
10
+ For authored questions, `prompt_user` means the current host's available question interface, not a literal tool name. Use the selected host relay when one is provided. Preserve every question, option, description, and answer; use native controls only when the live session permits them and the complete interaction fits its limits. Otherwise show complete numbered choices and accept the user's reply; collect free text through normal conversation. If a native control is refused, follow the relay's recovery mapping, or use text when no relay is provided. A delivery acknowledgement or preselected option is not an answer; wait for the user's actual response.
11
+ <!-- END FRAGMENT: host-question-interface -->
12
+
9
13
  Generate a single `task brief` markdown file under the okstra project domain
10
14
  (`.okstra/briefs/<task-group>/<task-id>.md`) so it can be fed to
11
15
  [`okstra-run`](../okstra-run/SKILL.md) as `--task-brief`.
@@ -114,10 +118,10 @@ and never writes them. Decision files (when raised by
114
118
 
115
119
  ## Step 0: Preflight
116
120
 
117
- Every Bash command in this skill is a **separate Bash tool call**.
121
+ Every shell command in this skill is a **separate shell-command tool call**.
118
122
 
119
123
  <!-- BEGIN FRAGMENT: bash-invocation-rule -->
120
- Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
124
+ Run one shell-command tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — literal command tokens keep execution permissions reviewable):
121
125
  <!-- END FRAGMENT: bash-invocation-rule -->
122
126
 
123
127
  ```bash
@@ -144,13 +148,7 @@ selection and the material it collects as separate interactions: a native
144
148
  choice selects the input path, and the next free-text interaction receives a
145
149
  path, URL, ticket reference, or body.
146
150
 
147
- In Codex, call `request_user_input` with the two or three listed options in
148
- their stated order. Do not add an `Other` option: the Codex client adds its
149
- own free-text `Other` row. When the user supplies a value through that row,
150
- classify it through the normal reporter-source rules (ticket, URL, or user
151
- input) and do not ask them to repeat it. If the host does not expose a
152
- compatible native control, render the same options as a numbered text choice
153
- and retain the explicit free-text follow-up where this skill specifies one.
151
+ Use the current host's native single-select mapping with every listed option in order. If the host supplies its own free-text row, do not add a duplicate. Classify a custom value through the normal reporter-source rules and do not ask the user to repeat it. If no compatible native control is available, render the complete options as numbered text and retain the specified free-text follow-up.
154
152
 
155
153
  ## Step 1: Choose input source
156
154
 
@@ -190,7 +188,7 @@ for every variant and every source type below.
190
188
 
191
189
  ### 1.0. brief variant
192
190
 
193
- `AskUserQuestion` (single-select):
191
+ `prompt_user` (single-select):
194
192
 
195
193
  - **Label**: `"Brief variant?"`
196
194
  - **Options**:
@@ -207,7 +205,7 @@ collected source material, then classify a ticket reference or URL normally.
207
205
 
208
206
  If the user picked `Reporter input`, proceed to Step 1.A's sub-flow below — this is the existing 4-source flow. Choose ONE of:
209
207
 
210
- `AskUserQuestion` (single-select):
208
+ `prompt_user` (single-select):
211
209
 
212
210
  - **Label**: "Source of this brief?"
213
211
  - **Options** (single-select):
@@ -252,7 +250,7 @@ Order of operations:
252
250
  placeholder group.
253
251
 
254
252
  **Sequential, never batched.** Run Step 2a as its **own**
255
- `AskUserQuestion` call, then ask sub-step 1's ticket question in a
253
+ `prompt_user` call, then ask sub-step 1's ticket question in a
256
254
  **separate** call — never combine the task-group and ticket-ref
257
255
  questions into one. When you relay this preflight, tell the user in one
258
256
  line *why* task group comes first (it scopes where this ticket's brief —
@@ -262,30 +260,30 @@ Order of operations:
262
260
  directory segment to equal the slugified frontmatter `task-group`, so a
263
261
  brief written before the group is known — or under an invented placeholder
264
262
  group — fails validation rather than landing in the wrong tree.
265
- 1. `AskUserQuestion` (free text):
263
+ 1. `prompt_user` (free text):
266
264
  `"Ticket key or URL (e.g. LIN-1234, PROJ-42, https://linear.app/..., https://your.atlassian.net/browse/...)"` →
267
265
  `ticket_ref`. Issue this only **after** Step 2a's task-group answer is in
268
266
  hand (per sub-step 0's sequential rule).
269
267
  2. Auto-detect the tracker by URL host or key prefix (`linear.app` → Linear,
270
268
  `atlassian.net` or `JIRA_*` pattern → Jira, `github.com/.../issues/` →
271
269
  GitHub, `notion.so` → Notion). If detection fails, ask once more via
272
- `AskUserQuestion`.
270
+ `prompt_user`.
273
271
  3. Check whether the tracker's **MCP tools** are loaded in the current
274
272
  session and prefer them when available:
275
273
  - Linear → `mcp__linear__*` family (e.g. `get_issue` /
276
- `getIssueByIdentifier`). If absent, try `ToolSearch` with the keyword
277
- `linear` — it auto-waits for connecting MCP servers.
278
- - Jira → `mcp__jira__*` (or `mcp__atlassian__*`). Same `ToolSearch`
279
- fallback.
274
+ `getIssueByIdentifier`). If absent, try the host's tool-discovery capability (when available) with the keyword
275
+ `linear`; if discovery is unavailable, continue to step 4.
276
+ - Jira → `mcp__jira__*` (or `mcp__atlassian__*`). Use the same capability-discovery
277
+ fallback when available; otherwise continue to step 4.
280
278
  - GitHub → `gh issue view <num> --repo <owner>/<repo> --json title,body,comments,labels,state`
281
279
  (an authenticated `gh` CLI is the default path — usable even when MCP
282
- is present). If `gh` is missing or unauthenticated, try `ToolSearch`
280
+ is present). If `gh` is missing or unauthenticated, try the host's tool-discovery capability (when available)
283
281
  for `mcp__github` family; otherwise fall through to step 4 (paste /
284
282
  skip).
285
283
  - Notion → `mcp__notion__API-retrieve-a-page` /
286
284
  `API-get-block-children`.
287
285
  4. If **no** MCP or CLI path is available:
288
- - Ask the user via `AskUserQuestion`:
286
+ - Ask the user via `prompt_user`:
289
287
  `"<tracker> MCP is not connected. (a) Paste the ticket body here / (b) Skip this source"`.
290
288
  - Never invent ticket content.
291
289
  5. Preserve the retrieved fields (summary/title, description body, comments,
@@ -311,11 +309,11 @@ Order of operations:
311
309
 
312
310
  Order of operations:
313
311
 
314
- 1. `AskUserQuestion` (free text): `"Link URL"`.
315
- 2. Fetch directly via `WebFetch`. If `WebFetch` is deferred, load its schema
316
- first via `ToolSearch` with `select:WebFetch`.
312
+ 1. `prompt_user` (free text): `"Link URL"`.
313
+ 2. Fetch through an available URL-fetch tool. If tools are deferred, discover
314
+ that capability through the current host when discovery is available.
317
315
  3. On fetch failure or auth required (login wall, intranet-only, etc.):
318
- - Ask the user via `AskUserQuestion` to paste the body.
316
+ - Ask the user via `prompt_user` to paste the body.
319
317
  - Never invent URL content.
320
318
  4. Preserve the fetched content (title + body + quotes/examples) **verbatim**
321
319
  in Source Material. Do not excerpt or summarize long pages to fit a line
@@ -333,7 +331,7 @@ Two sub-modes combined under one option.
333
331
  Material as `conversation synthesis` and quote key utterances verbatim
334
332
  wherever possible.
335
333
  - **Inline input**: if conversation context is empty or thin, take one
336
- free-text `AskUserQuestion`. The received text is preserved in Source
334
+ free-text `prompt_user`. The received text is preserved in Source
337
335
  Material without changing a character.
338
336
 
339
337
  When both are used, record them as two separate sub-sources under Source
@@ -343,20 +341,20 @@ Material.
343
341
 
344
342
  If the user picked `Codebase scan`, gather the codebase-scan-specific inputs:
345
343
 
346
- 1. `AskUserQuestion` (free text):
344
+ 1. `prompt_user` (free text):
347
345
  `"Scan scope — comma-separated paths inside the project (e.g. src/foo/, src/bar/baz.py)"` → `scan_scope` (CSV).
348
- 2. `AskUserQuestion` (multi-select, options = the lens values from `~/.okstra/lib/python/okstra_ctl/phases/improvement_discovery/lenses.py`'s `LENSES` — code source: `scripts/okstra_ctl/phases/improvement_discovery/lenses.py`):
346
+ 2. `prompt_user` (multi-select, options = the lens values from `~/.okstra/lib/python/okstra_ctl/phases/improvement_discovery/lenses.py`'s `LENSES` — code source: `scripts/okstra_ctl/phases/improvement_discovery/lenses.py`):
349
347
  `"Priority lenses (pick 1–4)"` → `priority_lenses` (1..4 values from the enum).
350
348
  Current `LENSES` values (this list mirrors the module — if they diverge, the module wins; Step 6.6 `validate-brief` enforces the subset): `performance`, `security`, `readability`, `architecture`, `test-coverage`, `dx`, `observability`, `accessibility`.
351
- 3. `AskUserQuestion` (free text):
349
+ 3. `prompt_user` (free text):
352
350
  `"Out-of-scope paths (optional, comma-separated)"` → `out_of_scope` (CSV, empty allowed).
353
- 4. `AskUserQuestion` (free text):
351
+ 4. `prompt_user` (free text):
354
352
  `"Candidate cap (1–12, default 8)"` → `candidate_cap` (integer; empty → 8).
355
- 5. `AskUserQuestion` (free text):
353
+ 5. `prompt_user` (free text):
356
354
  `"Context — why is this scope being scanned now? One short paragraph."` → `context`.
357
- 6. `AskUserQuestion` (free text):
355
+ 6. `prompt_user` (free text):
358
356
  `"Desired outcome — what kinds of improvements do you want surfaced? Anti-goals?"` → `desired_outcome`.
359
- 7. `AskUserQuestion` (free text):
357
+ 7. `prompt_user` (free text):
360
358
  `"Constraints — untouchable areas, deadlines, compatibility (optional)"` → `constraints`.
361
359
 
362
360
  Validation (before Step 4 sharpening):
@@ -374,7 +372,7 @@ If the user picked `Error feedback`, the input is a `.zip` produced by `okstra e
374
372
  Input: the `.zip` produced by `okstra error-zip`. Ask the user for the zip path (if a path was received earlier, propose reusing it as `Recommended:`).
375
373
 
376
374
  1. Read the zip's `report.md`, show the frequent-cluster table to the user, and have them pick **one cluster**.
377
- `AskUserQuestion` (single-select, `Specify manually` always last):
375
+ `prompt_user` (single-select, `Specify manually` always last):
378
376
  1. top-1 cluster (highest frequency) — marked `Recommended:`.
379
377
  2. top-2 cluster.
380
378
  3. `Specify manually` — the user names a cluster directly from the table.
@@ -413,7 +411,7 @@ shown in slug form.
413
411
 
414
412
  If `Status` is `error`, report `Failure stage` and `Failure reason`, then stop.
415
413
 
416
- `AskUserQuestion` (single-select):
414
+ `prompt_user` (single-select):
417
415
 
418
416
  - **Label**: `"Task group?"`
419
417
  - **Options** (recommendations first, `Enter directly` always last):
@@ -465,7 +463,7 @@ relations are also tracked via the brief's `parent-id` frontmatter
465
463
  - Jira `PROJ-42` → `PROJ-42`
466
464
  - GitHub `owner/repo#123` → `gh-<repo>-123`
467
465
  - Notion (no canonical id) → `notion-<8-char-page-short-id>`
468
- - **File / URL / User-input sources**: `AskUserQuestion` (free text):
466
+ - **File / URL / User-input sources**: `prompt_user` (free text):
469
467
  - `"Ticket / identifier (e.g. dev-9043, INV-1234, login-error)"` →
470
468
  `ticket_id`
471
469
 
@@ -483,7 +481,7 @@ to Source Material body, not to the filename):
483
481
  5. Trim leading/trailing `-`
484
482
  - Example: Linear `LIN-1234 "Fix flaky login retry on 5xx"` →
485
483
  `LIN-1234-fix-flaky-login-retry-on-5xx.md`
486
- - **Other sources**: `AskUserQuestion` (free text):
484
+ - **Other sources**: `prompt_user` (free text):
487
485
  - `"File title (short summary, e.g. fix-login-retry, dev-9043-brief)"` →
488
486
  `file_title`
489
487
  - Apply rules 1–5 above. Re-ask on empty input.
@@ -495,7 +493,7 @@ alphanumeric character after slugification. Apply Step 2c on collision.
495
493
 
496
494
  ### 2c. Collision handling
497
495
 
498
- If a brief file already exists, `AskUserQuestion` with (`Skip` /
496
+ If a brief file already exists, `prompt_user` with (`Skip` /
499
497
  `Append suffix` / `Overwrite`). Default recommendation: `Skip` —
500
498
  protects briefs the user has already edited. `Append suffix` adds a
501
499
  timestamp suffix to the filename stem.
@@ -503,7 +501,7 @@ timestamp suffix to the filename stem.
503
501
  When multiple collisions occur in tracker multi-generation mode:
504
502
 
505
503
  - First echo the colliding file list to the user.
506
- - Ask **once** with `AskUserQuestion` for a bulk policy.
504
+ - Ask **once** with `prompt_user` for a bulk policy.
507
505
  - **Even when `Overwrite` is selected, confirm one more time for each
508
506
  colliding file**, or warn that downgrading to `Append suffix` is safer. Silent bulk overwrite is forbidden.
509
507
 
@@ -585,7 +583,7 @@ project's domain.
585
583
 
586
584
  Inspect the synthesized brief draft. For each REQUIRED section below that is
587
585
  empty or trivially thin **after** reading Source Material verbatim, ask **at
588
- most one** `AskUserQuestion` to fill it. Never ask about sections already
586
+ most one** `prompt_user` to fill it. Never ask about sections already
589
587
  covered by the source material.
590
588
 
591
589
  **Enforced:** `validators/validate-brief.py` fails a brief whose required section is missing, has a blank body (`check_variant_required_sections`), or still carries the template's `<...>` text (`check_template_scaffold`). `## Source Material` is exempt from the template-text check because it holds the reporter's words verbatim.
@@ -599,12 +597,12 @@ The following rules absorb the useful parts of `grill-me` /
599
597
  inspection (e.g. "does this symbol exist?", "what does this endpoint
600
598
  return today?"), do that **instead of** asking the user. Only ask when
601
599
  the codebase cannot answer.
602
- 2. **Question carries a recommended answer** — every `AskUserQuestion`
600
+ 2. **Question carries a recommended answer** — every `prompt_user`
603
601
  issued in this step must put the recommended option **first** with the
604
602
  suffix `(Recommended)`. Free-text fallbacks are still allowed, but the
605
603
  recommendation must be visible.
606
604
  3. **One at a time** — never batch multiple questions into one
607
- `AskUserQuestion` call. Ask, wait for the answer, fold it into the
605
+ `prompt_user` call. Ask, wait for the answer, fold it into the
608
606
  draft, then ask the next.
609
607
  4. **Bounded budget** — at most **1** sharpening question per empty
610
608
  required section, plus **at most 2** disambiguation questions arising
@@ -788,7 +786,7 @@ the skill does not write outside okstra's subtree.
788
786
  ### 4.5b. Approval flow (per entry)
789
787
 
790
788
  For each draft, **first echo the target absolute path on one line** (so
791
- the user sees the exact file that will be written), then `AskUserQuestion`:
789
+ the user sees the exact file that will be written), then `prompt_user`:
792
790
 
793
791
  - **Label**: `"Apply glossary entry?"` (keep the label short — absolute
794
792
  paths belong in the echo line above, not in the option chip)
@@ -936,7 +934,7 @@ one row per requirement unit, its destination section, and for an end-state
936
934
  unit the `EB-` / `PB-` / `EO-` id it became. That table is what lets the
937
935
  reporter check their own list in one pass. Then ask:
938
936
 
939
- `AskUserQuestion`: `"Proceed with this brief?"` — options `Save` / `Edit`.
937
+ `prompt_user`: `"Proceed with this brief?"` — options `Save` / `Edit`.
940
938
  On `Edit`, return to Step 4 for the section to revise; a unit the user reports
941
939
  missing returns to this sweep instead.
942
940
 
@@ -991,7 +989,7 @@ in the brief's frontmatter and skip the rest of this step.
991
989
 
992
990
  ### 6.5b. Ask once: collect now or later
993
991
 
994
- If the list is non-empty, run **one** `AskUserQuestion`:
992
+ If the list is non-empty, run **one** `prompt_user`:
995
993
 
996
994
  - **Label**: `"This brief has <N> question(s) only the reporter can answer. Collect their answers now?"`
997
995
  - **Options**:
@@ -1094,7 +1092,7 @@ constraints, ticket relations — and no single brief can recover it
1094
1092
  (observed: a 15-brief group whose reason, "the origin keeps going down under
1095
1093
  attack traffic", appeared in none of them, so option selection measured a
1096
1094
  per-page ratio instead of origin load). If the file is absent, ask once via
1097
- `AskUserQuestion`:
1095
+ `prompt_user`:
1098
1096
 
1099
1097
  - `Create the group context skeleton now (Recommended for a new task-group)`
1100
1098
  → run `okstra group-context init --project-root <PROJECT_ROOT> --task-group <task-group>`
@@ -1168,7 +1166,7 @@ group context skeleton: <abs>/<task-group>/group-context.md (fill before /okst
1168
1166
  - If a URL fetch fails or hits an auth wall, do not guess — ask for a
1169
1167
  paste.
1170
1168
  - Never fabricate content for empty sections; use `_(none)_`.
1171
- - Echo each `AskUserQuestion` outcome on one short line.
1169
+ - Echo each `prompt_user` outcome on one short line.
1172
1170
 
1173
1171
  ## Failure Modes
1174
1172
 
@@ -1177,7 +1175,7 @@ group context skeleton: <abs>/<task-group>/group-context.md (fill before /okst
1177
1175
  | `project.json not found` | okstra not set up in this project | Tell user to run `okstra-setup`; stop. |
1178
1176
  | `briefs/<group>/<id>.md already exists` | duplicate task-key | Step 2c overwrite prompt. |
1179
1177
  | Source file not found | bad path in Step 1 | Re-ask. |
1180
- | Linear/Jira MCP not connected | tracker MCP missing from the session | Try `ToolSearch` once → if still missing, ask the user to paste the body. Never guess. |
1178
+ | Linear/Jira MCP not connected | tracker MCP missing from the session | Try the host's tool-discovery capability (when available) once → if still missing, ask the user to paste the body. Never guess. |
1181
1179
  | URL fetch fails or 401 / login wall | intranet, paywall, JS-rendered page | Ask the user to paste the core body. Never guess. |
1182
1180
  | Tracker body is in non-MD format (ADF / HTML) | Jira description, Notion blocks | Convert format only, preserve semantics. Annotate the conversion in the `format:` meta. |
1183
1181
  | Brief comes out skeletal after synthesis | source too thin AND user picked `User input` with little context | Switch to inline input mode and ask one or two targeted questions. |
@@ -5,9 +5,9 @@ pending list of `intent-check:` / `conversion-block:` rows.
5
5
 
6
6
  ## 6.5c. Batch the questions
7
7
 
8
- For each pending row, formulate one question. Because the `AskUserQuestion`
9
- tool caps options at 4 per call and questions per call also at 4, split into
10
- batches of ≤ 4 questions.
8
+ For each pending row, formulate one question. Batch only when the current
9
+ host's question interface can present every question and option within its
10
+ live limits; otherwise ask sequentially without dropping any option.
11
11
 
12
12
  **Cap — 12 questions per Step 6.5 run.** When the pending list exceeds 12
13
13
  rows, sort by priority and ask only the top 12 this round:
@@ -27,7 +27,7 @@ which briefs are emitted, and how re-runs stay idempotent.
27
27
 
28
28
  ## 2. Ask once at the top parent
29
29
 
30
- If there is one or more child, `AskUserQuestion` (single-select):
30
+ If there is one or more child, `prompt_user` (single-select):
31
31
 
32
32
  - **Label**: `"This ticket has sub-tickets. How should the child tree be handled?"`
33
33
  - **Options**:
@@ -18,7 +18,7 @@ The display name is typed at join. Do not invent a default name.
18
18
 
19
19
  ## Step 0: CLI
20
20
 
21
- Run as a separate Bash tool call with literal leading token:
21
+ Run as a separate shell-command tool call with literal leading token:
22
22
 
23
23
  ```bash
24
24
  okstra chat --help
@@ -34,17 +34,21 @@ This skill owns review calibration only:
34
34
 
35
35
  ## Step 0 — Preflight
36
36
 
37
+ <!-- BEGIN FRAGMENT: host-runtime-selection -->
38
+ Resolve `<host-runtime>` from the launcher's `OKSTRA_RUNTIME_HOST` when present; otherwise use the registered host ID declared by the current harness. Do not infer the host from worker models, installed executables, or `PATH`. If neither source identifies the host, report that it is unknown instead of substituting another host. Keep the same resolved host when retrying against another project directory.
39
+ <!-- END FRAGMENT: host-runtime-selection -->
40
+
37
41
  <!-- BEGIN FRAGMENT: bash-invocation-rule -->
38
- Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
42
+ Run one shell-command tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — literal command tokens keep execution permissions reviewable):
39
43
  <!-- END FRAGMENT: bash-invocation-rule -->
40
44
 
41
45
  ```bash
42
- okstra preflight --runtime claude-code
46
+ okstra preflight --runtime <host-runtime>
43
47
  ```
44
48
 
45
49
  Branch on the fixed first line:
46
50
  - `Okstra preflight: ready` → carry `Project root` as a literal string; every later step is anchored on it.
47
- - `Okstra preflight: failed` → the check only sees the cwd of the Bash call, so a project that is not the cwd can read as missing setup. Ask whether the user pointed at a specific project directory; if they did, re-run targeting it: `okstra preflight --runtime claude-code --cwd <that-dir>` (`--cwd` is the sanctioned way to target a project — a leading `cd` would break the permission match). Only if this also fails do you show `Reason` and `Recovery`, then stop.
51
+ - `Okstra preflight: failed` → the check only sees the cwd of the Bash call, so a project that is not the cwd can read as missing setup. Ask whether the user pointed at a specific project directory; if they did, re-run targeting it: `okstra preflight --runtime <host-runtime> --cwd <that-dir>` (`--cwd` is the sanctioned way to target a project — a leading `cd` would break the permission match). Only if this also fails do you show `Reason` and `Recovery`, then stop.
48
52
 
49
53
  <!-- BEGIN FRAGMENT: preflight-outdated-cli -->
50
54
  If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
@@ -154,7 +158,7 @@ it. **Every reviewer receives the same brief** — two reviewers are worth their
154
158
 
155
159
  - the diff, plus how to read whole files at the reviewed commit: the `worktreePath` when it is non-empty, otherwise `git -C <projectRoot> show <headCommit>:<path>`
156
160
  - the project layout in one or two lines (where source, tests, and — if the routing found one — domain / ports / adapters live)
157
- - **the whole census** — every cell of all four axes, verbatim
161
+ - **the whole census** — the shared rule catalog once and every grouped cell of all four axes, verbatim
158
162
  - the absolute paths of every applied pack (from step 2's routing)
159
163
  - the calibration path, written out in full as Step 2 fixed it — `~/.agents/skills/okstra-code-review/references/review-calibration.md`. The verdict format, the severity points, and the rules for a legitimate `clean` are defined there, not in the brief; a reviewer that cannot open this file cannot return a usable verdict, so never hand it a relative path or a "next to the skill" hint
160
164
 
@@ -169,7 +173,7 @@ Axis scope — each reviewer works all four axes; the `Reads` column names each
169
173
  | `state-and-tests` | non-test source file × `state-and-tests`, test file × `state-and-tests` | `clean-code.md` "Mutation and state boundaries" and "Testing discipline"; plus the routed language pack's self-mock signals |
170
174
  | `general` | non-test source file × `general` | correctness bugs, swallowed errors, `clean-code.md` "Untrusted input and security" (work that grows faster than the input before a size bound rejects it, input building SQL / shell / paths / fetch targets / templates / regexes, a new entry point without auth or ownership checks, secrets or personal data in code, logs or responses, evaluating input), clear performance problems, core principle 7 (fix at the cause — nothing previously green edited to silence a failure); `clean-code.md` "Wrapping a third-party call" — a recovery branch the library already performs, a comment naming a condition the call site never establishes, a rethrow that drops the original error; plus the routed language pack's language-specific traps |
171
175
 
172
- Say it plainly in every brief: **the census is law** — work the cells exactly as given, return a verdict for every one, never re-derive the list. There is no length budget; dropping a finding to stay brief is the failure this skill exists to prevent.
176
+ The materialized prompt injects the `code-reviewer` duty contract. Keep task briefs focused on the assigned census, comparison range, rule paths, and result format; do not restate the injected duty.
173
177
 
174
178
  Capture each raw model return under `.okstra/agent-invocations/code-review/.tmp/`. Publish it with
175
179
  `okstra agent-prompt materialize-result`, publish the immutable completion marker with
@@ -179,12 +183,19 @@ review result and cannot contribute a verdict.
179
183
 
180
184
  ## Step 3.5 — Audit the coverage
181
185
 
182
- Diff each reviewer's verified `returnedBody` cells against the census. Any cell without a verdict
183
- → dispatch **one gap-fill invocation per reviewer**, on that reviewer's slot, carrying only its missing cells and the same brief. Each
184
- gap-fill uses a new invocation ID and repeats the full materialize → verify → dispatch → materialize-result →
185
- complete → verify-completion boundary from Step 3. Repeat until every reviewer has a verdict for every cell.
186
+ Keep normal output to one compact Coverage Verdicts row per cell, with explicit rule IDs and one source anchor. No target-by-bullet verdict table or repeated per-rule clean prose.
187
+
188
+ For each reviewer separately, audit its grouped rows and explicit rule-ID accounting using its verified completion marker:
189
+
190
+ ```bash
191
+ okstra code-review check-coverage --project-root <projectRoot> --census <grouped-census-path> --completion <completion-path> [--completion <gap-fill-completion-path> ...]
192
+ ```
193
+
194
+ The command reuses the existing completion verification boundary and refuses mixed model assignments. Exit 0 means every cell accounts for all rules in its group with valid source evidence and no unknown cell ID; exit 1 lists missing cells, missing rule IDs or invalid grouped verdicts; exit 2 reports invalid input or completion evidence. It validates the shared catalog and source anchors, but does not establish whether the selected references are exhaustive or a verdict is true.
195
+
196
+ For missing checks, dispatch one gap-fill invocation per reviewer on that reviewer's original slot, carrying only its incomplete grouped cells, their shared catalog entries, the same range and rule paths. Ask for one complete row for each such cell, not separate replies per missing rule. Use a new invocation ID and repeat the full materialize → verify → dispatch → materialize-result → complete → verify-completion boundary. Re-run the audit with that reviewer's initial and gap-fill markers. Earlier usable verdicts remain unchanged. An invocation with unknown IDs is invalid; record its rejection and request a corrected new invocation before auditing the retained valid completions. Do not silently relabel IDs.
186
197
 
187
- A missing verdict is unfinished work, never an implicit `clean`. Do not start Step 3.6 while a single cell is unaccounted for.
198
+ The Checked/N/A/Findings fields must account for every expected rule ID exactly once; a generic clean or all-checked statement never covers an omitted rule. Do not start Step 3.6 until every reviewer passes the audit. If evidence remains unavailable, report the specific blocker and incomplete coverage instead of writing a completed review.
188
199
 
189
200
  ## Step 3.6 — Check every citation
190
201
 
@@ -198,19 +209,19 @@ okstra code-review check-lines --project-root <projectRoot> --base <baseCommit>
198
209
 
199
210
  ## Step 3.7 — Settle the findings
200
211
 
201
- Reviewers are wrong in a way a census cannot catch: a claim about code they did not open. What you settle depends on the reviewer count. A `must-fix` or `should-fix` with no `failure` or no `evidence` is rejected as "no failure scenario" in either case — the calibration requires both.
212
+ Reviewers are wrong in a way a census cannot catch: a claim about code they did not open. What you settle depends on the reviewer count. Before pairing findings, check every finding, including agreed ones, against its `kind`: an `execution-error` needs an evidenced reachable `failure`; a `design-violation` needs an applicable named rule, code evidence, and concrete `impact`. Both need a fix. Missing or unsupported required evidence goes to `Rejected` with the specific reason; do not demand a runtime failure for a design violation.
202
213
 
203
214
  **Two reviewers.** Pair the findings first: two findings are the same finding when they cite the same `path:line` and describe the same defect. Then:
204
215
 
205
- - **Agreed** — both reviewers report it with the same severity and timing. It stands as reported; you do not adjudicate it.
216
+ - **Agreed** — both reviewers report it with the same severity and timing. It keeps its severity and timing after the evidence and kind checks above; agreement does not replace those checks.
206
217
  - **Graded differently** — both report it, with a different severity or timing. Pick one of the two values after reading the code, and record both values and your reason in the finding. Never pick a third value.
207
218
  - **One-sided** — one reviewer reports it and the other returned `clean` for that cell or reported something else. Adjudicate it as below.
208
219
 
209
220
  **One reviewer.** Adjudicate every finding it returns.
210
221
 
211
- **Adjudicating** a finding: open each `evidence` location at `headCommit`, plus the definition of every symbol its `failure` depends on, and check the `failure` against that code.
222
+ **Adjudicating** a finding: open each `evidence` location at `headCommit` and the definitions its claim depends on. For `execution-error`, check reachability and the stated wrong result. For `design-violation`, read the assigned rule and check the cited violation and concrete `impact`. An axis does not determine the kind.
212
223
 
213
- - **Confirmed** — the code produces the stated failure. The finding keeps its severity and timing.
224
+ - **Confirmed** — the code supports the execution failure or the applicable design violation and its impact. The finding keeps its severity and timing.
214
225
  - **Rejected** — the code contradicts the claim (the called function never reads `this`, the value cannot be null there, the branch is unreachable). Move it to `Rejected` with the contradicting `path:line` and one sentence on what it shows.
215
226
 
216
227
  Rejecting is a statement about the code, so it always carries a `path:line`. Never reject for being unconvinced. Apart from choosing between two reviewers' values, never promote, demote, or re-time a finding. Confirmed and agreed `park` findings go to `Parked`, unscored.
@@ -257,5 +268,5 @@ generatedAt: <YYYY-MM-DD HH:MM>
257
268
  - **No finding without a fix.** Readability findings carry a pseudocode sketch; naming findings carry a concrete alternative name.
258
269
  - **The census is law.** A reviewer that rebuilds its own worklist reintroduces exactly the run-to-run variance this skill exists to kill.
259
270
  - **Every cell gets a verdict.** `clean` is a result, not an omission; the audit treats a gap as unfinished work.
260
- - **Only a checked claim scores.** A finding reaches the score after its citation is on a changed line and its failure holds against the code; everything else is `Parked` or `Rejected`, with its reason.
271
+ - **Only a checked claim scores.** A finding reaches the score after its citation is on a changed line and its kind-specific evidence holds against the code; everything else is `Parked` or `Rejected`, with its reason.
261
272
  - **The report prose follows `Report language`.** Paths, identifiers, rule names, and quoted code stay verbatim.
@@ -8,13 +8,48 @@ The census is the worklist: an explicit list of cells, each one a `target × axi
8
8
 
9
9
  This is what makes the cell count reproducible: the axis list is fixed at four, so the census size depends only on the diff. Splitting an axis per individual rule would make the count depend on how many rules the reader chose to enumerate that day — the exact variance this census exists to remove.
10
10
 
11
- The specific rule a finding violates is named **inside the verdict**, in its `rule` field (`review-calibration.md`), not in the cell label. One cell can therefore carry findings from several rules of its group, and it still counts as one cell for coverage.
11
+ The specific rule a finding violates is named **inside the verdict**, in its `rule` field (`review-calibration.md`), not in the cell label. One cell can therefore carry findings from several rules of its group, and it still counts as one cell. Rule coverage is represented inside each grouped verdict below.
12
+
13
+ ## Shared rule catalog and grouped cells
14
+
15
+ Keep one cell per target and axis. Each reviewer returns one compact row per cell, not one prose verdict per pack bullet. Define the shared rule catalog once, then let cells reference its group. Do not materialize a target-by-bullet Cartesian product.
16
+
17
+ Use these named review criteria. Each criterion includes the relevant subconditions and carve-outs in its canonical pack; those bullets do not get separate verdict IDs:
18
+
19
+ | Group | Rule IDs and scope |
20
+ |---|---|
21
+ | `structural` | `S1` reuse/DRY; `S2` simplicity/KISS; `S3` responsibilities/SOLID; `S4` current scope/YAGNI; when an architecture pack is routed: `S5` thin ports, `S6` I/O-only adapters, `S7` domain-object placement, `S8` dependency direction, `S9` boundary ports, `S10` domain-owned business rules |
22
+ | `semantic` | `M1` truthful and specific names; `M2` single-purpose readable flow, including size and nesting; `M3` comments and explanatory values |
23
+ | `state-and-tests` | `T1` state and mutation boundaries; `T2` no self-mocking; `T3` behavioral outcome assertions; `T4` discriminating fixtures, test titles and branch coverage |
24
+ | `general` | `G1` correctness and regressions; `G2` error handling and third-party wrappers; `G3` untrusted-input and resource bounds; `G4` performance; `G5` routed language/framework and project conventions |
25
+
26
+ Write the selected IDs with their absolute pack/section references under `## Rules`. Multiple references for a criterion go in the same Reference field, separated by semicolons. Do not turn every heading, bullet, formatter instruction or example into another rule. Read the original references rather than restating their bodies.
27
+
28
+ Write targets under `## Cells`; each has a stable `C-CR-...` ID, the target and its group. A function target includes its path and position, e.g. `src/orders/pricing.py:42 calculate_price`. Distinguish same-line anonymous functions with their columns. Targets are repository-relative; no absolute or parent-traversal paths.
29
+
30
+ ```markdown
31
+ ## Rules
32
+ | Rule | Group | Reference |
33
+ |---|---|---|
34
+ | S1 | structural | /absolute/pack/clean-code.md#DRY |
35
+ | S2 | structural | /absolute/pack/clean-code.md#KISS |
36
+
37
+ ## Cells
38
+ | Cell | Target | Group |
39
+ |---|---|---|
40
+ | C-CR-0001 | src/orders/pricing.py | structural |
41
+ | C-CR-0002 | src/orders/totals.py | structural |
42
+ ```
43
+
44
+ The example is abbreviated; the actual catalog includes the named criteria above for every used group. Add `S5` through `S10` only for a routed architecture pack; otherwise the base catalog has 16 criteria. Freeze the catalog and cell IDs before dispatch and send the same census to each reviewer. The command `code-review check-coverage` enforces catalog membership, rule accounting and source-anchor validity against this saved census; the lead still checks that the catalog names the intended references and that verdicts are true.
45
+
46
+ Do not omit a cell because a rule seems inapplicable. Record that criterion under `N/A` with its reason in the compact row. The normal case lists checked rule IDs once with one inspected source anchor for the cell. This preserves rule-level omission checks without repeating paths and explanations for every rule.
12
47
 
13
48
  ## The one principle
14
49
 
15
50
  **Membership is mechanical; judgment only ever decides a cell's verdict.**
16
51
 
17
- Never ask "does this rule apply to this file?" while building the census — that question is the reviewer's, and its answer is a verdict (`clean` when the rule turns out not to apply). Over-inclusion is cheap: a clean verdict costs one line. A judged exclusion is variance — it is the exact failure this census exists to remove, because the next run judges differently and the review changes shape.
52
+ Never ask "does this rule apply to this file?" while building the census — that question is the reviewer's, and its answer is a verdict (`n/a` with a reason when the rule turns out not to apply). Over-inclusion is cheap: a clean verdict costs one line. A judged exclusion is variance — it is the exact failure this census exists to remove, because the next run judges differently and the review changes shape.
18
53
 
19
54
  Two consequences:
20
55
 
@@ -94,6 +129,7 @@ In the orchestrator's response, before Step 3:
94
129
 
95
130
  - one table per axis, in the order `structural`, `semantic`, `state-and-tests`, `general`, listing every cell
96
131
  - the exclusion list, one line per file, each with its reason
132
+ - the shared rule catalog, grouped cell table and total cells per axis
97
133
  - the applied pack list (absolute paths)
98
134
  - the two completion criteria with their actual numbers
99
135