@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,10 +1,11 @@
1
1
  import { z } from 'zod';
2
- export declare const REVIEW_ARTIFACT_CUSTOM_MESSAGES: readonly ['severity count must match rejected finding count', 'filtered findings require a validation reason', 'risk-critical dispatches require a non-empty selection surface', 'satisfied risk coverage requires a citing input finding ID', 'unsatisfied risk coverage must not cite an input finding ID', 'passed validation must not include a reason; non-passed validation requires a reason'];
2
+ export declare const REVIEW_ARTIFACT_CUSTOM_MESSAGES: readonly ['severity count must match rejected finding count', 'filtered findings require a validation reason', 'risk-critical dispatches require a non-empty selection surface', 'satisfied risk coverage requires a citing input finding ID', 'unsatisfied risk coverage must not cite an input finding ID', 'passed validation must not include a reason; non-passed validation requires a reason', 'validation_unavailable dispatches must record zero input findings', 'a completed run must not contain validation_unavailable evidence', 'a validation_unavailable persona must not have an input finding', 'every synthesized input finding ID must resolve to an admitted ledger row', 'every provenance submitter must be represented by a cited admitted ledger row', 'satisfied risk coverage must cite an admitted ledger row', 'duplicate admitted input finding IDs are not allowed', 'every cited admitted reviewer must appear in provenance.submitters', 'provenance.submitters must not contain duplicate reviewers', 'provenance.agreement_credit must not contain duplicate reviewers', 'provenance.agreement_credit must not overlap provenance.submitters', 'provenance.agreement_credit requires an eligible returned persona with admitted evidence', 'satisfied risk coverage must cite a validated finding on the lost persona selection surface', 'satisfied risk coverage must cite an admitted ledger row owned by another persona'];
3
3
  export declare const DispatchOutcomeSchema: z.ZodEnum<{
4
4
  empty: "empty";
5
5
  findings: "findings";
6
6
  malformed: "malformed";
7
7
  never_returned: "never_returned";
8
+ validation_unavailable: "validation_unavailable";
8
9
  }>;
9
10
  export declare const DispositionSchema: z.ZodEnum<{
10
11
  filtered: "filtered";
@@ -131,6 +132,7 @@ export declare const ReviewArtifactSchema: z.ZodObject<{
131
132
  findings: "findings";
132
133
  malformed: "malformed";
133
134
  never_returned: "never_returned";
135
+ validation_unavailable: "validation_unavailable";
134
136
  }>;
135
137
  input_finding_count: z.ZodNumber;
136
138
  rejection_reason: z.ZodOptional<z.ZodString>;
@@ -266,4 +268,172 @@ export declare const ReviewArtifactSchema: z.ZodObject<{
266
268
  }, z.core.$strict>>;
267
269
  }, z.core.$strict>;
268
270
  export type ReviewArtifact = z.infer<typeof ReviewArtifactSchema>;
