okstra 0.224.0 → 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 (65) 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/duties/code-reviewer.json +5 -3
  12. package/runtime/prompts/wizard/prompts.ko.json +10 -0
  13. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +2 -0
  14. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +3 -1
  15. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +6 -0
  16. package/runtime/python/okstra_ctl/adapters/providers/zai/adapter.py +5 -1
  17. package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +25 -0
  18. package/runtime/python/okstra_ctl/code_review_coverage.py +213 -0
  19. package/runtime/python/okstra_ctl/code_review_target.py +31 -2
  20. package/runtime/python/okstra_ctl/coverage_census.py +2 -2
  21. package/runtime/python/okstra_ctl/dispatch_core.py +15 -0
  22. package/runtime/python/okstra_ctl/domain/worker_exec.py +6 -0
  23. package/runtime/python/okstra_ctl/phases/implementation_planning/authoring.py +66 -0
  24. package/runtime/python/okstra_ctl/phases/implementation_planning/instructions/plan-body-verification.md +5 -1
  25. package/runtime/python/okstra_ctl/phases/implementation_planning/profile.md +1 -0
  26. package/runtime/python/okstra_ctl/phases/implementation_planning/round_completion.py +177 -0
  27. package/runtime/python/okstra_ctl/render.py +2 -0
  28. package/runtime/python/okstra_ctl/run.py +23 -2
  29. package/runtime/python/okstra_ctl/team.py +12 -18
  30. package/runtime/python/okstra_ctl/wizard/__init__.py +1 -0
  31. package/runtime/python/okstra_ctl/wizard/confirmation.py +2 -0
  32. package/runtime/python/okstra_ctl/wizard/ids.py +3 -0
  33. package/runtime/python/okstra_ctl/wizard/registry.py +11 -0
  34. package/runtime/python/okstra_ctl/wizard/render.py +1 -0
  35. package/runtime/python/okstra_ctl/wizard/state.py +3 -0
  36. package/runtime/python/okstra_ctl/wizard/steps_options.py +41 -1
  37. package/runtime/python/okstra_ctl/worker_dispatch.py +9 -0
  38. package/runtime/python/okstra_ctl/worker_request.py +12 -1
  39. package/runtime/schemas/execution-manifest-v2.schema.json +1 -0
  40. package/runtime/skills/_fragments/bash-invocation-rule.md +1 -1
  41. package/runtime/skills/_fragments/host-question-interface.md +1 -0
  42. package/runtime/skills/_fragments/host-runtime-selection.md +1 -0
  43. package/runtime/skills/_fragments/wizard-bash-invocation-rule.md +1 -1
  44. package/runtime/skills/_fragments/wizard-preflight.md +1 -1
  45. package/runtime/skills/_fragments/wizard-render-bundle.md +4 -4
  46. package/runtime/skills/okstra-brief-gen/SKILL.md +45 -47
  47. package/runtime/skills/okstra-brief-gen/references/reporter-confirmations.md +3 -3
  48. package/runtime/skills/okstra-brief-gen/references/tracker-recursion.md +1 -1
  49. package/runtime/skills/okstra-chat/SKILL.md +1 -1
  50. package/runtime/skills/okstra-code-review/SKILL.md +26 -15
  51. package/runtime/skills/okstra-code-review/references/census-rules.md +38 -2
  52. package/runtime/skills/okstra-code-review/references/review-calibration.md +39 -15
  53. package/runtime/skills/okstra-container-build/SKILL.md +6 -2
  54. package/runtime/skills/okstra-discussion/SKILL.md +6 -6
  55. package/runtime/skills/okstra-inspect/SKILL.md +5 -3
  56. package/runtime/skills/okstra-manager/SKILL.md +8 -4
  57. package/runtime/skills/okstra-memory/SKILL.md +1 -1
  58. package/runtime/skills/okstra-pr-gen/SKILL.md +10 -6
  59. package/runtime/skills/okstra-rollup/SKILL.md +6 -2
  60. package/runtime/skills/okstra-run/SKILL.md +8 -7
  61. package/runtime/skills/okstra-schedule-gen/SKILL.md +13 -6
  62. package/runtime/skills/okstra-setup/SKILL.md +8 -4
  63. package/runtime/skills/okstra-setup/references/project-config.md +3 -3
  64. package/runtime/skills/okstra-usage/SKILL.md +5 -1
  65. package/runtime/skills/okstra-user-response/SKILL.md +7 -3
