* test(#4135): regression rows for pristine regen coverage collapse RED skeleton: src/pristine-baseline.cts exports findPristineInGit as a null-returning stub (wired into verifyFile after the #4145 orphan tier, behavior-neutral) so the git-history rows fail behaviorally, not at require time. Failing-first rows: baseline_covered aggregate on a 1-of-13 multi-version fixture, coverageHeadline typed renderer, the opt-in --min-baseline-coverage gate (exit 3, >= threshold semantics, vacuous-pass and malformed-value boundaries), git-history baseline recovery (dropped-line catch + surviving-line verify + older-commit hop), findPristineInGit unit, Step 5a workflow headline contract, and the installer-side describeBaselineCoverage honest N-of-M summary with the collapse disk-state pinned. Negative-space rows pin today: non-git ok_no_baseline posture, no-match-no-adoption, #3657 drift never rescued, canonical precedence, and no git tier without --pristine-dir. * fix(#4135): headline baseline coverage, opt-in strict gate, git-history widening The #3407 promotion rule regenerates gsd-pristine/ baselines from the INCOMING release source and keeps only candidates byte-identical with the OUTGOING recorded hash — correct in isolation, but on a multi-version jump the surviving set is precisely the files upstream did NOT change. The verifier then reports ok_no_baseline (advisory, exit 0) for everything else, and no surface distinguishes a 12-of-13-unverified green run from a fully-verified one: the human summary printed Checked/Failures only, the JSON had no coverage aggregate, and the installer's update output gave per-bucket counts without N-of-M framing. All three issue directions, none exclusive: - Report coverage prominently: --json gains an additive baseline_covered aggregate; the human summary leads with 'Baseline coverage: N of M file(s)...' on every run plus an advisory section naming each skipped file and reason; the installer prints an honest covered-of-modified line via the exported describeBaselineCoverage helper (typed return, exact contract); workflow Step 5a computes and prints the headline before any pass/fail framing. - Fail louder on low coverage: opt-in --min-baseline-coverage <0..1> exits with new documented code 3 when coverage falls below the threshold (>= semantics; empty run vacuously passes; content failure exit 1 outranks it; malformed values are usage errors, exit 2). Default posture unchanged — no_baseline stays advisory per #934. - Widen the promotion rule (its only trustworthy form): when no baseline resolves under gsd-pristine/ and a hash is recorded, the verifier now recovers the baseline from the config dir's own git history — the workflow's documented Option A — anchored by the same authority every tier trusts, exact pristine_hashes sha-256 equality. Read-only (git log/git show, windowsHide per #685), bounded (100 commits/file, 10s/subprocess), null-on-any-failure so ok_no_baseline remains the universal fallback. Tier order: canonical join -> #4145 orphan scan -> git history -> OK_NO_BASELINE; #3657 drift and canonical precedence untouched. Hash validation in saveLocalPatches is NOT relaxed — the collapse is legitimate conservatism; hiding it was the bug. Measured on the issue's shape (13 files, 12 changed upstream, 1.10->1.12): non-git installs report baseline_covered 1/13 with the headline and can gate at exit 3; a git-managed config dir with the outgoing bytes in history verifies 13/13. Review fixes folded in: workflow headline derives the unverified count from checked - baseline_covered (not the drift+no_baseline sum), and the new site-scoped allow-test-rule annotation carries its ADR-456 see-ref on the marker line. Emitted-Drift-Ack-Growth: reapply-patches.md — #4135 — +20 lines / ~1.5 KB, prose and bash only: two additive parse lines (BASELINE_COVERED, CHECKED_COUNT), a Step 5a coverage-headline block printed BEFORE any pass/fail statement (documents the opt-in --min-baseline-coverage exit-3 gate), and one Option B sentence noting the verifier's read-only git-history fallback. No step ordering, gate, tool-invocation, or dispatch shape changed; 5a's fail/drift/advisory handling is unchanged, the headline only precedes it. * chore(#4135): backfill PR number into changeset fragment --------- Co-authored-by: agent-4135 <agent-4135@gsd.local>
This commit is contained in:
@@ -10217,6 +10217,29 @@ function recoverOrphanedPristine(pristineDir, relPath, recordedHash, canonicalSk
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* #4135: honest N-of-M accounting for gsd-pristine/ baselines after an
|
||||
* update. The #3407 promotion rule keeps only hash-validated candidates
|
||||
* (byte-identical across the version span), so a multi-version update
|
||||
* legitimately ends with near-zero baselines — the collapse itself is NOT a
|
||||
* bug to hide; hiding it is. This renders the covered-of-total line the
|
||||
* update output prints either way, so "1 of 13" can never present like a
|
||||
* fully-covered run. Pure function (typed return) so tests lock the exact
|
||||
* contract without matching console prose.
|
||||
*/
|
||||
function describeBaselineCoverage(totalModified, covered) {
|
||||
const total = Math.max(0, totalModified);
|
||||
const have = Math.min(Math.max(0, covered), total);
|
||||
const uncovered = total - have;
|
||||
return {
|
||||
complete: uncovered === 0,
|
||||
uncovered,
|
||||
text: uncovered === 0
|
||||
? `gsd-pristine/ baselines cover ${have} of ${total} modified file(s)`
|
||||
: `gsd-pristine/ baselines cover ${have} of ${total} modified file(s) — ${uncovered} will be reported no_baseline by the reapply verifier`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect user-modified GSD files by comparing against install manifest.
|
||||
* Backs up modified files to gsd-local-patches/ for reapply after update.
|
||||
@@ -10467,6 +10490,17 @@ function saveLocalPatches(configDir, pristineCtx) {
|
||||
if (removed > 0) {
|
||||
console.log(' ' + yellow + 'i' + reset + ' Removed ' + removed + ' stale gsd-pristine/ snapshot(s); regenerated ' + regenerated + ' of those — falls back to over-broad verify heuristic for the rest');
|
||||
}
|
||||
// #4135: the honest N-of-M coverage line. Preserved/rescued/regenerated
|
||||
// are disjoint buckets (see their accounting comments above), so their
|
||||
// sum is exactly the files that ended this update with a hash-valid
|
||||
// baseline. A partial result renders as an info line, not an error:
|
||||
// the collapse is legitimate (#3407), hiding it was the bug.
|
||||
const coverage = describeBaselineCoverage(modified.length, preserved + rescued + regenerated);
|
||||
if (coverage.complete) {
|
||||
console.log(' ' + green + '✓' + reset + ' ' + coverage.text);
|
||||
} else {
|
||||
console.log(' ' + yellow + 'i' + reset + ' ' + coverage.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
@@ -14209,6 +14243,7 @@ module.exports = {
|
||||
reportLocalPatches,
|
||||
validateHookFields,
|
||||
populatePristineDir,
|
||||
describeBaselineCoverage,
|
||||
_resolveUserArtifactStagingRoot,
|
||||
_tryResolveUserArtifactStagingRoot,
|
||||
finishInstall,
|
||||
|
||||
Reference in New Issue
Block a user