271
+ /** A single finding as returned by a reviewer persona. */
272
+ export declare const SubAgentFindingSchema: z.ZodObject<{
273
+ title: z.ZodString;
274
+ severity: z.ZodEnum<{
275
+ P0: "P0";
276
+ P1: "P1";
277
+ P2: "P2";
278
+ P3: "P3";
279
+ }>;
280
+ file: z.ZodString;
281
+ line: z.ZodNumber;
282
+ why_it_matters: z.ZodString;
283
+ autofix_class: z.ZodEnum<{
284
+ advisory: "advisory";
285
+ gated_auto: "gated_auto";
286
+ manual: "manual";
287
+ safe_auto: "safe_auto";
288
+ }>;
289
+ owner: z.ZodEnum<{
290
+ "downstream-resolver": "downstream-resolver";
291
+ human: "human";
292
+ release: "release";
293
+ "review-fixer": "review-fixer";
294
+ }>;
295
+ requires_verification: z.ZodBoolean;
296
+ suggested_fix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
297
+ confidence: z.ZodNumber;
298
+ evidence: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
299
+ overflow: z.ZodLiteral<true>;
300
+ excerpt: z.ZodString;
301
+ }, z.core.$strict>]>>;
302
+ pre_existing: z.ZodBoolean;
303
+ }, z.core.$strict>;
304
+ /** A finding after the parent adds its disposition; parent-owned. */
305
+ export declare const ParentFindingSchema: z.ZodObject<{
306
+ title: z.ZodString;
307
+ severity: z.ZodEnum<{
308
+ P0: "P0";
309
+ P1: "P1";
310
+ P2: "P2";
311
+ P3: "P3";
312
+ }>;
313
+ file: z.ZodString;
314
+ line: z.ZodNumber;
315
+ why_it_matters: z.ZodString;
316
+ autofix_class: z.ZodEnum<{
317
+ advisory: "advisory";
318
+ gated_auto: "gated_auto";
319
+ manual: "manual";
320
+ safe_auto: "safe_auto";
321
+ }>;
322
+ owner: z.ZodEnum<{
323
+ "downstream-resolver": "downstream-resolver";
324
+ human: "human";
325
+ release: "release";
326
+ "review-fixer": "review-fixer";
327
+ }>;
328
+ requires_verification: z.ZodBoolean;
329
+ suggested_fix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
330
+ confidence: z.ZodNumber;
331
+ evidence: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
332
+ overflow: z.ZodLiteral<true>;
333
+ excerpt: z.ZodString;
334
+ }, z.core.$strict>]>>;
335
+ pre_existing: z.ZodBoolean;
336
+ disposition: z.ZodEnum<{
337
+ filtered: "filtered";
338
+ merged: "merged";
339
+ rejected: "rejected";
340
+ suppressed: "suppressed";
341
+ surviving: "surviving";
342
+ }>;
343
+ }, z.core.$strict>;
344
+ /** The raw return contract a reviewer persona must satisfy. */
345
+ export declare const SubAgentReturnSchema: z.ZodObject<{
346
+ reviewer: z.ZodString;
347
+ findings: z.ZodArray<z.ZodObject<{
348
+ title: z.ZodString;
349
+ severity: z.ZodEnum<{
350
+ P0: "P0";
351
+ P1: "P1";
352
+ P2: "P2";
353
+ P3: "P3";
354
+ }>;
355
+ file: z.ZodString;
356
+ line: z.ZodNumber;
357
+ why_it_matters: z.ZodString;
358
+ autofix_class: z.ZodEnum<{
359
+ advisory: "advisory";
360
+ gated_auto: "gated_auto";
361
+ manual: "manual";
362
+ safe_auto: "safe_auto";
363
+ }>;
364
+ owner: z.ZodEnum<{
365
+ "downstream-resolver": "downstream-resolver";
366
+ human: "human";
367
+ release: "release";
368
+ "review-fixer": "review-fixer";
369
+ }>;
370
+ requires_verification: z.ZodBoolean;
371
+ suggested_fix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
372
+ confidence: z.ZodNumber;
373
+ evidence: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
374
+ overflow: z.ZodLiteral<true>;
375
+ excerpt: z.ZodString;
376
+ }, z.core.$strict>]>>;
377
+ pre_existing: z.ZodBoolean;
378
+ }, z.core.$strict>>;
379
+ residual_risks: z.ZodArray<z.ZodString>;
380
+ testing_gaps: z.ZodArray<z.ZodString>;
381
+ }, z.core.$strict>;
382
+ /** The parent-persisted record contract; adds harness and dispatch outcome. */
383
+ export declare const ParentRecordSchema: z.ZodObject<{
384
+ reviewer: z.ZodString;
385
+ harness: z.ZodEnum<{
386
+ "claude-code": "claude-code";
387
+ opencode: "opencode";
388
+ pi: "pi";
389
+ }>;
390
+ dispatch_outcome: z.ZodEnum<{
391
+ empty: "empty";
392
+ findings: "findings";
393
+ malformed: "malformed";
394
+ never_returned: "never_returned";
395
+ validation_unavailable: "validation_unavailable";
396
+ }>;
397
+ findings: z.ZodArray<z.ZodObject<{
398
+ title: z.ZodString;
399
+ severity: z.ZodEnum<{
400
+ P0: "P0";
401
+ P1: "P1";
402
+ P2: "P2";
403
+ P3: "P3";
404
+ }>;
405
+ file: z.ZodString;
406
+ line: z.ZodNumber;
407
+ why_it_matters: z.ZodString;
408
+ autofix_class: z.ZodEnum<{
409
+ advisory: "advisory";
410
+ gated_auto: "gated_auto";
411
+ manual: "manual";
412
+ safe_auto: "safe_auto";
413
+ }>;
414
+ owner: z.ZodEnum<{
415
+ "downstream-resolver": "downstream-resolver";
416
+ human: "human";
417
+ release: "release";
418
+ "review-fixer": "review-fixer";
419
+ }>;
420
+ requires_verification: z.ZodBoolean;
421
+ suggested_fix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
422
+ confidence: z.ZodNumber;
423
+ evidence: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
424
+ overflow: z.ZodLiteral<true>;
425
+ excerpt: z.ZodString;
426
+ }, z.core.$strict>]>>;
427
+ pre_existing: z.ZodBoolean;
428
+ disposition: z.ZodEnum<{
429
+ filtered: "filtered";
430
+ merged: "merged";
431
+ rejected: "rejected";
432
+ suppressed: "suppressed";
433
+ surviving: "surviving";
434
+ }>;
435
+ }, z.core.$strict>>;
436
+ residual_risks: z.ZodArray<z.ZodString>;
437
+ testing_gaps: z.ZodArray<z.ZodString>;
438
+ }, z.core.$strict>;
269
439
  export {};
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Bounded, no-write structural validator for a single raw `ce:review` persona
3
+ * return read from stdin.
4
+ *
5
+ * The runner is deliberately synchronous to match `runLegacyCli`'s shape and to
6
+ * avoid a temp-file or async stdin dance. Behavior is injectable (argv, fd,
7
+ * TTY, chunk reader, sinks) so tests can exercise real code paths without
8
+ * mutating global state.
9
+ */
10
+ export declare const VALIDATE_REVIEW_RETURN_USAGE = "Usage: systematic validate-review-return";
11
+ /** Input cap in bytes. Reads stop at {@link MAX_REVIEW_RETURN_BYTES} plus one. */
12
+ export declare const MAX_REVIEW_RETURN_BYTES: number;
13
+ /** Maximum number of projected issue lines emitted before the summary. */
14
+ export declare const MAX_PROJECTED_ISSUE_LINES = 8;
15
+ export declare const REVIEW_RETURN_VALID_MESSAGE = "Review return is valid";
16
+ export declare const REVIEW_RETURN_EMPTY_MESSAGE = "Review return is empty";
17
+ export declare const REVIEW_RETURN_INVALID_UTF8_MESSAGE = "Review return is not valid UTF-8";
18
+ export declare const REVIEW_RETURN_MALFORMED_JSON_MESSAGE = "Review return is not valid JSON";
19
+ export declare const REVIEW_RETURN_OVERSIZED_MESSAGE = "Review return exceeds the 1 MiB input limit";
20
+ export declare const REVIEW_RETURN_READ_FAILED_MESSAGE = "Review return could not be read from stdin";
21
+ export declare const REVIEW_RETURN_TTY_MESSAGE = "validate-review-return reads one JSON document from stdin; interactive input is not supported";
22
+ /** One bounded, payload-safe projected validation issue. */
23
+ export interface ReviewReturnIssue {
24
+ readonly path: string;
25
+ readonly code: string;
26
+ }
27
+ export type ReviewReturnValidation = {
28
+ readonly ok: true;
29
+ } | {
30
+ readonly ok: false;
31
+ /** Full issue count, even when {@link ReviewReturnValidation.issues} is capped. */
32
+ readonly total: number;
33
+ readonly issues: readonly ReviewReturnIssue[];
34
+ };
35
+ /**
36
+ * Pure value-validation seam. Projects only safe Zod path segments and issue
37
+ * codes; never issue messages, unrecognized key names, or payload values.
38
+ */
39
+ export declare function validateReviewReturnValue(value: unknown): ReviewReturnValidation;
40
+ export declare function formatReviewReturnValidationFailure(total: number): string;
41
+ export type ReadChunk = (fd: number, buffer: Buffer, offset: number, length: number, position: number | null) => number;
42
+ export interface ReviewReturnValidatorOptions {
43
+ readonly argv: readonly string[];
44
+ readonly fd?: number;
45
+ readonly isTTY?: boolean;
46
+ readonly readChunk?: ReadChunk;
47
+ readonly outputSink?: (message: string) => void;
48
+ readonly errorSink?: (message: string) => void;
49
+ }
50
+ /**
51
+ * Validate exactly one raw persona return from stdin.
52
+ *
53
+ * Exit statuses:
54
+ * - 0: a schema-valid `SubAgentReturnSchema` document.
55
+ * - 1: the returned input was empty, oversized, invalid UTF-8, malformed JSON,
56
+ * carried trailing data, or failed the schema.
57
+ * - 2: the check did not run (usage, TTY, or stdin read failure).
58
+ */
59
+ export declare function runReviewReturnValidator(options: ReviewReturnValidatorOptions): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fro.bot/systematic",
3
- "version": "3.17.0",
3
+ "version": "3.18.0",
4
4
  "description": "Compound-engineering loops for OpenCode, Pi, and Claude Code",