@@ -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
 
@@ -4,32 +4,30 @@ Read this before verdicting. It defines the shape of an answer, not the rules
4
4
 
5
5
  ## One verdict per cell
6
6
 
7
- Return verdicts in census order, one per cell. A clean cell is a single line — do not pad it with prose:
8
-
9
- ```
10
- - cell: src/orders/pricing.py × structural — verdict: clean
11
- ```
7
+ Return detailed findings in census order. Normal checks appear only in compact coverage rows with the shared rule IDs; do not repeat clean cell summaries outside that table. A grouped row does not cover rules absent from its status fields.
12
8
 
13
9
  A cell covers its whole rule group, so it can carry several findings from several rules; repeat the cell once per finding and name the violated rule in each:
14
10
 
15
11
  ```
16
12
  - cell: src/orders/pricing.py × structural
17
13
  verdict: finding
14
+ id: F-001
15
+ kind: design-violation
18
16
  rule: DRY
19
17
  line: 42
20
18
  severity: must-fix
21
19
  timing: now
22
20
  snippet: `discount = subtotal * 0.15 if tier == "gold" else 0`
23
- failure: when the gold rate changes in `domain/tiers.py`, this line keeps charging 15% and the two prices disagree.
21
+ impact: the same tier-to-rate decision has two homes; changing the gold rate requires coordinating both implementations.
24
22
  evidence: `domain/tiers.py:18`
25
23
  note: The same tier→rate table is already in `domain/tiers.py:18`; a rate change now has two homes. Call the existing lookup instead of re-expressing it here.
26
24
  ```
27
25
 
28
- Fields: `cell` (`target × axis`, exactly as the census wrote it — the axis is the rule group, and there is never one cell per individual rule), `verdict` (`clean` | `finding`), `rule` (the specific rule this finding violates, spelled as its pack spells it; omitted on `clean`), `line` (a line **this diff changed**, numbered at the head commit), `severity`, `timing` (`now` | `park`, below), `snippet` (the quoted changed line, verbatim — the orchestrator locates the finding by it), `failure` (required at `must-fix` and `should-fix`: the input or state that produces the wrong result, and what goes wrong), `evidence` (required at `must-fix` and `should-fix`: every `path:line` you opened to establish the failure — the definitions of the symbols the claim depends on, not only the cited line), `note` (1–2 sentences: what is wrong plus a concrete fix).
26
+ Fields: `cell` (`target × axis`, exactly as the census wrote it — the axis is the rule group, and there is never one cell per individual rule), `verdict` (`finding` for a detailed finding), `rule` (the specific rule this finding violates, spelled as its pack spells it), `line` (a line **this diff changed**, numbered at the head commit), `severity`, `timing` (`now` | `park`, below), `snippet` (the quoted changed line, verbatim — the orchestrator locates the finding by it), `id` (a finding ID such as `F-001`), `kind` (`execution-error` | `design-violation`), `failure` (for an execution error: reachable input or state and the wrong result), `impact` (for a design violation: the concrete design or maintenance consequence of violating the applicable rule), `evidence` (the `path:line` locations opened to establish the claim, including definitions it depends on), `note` (1–2 sentences: what is wrong plus a concrete fix).
29
27
 
30
- **Open the definition before you claim its behaviour.** A claim about what a called function, method, or value does — that it reads `this`, throws, mutates its argument, returns `null` — cites that definition in `evidence`. A claim you could not check against its definition is not a finding; it is `clean`. The orchestrator opens every `evidence` location and rejects a finding the code contradicts.
28
+ **Open the definition before you claim its behaviour.** A claim about what a called function, method, or value does — that it reads `this`, throws, mutates its argument, returns `null` — cites that definition in `evidence`. A claim you could not check against its definition remains unevaluated: report the missing evidence and leave that rule unaccounted in the grouped row. Do not turn an inspection failure into `clean`. The orchestrator opens every `evidence` location and rejects a finding the code contradicts.
31
29
 
