@fro.bot/systematic 3.17.0 → 3.18.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.
@@ -1,6 +1,6 @@
1
1
  # Persona Catalog
2
2
 
3
- 13 reviewer personas organized into always-on, cross-cutting conditional, and stack-specific conditional layers, plus CE-specific agents. The orchestrator uses this catalog to select which reviewers to spawn for each review.
3
+ 13 reviewer personas organized into a three-persona core, cross-cutting conditional, and stack-specific conditional layers, plus CE-specific conditional agents. The orchestrator uses this catalog to select which reviewers to spawn for each review.
4
4
 
5
5
  ## Shared persona pool
6
6
 
@@ -10,38 +10,29 @@ The `agents/review/` directory is a shared persona pool, not `ce:review`'s roste
10
10
  - `systematic:review:pattern-recognition-specialist` — dispatched by `deepen-plan`, `ce-plan`'s deepening workflow, and `ce-compound` for consistency, duplication, and pattern analysis.
11
11
  - `systematic:review:code-simplicity-reviewer` — dispatched by `ce-compound` for code-heavy issues.
12
12
 
13
- ## Always-on (4 personas + 2 CE agents)
13
+ ## Always-on (3 core personas)
14
14
 
15
- Spawned on every review regardless of diff content.
16
-
17
- **Persona agents (structured JSON output):**
15
+ Selected on every review regardless of diff content. These three are the only reviewers with no selection condition.
18
16
 
19
17
  | Persona | Agent | Focus |
20
18
  |---------|-------|-------|
21
19
  | `correctness` | `systematic:review:correctness-reviewer` | Logic errors, edge cases, state bugs, error propagation, intent compliance |
22
20
  | `testing` | `systematic:review:testing-reviewer` | Coverage gaps, weak assertions, brittle tests, missing edge case tests |
23
- | `maintainability` | `systematic:review:maintainability-reviewer` | Coupling, complexity, naming, dead code, premature abstraction |
24
21
  | `project-standards` | `systematic:review:project-standards-reviewer` | AGENTS.md compliance -- frontmatter, references, naming, cross-platform portability, tool selection |
25
22
 
26
- **CE agents (unstructured output, synthesized separately):**
27
-
28
- | Agent | Focus |
29
- |-------|-------|
30
- | `systematic:review:agent-native-reviewer` | Verify new features are agent-accessible |
31
- | `systematic:research:learnings-researcher` | Search docs/solutions/ for past issues related to this PR's modules and patterns |
32
-
33
- ## Conditional (8 personas)
23
+ ## Conditional (9 personas)
34
24
 
35
- Spawned when the orchestrator identifies relevant patterns in the diff. The orchestrator reads the full diff and reasons about selection -- this is agent judgment, not keyword matching.
25
+ Spawned only when the orchestrator identifies a relevant surface in the diff. The orchestrator reads the full diff and reasons about selection -- this is agent judgment, not keyword matching. Record `selection_reason` and a non-empty `selection_surface` for each selected conditional persona.
36
26
 
37
27
  | Persona | Agent | Select when diff touches... |
38
28
  |---------|-------|---------------------------|
29
+ | `maintainability` | `systematic:review:maintainability-reviewer` | Materially adds or reshapes abstractions, raises cross-module coupling, adds state/control-flow complexity, changes naming or ownership structure, removes dead code, or performs a broad refactor. Do not select it for a tiny prose or fixture correction with no structural decision and no user- or agent-facing or other specialist surface. |
39
30
  | `security` | `systematic:review:security-reviewer` | Auth middleware, public endpoints, user input handling, permission checks, secrets management |
40
31
  | `performance` | `systematic:review:performance-reviewer` | Database queries, ORM calls, loop-heavy data transforms, caching layers, async/concurrent code |
41
32
  | `api-contract` | `systematic:review:api-contract-reviewer` | Route definitions, serializer/interface changes, event schemas, exported type signatures, API versioning |
