@fro.bot/systematic 3.12.3 → 3.13.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/ATTRIBUTIONS.md +2 -2
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +324 -2
- package/dist/{index-h26p98ny.js → index-0stf3ag0.js} +948 -948
- package/dist/index.js +1 -1
- package/dist/lib/review-artifact-schema.d.ts +239 -0
- package/package.json +9 -6
- package/skills/ce-review/SKILL.md +8 -7
- package/skills/ce-review/references/review-summary-schema.json +517 -0
- package/skills/ce-review/references/synthesis-artifact-contract.md +143 -22
package/dist/index.js
CHANGED
|
@@ -0,0 +1,239 @@
|
|
|
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'];
|
|
3
|
+
export declare const DispatchOutcomeSchema: z.ZodEnum<{
|
|
4
|
+
empty: "empty";
|
|
5
|
+
findings: "findings";
|
|
6
|
+
malformed: "malformed";
|
|
7
|
+
never_returned: "never_returned";
|
|
8
|
+
}>;
|
|
9
|
+
export declare const DispositionSchema: z.ZodEnum<{
|
|
10
|
+
filtered: "filtered";
|
|
11
|
+
merged: "merged";
|
|
12
|
+
rejected: "rejected";
|
|
13
|
+
suppressed: "suppressed";
|
|
14
|
+
surviving: "surviving";
|
|
15
|
+
}>;
|
|
16
|
+
export declare const HarnessSchema: z.ZodEnum<{
|
|
17
|
+
"claude-code": "claude-code";
|
|
18
|
+
opencode: "opencode";
|
|
19
|
+
pi: "pi";
|
|
20
|
+
}>;
|
|
21
|
+
export declare const RepoRelativePathSchema: z.ZodString;
|
|
22
|
+
export declare const InputFindingSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
23
|
+
record_type: z.ZodLiteral<"admitted">;
|
|
24
|
+
input_id: z.ZodString;
|
|
25
|
+
reviewer: z.ZodString;
|
|
26
|
+
confidence: z.ZodNumber;
|
|
27
|
+
disposition: z.ZodEnum<{
|
|
28
|
+
filtered: "filtered";
|
|
29
|
+
merged: "merged";
|
|
30
|
+
suppressed: "suppressed";
|
|
31
|
+
surviving: "surviving";
|
|
32
|
+
}>;
|
|
33
|
+
reason: z.ZodString;
|
|
34
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
35
|
+
record_type: z.ZodLiteral<"rejected_summary">;
|
|
36
|
+
reviewer: z.ZodString;
|
|
37
|
+
dispatch_outcome: z.ZodEnum<{
|
|
38
|
+
empty: "empty";
|
|
39
|
+
findings: "findings";
|
|
40
|
+
malformed: "malformed";
|
|
41
|
+
never_returned: "never_returned";
|
|
42
|
+
}>;
|
|
43
|
+
rejected_finding_count: z.ZodNumber;
|
|
44
|
+
rejected_severities: z.ZodArray<z.ZodEnum<{
|
|
45
|
+
P0: "P0";
|
|
46
|
+
P1: "P1";
|
|
47
|
+
P2: "P2";
|
|
48
|
+
P3: "P3";
|
|
49
|
+
unknown: "unknown";
|
|
50
|
+
}>>;
|
|
51
|
+
disposition: z.ZodEnum<{
|
|
52
|
+
rejected: "rejected";
|
|
53
|
+
}>;
|
|
54
|
+
reason: z.ZodString;
|
|
55
|
+
}, z.core.$strict>], "record_type">;
|
|
56
|
+
declare const SynthesizedFindingFieldsSchema: z.ZodObject<{
|
|
57
|
+
title: z.ZodString;
|
|
58
|
+
severity: z.ZodEnum<{
|
|
59
|
+
P0: "P0";
|
|
60
|
+
P1: "P1";
|
|
61
|
+
P2: "P2";
|
|
62
|
+
P3: "P3";
|
|
63
|
+
}>;
|
|
64
|
+
file: z.ZodString;
|
|
65
|
+
line: z.ZodNumber;
|
|
66
|
+
why_it_matters: z.ZodString;
|
|
67
|
+
autofix_class: z.ZodEnum<{
|
|
68
|
+
advisory: "advisory";
|
|
69
|
+
gated_auto: "gated_auto";
|
|
70
|
+
manual: "manual";
|
|
71
|
+
safe_auto: "safe_auto";
|
|
72
|
+
}>;
|
|
73
|
+
owner: z.ZodEnum<{
|
|
74
|
+
"downstream-resolver": "downstream-resolver";
|
|
75
|
+
human: "human";
|
|
76
|
+
release: "release";
|
|
77
|
+
"review-fixer": "review-fixer";
|
|
78
|
+
}>;
|
|
79
|
+
requires_verification: z.ZodBoolean;
|
|
80
|
+
confidence: z.ZodNumber;
|
|
81
|
+
evidence: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
82
|
+
overflow: z.ZodLiteral<true>;
|
|
83
|
+
excerpt: z.ZodString;
|
|
84
|
+
}, z.core.$strict>]>>;
|
|
85
|
+
pre_existing: z.ZodBoolean;
|
|
86
|
+
suggested_fix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
87
|
+
validated: z.ZodOptional<z.ZodBoolean>;
|
|
88
|
+
validation_reason: z.ZodOptional<z.ZodString>;
|
|
89
|
+
input_finding_ids: z.ZodArray<z.ZodString>;
|
|
90
|
+
provenance: z.ZodObject<{
|
|
91
|
+
fingerprint: z.ZodString;
|
|
92
|
+
submitters: z.ZodArray<z.ZodString>;
|
|
93
|
+
agreement_credit: z.ZodArray<z.ZodString>;
|
|
94
|
+
}, z.core.$strict>;
|
|
95
|
+
}, z.core.$strict>;
|
|
96
|
+
type SynthesizedFindingFields = z.infer<typeof SynthesizedFindingFieldsSchema>;
|
|
97
|
+
type SynthesizedFinding = Omit<SynthesizedFindingFields, 'validated' | 'validation_reason'> & ({
|
|
98
|
+
validated: false;
|
|
99
|
+
validation_reason: string;
|
|
100
|
+
} | {
|
|
101
|
+
validated?: true;
|
|
102
|
+
validation_reason?: string;
|
|
103
|
+
});
|
|
104
|
+
export declare const ReviewArtifactSchema: z.ZodObject<{
|
|
105
|
+
schema_version: z.ZodLiteral<1>;
|
|
106
|
+
run_id: z.ZodString;
|
|
107
|
+
mode: z.ZodEnum<{
|
|
108
|
+
autofix: "autofix";
|
|
109
|
+
headless: "headless";
|
|
110
|
+
interactive: "interactive";
|
|
111
|
+
}>;
|
|
112
|
+
harness: z.ZodEnum<{
|
|
113
|
+
"claude-code": "claude-code";
|
|
114
|
+
opencode: "opencode";
|
|
115
|
+
pi: "pi";
|
|
116
|
+
}>;
|
|
117
|
+
run_status: z.ZodEnum<{
|
|
118
|
+
abnormal: "abnormal";
|
|
119
|
+
completed: "completed";
|
|
120
|
+
degraded: "degraded";
|
|
121
|
+
in_progress: "in_progress";
|
|
122
|
+
}>;
|
|
123
|
+
verdict: z.ZodString;
|
|
124
|
+
dispatches: z.ZodArray<z.ZodObject<{
|
|
125
|
+
persona: z.ZodString;
|
|
126
|
+
dispatch_outcome: z.ZodEnum<{
|
|
127
|
+
empty: "empty";
|
|
128
|
+
findings: "findings";
|
|
129
|
+
malformed: "malformed";
|
|
130
|
+
never_returned: "never_returned";
|
|
131
|
+
}>;
|
|
132
|
+
input_finding_count: z.ZodNumber;
|
|
133
|
+
rejection_reason: z.ZodOptional<z.ZodString>;
|
|
134
|
+
}, z.core.$strict>>;
|
|
135
|
+
input_findings: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
136
|
+
record_type: z.ZodLiteral<"admitted">;
|
|
137
|
+
input_id: z.ZodString;
|
|
138
|
+
reviewer: z.ZodString;
|
|
139
|
+
confidence: z.ZodNumber;
|
|
140
|
+
disposition: z.ZodEnum<{
|
|
141
|
+
filtered: "filtered";
|
|
142
|
+
merged: "merged";
|
|
143
|
+
suppressed: "suppressed";
|
|
144
|
+
surviving: "surviving";
|
|
145
|
+
}>;
|
|
146
|
+
reason: z.ZodString;
|
|
147
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
148
|
+
record_type: z.ZodLiteral<"rejected_summary">;
|
|
149
|
+
reviewer: z.ZodString;
|
|
150
|
+
dispatch_outcome: z.ZodEnum<{
|
|
151
|
+
empty: "empty";
|
|
152
|
+
findings: "findings";
|
|
153
|
+
malformed: "malformed";
|
|
154
|
+
never_returned: "never_returned";
|
|
155
|
+
}>;
|
|
156
|
+
rejected_finding_count: z.ZodNumber;
|
|
157
|
+
rejected_severities: z.ZodArray<z.ZodEnum<{
|
|
158
|
+
P0: "P0";
|
|
159
|
+
P1: "P1";
|
|
160
|
+
P2: "P2";
|
|
161
|
+
P3: "P3";
|
|
162
|
+
unknown: "unknown";
|
|
163
|
+
}>>;
|
|
164
|
+
disposition: z.ZodEnum<{
|
|
165
|
+
rejected: "rejected";
|
|
166
|
+
}>;
|
|
167
|
+
reason: z.ZodString;
|
|
168
|
+
}, z.core.$strict>], "record_type">>;
|
|
169
|
+
findings: z.ZodArray<z.ZodType<SynthesizedFinding, {
|
|
170
|
+
title: string;
|
|
171
|
+
severity: "P0" | "P1" | "P2" | "P3";
|
|
172
|
+
file: string;
|
|
173
|
+
line: number;
|
|
174
|
+
why_it_matters: string;
|
|
175
|
+
autofix_class: "advisory" | "gated_auto" | "manual" | "safe_auto";
|
|
176
|
+
owner: "downstream-resolver" | "human" | "release" | "review-fixer";
|
|
177
|
+
requires_verification: boolean;
|
|
178
|
+
confidence: number;
|
|
179
|
+
evidence: (string | {
|
|
180
|
+
overflow: true;
|
|
181
|
+
excerpt: string;
|
|
182
|
+
})[];
|
|
183
|
+
pre_existing: boolean;
|
|
184
|
+
suggested_fix?: string | null | undefined;
|
|
185
|
+
validated?: boolean | undefined;
|
|
186
|
+
validation_reason?: string | undefined;
|
|
187
|
+
input_finding_ids: string[];
|
|
188
|
+
provenance: {
|
|
189
|
+
fingerprint: string;
|
|
190
|
+
submitters: string[];
|
|
191
|
+
agreement_credit: string[];
|
|
192
|
+
};
|
|
193
|
+
}, z.core.$ZodTypeInternals<SynthesizedFinding, {
|
|
194
|
+
title: string;
|
|
195
|
+
severity: "P0" | "P1" | "P2" | "P3";
|
|
196
|
+
file: string;
|
|
197
|
+
line: number;
|
|
198
|
+
why_it_matters: string;
|
|
199
|
+
autofix_class: "advisory" | "gated_auto" | "manual" | "safe_auto";
|
|
200
|
+
owner: "downstream-resolver" | "human" | "release" | "review-fixer";
|
|
201
|
+
requires_verification: boolean;
|
|
202
|
+
confidence: number;
|
|
203
|
+
evidence: (string | {
|
|
204
|
+
overflow: true;
|
|
205
|
+
excerpt: string;
|
|
206
|
+
})[];
|
|
207
|
+
pre_existing: boolean;
|
|
208
|
+
suggested_fix?: string | null | undefined;
|
|
209
|
+
validated?: boolean | undefined;
|
|
210
|
+
validation_reason?: string | undefined;
|
|
211
|
+
input_finding_ids: string[];
|
|
212
|
+
provenance: {
|
|
213
|
+
fingerprint: string;
|
|
214
|
+
submitters: string[];
|
|
215
|
+
agreement_credit: string[];
|
|
216
|
+
};
|
|
217
|
+
}>>>;
|
|
218
|
+
disposition_counts: z.ZodObject<{
|
|
219
|
+
surviving: z.ZodNumber;
|
|
220
|
+
merged: z.ZodNumber;
|
|
221
|
+
suppressed: z.ZodNumber;
|
|
222
|
+
filtered: z.ZodNumber;
|
|
223
|
+
rejected: z.ZodNumber;
|
|
224
|
+
}, z.core.$strict>;
|
|
225
|
+
applied_fixes: z.ZodArray<z.ZodString>;
|
|
226
|
+
residual_actionable_work: z.ZodArray<z.ZodString>;
|
|
227
|
+
advisory_outputs: z.ZodArray<z.ZodString>;
|
|
228
|
+
coverage: z.ZodObject<{
|
|
229
|
+
reviewers: z.ZodOptional<z.ZodNumber>;
|
|
230
|
+
validators: z.ZodOptional<z.ZodNumber>;
|
|
231
|
+
residual_risks: z.ZodArray<z.ZodString>;
|
|
232
|
+
testing_gaps: z.ZodArray<z.ZodString>;
|
|
233
|
+
failed_reviewers: z.ZodArray<z.ZodString>;
|
|
234
|
+
validator_failures: z.ZodArray<z.ZodString>;
|
|
235
|
+
intent_uncertainty: z.ZodArray<z.ZodString>;
|
|
236
|
+
}, z.core.$strict>;
|
|
237
|
+
}, z.core.$strict>;
|
|
238
|
+
export type ReviewArtifact = z.infer<typeof ReviewArtifactSchema>;
|
|
239
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fro.bot/systematic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0",
|
|
4
4
|
"description": "Compound-engineering loops for OpenCode, Pi, and Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://fro.bot/systematic",
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
"docs:generate": "bun docs/scripts/generate-stats.ts && bun docs/scripts/transform-content.ts && bun scripts/generate-config-schema.ts && bun docs/scripts/generate-config-reference.ts",
|
|
50
50
|
"schema:generate": "bun scripts/generate-config-schema.ts",
|
|
51
51
|
"schema:drift": "bun scripts/generate-config-schema.ts --check",
|
|
52
|
+
"review-schema:generate": "bun scripts/generate-review-artifact-schema.ts",
|
|
53
|
+
"review-schema:drift": "bun scripts/generate-review-artifact-schema.ts --check",
|
|
52
54
|
"agent-browser:build": "bun scripts/generate-agent-browser-skill.ts",
|
|
53
55
|
"agent-browser:drift": "bun scripts/generate-agent-browser-skill.ts --check",
|
|
54
56
|
"registry:build": "bun scripts/build-registry.ts",
|
|
@@ -91,7 +93,7 @@
|
|
|
91
93
|
}
|
|
92
94
|
},
|
|
93
95
|
"devDependencies": {
|
|
94
|
-
"@biomejs/biome": "2.5.
|
|
96
|
+
"@biomejs/biome": "2.5.9",
|
|
95
97
|
"@earendil-works/pi-coding-agent": "0.83.0",
|
|
96
98
|
"@opencode-ai/plugin": "1.18.18",
|
|
97
99
|
"@opencode-ai/sdk": "1.18.18",
|
|
@@ -100,15 +102,15 @@
|
|
|
100
102
|
"@types/bun": "latest",
|
|
101
103
|
"@types/js-yaml": "4.0.9",
|
|
102
104
|
"@types/node": "26.2.0",
|
|
103
|
-
"agent-browser": "0.
|
|
105
|
+
"agent-browser": "0.34.0",
|
|
104
106
|
"ajv": "8.20.0",
|
|
105
107
|
"ajv-formats": "3.0.1",
|
|
106
|
-
"conventional-changelog-conventionalcommits": "10.
|
|
108
|
+
"conventional-changelog-conventionalcommits": "10.4.0",
|
|
107
109
|
"markdownlint-cli": "0.49.1",
|
|
108
110
|
"rimraf": "6.1.3",
|
|
109
111
|
"semantic-release": "25.0.9",
|
|
110
112
|
"semantic-release-export-data": "1.2.0",
|
|
111
|
-
"typebox": "1.3.
|
|
113
|
+
"typebox": "1.3.16",
|
|
112
114
|
"typescript": "7.0.2"
|
|
113
115
|
},
|
|
114
116
|
"dependencies": {
|
|
@@ -120,7 +122,8 @@
|
|
|
120
122
|
},
|
|
121
123
|
"overrides": {
|
|
122
124
|
"@earendil-works/pi-ai": "0.83.0",
|
|
123
|
-
"brace-expansion": "^5.0.8"
|
|
125
|
+
"brace-expansion": "^5.0.8",
|
|
126
|
+
"conventional-changelog-writer": ">=9"
|
|
124
127
|
},
|
|
125
128
|
"publishConfig": {
|
|
126
129
|
"access": "public",
|
|
@@ -456,17 +456,17 @@ The parent-owned artifact and its reconciliation rules are defined in the [synth
|
|
|
456
456
|
|
|
457
457
|
Convert multiple reviewer JSON returns into one deduplicated, confidence-gated finding set. Each persona return already contains both tiers. The parent must retain the validated payload in memory for merge and synthesis, then persist only the same validated data.
|
|
458
458
|
|
|
459
|
-
Before applying the confidence gate,
|
|
459
|
+
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.
|
|
460
460
|
|
|
461
461
|
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`.
|
|
462
462
|
- **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.
|
|
463
463
|
- **Per-finding required:** title, severity, file, line, why_it_matters, confidence, evidence, autofix_class, owner, requires_verification, pre_existing.
|
|
464
464
|
- **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.
|
|
465
|
-
- **Environment-value detection:** JSON Schema cannot determine where a string came from, so recursively inspect every string leaf in the parsed payload before writing.
|
|
466
|
-
- **Safe rejection message:**
|
|
467
|
-
- **No partial writes:**
|
|
465
|
+
- **Environment-value detection:** JSON Schema cannot determine where a string came from, so recursively inspect every string leaf in the parsed payload before writing. Apply the environment-value matching, structural-detector, and finding-granularity rules in the [synthesis artifact contract](./references/synthesis-artifact-contract.md). This detector is an additional parent-side check, not a schema claim.
|
|
466
|
+
- **Safe rejection message:** Use the safe rejection message rule in the [synthesis artifact contract](./references/synthesis-artifact-contract.md); never include the offending value, raw return, or validator parameters.
|
|
467
|
+
- **No partial writes:** Do not write or merge a finding until it passes the parent-side validation rules. Apply the admitted-finding persistence and rejected-payload ledger rules in the [synthesis artifact contract](./references/synthesis-artifact-contract.md). A valid admitted finding is then annotated by the parent with `harness` and `dispatch_outcome` and written by the parent only. Revalidate the enriched record before persistence.
|
|
468
468
|
- **Dispatch outcome:** Record the parent-owned dispatch outcomes and ledger dispositions according to the [synthesis artifact contract](./references/synthesis-artifact-contract.md); keep dispatch outcomes separate from finding dispositions.
|
|
469
|
-
- **Rejection policy: degrade, do not fail the whole review.** Continue merging conforming returns when a persona is rejected; record the rejection according to the [synthesis artifact contract](./references/synthesis-artifact-contract.md). If every persona fails or times out, use the existing degraded-review behavior.
|
|
469
|
+
- **Rejection policy: degrade, do not fail the whole review.** Continue merging conforming returns when a persona or finding is rejected; record the rejection and apply the risk-aware verdict according to the [synthesis artifact contract](./references/synthesis-artifact-contract.md). If every persona fails or times out, use the existing degraded-review behavior.
|
|
470
470
|
2. **Confidence gate.** Suppress findings below 0.60 confidence. Exception: P0 findings at 0.50+ confidence survive the gate -- critical-but-uncertain issues must not be silently dropped. Record the suppressed finding's original confidence and an explicit reason in the input ledger. A retained P0 at 0.50+ is recorded as `surviving` unless it later participates in a deduplication merge. This matches the persona instructions and the schema's confidence thresholds.
|
|
471
471
|
3. **Deduplicate.** Compute fingerprint: `normalize(file) + line_bucket(line, +/-3) + normalize(title)`. When fingerprints match, merge: keep highest severity, keep highest confidence, preserve the exact fingerprint, and retain the input IDs that produced the merged entry. A singleton that passes the gate is `surviving`; each input in a multi-input merge is provisionally `merged`.
|
|
472
472
|
4. **Cross-reviewer agreement.** When 2+ independent reviewers flag the same issue (same fingerprint), boost the merged confidence by 0.10 (capped at 1.0). Cross-reviewer agreement is strong signal -- independent reviewers converging on the same issue is more reliable than any single reviewer's confidence. Preserve the distinction in the merged finding's artifact provenance according to the [synthesis artifact contract](./references/synthesis-artifact-contract.md).
|
|
@@ -480,7 +480,7 @@ Before applying the confidence gate, assign every finding in a valid return a st
|
|
|
480
480
|
9. **Sort.** Order by severity (P0 first) -> confidence (descending) -> file path -> line number.
|
|
481
481
|
10. **Collect coverage data.** Union residual_risks and testing_gaps across reviewers.
|
|
482
482
|
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.
|
|
483
|
-
12. **Keep the input ledger complete.**
|
|
483
|
+
12. **Keep the input ledger complete.** Reconcile admitted findings and rejected-payload summaries according to the [synthesis artifact contract](./references/synthesis-artifact-contract.md).
|
|
484
484
|
|
|
485
485
|
### Stage 5b: Validation pass
|
|
486
486
|
|
|
@@ -519,7 +519,7 @@ Assemble the final report using **pipe-delimited markdown tables for findings**
|
|
|
519
519
|
9. **Agent-Native Gaps.** Surface agent-native-reviewer results. Omit section if no gaps found.
|
|
520
520
|
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.
|
|
521
521
|
11. **Coverage.** Suppressed count, residual risks, testing gaps, failed/timed-out reviewers, validator failures, and any intent uncertainty carried by non-interactive modes.
|
|
522
|
-
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.
|
|
522
|
+
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).
|
|
523
523
|
|
|
524
524
|
Do not include time estimates.
|
|
525
525
|
|
|
@@ -704,6 +704,7 @@ After presenting findings and verdict (Stage 6), route the next steps by mode. R
|
|
|
704
704
|
|
|
705
705
|
- In interactive, autofix, and headless modes, write **`review-summary.json` unconditionally** under `.context/systematic/ce-review/<run-id>`; `mode:report-only` remains the deliberate no-write exception.
|
|
706
706
|
- `review-summary.json` is the parent-owned synthesis artifact. Its lifecycle, dispatch outcomes, complete input ledger, synthesized and filtered findings with provenance, disposition counts, and downstream work are defined in the [canonical synthesis artifact contract](./references/synthesis-artifact-contract.md), whose vocabulary and bounds are executable in [`findings-schema.json`](./references/findings-schema.json).
|
|
707
|
+
- Before finalizing, follow the [artifact validation and failure path](./references/synthesis-artifact-contract.md).
|
|
707
708
|
- Initialize the artifact before dispatch and persist only validated parent-owned records. Finalize lifecycle and reconciliation after synthesis; preserve the existing degraded and abnormal-run behavior described in the canonical contract.
|
|
708
709
|
- Also write `metadata.json` alongside the findings so downstream skills can verify the artifact matches the current branch and HEAD. Minimum fields:
|
|
709
710
|
```json
|