5
5
  "type": "module",
6
6
  "homepage": "https://fro.bot/systematic",
@@ -53,13 +53,15 @@
53
53
  "schema:drift": "bun scripts/generate-config-schema.ts --check",
54
54
  "review-schema:generate": "bun scripts/generate-review-artifact-schema.ts",
55
55
  "review-schema:drift": "bun scripts/generate-review-artifact-schema.ts --check",
56
+ "ce-review-validator:build": "bun scripts/generate-ce-review-validator.ts",
57
+ "ce-review-validator:drift": "bun scripts/generate-ce-review-validator.ts --check",
56
58
  "agent-browser:build": "bun scripts/generate-agent-browser-skill.ts",
57
59
  "agent-browser:drift": "bun scripts/generate-agent-browser-skill.ts --check",
58
60
  "registry:build": "bun scripts/build-registry.ts",
59
61
  "registry:drift": "bun scripts/generate-registry.ts --check",
60
62
  "registry:validate": "bun scripts/build-registry.ts --validate-only",
61
63
  "claude-code:build": "bun scripts/build-claude-code-plugin.ts",
62
- "postupgrade": "bun run build && bun run agent-browser:build && bun run schema:generate && bun run review-schema:generate && bun scripts/generate-registry.ts",
64
+ "postupgrade": "bun run build && bun run agent-browser:build && bun run schema:generate && bun run review-schema:generate && bun run ce-review-validator:build && bun scripts/generate-registry.ts",
63
65
  "prepublishOnly": "bun run build && bun run schema:generate"
64
66
  },
65
67
  "keywords": [
@@ -112,7 +114,7 @@
112
114
  "rimraf": "6.1.3",
113
115
  "semantic-release": "25.0.9",
114
116
  "semantic-release-export-data": "1.2.0",
115
- "typebox": "1.3.28",
117
+ "typebox": "1.3.29",
116
118
  "typescript": "7.0.2"
117
119
  },