42
33
  | `data-migrations` | `systematic:review:data-migrations-reviewer` | Migration files, schema changes, backfill scripts, data transformations |
43
34
  | `reliability` | `systematic:review:reliability-reviewer` | Error handling, retry logic, circuit breakers, timeouts, background jobs, async handlers, health checks |
44
- | `adversarial` | `systematic:review:adversarial-reviewer` | Diff has >=50 changed lines of executable code (not prose/instruction Markdown, JSON schemas, or config), OR touches auth, payments, data mutations, external API integrations, or other high-risk domains regardless of file type |
35
+ | `adversarial` | `systematic:review:adversarial-reviewer` | >=50 changed lines of executable production code, excluding tests, generated files, lockfiles, instruction/prose Markdown, JSON schemas, and config; OR regardless of file type for auth, payments, data mutations, external APIs, or another explicitly high-risk domain |
45
36
  | `cli-readiness` | `systematic:review:cli-readiness-reviewer` | CLI command definitions, argument parsing, CLI framework usage, command handler implementations |
46
37
  | `previous-comments` | `systematic:review:previous-comments-reviewer` | **PR-only.** Reviewing a PR that has existing review comments or review threads from prior review rounds. Skip entirely when no PR metadata was gathered in Stage 1. |
47
38
 
@@ -53,18 +44,41 @@ These reviewers keep their original opinionated lens. They are additive with the
53
44
  |---------|-------|---------------------------|
54
45
  | `kieran-typescript` | `systematic:review:kieran-typescript-reviewer` | TypeScript components, services, hooks, utilities, or shared types |
55
46
 
56
- ## CE Conditional Agents (migration-specific)
47
+ ## CE Conditional Agents (3)
57
48
 
58
- These CE-native agents provide specialized analysis beyond what the persona agents cover. Spawn them when the diff includes database migrations, schema.rb, or data backfills.
49
+ These CE-native agents provide specialized analysis beyond what the persona agents cover. Their output is unstructured and synthesized separately; they never receive a raw-return dispatch record. Announce and report their selection reason/surface in the team and Coverage instead.
59
50
 
60
- | Agent | Focus |
61
- |-------|-------|
62
- | `systematic:review:deployment-verification-agent` | Produces Go/No-Go deployment checklist with SQL verification queries and rollback procedures |
51
+ | Agent | Select when diff includes... |
52
+ |-------|------------------------------|
53
+ | `systematic:review:agent-native-reviewer` | A user- or agent-facing UI, CLI, tool, or workflow capability, or a changed access path where agent parity or discoverability is material. Do not select it for every review. |
54
+ | `systematic:research:learnings-researcher` | Bug, regression, or hardening work; a recurring failure class; a change to a documented solution or module; or a plan/PR that cites relevant prior art. Do not select it for every review. |
55
+ | `systematic:review:deployment-verification-agent` | Database migrations (`db/migrate/*.rb`, `db/schema.rb`) or data backfill scripts. |
63
56
 
64
57
  ## Selection rules
65
58
 
66
- 1. **Always spawn all 4 always-on personas** plus the 2 CE always-on agents.
67
- 2. **For each cross-cutting conditional persona**, the orchestrator reads the diff and decides whether the persona's domain is relevant. This is a judgment call, not a keyword match.
59
+ 1. **Always select exactly the three core personas:** `correctness`, `testing`, and `project-standards`. Nothing else is always-on.
60
+ 2. **For each cross-cutting conditional persona**, read the diff and decide whether the persona's domain is relevant. This is a judgment call, not a keyword match. A tiny prose or fixture correction is core-only only when it has no structural decision and no user- or agent-facing behavior, access change, or other specialist surface; a structural refactor may add `maintainability`; an auth feature may add `security` and `reliability`.
68
61
  3. **For each stack-specific conditional persona**, use file types and changed patterns as a starting point, then decide whether the diff actually introduces meaningful work for that reviewer. Do not spawn language-specific reviewers just because one config or generated file happens to match the extension.
