fix: close the gaps the review of the trial-5 fixes found
- park-attempt-files: parks in the git dir (outside the work tree, so Sub-step C.2 never registers a parked file again), reads a path with a space, names a new file under a header it cannot read, parks a symlink, never overwrites a parked file, and exits 1 while a file the attempt made is still in the tree; the upward diff is saved in one form whatever the git config. - brief-validator: BRIEF_SC_NOT_RUNNABLE only when a later backticked span is a command; a criterion that only names a flag or a path stays prose (Handover 1 says so now). - review ids: counted over the final list (runContract), as fill-finding-ids counts review.md; trimmed_count matches rows by id; engine-findings and fidelity-diff use computeFindingIds; the --workflow dedup keeps one source's different findings apart, the decision-23 put-back stays one copy per triplet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3238327483
commit
8677836060
16 changed files with 274 additions and 98 deletions
|
|
@ -126,16 +126,33 @@ const VERDICT_SCHEMA = {
|
|||
// the workflow has no conformance rule, so otherwise the first-seen one stays.
|
||||
// The survivor carries the highest severity any finding in its group asserted,
|
||||
// so an engine's mark never lowers what a reviewer agent asserted.
|
||||
function dedupByTriplet(findings) {
|
||||
const groups = new Map();
|
||||
// As in coordinator-contract Pass 1, one source never duplicates itself: its
|
||||
// second finding on a triplet under another title opens a group of its own
|
||||
// (voyage-lekerepo trial 5, finding 7). `sourceOf` names the reviewer or engine.
|
||||
const sameTitle = (a, b) => a.title === undefined || b.title === undefined ||
|
||||
String(a.title).trim().toLowerCase() === String(b.title).trim().toLowerCase();
|
||||
const sourceOf = new Map();
|
||||
function dedupByTriplet(findings, { perSource = true } = {}) {
|
||||
const byTriplet = new Map();
|
||||
for (const f of findings) {
|
||||
const key = tripletOf(f);
|
||||
if (!groups.has(key)) groups.set(key, []);
|
||||
groups.get(key).push(f);
|
||||
if (!byTriplet.has(key)) byTriplet.set(key, []);
|
||||
const subs = byTriplet.get(key);
|
||||
const src = sourceOf.get(f) ?? null;
|
||||
const fits = (g) => g.every((m) => (sourceOf.get(m) ?? null) !== src || sameTitle(m, f));
|
||||
let sub = !perSource ? subs[0]
|
||||
: subs.find((g) => fits(g) && g.some((m) => m.title !== undefined && f.title !== undefined && sameTitle(m, f)))
|
||||
?? subs.find(fits);
|
||||
if (!sub) {
|
||||
sub = [];
|
||||
subs.push(sub);
|
||||
}
|
||||
sub.push(f);
|
||||
}
|
||||
const groups = [...byTriplet.values()].flat();
|
||||
const tier = (f) => sevRank(tierOf(f.rule_key) ?? f.severity);
|
||||
const out = [];
|
||||
for (const group of groups.values()) {
|
||||
for (const group of groups) {
|
||||
let survivor = group[0];
|
||||
for (const f of group.slice(1)) {
|
||||
const tf = tier(f);
|
||||
|
|
@ -248,9 +265,15 @@ const withEngine = (f, eng) => {
|
|||
const { engine: _dropped, ...rest } = f;
|
||||
return eng ? { ...rest, engine: eng } : rest;
|
||||
};
|
||||
const tagged = (f, src) => {
|
||||
if (f && typeof f === 'object') sourceOf.set(f, src);
|
||||
return f;
|
||||
};
|
||||
const allFindings = [
|
||||
...reviewerResults.flatMap((r) => (Array.isArray(r?.findings) ? r.findings : []).map((f) => withEngine(f, null))),
|
||||
...enginePayloads.flatMap((p) => (Array.isArray(p?.findings) ? p.findings : []).map((f) => withEngine(f, engineOf(p?.reviewer)))),
|
||||
...reviewerResults.flatMap((r, i) => (Array.isArray(r?.findings) ? r.findings : [])
|
||||
.map((f) => tagged(withEngine(f, null), `reviewer:${r?.reviewer ?? i}`))),
|
||||
...enginePayloads.flatMap((p) => (Array.isArray(p?.findings) ? p.findings : [])
|
||||
.map((f) => tagged(withEngine(f, engineOf(p?.reviewer)), `engine:${p?.reviewer}`))),
|
||||
];
|
||||
const merged = dedupByTriplet(allFindings);
|
||||
log(`Arm B: ${reviewerResults.length} reviewer(s); ${allFindings.length} raw → ${merged.length} after triplet-dedup`);
|
||||
|
|
@ -333,9 +356,10 @@ for (const f of returned) {
|
|||
}
|
||||
// (c) the put-back (decision 23).
|
||||
const returnedTriplets = new Set(returned.filter((f) => f && typeof f === 'object').map(tripletOf));
|
||||
// Engine findings only, so dedupByTriplet keeps the higher severity, else the first seen.
|
||||
// Engine findings only, so dedupByTriplet keeps the higher severity, else the
|
||||
// first seen: one copy per triplet (decision 23), so not split per source here.
|
||||
const putBack = dedupByTriplet(allFindings.filter((f) =>
|
||||
f && typeof f === 'object' && f.engine && !returnedTriplets.has(tripletOf(f))));
|
||||
f && typeof f === 'object' && f.engine && !returnedTriplets.has(tripletOf(f))), { perSource: false });
|
||||
for (const f of putBack) {
|
||||
const { brief_lowered_from: _lowered, ...copy } = f;
|
||||
returned.push(copy);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue