@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.
- package/HARNESSES.md +26 -0
- package/dist/ce-review-validator.d.ts +23 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +436 -28
- package/dist/lib/review-artifact-schema.d.ts +171 -1
- package/dist/lib/review-return-validator.d.ts +59 -0
- package/package.json +5 -3
- package/skills/ce-review/SKILL.md +72 -36
- package/skills/ce-review/references/findings-schema.json +271 -192
- package/skills/ce-review/references/persona-catalog.md +39 -25
- package/skills/ce-review/references/review-output-template.md +3 -3
- package/skills/ce-review/references/review-summary-schema.json +7 -1
- package/skills/ce-review/references/subagent-template.md +8 -2
- package/skills/ce-review/references/synthesis-artifact-contract.md +96 -11
- package/skills/ce-review/scripts/validate-review.mjs +6922 -0
|
@@ -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.
|
|
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.
|
|
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` |
|
|
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 (
|
|
137
|
+
**CE conditional (selected per diff):**
|
|
139
138
|
|
|
140
|
-
| Agent | Select when diff includes
|
|
141
|
-
|
|
142
|
-
| `systematic:review:
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
358
|
-
- testing (
|
|
359
|
-
-
|
|
360
|
-
-
|
|
361
|
-
-
|
|
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
|
|
370
|
-
|
|
371
|
-
for the
|
|
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
|
|
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. **
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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.**
|
|
545
|
-
9. **Agent-Native Gaps.**
|
|
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
|
|
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
|
|