@fro.bot/systematic 3.12.1 → 3.12.3
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/agents/review/README.md +10 -0
- package/dist/cli.js +1 -1
- package/dist/{index-y8nc60tz.js → index-h26p98ny.js} +6 -3
- package/dist/index.js +3 -2
- package/dist/lib/walk-dir.d.ts +10 -0
- package/dist/pi.js +4 -1
- package/package.json +1 -1
- package/skills/ce-plan/references/universal-planning.md +1 -1
- package/skills/ce-review/SKILL.md +17 -17
- package/skills/ce-review/references/review-output-template.md +3 -60
- package/skills/ce-review/references/synthesis-artifact-contract.md +127 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Shared Review Persona Pool
|
|
2
|
+
|
|
3
|
+
`agents/review/` is a shared persona pool, not a single workflow roster.
|
|
4
|
+
Workflow-specific rosters live in the relevant skill catalogs.
|
|
5
|
+
|
|
6
|
+
These agents are not dispatched by `ce:review`:
|
|
7
|
+
|
|
8
|
+
- `systematic:review:architecture-strategist` — dispatched by `deepen-plan` and the `ce-plan` deepening workflow.
|
|
9
|
+
- `systematic:review:pattern-recognition-specialist` — dispatched by `deepen-plan`, the `ce-plan` deepening workflow, and `ce-compound`.
|
|
10
|
+
- `systematic:review:code-simplicity-reviewer` — dispatched by `ce-compound`.
|
package/dist/cli.js
CHANGED
|
@@ -16782,6 +16782,9 @@ function extractBoolean(data, key) {
|
|
|
16782
16782
|
// src/lib/walk-dir.ts
|
|
16783
16783
|
import fs2 from "fs";
|
|
16784
16784
|
import path2 from "path";
|
|
16785
|
+
function isDiscoverableMarkdown(fileName) {
|
|
16786
|
+
return fileName.endsWith(".md") && fileName.toLowerCase() !== "readme.md";
|
|
16787
|
+
}
|
|
16785
16788
|
function walkDir(rootDir, options = {}) {
|
|
16786
16789
|
const { maxDepth = 3, filter } = options;
|
|
16787
16790
|
const results = [];
|
|
@@ -16816,7 +16819,7 @@ function walkDir(rootDir, options = {}) {
|
|
|
16816
16819
|
function findAgentsInDir(dir, maxDepth = 2) {
|
|
16817
16820
|
const entries = walkDir(dir, {
|
|
16818
16821
|
maxDepth,
|
|
16819
|
-
filter: (e) => !e.isDirectory && e.name
|
|
16822
|
+
filter: (e) => !e.isDirectory && isDiscoverableMarkdown(e.name)
|
|
16820
16823
|
});
|
|
16821
16824
|
return entries.map((entry) => ({
|
|
16822
16825
|
name: entry.name.replace(/\.md$/, ""),
|
|
@@ -16851,7 +16854,7 @@ function extractAgentFrontmatter(content) {
|
|
|
16851
16854
|
function findCommandsInDir(dir, maxDepth = 2) {
|
|
16852
16855
|
const entries = walkDir(dir, {
|
|
16853
16856
|
maxDepth,
|
|
16854
|
-
filter: (e) => !e.isDirectory && e.name
|
|
16857
|
+
filter: (e) => !e.isDirectory && isDiscoverableMarkdown(e.name)
|
|
16855
16858
|
});
|
|
16856
16859
|
return entries.map((entry) => {
|
|
16857
16860
|
const baseName = entry.name.replace(/\.md$/, "");
|
|
@@ -17094,4 +17097,4 @@ function discoverSkills(options) {
|
|
|
17094
17097
|
return Array.from(byName.values());
|
|
17095
17098
|
}
|
|
17096
17099
|
|
|
17097
|
-
export { __require, parseFrontmatter, parse2 as parse, parseTree2 as parseTree, modify, applyEdits, exports_external, AgentOverlaySchema, CategoryOverlaySchema, loadConfig, loadConfigWithSources, getConfigPaths, isRecord2 as isRecord, extractString, findAgentsInDir, extractAgentFrontmatter, findCommandsInDir, extractCommandFrontmatter, findSkillsInDir, discoverSkills };
|
|
17100
|
+
export { __require, parseFrontmatter, parse2 as parse, parseTree2 as parseTree, modify, applyEdits, exports_external, AgentOverlaySchema, CategoryOverlaySchema, loadConfig, loadConfigWithSources, getConfigPaths, isRecord2 as isRecord, extractString, isDiscoverableMarkdown, findAgentsInDir, extractAgentFrontmatter, findCommandsInDir, extractCommandFrontmatter, findSkillsInDir, discoverSkills };
|
package/dist/index.js
CHANGED
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
findAgentsInDir,
|
|
11
11
|
findCommandsInDir,
|
|
12
12
|
findSkillsInDir,
|
|
13
|
+
isDiscoverableMarkdown,
|
|
13
14
|
isRecord,
|
|
14
15
|
loadConfig,
|
|
15
16
|
loadConfigWithSources,
|
|
16
17
|
parseFrontmatter
|
|
17
|
-
} from "./index-
|
|
18
|
+
} from "./index-h26p98ny.js";
|
|
18
19
|
|
|
19
20
|
// src/index.ts
|
|
20
21
|
import { createHash as createHash4 } from "crypto";
|
|
@@ -282,7 +283,7 @@ function readCategoryDirs(agentsDir) {
|
|
|
282
283
|
}
|
|
283
284
|
function readMarkdownFiles(categoryDir) {
|
|
284
285
|
try {
|
|
285
|
-
return fs3.readdirSync(categoryDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name
|
|
286
|
+
return fs3.readdirSync(categoryDir, { withFileTypes: true }).filter((entry) => entry.isFile() && isDiscoverableMarkdown(entry.name)).map((entry) => entry.name).sort();
|
|
286
287
|
} catch {
|
|
287
288
|
return [];
|
|
288
289
|
}
|
package/dist/lib/walk-dir.d.ts
CHANGED
|
@@ -9,4 +9,14 @@ export interface WalkOptions {
|
|
|
9
9
|
maxDepth?: number;
|
|
10
10
|
filter?: (entry: WalkEntry) => boolean;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Whether a markdown filename is discoverable content rather than documentation
|
|
14
|
+
* about the directory that holds it.
|
|
15
|
+
*
|
|
16
|
+
* Agent and command discovery register every `.md` file they walk, so a plain
|
|
17
|
+
* `README.md` placed in `agents/<category>/` would otherwise be registered as a
|
|
18
|
+
* dispatchable agent named `README`. README is documentation by universal
|
|
19
|
+
* convention, so it is never content.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isDiscoverableMarkdown(fileName: string): boolean;
|
|
12
22
|
export declare function walkDir(rootDir: string, options?: WalkOptions): WalkEntry[];
|
package/dist/pi.js
CHANGED
|
@@ -3170,6 +3170,9 @@ function extractBoolean(data, key) {
|
|
|
3170
3170
|
// src/lib/walk-dir.ts
|
|
3171
3171
|
import fs from "node:fs";
|
|
3172
3172
|
import path from "node:path";
|
|
3173
|
+
function isDiscoverableMarkdown(fileName) {
|
|
3174
|
+
return fileName.endsWith(".md") && fileName.toLowerCase() !== "readme.md";
|
|
3175
|
+
}
|
|
3173
3176
|
function walkDir(rootDir, options = {}) {
|
|
3174
3177
|
const { maxDepth = 3, filter } = options;
|
|
3175
3178
|
const results = [];
|
|
@@ -3204,7 +3207,7 @@ function walkDir(rootDir, options = {}) {
|
|
|
3204
3207
|
function findAgentsInDir(dir, maxDepth = 2) {
|
|
3205
3208
|
const entries = walkDir(dir, {
|
|
3206
3209
|
maxDepth,
|
|
3207
|
-
filter: (e) => !e.isDirectory && e.name
|
|
3210
|
+
filter: (e) => !e.isDirectory && isDiscoverableMarkdown(e.name)
|
|
3208
3211
|
});
|
|
3209
3212
|
return entries.map((entry) => ({
|
|
3210
3213
|
name: entry.name.replace(/\.md$/, ""),
|
package/package.json
CHANGED
|
@@ -28,7 +28,7 @@ Evaluate two things before planning:
|
|
|
28
28
|
| Research need | Signals | Action |
|
|
29
29
|
|--------------|---------|--------|
|
|
30
30
|
| **None** | Generic, timeless, or conceptual plan (study curriculum methodology, project management approach, personal goal breakdown) | Skip research. Model knowledge is sufficient. After structuring the plan, offer: "I based this on general knowledge. Want me to search for [specific thing research would improve]?" — e.g., sourced recipes, current product recommendations, expert frameworks. Only if the user accepts. |
|
|
31
|
-
| **Recommended** | Plan references specific locations, venues, dates, prices, schedules, seasonal availability, or current events — anything where stale information would break the plan (closed restaurants, changed prices, cancelled events, wrong seasonal dates). | Research before planning. Decompose into 2-5 focused research questions and dispatch parallel web searches.
|
|
31
|
+
| **Recommended** | Plan references specific locations, venues, dates, prices, schedules, seasonal availability, or current events — anything where stale information would break the plan (closed restaurants, changed prices, cancelled events, wrong seasonal dates). | Research before planning. Decompose into 2-5 focused research questions and dispatch parallel web searches. Collate findings before structuring the plan. |
|
|
32
32
|
|
|
33
33
|
When research is recommended, do it — don't just offer. Stale recommendations (closed restaurants, rethemed attractions, outdated prices) are worse than no recommendations. The user invoked `ce:plan` because they want a good plan, not a disclaimer about training data.
|
|
34
34
|
|
|
@@ -452,9 +452,11 @@ Returning the detail tier inline increases parent context per persona. The previ
|
|
|
452
452
|
|
|
453
453
|
### Stage 5: Merge findings
|
|
454
454
|
|
|
455
|
+
The parent-owned artifact and its reconciliation rules are defined in the [synthesis artifact contract](./references/synthesis-artifact-contract.md). The stages below describe when synthesis decisions are made.
|
|
456
|
+
|
|
455
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.
|
|
456
458
|
|
|
457
|
-
Before applying the confidence gate, assign every finding in a valid return a stable parent-owned `input_id` of `<reviewer>#<1-based finding index
|
|
459
|
+
Before applying the confidence gate, assign every finding in a valid return a stable parent-owned `input_id` of `<reviewer>#<1-based finding index>` and keep the parent-owned ledger through every later stage. See the [synthesis artifact contract](./references/synthesis-artifact-contract.md) for the ledger's authoritative reconciliation rules.
|
|
458
460
|
|
|
459
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`.
|
|
460
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,11 +465,11 @@ Before applying the confidence gate, assign every finding in a valid return a st
|
|
|
463
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. Reject the payload when a string contains a shell/environment reference (`$NAME`, `${NAME}`, `process.env.NAME`, or `os.environ[...]`), an assignment using a known environment variable (`NAME=value`), or a current environment value as an exact or embedded match. Use a conservative match set of non-empty runtime environment values; never log the matched value. This detector is an additional parent-side check, not a schema claim.
|
|
464
466
|
- **Safe rejection message:** report only `Rejected persona <name> return: field <JSON path> failed <reason>.` Derive `<JSON path>` from the validator or recursive scan and use a fixed reason such as `schema validation`, `environment-value detection`, or `malformed JSON`; never include the offending value, raw return, or validator parameters.
|
|
465
467
|
- **No partial writes:** do not write a per-agent record or merge any finding until the entire persona payload passes schema and environment-value validation. A valid payload is then annotated by the parent with `harness` and `dispatch_outcome` and written by the parent only. Revalidate the enriched record before persistence.
|
|
466
|
-
- **Dispatch outcome:**
|
|
467
|
-
- **Rejection policy: degrade, do not fail the whole review.** Continue merging conforming returns
|
|
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.
|
|
468
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.
|
|
469
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`.
|
|
470
|
-
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
|
|
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).
|
|
471
473
|
5. **Separate pre-existing.** Pull out findings with `pre_existing: true` into a separate list.
|
|
472
474
|
6. **Resolve disagreements.** When reviewers flag the same code region but disagree on severity, autofix_class, or owner, annotate the Reviewer column with the disagreement (e.g., "security (P0), correctness (P1) -- kept P0"). This transparency helps the user understand why a finding was routed the way it was.
|
|
473
475
|
7. **Normalize routing.** For each merged finding, set the final `autofix_class`, `owner`, and `requires_verification`. If reviewers disagree, keep the most conservative route. Synthesis may narrow a finding from `safe_auto` to `gated_auto` or `manual`, but must not widen it without new evidence.
|
|
@@ -478,7 +480,7 @@ Before applying the confidence gate, assign every finding in a valid return a st
|
|
|
478
480
|
9. **Sort.** Order by severity (P0 first) -> confidence (descending) -> file path -> line number.
|
|
479
481
|
10. **Collect coverage data.** Union residual_risks and testing_gaps across reviewers.
|
|
480
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.
|
|
481
|
-
12. **Keep the input ledger complete.** Every enumerated input finding
|
|
483
|
+
12. **Keep the input ledger complete.** Every enumerated input finding must receive exactly one final disposition and reason; reconcile the ledger according to the [synthesis artifact contract](./references/synthesis-artifact-contract.md).
|
|
482
484
|
|
|
483
485
|
### Stage 5b: Validation pass
|
|
484
486
|
|
|
@@ -491,9 +493,9 @@ Run an independent validation pass over the merged finding set before synthesis.
|
|
|
491
493
|
1. Identify all gated findings from the Stage 5 merged set.
|
|
492
494
|
2. For each gated finding, spawn one validator subagent in parallel using the validator template at `references/validator-template.md`. Pass the finding fields, the intent summary, the file list, and the full diff.
|
|
493
495
|
3. Collect `{validated, reason}` from each validator. Attach both fields to the finding.
|
|
494
|
-
4. **Reconcile filtered inputs.** A finding with `validated: false` moves to the "Filtered (not validated)" presentation group in Stage 6, and every input ID contributing to that merged finding is updated to disposition `filtered` with the validator's exact one-sentence reason.
|
|
495
|
-
5. Findings with `validated: true` flow through to Stage 6 unchanged — they appear in the normal severity tables.
|
|
496
|
-
6. Findings outside the gating band carry no `validated` annotation and appear in Stage 6 severity tables unchanged
|
|
496
|
+
4. **Reconcile filtered inputs.** A finding with `validated: false` moves to the "Filtered (not validated)" presentation group in Stage 6, and every input ID contributing to that merged finding is updated to disposition `filtered` with the validator's exact one-sentence reason. Apply the remaining ledger rules from the [synthesis artifact contract](./references/synthesis-artifact-contract.md).
|
|
497
|
+
5. Findings with `validated: true` flow through to Stage 6 unchanged — they appear in the normal severity tables.
|
|
498
|
+
6. Findings outside the gating band carry no `validated` annotation and appear in Stage 6 severity tables unchanged.
|
|
497
499
|
|
|
498
500
|
**Failure handling:** If a validator subagent fails or times out, treat the finding as `validated: true` (conservative fallback — keep it in the actioned set) and note the validator failure in the Coverage section.
|
|
499
501
|
|
|
@@ -700,15 +702,9 @@ After presenting findings and verdict (Stage 6), route the next steps by mode. R
|
|
|
700
702
|
|
|
701
703
|
#### Step 4: Emit artifacts and downstream handoff
|
|
702
704
|
|
|
703
|
-
- In interactive, autofix, and headless modes, write **`review-summary.json` unconditionally** under `.context/systematic/ce-review/<run-id
|
|
704
|
-
- `review-summary.json` is the parent-owned synthesis artifact and
|
|
705
|
-
|
|
706
|
-
- a `dispatches` entry for every selected persona with `persona`, `dispatch_outcome` (`findings`, `empty`, `malformed`, or `never_returned`), the number of safely enumerated input findings, and the exact safe `rejection_reason` when applicable;
|
|
707
|
-
- an `input_findings` ledger with one entry per safely enumerated input, its `input_id`, reviewer, original confidence, final `disposition` (`surviving`, `merged`, `suppressed`, `filtered`, or `rejected`), and a stated reason. Its count must reconcile exactly with the sum of disposition counts. A malformed JSON return with no safely enumerable finding has zero ledger entries, not a fabricated finding;
|
|
708
|
-
- surviving synthesized findings and filtered findings, each retaining their original fields plus `input_finding_ids` and provenance. Every synthesized finding's provenance must include the exact dedup `fingerprint`, `submitters`, and `agreement_credit` arrays. `submitters` means independent input submissions; `agreement_credit` means agreement boost credit without a corresponding input submission;
|
|
709
|
-
- applied fixes, residual actionable work, advisory-only outputs, coverage data, and the harness value.
|
|
710
|
-
- During the pre-dispatch setup described in Stage 4, initialize the synthesis artifact with lifecycle state `in_progress` and all selected personas initialized as `never_returned`. Update each dispatch entry as returns arrive. Finalize it as `completed` or `degraded` after synthesis; if the parent catches an abort or storage/orchestration failure, finalize it as `abnormal` with the stated termination reason. If the process dies before finalization, the pre-written `in_progress` artifact is itself an explicit incomplete run and must be counted as abnormal rather than treated as a missing or clean run. Never infer a clean run from an absent artifact.
|
|
711
|
-
- Per-agent full-detail JSON files (`{reviewer_name}.json`) are written by the parent only after the persona return passes full-schema and environment-value validation. Rejected or never-returned personas do not produce a per-agent file; their dispatch outcome remains in the synthesis artifact. If a later confidence or validation stage changes an input disposition, update the parent-owned record and synthesis ledger before finalizing `review-summary.json`.
|
|
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
|
+
- `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
|
+
- 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.
|
|
712
708
|
- Also write `metadata.json` alongside the findings so downstream skills can verify the artifact matches the current branch and HEAD. Minimum fields:
|
|
713
709
|
```json
|
|
714
710
|
{
|
|
@@ -770,6 +766,10 @@ If the platform doesn't support parallel sub-agents, run reviewers sequentially.
|
|
|
770
766
|
|
|
771
767
|
@./references/findings-schema.json
|
|
772
768
|
|
|
769
|
+
### Synthesis Artifact Contract
|
|
770
|
+
|
|
771
|
+
@./references/synthesis-artifact-contract.md
|
|
772
|
+
|
|
773
773
|
### Review Output Template
|
|
774
774
|
|
|
775
775
|
@./references/review-output-template.md
|
|
@@ -125,7 +125,7 @@ This fails because: no pipe-delimited tables, no severity-grouped `###` headers,
|
|
|
125
125
|
- **Pipe-delimited markdown tables** for findings -- never ASCII box-drawing characters or per-finding horizontal-rule separators between entries (the report-level `---` before the verdict is still required)
|
|
126
126
|
- **Severity-grouped sections** -- `### P0 -- Critical`, `### P1 -- High`, `### P2 -- Moderate`, `### P3 -- Low`. Omit empty severity levels.
|
|
127
127
|
- **Always include file:line location** for code review issues
|
|
128
|
-
- **Reviewer column** shows which persona(s) submitted the issue.
|
|
128
|
+
- **Reviewer column** shows which persona(s) submitted the issue. For the machine-readable distinction between submissions and agreement credit, see the [synthesis artifact contract](./synthesis-artifact-contract.md); do not infer submission from the display column alone.
|
|
129
129
|
- **Confidence column** shows the finding's confidence score
|
|
130
130
|
- **Route column** shows the synthesized handling decision as ``<autofix_class> -> <owner>``.
|
|
131
131
|
- **Header includes** scope, intent, and reviewer team with per-conditional justifications
|
|
@@ -149,7 +149,7 @@ In `mode:headless`, replace the interactive pipe-delimited table report with a s
|
|
|
149
149
|
- **No pipe-delimited tables.** Findings use `[severity][autofix_class -> owner] File: <file:line> -- <title>` line format with indented Why/Evidence/Suggested fix lines.
|
|
150
150
|
- **Findings grouped by autofix_class** (gated-auto, manual, advisory) instead of severity. Within each group, findings are sorted by severity.
|
|
151
151
|
- **Verdict in header** (top of output) instead of bottom, so programmatic callers get it first.
|
|
152
|
-
- **`Artifact:` line** in metadata header gives callers the path to `review-summary.json
|
|
152
|
+
- **`Artifact:` line** in the metadata header gives callers the path to `review-summary.json`; its full contract is defined in the [synthesis artifact contract](./synthesis-artifact-contract.md).
|
|
153
153
|
- **`[needs-verification]` marker** on findings where `requires_verification: true`.
|
|
154
154
|
- **Evidence lines** included per finding.
|
|
155
155
|
- **"Filtered (not validated)" section** included when Stage 5b produced findings with `validated: false`. Uses `[severity][autofix_class -> owner] File: <file:line> -- <title>` format with an indented `Validator reason:` line. These findings are surfaced for human review, not removed.
|
|
@@ -157,61 +157,4 @@ In `mode:headless`, replace the interactive pipe-delimited table report with a s
|
|
|
157
157
|
|
|
158
158
|
## Synthesis Artifact Contract
|
|
159
159
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
The artifact must preserve the following distinctions:
|
|
163
|
-
|
|
164
|
-
```json
|
|
165
|
-
{
|
|
166
|
-
"run_id": "<run-id>",
|
|
167
|
-
"mode": "<interactive | autofix | headless>",
|
|
168
|
-
"harness": "<opencode | pi | claude-code>",
|
|
169
|
-
"run_status": "<in_progress | completed | degraded | abnormal>",
|
|
170
|
-
"dispatches": [
|
|
171
|
-
{
|
|
172
|
-
"persona": "correctness",
|
|
173
|
-
"dispatch_outcome": "findings",
|
|
174
|
-
"input_finding_count": 2
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
"persona": "kieran-typescript",
|
|
178
|
-
"dispatch_outcome": "malformed",
|
|
179
|
-
"input_finding_count": 1,
|
|
180
|
-
"rejection_reason": "Rejected persona kieran-typescript return: field findings[0].evidence failed schema validation."
|
|
181
|
-
}
|
|
182
|
-
],
|
|
183
|
-
"input_findings": [
|
|
184
|
-
{
|
|
185
|
-
"input_id": "correctness#1",
|
|
186
|
-
"reviewer": "correctness",
|
|
187
|
-
"confidence": 0.55,
|
|
188
|
-
"disposition": "suppressed",
|
|
189
|
-
"reason": "confidence 0.55 is below the 0.60 gate"
|
|
190
|
-
}
|
|
191
|
-
],
|
|
192
|
-
"findings": [
|
|
193
|
-
{
|
|
194
|
-
"title": "<merged finding>",
|
|
195
|
-
"input_finding_ids": ["correctness#2", "testing#1"],
|
|
196
|
-
"provenance": {
|
|
197
|
-
"fingerprint": "<normalize(file) + line_bucket(line, +/-3) + normalize(title)>",
|
|
198
|
-
"submitters": ["correctness", "testing"],
|
|
199
|
-
"agreement_credit": []
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
],
|
|
203
|
-
"disposition_counts": {
|
|
204
|
-
"surviving": 0,
|
|
205
|
-
"merged": 2,
|
|
206
|
-
"suppressed": 1,
|
|
207
|
-
"filtered": 0,
|
|
208
|
-
"rejected": 0
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
- `dispatch_outcome` records what a persona returned: `findings`, `empty`, `malformed`, or `never_returned`. A rejection reason is preserved as the exact safe validation reason, naming persona and field without echoing the offending value.
|
|
214
|
-
- `disposition` records what happened to each input finding: `surviving`, `merged`, `suppressed`, `filtered`, or `rejected`. Every safely enumerable input has exactly one disposition and stated reason; the disposition counts must equal the input-finding count.
|
|
215
|
-
- `submitters` contains only personas with an input finding in the merged fingerprint group. `agreement_credit` contains only personas credited by the cross-reviewer agreement boost without an input finding in that group. A persona returning zero findings never appears in `submitters`.
|
|
216
|
-
- `filtered` findings remain available for human review with the validator's stated reason, but are not part of the surviving/actioned set. A suppressed finding retains its original confidence, including the P0 exception for confidence `0.50` or higher.
|
|
217
|
-
- The parent initializes the artifact as `in_progress` before dispatch. A completed run becomes `completed` or `degraded`; an interrupted or failed run is `abnormal` with its stated termination reason. An unfinished `in_progress` artifact is evidence of an abnormal run, not evidence of a clean run.
|
|
160
|
+
Interactive, autofix, and headless runs write the parent-owned `review-summary.json`, while `mode:report-only` deliberately writes no artifact. Follow the [canonical synthesis artifact contract](./synthesis-artifact-contract.md); use [`findings-schema.json`](./findings-schema.json) for executable field vocabulary and bounds.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Synthesis Artifact Contract
|
|
2
|
+
|
|
3
|
+
This is the canonical prose definition of the parent-owned
|
|
4
|
+
`.context/systematic/ce-review/<run-id>/review-summary.json` synthesis artifact.
|
|
5
|
+
The executable field vocabulary and bounds remain defined by
|
|
6
|
+
[`findings-schema.json`](./findings-schema.json).
|
|
7
|
+
|
|
8
|
+
## Scope and lifecycle
|
|
9
|
+
|
|
10
|
+
For interactive, autofix, and headless runs, the parent writes
|
|
11
|
+
`review-summary.json` even when every selected persona returns `empty` and no
|
|
12
|
+
finding survives. `mode:report-only` is the deliberate no-write exception.
|
|
13
|
+
|
|
14
|
+
The parent initializes the artifact as `in_progress` before dispatch, with all
|
|
15
|
+
selected personas initialized as `never_returned`, and updates each dispatch
|
|
16
|
+
entry as returns arrive. A completed run becomes `completed` or `degraded`. An
|
|
17
|
+
interrupted or failed run becomes `abnormal` with its stated termination
|
|
18
|
+
reason. An unfinished `in_progress` artifact is evidence of an abnormal run,
|
|
19
|
+
not evidence of a clean run. Never infer a clean run from an absent artifact.
|
|
20
|
+
|
|
21
|
+
The artifact is parent-owned. Per-agent full-detail JSON files are written
|
|
22
|
+
only after the persona return passes full-schema and environment-value
|
|
23
|
+
validation. Rejected or never-returned personas do not produce per-agent
|
|
24
|
+
files. If a later confidence or validation stage changes an input disposition,
|
|
25
|
+
the parent updates the record and synthesis ledger before finalizing the
|
|
26
|
+
artifact.
|
|
27
|
+
|
|
28
|
+
## Required distinctions and reconciliation
|
|
29
|
+
|
|
30
|
+
The artifact must preserve these distinctions:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"run_id": "<run-id>",
|
|
35
|
+
"mode": "<interactive | autofix | headless>",
|
|
36
|
+
"harness": "<opencode | pi | claude-code>",
|
|
37
|
+
"run_status": "<in_progress | completed | degraded | abnormal>",
|
|
38
|
+
"dispatches": [
|
|
39
|
+
{
|
|
40
|
+
"persona": "correctness",
|
|
41
|
+
"dispatch_outcome": "findings",
|
|
42
|
+
"input_finding_count": 2
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"persona": "kieran-typescript",
|
|
46
|
+
"dispatch_outcome": "malformed",
|
|
47
|
+
"input_finding_count": 1,
|
|
48
|
+
"rejection_reason": "Rejected persona kieran-typescript return: field findings[0].evidence failed schema validation."
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"input_findings": [
|
|
52
|
+
{
|
|
53
|
+
"input_id": "correctness#1",
|
|
54
|
+
"reviewer": "correctness",
|
|
55
|
+
"confidence": 0.55,
|
|
56
|
+
"disposition": "suppressed",
|
|
57
|
+
"reason": "confidence 0.55 is below the 0.60 gate"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"findings": [
|
|
61
|
+
{
|
|
62
|
+
"title": "<merged finding>",
|
|
63
|
+
"input_finding_ids": ["correctness#2", "testing#1"],
|
|
64
|
+
"provenance": {
|
|
65
|
+
"fingerprint": "<normalize(file) + line_bucket(line, +/-3) + normalize(title)>",
|
|
66
|
+
"submitters": ["correctness", "testing"],
|
|
67
|
+
"agreement_credit": []
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"disposition_counts": {
|
|
72
|
+
"surviving": 0,
|
|
73
|
+
"merged": 2,
|
|
74
|
+
"suppressed": 1,
|
|
75
|
+
"filtered": 0,
|
|
76
|
+
"rejected": 0
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- `dispatches` has an entry for every selected persona. `dispatch_outcome`
|
|
82
|
+
records what a persona returned: `findings`, `empty`, `malformed`, or
|
|
83
|
+
`never_returned`. A rejection reason is the exact safe validation reason,
|
|
84
|
+
naming persona and field without echoing the offending value. Dispatch
|
|
85
|
+
outcome is separate from finding disposition.
|
|
86
|
+
- `input_findings` is the authoritative parent-owned ledger. Before the
|
|
87
|
+
confidence gate, every safely enumerable finding receives an `input_id` of
|
|
88
|
+
`<reviewer>#<1-based finding index>`. Every enumerated input has exactly one
|
|
89
|
+
final `disposition`: `surviving`, `merged`, `suppressed`, `filtered`, or
|
|
90
|
+
`rejected`, plus a reason. Disposition counts equal the input-finding count.
|
|
91
|
+
If a rejected return has a safely enumerable `findings` array, assign IDs and
|
|
92
|
+
record each enumerated input as `rejected`.
|
|
93
|
+
A malformed JSON return with no safely enumerable finding has zero ledger
|
|
94
|
+
entries, not a fabricated finding. A rejected payload's reason is the exact
|
|
95
|
+
safe rejection message, not a bucket such as `invalid`; never include the
|
|
96
|
+
offending value.
|
|
97
|
+
- Synthesized and filtered findings retain their original fields plus
|
|
98
|
+
`input_finding_ids` and provenance. Provenance contains the exact dedup
|
|
99
|
+
fingerprint `normalize(file) + line_bucket(line, +/-3) + normalize(title)`,
|
|
100
|
+
`submitters`, and `agreement_credit` arrays.
|
|
101
|
+
- `submitters` contains only personas with an input finding in the merged
|
|
102
|
+
fingerprint group. `agreement_credit` contains only personas credited by the
|
|
103
|
+
cross-reviewer agreement boost without an input finding in that group. A
|
|
104
|
+
persona returning zero findings never appears in `submitters`; do not infer
|
|
105
|
+
submission from the report's Reviewer column.
|
|
106
|
+
- A `filtered` finding remains available for human review with the validator's
|
|
107
|
+
stated reason, but is not part of the surviving/actioned set. Every input ID
|
|
108
|
+
contributing to a finding with `validated: false` receives disposition
|
|
109
|
+
`filtered` with the validator's exact one-sentence reason; it is not
|
|
110
|
+
`suppressed`, `rejected`, or silently excluded. A suppressed finding retains
|
|
111
|
+
its original confidence, including the P0 exception for confidence `0.50`
|
|
112
|
+
or higher.
|
|
113
|
+
|
|
114
|
+
The artifact also includes applied fixes, residual actionable work,
|
|
115
|
+
advisory-only outputs, coverage data, and the harness value. Alongside the
|
|
116
|
+
findings, the parent writes `metadata.json` with the run ID, branch and HEAD
|
|
117
|
+
captured at dispatch time, harness, verdict, and completion timestamp. The
|
|
118
|
+
branch and HEAD are captured before autofixes land; metadata is written after
|
|
119
|
+
the verdict is finalized. Existing artifacts without this additive metadata
|
|
120
|
+
remain valid, with downstream consumers falling back to file mtime.
|
|
121
|
+
|
|
122
|
+
Validation and persistence remain parent-side: no per-agent record or finding
|
|
123
|
+
is written or merged until the complete persona payload passes schema and
|
|
124
|
+
environment-value validation. Rejected or malformed persona returns do not
|
|
125
|
+
fail the whole review; the review degrades while conforming returns continue
|
|
126
|
+
through synthesis. Only an orchestration or storage failure that prevents the
|
|
127
|
+
parent from producing the required run artifact is run-fatal.
|