69
- 4. **For CE conditional agents**, spawn when the diff includes migration files (`db/migrate/*.rb`, `db/schema.rb`) or data backfill scripts.
70
- 5. **Announce the team** before spawning with a one-line justification per conditional reviewer selected.
62
+ 4. **For CE conditional agents**, select `agent-native-reviewer` for agent-facing capability changes, `learnings-researcher` for bug/regression/hardening or recurring-defect work, and `deployment-verification-agent` for migrations or data backfills. Their output is unstructured; report their selection reason/surface in the team and Coverage rather than a dispatch record.
63
+ 5. **Record selection metadata.** Every selected structured conditional persona records `selection_reason` and a non-empty `selection_surface`; core personas may omit both.
64
+ 6. **Announce the team** before spawning with a one-line rationale and the triggering repository-relative paths/surfaces per selected conditional. Distinguish core reviewers, selected conditionals, an explicit "no conditional selected" case, and selected-but-failed/malformed/validation-unavailable reviewers. Never label an unselected reviewer as failed.
65
+ 7. **Reviewer count is an outcome, not a target.** Never restore a fixed floor; a smaller selected set is not automatically better or worse than a larger one.
66
+
67
+ ## Selection scenarios
68
+
69
+ | Scenario | Selected |
70
+ |----------|----------|
71
+ | Tiny prose or fixture correction with no structural decision and no user- or agent-facing or other specialist surface | `correctness`, `testing`, `project-standards` only; no runtime probe |
72
+ | Structural refactor adding or reshaping abstractions, coupling, state, or control flow | core plus `maintainability` |
73
+ | Agent-facing UI/CLI/tool/workflow capability or changed access path | core plus CE `agent-native-reviewer` |
74
+ | Bug, regression, or hardening work; recurring failure class; documented solution/module touched; plan/PR citing prior art | core plus CE `learnings-researcher` |
75
+ | Auth surface | core plus `security` |
76
+ | Migration or backfill | core plus `data-migrations` and CE `deployment-verification-agent` |
77
+ | Public API/route/serializer/type-signature change | core plus `api-contract` |
78
+ | Error handling, retries, timeouts, background jobs | core plus `reliability` |
79
+ | Query, loop, caching, or concurrency hot path | core plus `performance` |
80
+ | Renderer or asset-delivery change needing runtime evidence | a focused execution probe (independent of reviewer selection); a change that separately triggers a conditional still selects that reviewer |
81
+
82
+ ## Execution probes (separate parent decision)
83
+
84
+ A focused execution probe is independent of reviewer selection. A pure renderer or asset-delivery change with no conditional surface may be core plus a probe, but a change that separately triggers `agent-native-reviewer`, `security`, or another conditional still selects those reviewers. An execution probe is not a reviewer and never substitutes for risk-critical reviewer coverage; selecting a probe never adds `maintainability` and never restores a reviewer floor. When selected, record the probe target and its permission boundary in Coverage.
@@ -134,10 +134,10 @@ This fails because: no pipe-delimited tables, no severity-grouped `###` headers,
134
134
  - **Residual Actionable Work section** -- include only when unresolved actionable findings were handed off for later work
135
135
  - **Pre-existing section** -- separate table, no confidence column (these are informational)
136
136
  - **Filtered (not validated) section** -- findings where Stage 5b returned `validated: false`. Rendered as a pipe-delimited table with columns `#`, `File`, `Issue`, `Reviewer`, `Confidence`, `Validator reason`. These findings are surfaced for human review, not removed. Omit this section when Stage 5b produced no filtered findings.