32
- There is no length budget. Dropping a real finding to stay brief is the failure this review exists to prevent, and a missing cell is re-dispatched as unfinished work, never read as a clean.
30
+ There is no length budget. Dropping a real finding to stay brief is the failure this review exists to prevent, and a cell with an unaccounted rule is re-dispatched as unfinished work, never read as a clean.
33
31
 
34
32
  ## Severity and points
35
33
 
@@ -39,23 +37,49 @@ There is no length budget. Dropping a real finding to stay brief is the failure
39
37
  | `should-fix` | a clear rule violation with no immediate breakage — a name that misleads, a function that fails the plain-English test, business logic in the wrong layer | 2 |
40
38
  | `nit` | real but small; the fix is cheap and obviously better | 1 |
41
39
 
42
- Grade the defect, not your confidence. If you are not confident, the verdict is `clean` — see the hedge test below.
40
+ Grade the established consequence, not your confidence. An unsupported candidate is not a finding; incomplete inspection remains a coverage gap.
41
+
42
+ ## Separate execution errors from design violations
43
+
44
+ - `execution-error`: establish a reachable input or state, the expected behavior, and the actual wrong result in `failure`, supported by opened code in `evidence`.
45
+ - `design-violation`: identify the applicable pack rule, quote the violating code, and describe its concrete design or maintenance consequence in `impact`, supported by `evidence`. A runtime failure is not required. Examples include duplicated domain decisions, business logic in an I/O adapter, misleading names, and a function that violates the assigned readability rule.
46
+
47
+ Both kinds carry a concrete fix. Severity and timing are independent of kind: a design violation can be `should-fix` and `now` even when current outputs are correct. Neither an alternative implementation nor a preferred name alone establishes a violation. An unreachable defensive scenario does not establish an execution error. The orchestrator checks these criteria in Step 3.7; semantic correctness is a review judgment, not a machine-validated guarantee.
48
+
49
+ ## Compact rule coverage
50
+
51
+ Return exactly one row per assigned cell under `## Coverage Verdicts`. Keep detailed prose for findings; normal checks share one inspected source location. Rule IDs refer to the shared catalog, not to individual pack bullets.
52
+
53
+ ```markdown
54
+ ## Coverage Verdicts
55
+ | Cell | Checked | N/A | Findings | Evidence |
56
+ |---|---|---|---|---|
57
+ | C-CR-0001 | S1,S2,S3,S4 | - | - | src/orders/pricing.py:42 |
58
+ | C-CR-0002 | S2,S3,S4 | - | S1=F-001 | src/orders/totals.py:18 |
59
+ | C-CR-0003 | - | T1=no state mutation; T2=non-test source; T3=non-test source; T4=no tests or new branches | - | src/orders/schema.py:1 |
60
+ ```
61
+
62
+ The example uses the base structural and state-and-tests catalogs. `Checked` means inspected and no actionable finding for those rules. A criterion includes all applicable subconditions in its references; one inapplicable subcondition does not make the entire criterion N/A. Explicitly list the IDs; `all`, counts and ID ranges are not verdicts. `N/A` assigns `rule=reason`; `Findings` assigns `rule=finding-id`. Separate assignments with semicolons and multiple finding IDs for one rule with commas. Use `-` for an empty field. Every expected rule appears exactly once across the three status fields. Never infer an omitted rule as clean.
63
+
64
+ Every row supplies one literal repository-relative source `path:line` for that cell's target file. Do not substitute a rule-pack citation such as `cc:5`, a global alias such as `E01`, or another target's path. The checker verifies that the file and line exist inside the project. Additional definitions and evidence remain in finding bodies.
65
+
66
+ A finding ID names a body outside this section, in this reviewer's original or gap-fill result. N/A reasons are short inline applicability explanations, not aliases or rule references. Missing sources, unread rules and blocked inspection remain unaccounted checks and name their blocker. A partial row is rejected with the missing rule IDs; gap-fill rechecks that grouped cell, not every individual rule as a separate output row.
43
67
 
44
- **Name the failure.** A `must-fix` or `should-fix` states the input or state that produces the wrong result (`if the header is absent, line 42 throws before the 401 is returned`). Code that works as written is not a defect, however you would have written it differently: alternative structures, defensive guards for states no caller reaches, and "consider extracting / renaming / memoizing" are improvements. An improvement is either `park` or `clean`, never a `now` finding.
68
+ The command validates structured accounting and source locations; it cannot prove inspection quality or semantic truth. The lead still checks findings and applicability claims against the code.
45
69
 