118
120
  "dependencies": {
@@ -55,6 +55,7 @@ All tokens are optional. Each one present means one less thing to infer. When ab
55
55
 
56
56
  - **Skip all user questions.** Infer intent conservatively if the diff metadata is thin.
57
57
  - **Never edit files or externalize work.** Do not write `.context/systematic/ce-review/<run-id>/`, do not create todo files, and do not commit, push, or create a PR.
58
+ - **Report-only runs in memory.** Run raw-return structural validation, environment screening, synthesis, and reporting without writing a run directory, artifact, or ignore file.
58
59
  - **Safe for parallel read-only verification.** `mode:report-only` is the only mode that is safe to run concurrently with browser testing on the same checkout.
59
60
  - **Do not switch the shared checkout.** If the caller passes an explicit PR or branch target, `mode:report-only` must run in an isolated checkout/worktree or stop instead of running `gh pr checkout` / `git checkout`.
60
61
  - **Do not overlap mutating review with browser testing on the same checkout.** If a future orchestrator wants fixes, run the mutating review phase after browser testing or in an isolated checkout/worktree.
@@ -103,7 +104,7 @@ Routing rules:
103
104
 
104
105
  ## Reviewers
105
106
 
106
- 13 reviewer personas in layered conditionals, plus CE-specific agents. See the persona catalog included below for the full catalog.
107
+ 13 reviewer personas in layered conditionals, plus CE-specific conditional agents. See the persona catalog included below for the full catalog.
107
108
 
108
109
  **Always-on (every review):**
109
110
 
@@ -111,21 +112,19 @@ Routing rules:
111
112
  |-------|-------|
112
113
  | `systematic:review:correctness-reviewer` | Logic errors, edge cases, state bugs, error propagation |
113
114
  | `systematic:review:testing-reviewer` | Coverage gaps, weak assertions, brittle tests |
114
- | `systematic:review:maintainability-reviewer` | Coupling, complexity, naming, dead code, abstraction debt |
115
115
  | `systematic:review:project-standards-reviewer` | AGENTS.md compliance -- frontmatter, references, naming, portability |
116
- | `systematic:review:agent-native-reviewer` | Verify new features are agent-accessible |
117
- | `systematic:research:learnings-researcher` | Search docs/solutions/ for past issues related to this PR |
118
116
 
119
117
  **Cross-cutting conditional (selected per diff):**
120
118
 
121
119
  | Agent | Select when diff touches... |
122
120
  |-------|---------------------------|
121
+ | `systematic:review:maintainability-reviewer` | Materially adds/reshapes abstractions, coupling, state/control-flow complexity, naming/ownership, dead code, or a broad refactor |
123
122
  | `systematic:review:security-reviewer` | Auth, public endpoints, user input, permissions |
124
123
  | `systematic:review:performance-reviewer` | DB queries, data transforms, caching, async |
125
124
  | `systematic:review:api-contract-reviewer` | Routes, serializers, type signatures, versioning |
126
125
  | `systematic:review:data-migrations-reviewer` | Migrations, schema changes, backfills |
127
126
  | `systematic:review:reliability-reviewer` | Error handling, retries, timeouts, background jobs |
128
- | `systematic:review:adversarial-reviewer` | Diff >=50 changed non-test/non-generated/non-lockfile lines, or auth, payments, data mutations, external APIs |
127
+ | `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 |
129
128
  | `systematic:review:cli-readiness-reviewer` | CLI command definitions, argument parsing, CLI framework usage, command handler implementations |
130
129
  | `systematic:review:previous-comments-reviewer` | Reviewing a PR that has existing review comments or threads |
131
130
 
@@ -135,15 +134,17 @@ Routing rules:
135
134
  |-------|---------------------------|
136
135
  | `systematic:review:kieran-typescript-reviewer` | TypeScript components, services, hooks, utilities, or shared types |
137
136
 
138
- **CE conditional (migration-specific):**
137
+ **CE conditional (selected per diff):**
139
138
 
140
- | Agent | Select when diff includes migration files |
141
- |-------|------------------------------------------|
142
- | `systematic:review:deployment-verification-agent` | Produces deployment checklist with SQL verification queries |
139
+ | Agent | Select when diff includes... |
140
+ |-------|------------------------------|
141
+ | `systematic:review:agent-native-reviewer` | User- or agent-facing UI/CLI/tool/workflow capability, or a changed access path where agent parity/discoverability is material |
142
+ | `systematic:research:learnings-researcher` | Bug/regression/hardening work, a recurring failure class, a documented solution/module change, or a plan/PR citing relevant prior art |
143
+ | `systematic:review:deployment-verification-agent` | Database migrations, schema changes, or data backfills |
143
144
 
144
145
  ## Review Scope
145
146
 
146
- Every review spawns all 4 always-on personas plus the 2 CE always-on agents, then adds whichever cross-cutting and stack-specific conditionals fit the diff. The model naturally right-sizes: a small config change triggers 0 conditionals = 6 reviewers. An auth feature touching data migrations might trigger security + reliability + data-migrations = 9 reviewers.
147
+ Every review selects exactly the three always-on personas -- `correctness`, `testing`, and `project-standards` -- then adds the cross-cutting, stack-specific, and CE conditional agents that fit the diff. A tiny prose or fixture correction with no structural decision and no user- or agent-facing or other specialist surface selects only the core three; a structural refactor may add `maintainability`; an auth feature may add `security` and `reliability`. Reviewer count is an outcome, not a target or a success metric. All four modes (interactive, autofix, report-only, and headless) use the same reviewer-selection policy; only mutation and output behavior differ.
147
148
 
148
149
  ## Protected Artifacts
149
150
 
@@ -315,7 +316,7 @@ Intent: Simplify tax calculation by replacing the multi-tier rate lookup
315
316
  with a flat-rate computation. Must not regress edge cases in tax-exempt handling.
316
317
  ```
317
318
 
318
- Pass this to every reviewer in their spawn prompt. Intent shapes *how hard each reviewer looks*, not which reviewers are selected.
319
+ Pass this to every reviewer in their spawn prompt. Intent shapes *how hard each reviewer looks* and may clarify *what kind of surface* a change represents -- for example whether it is a bug/regression, an agent-facing capability, or a risk domain -- but it never selects a reviewer without a corresponding changed repository surface.
319
320
 
320
321
  **When intent is ambiguous:**
321
322
 
@@ -340,35 +341,43 @@ If a plan is found, read its **Requirements Trace** (R1, R2, etc.) and **Impleme
340
341
 
341
342
  ### Stage 3: Select reviewers
342
343
 
343
- Read the diff and file list from Stage 1. The 4 always-on personas and 2 CE always-on agents are automatic. For each cross-cutting and stack-specific conditional persona in the persona catalog included below, decide whether the diff warrants it. This is agent judgment, not keyword matching.
344
+ Read the diff and file list from Stage 1. Always select exactly the three always-on personas: `correctness`, `testing`, and `project-standards`. For each cross-cutting, stack-specific, and CE conditional in the persona catalog included below, decide whether the diff warrants it. This is agent judgment, not keyword matching. Intent, PR, and plan context may clarify whether a changed surface is a bug/regression, an agent-facing capability, or a risk domain, but they never select a reviewer without a corresponding changed repository surface.
344
345
 
345
- **File-type awareness for conditional selection:** Instruction-prose files (Markdown skill definitions, JSON schemas, config files) are product code but do not benefit from runtime-focused reviewers. The adversarial reviewer's techniques (race conditions, cascade failures, abuse cases) target executable code behavior. For diffs that only change instruction-prose files, skip adversarial unless the prose describes auth, payment, or data-mutation behavior. Count only executable code lines toward line-count thresholds.
346
+ - **`maintainability`** -- select when the diff 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.
347
+ - **CE `agent-native-reviewer`** -- select for user- or agent-facing UI/CLI/tool/workflow capabilities or changed access paths where agent parity or discoverability is material.
348
+ - **CE `learnings-researcher`** -- select for 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.
349
+ - **CE `deployment-verification-agent`** -- select for migrations, schema changes, or data backfills.
350
+ - **All other conditionals** -- preserve their catalog triggers: `security`, `performance`, `api-contract`, `data-migrations`, `reliability`, `adversarial`, `cli-readiness`, `previous-comments`, and `kieran-typescript`.
351
+
352
+ **File-type awareness for conditional selection:** Instruction-prose files (Markdown skill definitions, JSON schemas, config files) are product code but do not benefit from runtime-focused reviewers. The adversarial reviewer's techniques (race conditions, cascade failures, abuse cases) target executable code behavior. Select it for >=50 changed lines of executable production code -- excluding tests, generated files, lockfiles, instruction/prose Markdown, JSON schemas, and config -- or regardless of file type when the diff touches auth, payments, data mutations, external APIs, or another explicitly high-risk domain; count only executable production code lines toward the line-count threshold.
346
353
 
347
354
  **`previous-comments` is PR-only.** Only select this persona when Stage 1 gathered PR metadata (PR number or URL was provided as an argument, or `gh pr view` returned metadata for the current branch). Skip it entirely for standalone branch reviews with no associated PR -- there are no prior comments to check.
348
355
 
349
356
  Stack-specific personas are additive. A TypeScript API diff may warrant `kieran-typescript` plus `api-contract` and `reliability`.
350
357
 
351
- For CE conditional agents, check if the diff includes files matching `db/migrate/*.rb`, `db/schema.rb`, or data backfill scripts.
358
+ Record `selection_reason` and a non-empty `selection_surface` for each selected structured conditional persona; core personas may omit both. Pass the selection reason and surface into the structured reviewer prompt. CE conditional agents receive the same reason/surface in their unstructured prompt and report it in the team and Coverage; they never receive a raw-return dispatch record.
359
+
360
+ A selected risk-critical reviewer's failure (`malformed`, `never_returned`, or `validation_unavailable`) remains blocking and cannot disappear from Coverage by shrinking the reported team; it stays blocking unless a qualifying validated finding from another persona covers the lost surface.
352
361
 
353
362
  Announce the team before spawning:
354
363
 
355
364
  ```
356
- Review team:
357
- - correctness (always)
358
- - testing (always)
359
- - maintainability (always)
360
- - project-standards (always)
361
- - agent-native-reviewer (always)
362
- - learnings-researcher (always)
363
- - security -- new endpoint in routes.rb accepts user-provided redirect URL
365
+ Review team (all four modes use the same reviewer-selection policy):
366
+ - correctness (core)
367
+ - testing (core)
368
+ - project-standards (core)
369
+ - maintainability -- structural refactor reshaped the merge pipeline
370
+ - security -- new endpoint in routes.rb accepts a user-provided redirect URL
364
371
  - data-migrations -- adds migration 20260303_add_index_to_orders
372
+ - agent-native-reviewer -- new export CLI capability
373
+ - No other conditional selected: no additional surface triggered
365
374
  ```
366
375
 
367
- This is progress reporting, not a blocking confirmation.
376
+ This is progress reporting, not a blocking confirmation. Distinguish core reviewers, each selected conditional with a one-line rationale and its triggering repository-relative paths/surfaces, an explicit "no conditional selected" case, and any selected-but-failed/malformed/validation-unavailable reviewer. Never label an unselected reviewer as failed.
368
377
 
369
- Record each conditional persona's selection reason and triggering file paths
370
- on its dispatch record; see the [synthesis artifact contract](./references/synthesis-artifact-contract.md)
371
- for the field semantics.
378
+ Record each structured conditional persona's selection reason and triggering repository-relative paths on its dispatch record; see the [synthesis artifact contract](./references/synthesis-artifact-contract.md) for the field semantics.
379
+
380
+ **Execution probes.** An execution probe is a separate parent decision, independent of reviewer selection, not a reviewer and not a substitute for risk-critical coverage. A pure renderer or asset-delivery change with no conditional surface may warrant one focused runtime or browser probe, but a change that separately triggers `agent-native-reviewer`, `security`, or another conditional still selects those reviewers. A probe never adds `maintainability` and never restores a reviewer floor; when selected, record the probe target and its permission boundary in Coverage.
372
381
 
373
382
  ### Stage 3b: Discover project standards paths
374
383
 
@@ -387,7 +396,7 @@ Persona sub-agents do focused, scoped work. Dispatch the named bundled agent for
387
396
 
388
397
  Dispatch named bundled agents for all persona and CE sub-agents. The named agent applies the user's configured model assignment; model policy is user-owned configuration, not a skill-level dispatch parameter.
389
398
 
390
- The same applies to CE always-on agents (`systematic:review:agent-native-reviewer`, `systematic:research:learnings-researcher`) and CE conditional agents (`systematic:review:deployment-verification-agent`): dispatch each by its bundled name so its configured assignment applies.
399
+ The same applies to CE conditional agents (`systematic:review:agent-native-reviewer`, `systematic:research:learnings-researcher`, `systematic:review:deployment-verification-agent`): dispatch each by its bundled name so its configured assignment applies.
391
400
 
392
401
  The orchestrator (this skill) stays on the default model because it handles intent discovery, reviewer selection, finding merge/dedup, and synthesis -- tasks that benefit from stronger reasoning.
393
402
 
@@ -432,7 +441,8 @@ Spawn each selected persona reviewer as a parallel sub-agent using the subagent
432
441
  4. PR metadata: title, body, and URL when reviewing a PR (empty string otherwise). Passed in a `<pr-context>` block so reviewers can verify code against stated intent
433
442
  5. Review context: intent summary, file list, diff
434
443
  6. Reviewer name for the returned `reviewer` field
435
- 7. **For `project-standards` only:** the standards file path list from Stage 3b, wrapped in a `<standards-paths>` block appended to the review context
444
+ 7. **Selected structured conditionals only:** the `selection_reason` and non-empty `selection_surface` from Stage 3, passed into the review context. Core personas receive empty values for both
445
+ 8. **For `project-standards` only:** the standards file path list from Stage 3b, wrapped in a `<standards-paths>` block appended to the review context
436
446
 
437
447
  Persona sub-agents are **read-only** with respect to the project: they review and return structured JSON. They do not edit project files, write artifacts, or propose refactors. The parent orchestrator owns all persistence.
438
448
 
@@ -470,9 +480,35 @@ Each persona sub-agent returns one full JSON payload (all schema fields) to the
470
480
 
471
481
  Returning the detail tier inline increases parent context per persona. The previous compact/detail split kept synthesis context lean, so this is an intentional cost of deleting the sub-agent write path. Verify it against a real multi-persona run. If it materially degrades synthesis, use a second targeted request per persona and keep the write parent-side; never restore sub-agent disk access.
472
482
 
473
- **CE always-on agents** (agent-native-reviewer, learnings-researcher) are dispatched as standard Agent calls in parallel with the persona agents. Give them the same review context bundle the personas receive: entry mode, any PR metadata gathered in Stage 1, intent summary, review base branch name when known, `BASE:` marker, file list, diff, and `UNTRACKED:` scope notes. Do not invoke them with a generic "review this" prompt. Their output is unstructured and synthesized separately in Stage 6.
483
+ **CE conditional agents** (agent-native-reviewer, learnings-researcher) are dispatched as standard Agent calls when their Stage 3 triggers apply, in parallel with the persona agents. Give them the same review context bundle the personas receive (entry mode, any PR metadata gathered in Stage 1, intent summary, review base branch name when known, `BASE:` marker, file list, diff, and `UNTRACKED:` scope notes) plus the selection reason and triggering surface. Do not invoke them with a generic "review this" prompt. Their output is unstructured and synthesized separately in Stage 6; they never receive a raw-return dispatch record.
484
+
485
+ **CE conditional agents** (deployment-verification-agent) are also dispatched as standard Agent calls when applicable. Pass the same review context bundle plus the selection reason and triggering surface (for example, which migration files triggered the agent). Their output is unstructured and must be preserved for Stage 6 synthesis just like the other CE conditional agents.
486
+
487
+ #### Raw return admission (all modes)
488
+
489
+ Before parsing a persona return into fields, screening it for environment values, assessing evidence, synthesizing, or persisting anything, admit it with the packaged structural validator. Invoke the validator through this skill's own installed directory so every harness resolves the same committed bytes:
490
+
491
+ ```bash
492
+ # Resolve the validator relative to this skill's directory.
493
+ SKILL_DIR="<skill directory stated when this skill loads>";
494
+ node "$SKILL_DIR/scripts/validate-review.mjs" return <<'REVIEW_RETURN_A1B2C3D4'
495
+ <the persona's returned JSON payload, copied verbatim>
496
+ REVIEW_RETURN_A1B2C3D4
497
+ ```
498
+
499
+ Before each invocation, choose a fresh delimiter for that exact raw payload over a safe token alphabet (`A-Z`, `0-9`, `_`), for example a random hex token. Verify the delimiter is absent as a complete line in that exact raw payload before running. The `REVIEW_RETURN_A1B2C3D4` token above is only an illustration; never reuse a fixed delimiter, and choose a new token for every payload. Open the heredoc with a single-quoted heredoc opener (`<<'DELIM'`) so the payload is never interpolated, and close it with a line containing exactly that delimiter. Feed the payload on stdin (never as a command argument) so it cannot appear in argv or a process listing; never use unquoted interpolation or command substitution to pass the payload, and never write it to a temp file. This block is self-contained for one-block execution: each fenced block re-assigns `SKILL_DIR` and terminates the assignment with `;`.
500
+
501
+ Read the exit status:
502
+
503
+ - **exit 0** — structurally admitted. Parse the already structurally validated JSON without logging the raw text, then run the existing environment-value screen unchanged over that parsed object before persistence; only after parsing and a clean screen may the parent add `harness`, `dispatch_outcome`, and finding `disposition`. `exit 0` with zero findings is `dispatch_outcome: "empty"`; `exit 0` with findings is `dispatch_outcome: "findings"`.
504
+ - **exit 1** — the whole return is `dispatch_outcome: "malformed"`. Retain only the bounded validator diagnostics in Coverage; never parse, screen, or persist its payload fields or values.
505
+ - **exit 2**, a missing or unreadable helper, or a command launch failure — validation unavailable. Withhold the return and report the exact unavailability and what was withheld. Update that selected persona's preinitialized dispatch entry from `never_returned` to `dispatch_outcome: "validation_unavailable"` with `input_finding_count: 0` and, optionally, a safe `rejection_reason` naming the exit status, missing helper, or launch failure without payload values; set `run_status` to `degraded`. A run that contains `validation_unavailable` evidence can never finalize as `completed`, and that persona must not have an admitted input finding. Never omit the dispatch entry, never leave it as `never_returned`, never label it `malformed`, never admit the payload, and never fabricate a reviewer record or a rejected-summary ledger row. The word `unavailable` also names the artifact-level self-validation status, a different object and phase; never repurpose the artifact-level `validation` fields.
506
+
507
+ **Dispatch identity binding.** Structural admission does not prove who produced a return. Immediately after `exit 0` and before the environment-value screen, persistence, or synthesis, parse the admitted return's `reviewer` field and confirm it equals the dispatched persona. A return whose `reviewer` does not match the dispatched persona is an identity mismatch: reject the whole return as `dispatch_outcome: "malformed"`, record only a bounded rejection reason naming the expected persona, set `run_status` to `degraded`, and do not admit, screen, persist, or synthesize its payload. The stdin-only, argument-free validator cannot see the dispatch identity, so this comparison stays the parent's responsibility.
508
+
509
+ A task that did not return is `never_returned`: a task-lifecycle fact recorded without invoking the validator. Validation unavailable is not malformed and is not never_returned; they are distinct coverage states. The public `systematic validate-review-return` command is an operator/development fallback selected before invocation, never a fallback chosen because a validator run exited 1 or 2.
474
510
 
475
- **CE conditional agents** (deployment-verification-agent) are also dispatched as standard Agent calls when applicable. Pass the same review context bundle plus the applicability reason (for example, which migration files triggered the agent). Their output is unstructured and must be preserved for Stage 6 synthesis just like the CE always-on agents.
511
+ Structural validity never implies evidence validity. A return that passes the validator is admitted structurally only; its claims still require evidence assessment, and a wrong-checkout or unsupported citation remains unverified until current-target evidence resolves it.
476
512
 
477
513
  ### Stage 5: Merge findings
478
514
 
@@ -482,7 +518,7 @@ Convert multiple reviewer JSON returns into one deduplicated, confidence-gated f
482
518
 
483
519
  Before applying the confidence gate, keep the parent-owned ledger through every later stage. See the [synthesis artifact contract](./references/synthesis-artifact-contract.md) for the input-ID and reconciliation rules.
484
520
 
485
- 1. **Validate before any write.** Treat every persona return as untrusted input. Parse the returned text as JSON without logging the raw text, then validate the complete parsed object against `references/findings-schema.json`, including `why_it_matters` and `evidence`.
521
+ 1. **Validate before any write.** Treat every persona return as untrusted input. The order is fixed: the packaged raw validator (Stage 4's Raw return admission) must exit 0 before the parent parses anything. Then parse the already structurally validated JSON without logging the raw text, run the unchanged environment-value screen over that parsed object, assess evidence, and only then add parent annotations, persist, or synthesize. The executable validator already enforces `references/findings-schema.json` (including `why_it_matters` and `evidence`), so confirming the parsed object is a cross-check, not the admission gate. On exit 1 the return is `malformed`: do not parse, screen, or persist it.
486
522
  - **Top-level required:** reviewer (string), findings (array), residual_risks (array), testing_gaps (array). Reject the entire persona return if any are missing or wrong type.
487
523
  - **Per-finding required:** title, severity, file, line, why_it_matters, confidence, evidence, autofix_class, owner, requires_verification, pre_existing.
488
524
  - **Schema constraints:** enforce every enum, type, confidence, line, path, evidence count, evidence length, and explicit overflow-marker bound from the schema. Empty evidence, absolute paths, and over-bound evidence are rejection cases, not truncation cases.
@@ -505,7 +541,7 @@ Before applying the confidence gate, keep the parent-owned ledger through every
505
541
  - report-only queue: `advisory` findings plus anything owned by `human` or `release`
506
542
  9. **Sort.** Order by severity (P0 first) -> confidence (descending) -> file path -> line number.
507
543
  10. **Collect coverage data.** Union residual_risks and testing_gaps across reviewers.
508
- 11. **Preserve CE agent artifacts.** Keep the learnings, agent-native, schema-drift, and deployment-verification outputs alongside the merged finding set. Do not drop unstructured agent output just because it does not match the persona JSON schema.
544
+ 11. **Preserve CE agent artifacts.** Keep the outputs of the selected learnings, agent-native, schema-drift, and deployment-verification agents alongside the merged finding set. Do not drop unstructured agent output just because it does not match the persona JSON schema.
509
545
  12. **Keep the input ledger complete.** Reconcile admitted findings and rejected-payload summaries according to the [synthesis artifact contract](./references/synthesis-artifact-contract.md).
510
546
 
511
547
  ### Stage 5b: Validation pass
@@ -541,10 +577,10 @@ Assemble the final report using **pipe-delimited markdown tables for findings**
541
577
  5. **Residual Actionable Work.** Include when unresolved actionable findings were handed off or should be handed off.
542
578
  6. **Pre-existing.** Separate section, does not count toward verdict.
543
579
  7. **Filtered (not validated).** Include when Stage 5b produced any findings with `validated: false`. Render as a pipe-delimited table with columns `#`, `File`, `Issue`, `Reviewer`, `Confidence`, `Validator reason`. These findings are surfaced for human review — they are not removed from the report. The validator found evidence that the issue may not be real in the code as written, was not introduced by this diff, or is already handled elsewhere; the human reviewer makes the final call. Omit this section when no findings were filtered.
544
- 8. **Learnings & Past Solutions.** Surface learnings-researcher results: if past solutions are relevant, flag them as "Known Pattern" with links to docs/solutions/ files.
545
- 9. **Agent-Native Gaps.** Surface agent-native-reviewer results. Omit section if no gaps found.
580
+ 8. **Learnings & Past Solutions.** Render only when CE `learnings-researcher` was selected and returned relevant output: if past solutions are relevant, flag them as "Known Pattern" with links to docs/solutions/ files. Omit the section otherwise.
581
+ 9. **Agent-Native Gaps.** Render only when CE `agent-native-reviewer` was selected and returned relevant output. Omit the section otherwise.
546
582
  10. **Deployment Notes.** If deployment-verification-agent ran, surface the key Go/No-Go items: blocking pre-deploy checks, the most important verification queries, rollback caveats, and monitoring focus areas. Keep the checklist actionable rather than dropping it into Coverage.
547
- 11. **Coverage.** Suppressed count, residual risks, testing gaps, failed/timed-out reviewers, validator failures, risk-coverage entries with citing input finding IDs and exit conditions for blocked entries, and any intent uncertainty carried by non-interactive modes.
583
+ 11. **Coverage.** Suppressed count, residual risks, testing gaps, failed/timed-out reviewers, validator failures, risk-coverage entries with citing input finding IDs and exit conditions for blocked entries, and any intent uncertainty carried by non-interactive modes. For raw returns, state each selected persona's admission state — `findings`, `empty`, `malformed`, `never_returned`, `validation_unavailable` (the persisted raw dispatch outcome; distinct from the artifact-level `validation.status: "unavailable"`), `environment-screen` rejection — and what was admitted or withheld. Report admission states here only; do not add fields to `review-summary.v1`. Distinguish core reviewers, each selected conditional with its one-line rationale and triggering repository-relative paths, an explicit "no conditional selected" case, and any selected-but-failed/malformed/validation-unavailable reviewer; never label an unselected reviewer as failed.
548
584
  12. **Verdict.** Ready to merge / Ready with fixes / Not ready. Fix order if applicable. When an `explicit` plan has unaddressed requirements, the verdict must reflect it — a PR that's code-clean but missing planned requirements is "Not ready" unless the omission is intentional. When an `inferred` plan has unaddressed requirements, note it in the verdict reasoning but do not block on it alone. Apply the risk-aware degraded verdict rule from the [synthesis artifact contract](./references/synthesis-artifact-contract.md), including the recorded exit condition for a blocked risk-critical verdict.
549
585
 
550
586
  Do not include time estimates.
@@ -650,7 +686,7 @@ Before delivering the review, verify:
650
686
 
651
687
  ## Language-Aware Conditionals
652
688
 
653
- This skill uses stack-specific reviewer agents when the diff clearly warrants them. Keep those agents opinionated. They are not generic language checkers; they add a distinct review lens on top of the always-on and cross-cutting personas.
689
+ This skill uses stack-specific reviewer agents when the diff clearly warrants them. Keep those agents opinionated. They are not generic language checkers; they add a distinct review lens on top of the core and cross-cutting personas.
654
690
 
655
691
  Do not spawn them mechanically from file extensions alone. The trigger is meaningful changed behavior, architecture, or UI state in that stack.
656
692