137
- - **Learnings & Past Solutions section** -- results from learnings-researcher, with links to docs/solutions/ files
138
- - **Agent-Native Gaps section** -- results from agent-native-reviewer. Omit if no gaps found.
137
+ - **Learnings & Past Solutions section** -- render only when CE `learnings-researcher` was selected and returned relevant output; results with links to docs/solutions/ files. Omit otherwise.
138
+ - **Agent-Native Gaps section** -- render only when CE `agent-native-reviewer` was selected and returned relevant output. Omit otherwise.
139
139
  - **Deployment Notes section** -- key checklist items from deployment-verification-agent. Omit if the agent did not run.
140
- - **Coverage section** -- suppressed count with original confidences, residual risks, testing gaps, failed reviewers, disposition reconciliation, and risk-coverage entries with their citing input finding IDs and blocked-entry exit conditions
140
+ - **Coverage section** -- suppressed count with original confidences, residual risks, testing gaps, failed reviewers, disposition reconciliation, and risk-coverage entries with their citing input finding IDs and blocked-entry exit conditions. For raw returns, distinguish `findings`, `empty`, `malformed`, `never_returned`, `validation_unavailable` (the persisted raw dispatch outcome, distinct from the artifact-level `validation.status: "unavailable"`), and `environment-screen` rejection, and state what was admitted or withheld
141
141
  - **Summary uses blockquotes** for verdict, reasoning, and fix order
142
142
  - **Horizontal rule** (`---`) separates findings from verdict
143
143
  - **`###` headers** for each section -- never plain text headers
@@ -57,7 +57,13 @@
57
57
  },
58
58
  "dispatch_outcome": {
59
59
  "type": "string",
60
- "enum": ["findings", "empty", "malformed", "never_returned"]
60
+ "enum": [
61
+ "findings",
62
+ "empty",
63
+ "malformed",
64
+ "never_returned",
65
+ "validation_unavailable"
66
+ ]
61
67
  },
62
68
  "input_finding_count": {
63
69
  "type": "integer",
@@ -24,7 +24,7 @@ The supplied diff is the primary source of truth. Use the supplied paths and lin
24
24
  <output-contract>
25
25
  Return exactly one JSON payload to the parent. The payload contains the complete schema for every finding, including both the merge tier and the detail tier (`why_it_matters`, `evidence`, and `suggested_fix` when present).
26
26
 
27
- Do not write any file. Do not use a Run ID or an artifact path. Persistence is owned by the parent orchestrator: it validates this returned payload, adds parent-owned provenance, and writes only conforming data. This rule is the same in every supported harness.
27
+ Do not write any file. Do not use a Run ID or an artifact path. Persistence is owned by the parent orchestrator: it structurally validates this returned payload with the packaged validator before it parses any field, adds parent-owned provenance, and writes only conforming data. Return exactly one JSON payload; never wrap it in prose, markdown, or a parent-owned annotation. This rule is the same in every supported harness.
28
28
 
29
29
  The schema below defines the payload's fields and bounds. Its transport is inline for this contract; any schema metadata describing a compact return or a separate detail artifact is superseded by this output contract.
30
30
 
@@ -53,7 +53,7 @@ Rules:
53
53
  - Every returned finding MUST include at least one evidence item grounded in the actual code. Detail fields are part of the returned payload, not a second output.
54
54
  - Evidence is bounded to at most 5 entries of at most 500 characters each. Split a longer trail across entries when it fits; otherwise retain a bounded `excerpt` with `{ "overflow": true, "excerpt": "..." }`. Never silently truncate evidence.
55
55
  - Finding paths MUST be repository-relative. The schema rejects absolute paths, while the parent-side validator in Unit 3 detects environment values because JSON Schema cannot infer where a string came from.
56
- - The parent adds `harness`, `dispatch_outcome`, and `disposition` after validating the return. Do not invent those parent-owned fields. The parent uses only the canonical values defined by the schema (`findings`, `empty`, `malformed`, `never_returned` and `surviving`, `merged`, `suppressed`, `filtered`, `rejected`).
56
+ - The parent adds `harness`, `dispatch_outcome`, and `disposition` after validating the return. Do not invent those parent-owned fields. The parent uses only the canonical values defined by the schema (`findings`, `empty`, `malformed`, `never_returned`, `validation_unavailable` and `surviving`, `merged`, `suppressed`, `filtered`, `rejected`).
57
57
  - Set pre_existing to true ONLY for issues in unchanged code that are unrelated to this diff. If the diff makes the issue newly relevant, it is NOT pre-existing.
58
58
  - You are operationally read-only. You may use non-mutating inspection commands, including read-oriented `git` / `gh` commands, to gather evidence. Do not write files, edit project files, change branches, commit, push, create PRs, or otherwise mutate the checkout or repository state.
59
59
  - Set `autofix_class` accurately -- not every finding is `advisory`. Use this decision guide:
@@ -78,6 +78,10 @@ Reviewer name: {reviewer_name}
78
78
 
79
79
  Intent: {intent_summary}
80
80
 
81
+ Selection reason: {selection_reason}
82
+
83
+ Selection surface: {selection_surface}
84
+
81
85
  Changed files: {file_list}
82
86
 
83
87
  Diff:
@@ -97,3 +101,5 @@ Diff:
97
101
  | `{file_list}` | Stage 1 output | List of changed files from the scope step |
98
102
  | `{diff}` | Stage 1 output | The actual diff content to review |
99
103
  | `{reviewer_name}` | Stage 3 output | Persona name used in the returned `reviewer` field |
104
+ | `{selection_reason}` | Stage 3 output | One-line reason this conditional persona was selected. Empty for core personas, which have no selection condition |
105
+ | `{selection_surface}` | Stage 3 output | Non-empty repository-relative paths/surfaces that triggered selection. Empty for core personas. These are bounded domain hints for the reviewer, not fields of the returned payload |
@@ -19,7 +19,10 @@ run artifact for a run that never started.
19
19
 
20
20
  The parent initializes the artifact as `in_progress` before dispatch, with all
21
21
  selected personas initialized as `never_returned`, and updates each dispatch
22
- entry as returns arrive. A completed run becomes `completed` or `degraded`. An
22
+ entry as returns arrive. A persona that returned but whose raw validator could
23
+ not run is updated from `never_returned` to `dispatch_outcome:
24
+ "validation_unavailable"`; it is never omitted and never left as
25
+ `never_returned`. A completed run becomes `completed` or `degraded`. An
23
26
  interrupted or failed run becomes `abnormal` with its stated termination
24
27
  reason. An unfinished `in_progress` artifact is evidence of an abnormal run,
25
28
  not evidence of a clean run. Never infer a clean run from an absent artifact.
@@ -28,8 +31,8 @@ The artifact is parent-owned. Per-agent full-detail JSON files are written
28
31
  only for findings admitted after the parent completes schema and
29
32
  environment-value validation. A finding rejected by environment-value
30
33
  detection is not persisted; other findings from the same return may proceed.
31
- A payload rejected at top level, or a rejected or never-returned persona,
32
- does not produce a per-agent file. If a later confidence or validation stage
34
+ A payload rejected at top level, or a rejected, never-returned, or
35
+ validation-unavailable persona, does not produce a per-agent file. If a later confidence or validation stage
33
36
  changes an input disposition, the parent updates the record and synthesis
34
37
  ledger before finalizing the artifact.
35
38
 
@@ -108,8 +111,9 @@ The artifact must preserve these distinctions:
108
111
  ```
109
112
 
110
113
  - `dispatches` has an entry for every selected persona. `dispatch_outcome`
111
- records what a persona returned: `findings`, `empty`, `malformed`, or
112
- `never_returned`. A rejection reason is the exact safe validation reason,
114
+ records what a persona returned: `findings`, `empty`, `malformed`,
115
+ `never_returned`, or `validation_unavailable`. A rejection reason is the exact
116
+ safe validation reason,
113
117
  naming persona and field without echoing the offending value. Dispatch
114
118
  outcome is separate from finding disposition. Conditional selections record
115
119
  their triggering changed-file paths in `selection_surface` and the announced
@@ -129,6 +133,13 @@ The artifact must preserve these distinctions:
129
133
  never includes the offending value. Do not enumerate rejected findings or
130
134
  assign them input IDs. A finding-level environment rejection uses the same
131
135
  summary entry while admitted findings from that return continue normally.
136
+ New writers must emit a rejected-summary row only for `findings` or
137
+ `malformed`. The schema_version 1 validator deliberately continues to accept a
138
+ historical `empty` or `never_returned` rejected-summary row for backward
139
+ compatibility with existing artifacts, but that reader leniency is not
140
+ authoring permission to emit either. The validator rejects a rejected-summary
141
+ row for `validation_unavailable`, the additive outcome this contract
142
+ introduces, and writers must never emit it.
132
143
  Disposition counts are weighted by `rejected_finding_count` for that summary
133
144
  entry, so their sum equals the total number of findings observed, not the
134
145
  number of ledger rows. A malformed JSON return with no safely enumerable
@@ -180,6 +191,71 @@ the review degrades while conforming returns continue through synthesis. Only
180
191
  an orchestration or storage failure that prevents the parent from producing the
181
192
  required run artifact is run-fatal.
182
193
 
194
+ ## Raw-return admission and validation availability
195
+
196
+ Before a persona return is parsed into fields, screened for environment values,
197
+ assessed for evidence, synthesized, or persisted, the parent admits it with the
198
+ packaged structural validator:
199
+
200
+ ```bash
201
+ # Resolve the validator relative to the skill's own directory.
202
+ SKILL_DIR="<skill directory stated when this skill loads>";
203
+ node "$SKILL_DIR/scripts/validate-review.mjs" return <<'REVIEW_RETURN_A1B2C3D4'
204
+ <the persona's returned JSON payload, copied verbatim>
205
+ REVIEW_RETURN_A1B2C3D4
206
+ ```
207
+
208
+ Before each invocation, choose a fresh delimiter for that exact raw payload from
209
+ a safe token alphabet (`A-Z`, `0-9`, `_`), verify the delimiter is absent as a
210
+ complete line in that exact payload, and never reuse a fixed delimiter. The
211
+ `REVIEW_RETURN_A1B2C3D4` token above is only an illustration. Open the heredoc
212
+ with a single-quoted heredoc opener (`<<'DELIM'`) and close it with a line
213
+ containing exactly that delimiter. The payload travels on stdin, never in argv;
214
+ never use unquoted interpolation or command substitution, and never write the
215
+ payload to a temp file.
216
+
217
+ The parent maps the result to `dispatch_outcome`, keeping lifecycle, structural
218
+ validity, environment screening, and evidence assessment separate:
219
+
220
+ - **exit 0** — structurally admitted. Parse the already structurally validated
221
+ JSON without logging the raw text, then run the existing environment-value
222
+ screen over that parsed object before persistence. Zero findings is `empty`;
223
+ one or more findings is `findings`. Admission is structural only: it never
224
+ asserts that a finding's claims or cited evidence are true.
225
+ - **exit 1** — `malformed`. Record bounded validator diagnostics only; do not
226
+ parse, screen, or persist payload fields or values.
227
+ - **exit 2**, a missing or unreadable helper, or a launch failure — validation
228
+ unavailable. Withhold the return and report the exact unavailability and what
229
+ was withheld. Update that selected persona's preinitialized dispatch entry
230
+ from `never_returned` to `dispatch_outcome: "validation_unavailable"` with
231
+ `input_finding_count: 0` and, optionally, a safe `rejection_reason` naming the
232
+ exit status, missing helper, or launch failure without payload values. Set
233
+ `run_status` to `degraded`. Never omit the dispatch entry, never leave it as
234
+ `never_returned`, never label it `malformed`, never admit the payload, and
235
+ never fabricate a rejected-summary ledger row. `never_returned` is a
236
+ task-lifecycle fact for a task that did not return. Validation unavailable is
237
+ not malformed and is not never_returned.
238
+
239
+ **Dispatch identity binding.** Structural admission does not prove who produced a
240
+ return. Immediately after `exit 0` and before the environment-value screen,
241
+ persistence, or synthesis, the parent parses the admitted return's `reviewer`
242
+ field and confirms it equals the dispatched persona. A return whose `reviewer`
243
+ does not match the dispatched persona is an identity mismatch: reject the whole
244
+ return as `dispatch_outcome: "malformed"`, record only a bounded rejection reason
245
+ naming the expected persona, set `run_status` to `degraded`, and do not admit,
246
+ screen, persist, or synthesize its payload.
247
+
248
+ `validation_unavailable` is an additive enum value: `schema_version` stays `1`,
249
+ existing v1 artifacts remain valid, and no new field or migration is introduced.
250
+ A run that contains `validation_unavailable` evidence records a zero
251
+ `input_finding_count`, no admitted input finding for that persona, and a
252
+ non-`completed` run status; unavailable evidence can never finalize as a clean,
253
+ completed run.
254
+ The word `unavailable` also names the artifact-level `validation.status` value;
255
+ that is a different object and phase, and these fields are never repurposed for
256
+ raw-dispatch availability. These admission states are surfaced in the report's
257
+ Coverage, which reports the exact unavailability and what was withheld.
258
+
183
259
  ## Environment-value validation
184
260
 
185
261
  The parent recursively inspects every string leaf without logging the raw
@@ -217,10 +293,18 @@ validating it. This ordering makes the artifact validatable at all: without
217
293
  a real validation result.
218
294
 
219
295
  After writing `review-summary.json`, the parent resolves and runs a validator
220
- in this order: the bundled `systematic-validate-review-artifact <path>` command
221
- first, then the npm-installed `systematic validate-review-artifact <path>`
222
- command. Both can be present at once. The bundled command ships beside the
223
- prose being executed, so it is the one whose behavior matches the contract.
296
+ in this order: the skill-local helper first, then the bundled
297
+ `systematic-validate-review-artifact <path>` command, then the npm-installed
298
+ `systematic validate-review-artifact <path>` command. All can be present at
299
+ once. The skill-local helper ships beside the prose being executed, so it is
300
+ the one whose behavior matches the contract; the other two remain fallbacks.
301
+
302
+ ```bash
303
+ # Resolve the validator relative to the skill's own directory.
304
+ SKILL_DIR="<skill directory stated when this skill loads>";
305
+ ARTIFACT_PATH="<review-summary.json path>";
306
+ node "$SKILL_DIR/scripts/validate-review.mjs" artifact "$ARTIFACT_PATH"
307
+ ```
224
308
  The parent runs the first command it resolves and reads its result; it does not
225
309
  merely test whether a name is on `PATH`, because a version-manager shim can be
226
310
  present there and fail on every invocation.
@@ -296,8 +380,9 @@ against at all.
296
380
  The risk-critical surfaces are `security`, `data-migrations`, `api-contract`,
297
381
  `reliability`, and `performance`. They are the conditional personas selected
298
382
  specifically for the matching diff shape in Stage 3. If one of those selected
299
- personas has `dispatch_outcome: "malformed"` or
300
- `dispatch_outcome: "never_returned"`, the review verdict must not be clean:
383
+ personas has `dispatch_outcome: "malformed"`,
384
+ `dispatch_outcome: "never_returned"`, or `dispatch_outcome:
385
+ "validation_unavailable"`, the review verdict must not be clean:
301
386
  it is blocking unless another persona covered the lost surface with validated
302
387
  evidence. For this rule, a validated finding from another persona covers a
303
388
  lost risk-critical surface if and only if the finding's `file` appears in the