46
70
  ## Timing — `now` or `park`
47
71
 
48
72
  - `now` — this change should fix it before it merges: a defect on a changed line, or a rule violation the change introduced.
49
73
  - `park` — real, but not this change's to fix: the fix lies outside the diff, or the code is correct and the finding asks for more (a regression test for behaviour that already works, a follow-up refactor). Parked findings are listed in the report and **do not score**.
50
74
 
51
- Every finding carries `timing`. When unsure, ask whether merging without the fix leaves this change wrong; if not, it is `park`.
75
+ Every finding carries `timing`. For `now`, establish either an execution error or an applicable design rule violation introduced on a changed line. Correct current outputs alone do not make a design violation `park`.
52
76
 
53
77
  ## When `clean` is the right verdict
54
78
 
55
79
  `clean` is a result, not a concession. Return it when:
56
80
 
57
81
  - **The defect sits only on lines this diff did not change.** Findings anchor on changed lines; pre-existing warts are not this change's problem.
58
- - **The rule does not apply to this cell.** The census over-includes on purpose, so cells where a rule turns out to be irrelevant are expected — that is exactly what a `clean` verdict records.
82
+ - **The rule does not apply to this cell.** Record the rule ID and applicability reason under `N/A`; other rules in the row may still be checked.
59
83
  - **The change is mechanical for this rule.** A signature-only or type-annotation-only edit does not reopen a function's body-level rules; a function the diff neither added nor renamed does not reopen its name.
60
84
 
61
85
  **The hedge test breaks a borderline `must-fix` or `should-fix`.** At those two severities the finding is one you would defend without qualifiers. If your draft note reaches for a softener that questions whether the point is worth raising at all — "marginal", "arguably", "if you are touching it anyway" — you are looking at a `nit` or at nothing: downgrade it or verdict `clean`, but never ship a hedged `should-fix`.
@@ -92,8 +116,8 @@ Merged and adjudicated by the orchestrator, written to `reviewPath`. Sections, i
92
116
  - **Parked** — `park` findings, with their severity, not scored.
93
117
  - **Rejected** — findings the orchestrator's adjudication disproved or that cite no changed line: the reviewer's claim in one line, then the contradicting `path:line` and what it shows. Not scored.
94
118
 
95
- - **Coverage** — two or three lines: the reviewers (`provider/model`, one or two); N changed files → S `structural` / F `semantic` / T `state-and-tests` / G `general` cells, all verdicted by every reviewer; M files excluded with reasons; the applied packs, and any pack that was unavailable; and how the findings were settled — agreed, graded by the orchestrator, confirmed by the orchestrator, rejected. The four cell counts are at the census's granularity — one cell per target per axis.
96
- - **Findings** — each one opens with `` `path/to/file.py:42` `` + the verdict's `rule` name + severity + points, then the snippet as a blockquote, then the 1–2 sentence note with its fix. Its note then says how it was settled: `agreed`, `graded by orchestrator` (with both reviewers' values), or `confirmed by orchestrator`.
119
+ - **Coverage** — two or three lines: the reviewers (`provider/model`, one or two); N changed files → S `structural` / F `semantic` / T `state-and-tests` / G `general` cells, all verdicted by every reviewer; M files excluded with reasons; the applied packs, and any pack that was unavailable; and how the findings were settled — agreed, graded by the orchestrator, confirmed by the orchestrator, rejected. The four cell counts are at the census's granularity — one cell per target per axis. Also show grouped cells expected and covered per reviewer, list any missing rule IDs and count reasoned applicability exclusions. Never present the number of rule flags as the number of independent review targets.
120
+ - **Findings** — each one opens with `` `path/to/file.py:42` `` + the verdict's `rule` name + `kind` + severity + points, then the snippet as a blockquote, then the 1–2 sentence note with its fix, the execution `failure` or design `impact`, and supporting `evidence`. Its note then says how it was settled: `agreed`, `graded by orchestrator` (with both reviewers' values), or `confirmed by orchestrator`.
97
121
  - **Score** — every confirmed `now` finding gets a row, and the table is emitted even when there are none, with a single total row reading 0:
98
122
 
99
123
  ```