@fro.bot/systematic 3.13.5 → 3.13.7
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/dist/cli.js +45 -3
- package/dist/lib/review-artifact-schema.d.ts +14 -1
- package/package.json +1 -1
- package/skills/ce-compound/assets/resolution-template.md +1 -1
- package/skills/ce-compound-refresh/assets/resolution-template.md +1 -1
- package/skills/ce-review/SKILL.md +6 -2
- package/skills/ce-review/references/review-output-template.md +1 -1
- package/skills/ce-review/references/review-summary-schema.json +55 -0
- package/skills/ce-review/references/synthesis-artifact-contract.md +34 -12
package/dist/cli.js
CHANGED
|
@@ -1988,7 +1988,10 @@ var MAX_FINDINGS = 32;
|
|
|
1988
1988
|
var MAX_PERSONAS = 64;
|
|
1989
1989
|
var REVIEW_ARTIFACT_CUSTOM_MESSAGES = [
|
|
1990
1990
|
"severity count must match rejected finding count",
|
|
1991
|
-
"filtered findings require a validation reason"
|
|
1991
|
+
"filtered findings require a validation reason",
|
|
1992
|
+
"risk-critical dispatches require a non-empty selection surface",
|
|
1993
|
+
"satisfied risk coverage requires a citing input finding ID",
|
|
1994
|
+
"unsatisfied risk coverage must not cite an input finding ID"
|
|
1992
1995
|
];
|
|
1993
1996
|
var boundedText = (maxLength) => exports_external.string().min(1).max(maxLength).regex(/\S/);
|
|
1994
1997
|
var DispatchOutcomeSchema = exports_external.enum([
|
|
@@ -2012,6 +2015,14 @@ var BranchSchema = exports_external.string().max(MAX_BRANCH_LENGTH);
|
|
|
2012
2015
|
var HeadShaSchema = exports_external.string().regex(/^[0-9a-f]{40}$/);
|
|
2013
2016
|
var CompletedAtSchema = exports_external.iso.datetime({ offset: false });
|
|
2014
2017
|
var ReasonSchema = boundedText(MAX_REASON_LENGTH);
|
|
2018
|
+
var RISK_CRITICAL_PERSONAS = [
|
|
2019
|
+
"security",
|
|
2020
|
+
"data-migrations",
|
|
2021
|
+
"api-contract",
|
|
2022
|
+
"reliability",
|
|
2023
|
+
"performance"
|
|
2024
|
+
];
|
|
2025
|
+
var RiskCriticalPersonaSchema = exports_external.enum(RISK_CRITICAL_PERSONAS);
|
|
2015
2026
|
var FindingTitleSchema = boundedText(256);
|
|
2016
2027
|
var SeveritySchema = exports_external.enum(["P0", "P1", "P2", "P3", "unknown"]);
|
|
2017
2028
|
var FindingSeveritySchema = SeveritySchema.exclude(["unknown"]);
|
|
@@ -2098,8 +2109,18 @@ var DispatchSchema = exports_external.object({
|
|
|
2098
2109
|
persona: ReviewerSchema,
|
|
2099
2110
|
dispatch_outcome: DispatchOutcomeSchema,
|
|
2100
2111
|
input_finding_count: exports_external.number().int().nonnegative().max(MAX_FINDINGS),
|
|
2101
|
-
rejection_reason: ReasonSchema.optional()
|
|
2102
|
-
|
|
2112
|
+
rejection_reason: ReasonSchema.optional(),
|
|
2113
|
+
selection_surface: exports_external.array(RepoRelativePathSchema).max(MAX_FINDINGS).optional(),
|
|
2114
|
+
selection_reason: ReasonSchema.optional()
|
|
2115
|
+
}).strict().superRefine((dispatch, ctx) => {
|
|
2116
|
+
if (RISK_CRITICAL_PERSONAS.includes(dispatch.persona) && (!dispatch.selection_surface || dispatch.selection_surface.length === 0)) {
|
|
2117
|
+
ctx.addIssue({
|
|
2118
|
+
code: "custom",
|
|
2119
|
+
path: ["selection_surface"],
|
|
2120
|
+
message: REVIEW_ARTIFACT_CUSTOM_MESSAGES[2]
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
});
|
|
2103
2124
|
var DispositionCountsSchema = exports_external.object({
|
|
2104
2125
|
surviving: exports_external.number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
|
|
2105
2126
|
merged: exports_external.number().int().nonnegative().max(MAX_FINDINGS * MAX_PERSONAS),
|
|
@@ -2121,6 +2142,26 @@ var DeclinedMergeSchema = exports_external.object({
|
|
|
2121
2142
|
input_finding_ids: exports_external.array(boundedText(MAX_INPUT_ID_LENGTH)).min(2).max(MAX_FINDINGS),
|
|
2122
2143
|
reason: ReasonSchema
|
|
2123
2144
|
}).strict();
|
|
2145
|
+
var RiskCoverageSchema = exports_external.object({
|
|
2146
|
+
persona: RiskCriticalPersonaSchema,
|
|
2147
|
+
satisfied: exports_external.boolean(),
|
|
2148
|
+
input_finding_id: boundedText(MAX_INPUT_ID_LENGTH).optional()
|
|
2149
|
+
}).strict().superRefine((coverage, ctx) => {
|
|
2150
|
+
if (coverage.satisfied && coverage.input_finding_id === undefined) {
|
|
2151
|
+
ctx.addIssue({
|
|
2152
|
+
code: "custom",
|
|
2153
|
+
path: ["input_finding_id"],
|
|
2154
|
+
message: REVIEW_ARTIFACT_CUSTOM_MESSAGES[3]
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
if (!coverage.satisfied && coverage.input_finding_id !== undefined) {
|
|
2158
|
+
ctx.addIssue({
|
|
2159
|
+
code: "custom",
|
|
2160
|
+
path: ["input_finding_id"],
|
|
2161
|
+
message: REVIEW_ARTIFACT_CUSTOM_MESSAGES[4]
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
});
|
|
2124
2165
|
var ReviewArtifactSchema = exports_external.object({
|
|
2125
2166
|
schema_version: exports_external.literal(1),
|
|
2126
2167
|
run_id: boundedText(MAX_RUN_ID_LENGTH),
|
|
@@ -2140,6 +2181,7 @@ var ReviewArtifactSchema = exports_external.object({
|
|
|
2140
2181
|
input_findings: exports_external.array(InputFindingSchema).max(MAX_FINDINGS * MAX_PERSONAS),
|
|
2141
2182
|
findings: exports_external.array(SynthesizedFindingSchema).max(MAX_FINDINGS),
|
|
2142
2183
|
declined_merges: exports_external.array(DeclinedMergeSchema).max(MAX_FINDINGS).optional(),
|
|
2184
|
+
risk_coverage: exports_external.array(RiskCoverageSchema).max(MAX_PERSONAS).optional(),
|
|
2143
2185
|
disposition_counts: DispositionCountsSchema,
|
|
2144
2186
|
applied_fixes: exports_external.array(ReasonSchema).max(MAX_FINDINGS),
|
|
2145
2187
|
residual_actionable_work: exports_external.array(ReasonSchema).max(MAX_FINDINGS),
|
|
@@ -1,5 +1,5 @@
|
|
|
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'];
|
|
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'];
|
|
3
3
|
export declare const DispatchOutcomeSchema: z.ZodEnum<{
|
|
4
4
|
empty: "empty";
|
|
5
5
|
findings: "findings";
|
|
@@ -134,6 +134,8 @@ export declare const ReviewArtifactSchema: z.ZodObject<{
|
|
|
134
134
|
}>;
|
|
135
135
|
input_finding_count: z.ZodNumber;
|
|
136
136
|
rejection_reason: z.ZodOptional<z.ZodString>;
|
|
137
|
+
selection_surface: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
138
|
+
selection_reason: z.ZodOptional<z.ZodString>;
|
|
137
139
|
}, z.core.$strict>>;
|
|
138
140
|
input_findings: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
139
141
|
record_type: z.ZodLiteral<"admitted">;
|
|
@@ -223,6 +225,17 @@ export declare const ReviewArtifactSchema: z.ZodObject<{
|
|
|
223
225
|
input_finding_ids: z.ZodArray<z.ZodString>;
|
|
224
226
|
reason: z.ZodString;
|
|
225
227
|
}, z.core.$strict>>>;
|
|
228
|
+
risk_coverage: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
229
|
+
persona: z.ZodEnum<{
|
|
230
|
+
"api-contract": "api-contract";
|
|
231
|
+
"data-migrations": "data-migrations";
|
|
232
|
+
performance: "performance";
|
|
233
|
+
reliability: "reliability";
|
|
234
|
+
security: "security";
|
|
235
|
+
}>;
|
|
236
|
+
satisfied: z.ZodBoolean;
|
|
237
|
+
input_finding_id: z.ZodOptional<z.ZodString>;
|
|
238
|
+
}, z.core.$strict>>>;
|
|
226
239
|
disposition_counts: z.ZodObject<{
|
|
227
240
|
surviving: z.ZodNumber;
|
|
228
241
|
merged: z.ZodNumber;
|
package/package.json
CHANGED
|
@@ -52,7 +52,7 @@ tags: [keyword-one, keyword-two]
|
|
|
52
52
|
|
|
53
53
|
## Knowledge Track Template
|
|
54
54
|
|
|
55
|
-
Use for: `best_practice`, `documentation_gap`, `workflow_issue`, `developer_experience`
|
|
55
|
+
Use for: `best_practice`, `documentation_gap`, `workflow_issue`, `developer_experience`, `architecture_pattern`, `design_pattern`, `tooling_decision`, `convention`
|
|
56
56
|
|
|
57
57
|
```markdown
|
|
58
58
|
---
|
|
@@ -52,7 +52,7 @@ tags: [keyword-one, keyword-two]
|
|
|
52
52
|
|
|
53
53
|
## Knowledge Track Template
|
|
54
54
|
|
|
55
|
-
Use for: `best_practice`, `documentation_gap`, `workflow_issue`, `developer_experience`
|
|
55
|
+
Use for: `best_practice`, `documentation_gap`, `workflow_issue`, `developer_experience`, `architecture_pattern`, `design_pattern`, `tooling_decision`, `convention`
|
|
56
56
|
|
|
57
57
|
```markdown
|
|
58
58
|
---
|
|
@@ -360,6 +360,10 @@ Review team:
|
|
|
360
360
|
|
|
361
361
|
This is progress reporting, not a blocking confirmation.
|
|
362
362
|
|
|
363
|
+
Record each conditional persona's selection reason and triggering file paths
|
|
364
|
+
on its dispatch record; see the [synthesis artifact contract](./references/synthesis-artifact-contract.md)
|
|
365
|
+
for the field semantics.
|
|
366
|
+
|
|
363
367
|
### Stage 3b: Discover project standards paths
|
|
364
368
|
|
|
365
369
|
Before spawning sub-agents, find the file paths (not contents) of all relevant standards files for the `project-standards` persona. Use the native file-search/glob tool to locate:
|
|
@@ -520,8 +524,8 @@ Assemble the final report using **pipe-delimited markdown tables for findings**
|
|
|
520
524
|
8. **Learnings & Past Solutions.** Surface learnings-researcher results: if past solutions are relevant, flag them as "Known Pattern" with links to docs/solutions/ files.
|
|
521
525
|
9. **Agent-Native Gaps.** Surface agent-native-reviewer results. Omit section if no gaps found.
|
|
522
526
|
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.
|
|
523
|
-
11. **Coverage.** Suppressed count, residual risks, testing gaps, failed/timed-out reviewers, validator failures, and any intent uncertainty carried by non-interactive modes.
|
|
524
|
-
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).
|
|
527
|
+
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.
|
|
528
|
+
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.
|
|
525
529
|
|
|
526
530
|
Do not include time estimates.
|
|
527
531
|
|
|
@@ -137,7 +137,7 @@ This fails because: no pipe-delimited tables, no severity-grouped `###` headers,
|
|
|
137
137
|
- **Learnings & Past Solutions section** -- results from learnings-researcher, with links to docs/solutions/ files
|
|
138
138
|
- **Agent-Native Gaps section** -- results from agent-native-reviewer. Omit if no gaps found.
|
|
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, and
|
|
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
|
|
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
|
|
@@ -69,6 +69,31 @@
|
|
|
69
69
|
"minLength": 1,
|
|
70
70
|
"maxLength": 2048,
|
|
71
71
|
"pattern": "\\S"
|
|
72
|
+
},
|
|
73
|
+
"selection_surface": {
|
|
74
|
+
"maxItems": 32,
|
|
75
|
+
"type": "array",
|
|
76
|
+
"items": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"minLength": 1,
|
|
79
|
+
"maxLength": 256,
|
|
80
|
+
"allOf": [
|
|
81
|
+
{
|
|
82
|
+
"type": "string",
|
|
83
|
+
"pattern": "\\S"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"type": "string",
|
|
87
|
+
"pattern": "^(?!\\/)(?![A-Za-z]:[\\\\/])(?!\\\\).+"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"selection_reason": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"minLength": 1,
|
|
95
|
+
"maxLength": 2048,
|
|
96
|
+
"pattern": "\\S"
|
|
72
97
|
}
|
|
73
98
|
},
|
|
74
99
|
"required": ["persona", "dispatch_outcome", "input_finding_count"],
|
|
@@ -417,6 +442,36 @@
|
|
|
417
442
|
"additionalProperties": false
|
|
418
443
|
}
|
|
419
444
|
},
|
|
445
|
+
"risk_coverage": {
|
|
446
|
+
"maxItems": 64,
|
|
447
|
+
"type": "array",
|
|
448
|
+
"items": {
|
|
449
|
+
"type": "object",
|
|
450
|
+
"properties": {
|
|
451
|
+
"persona": {
|
|
452
|
+
"type": "string",
|
|
453
|
+
"enum": [
|
|
454
|
+
"security",
|
|
455
|
+
"data-migrations",
|
|
456
|
+
"api-contract",
|
|
457
|
+
"reliability",
|
|
458
|
+
"performance"
|
|
459
|
+
]
|
|
460
|
+
},
|
|
461
|
+
"satisfied": {
|
|
462
|
+
"type": "boolean"
|
|
463
|
+
},
|
|
464
|
+
"input_finding_id": {
|
|
465
|
+
"type": "string",
|
|
466
|
+
"minLength": 1,
|
|
467
|
+
"maxLength": 128,
|
|
468
|
+
"pattern": "\\S"
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
"required": ["persona", "satisfied"],
|
|
472
|
+
"additionalProperties": false
|
|
473
|
+
}
|
|
474
|
+
},
|
|
420
475
|
"disposition_counts": {
|
|
421
476
|
"type": "object",
|
|
422
477
|
"properties": {
|
|
@@ -105,7 +105,10 @@ The artifact must preserve these distinctions:
|
|
|
105
105
|
records what a persona returned: `findings`, `empty`, `malformed`, or
|
|
106
106
|
`never_returned`. A rejection reason is the exact safe validation reason,
|
|
107
107
|
naming persona and field without echoing the offending value. Dispatch
|
|
108
|
-
outcome is separate from finding disposition.
|
|
108
|
+
outcome is separate from finding disposition. Conditional selections record
|
|
109
|
+
their triggering changed-file paths in `selection_surface` and the announced
|
|
110
|
+
selection explanation in `selection_reason`; always-on personas may omit
|
|
111
|
+
both fields.
|
|
109
112
|
- `input_findings` is the authoritative parent-owned ledger. Before the
|
|
110
113
|
confidence gate, every admitted finding receives an `input_id` of
|
|
111
114
|
`<reviewer>#<1-based finding index>`. Every admitted input has exactly one
|
|
@@ -207,10 +210,17 @@ validating it. This ordering makes the artifact validatable at all: without
|
|
|
207
210
|
`schema_version`, the validator reports the legacy status (exit 3) rather than
|
|
208
211
|
a real validation result.
|
|
209
212
|
|
|
210
|
-
After writing `review-summary.json`, the parent
|
|
211
|
-
`systematic
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
After writing `review-summary.json`, the parent checks whether the
|
|
214
|
+
`systematic` executable is available on the invoking environment's `PATH`.
|
|
215
|
+
When it is available, the parent runs
|
|
216
|
+
`systematic validate-review-artifact <path>` against it. The executable ships
|
|
217
|
+
through the npm package's `bin` entry; a harness that installs bundled
|
|
218
|
+
markdown without that package will not have it. When it is unavailable, the
|
|
219
|
+
parent records that validation did not run and why in the run record.
|
|
220
|
+
Unavailable validation is distinct from skipped validation, and the parent
|
|
221
|
+
does not represent the artifact as validated. A nonzero exit means the run is
|
|
222
|
+
not complete. The [executable schema](./review-summary-schema.json) is
|
|
223
|
+
generated from a Zod source and is the machine-checkable form of the shape
|
|
214
224
|
described here.
|
|
215
225
|
|
|
216
226
|
On validation failure, the parent repairs the artifact and re-runs the
|
|
@@ -223,7 +233,8 @@ This is enforcement by visible failure, not by containment. An agent that
|
|
|
223
233
|
never runs the command can still finalize an artifact, but produces no evidence
|
|
224
234
|
in either direction. That is why the command exists as an independently
|
|
225
235
|
runnable check rather than as a self-validation instruction, and why its result
|
|
226
|
-
belongs in the run record.
|
|
236
|
+
belongs in the run record. An unavailable validator is recorded as unavailable,
|
|
237
|
+
not as skipped or validated.
|
|
227
238
|
|
|
228
239
|
`mode:report-only` writes no artifact and therefore performs no validation.
|
|
229
240
|
|
|
@@ -246,18 +257,20 @@ The risk-critical surfaces are `security`, `data-migrations`, `api-contract`,
|
|
|
246
257
|
specifically for the matching diff shape in Stage 3. If one of those selected
|
|
247
258
|
personas has `dispatch_outcome: "malformed"` or
|
|
248
259
|
`dispatch_outcome: "never_returned"`, the review verdict must not be clean:
|
|
249
|
-
it is blocking unless another persona covered the
|
|
250
|
-
|
|
260
|
+
it is blocking unless another persona covered the lost surface with validated
|
|
261
|
+
evidence. For this rule, a validated finding from another persona covers a
|
|
262
|
+
lost risk-critical surface if and only if the finding's `file` appears in the
|
|
263
|
+
lost persona's recorded `selection_surface`; validated evidence means at least
|
|
251
264
|
one finding from that other persona's return passed complete schema and
|
|
252
|
-
environment-value validation
|
|
253
|
-
|
|
254
|
-
risk-critical evidence.
|
|
265
|
+
environment-value validation. A coverage note alone cannot satisfy this rule;
|
|
266
|
+
the verdict must reflect the missing risk-critical evidence.
|
|
255
267
|
|
|
256
268
|
Finding-level rejection is keyed by the severities in
|
|
257
269
|
`rejected_severities`. A selected risk-critical persona whose rejected
|
|
258
270
|
findings include any `P0`, `P1`, or `unknown` severity is treated exactly as a
|
|
259
271
|
rejected persona for this verdict rule: blocking unless another persona
|
|
260
|
-
covered the
|
|
272
|
+
covered the lost surface with a validated finding whose `file` appears in the
|
|
273
|
+
lost persona's recorded `selection_surface`. A selected risk-critical
|
|
261
274
|
persona whose rejected findings are only `P2` or `P3` does not block on that
|
|
262
275
|
basis alone; record it in the Coverage section instead. Unknown severity is
|
|
263
276
|
treated as blocking as deliberate fail-closed behavior because the parent
|
|
@@ -265,3 +278,12 @@ could not determine what was lost. Admitted findings and verdict blocking
|
|
|
265
278
|
are independent: surviving findings from the same return continue through
|
|
266
279
|
synthesis normally. Partial return is not partial coverage when the lost
|
|
267
280
|
part was critical.
|
|
281
|
+
|
|
282
|
+
Record each lost risk-critical persona in the optional `risk_coverage` array
|
|
283
|
+
with `satisfied: true` and `input_finding_id` set to the citing input finding
|
|
284
|
+
ID when coverage is satisfied, or `satisfied: false` without an
|
|
285
|
+
`input_finding_id` otherwise. A blocked verdict must name its exit condition
|
|
286
|
+
in the report: re-run the lost persona, or supply a validated finding from
|
|
287
|
+
another persona whose `file` appears in the lost persona's recorded
|
|
288
|
+
`selection_surface`. Derive that condition from the dispatch and risk-coverage
|
|
289
|
+
records, not from a coverage note.
|