From 0470757d14d1f75886abfc68263f4e2c8c8b8cd3 Mon Sep 17 00:00:00 2001 From: jaso0n0818 Date: Sun, 21 Jun 2026 03:23:36 +0000 Subject: [PATCH] feat(signals): slop golden-fixture + determinism coverage and documented thresholds (#565) Adds golden-fixture coverage of buildSlopAssessment across all four bands (clean/low/elevated/high) with exact slopRisk, band, and finding codes, plus an explicit determinism check (identical metadata -> identical slopRisk + findings), and documents the band thresholds on slopBandFor. Per the issue's acceptance (documented thresholds; identical metadata -> identical slopRisk). Weights are intentionally left unchanged: the golden fixtures show the current weights already separate every band cleanly, so no retuning is warranted. Closes #565. --- src/signals/slop.ts | 3 +++ test/unit/slop.test.ts | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/signals/slop.ts b/src/signals/slop.ts index 404f090327..ea76ac9823 100644 --- a/src/signals/slop.ts +++ b/src/signals/slop.ts @@ -436,6 +436,9 @@ function ensurePublicSafeText(text: string, fallback: string): string { return isFocusManifestPublicSafe(text) ? text : fallback; } +// Documented thresholds (#565): the deterministic slopRisk (0-100) maps to fixed bands — clean = 0, +// low = 1-24, elevated = 25-59, high = 60-100. Strong signals weigh 30 (any two reach `high`); weak/ +// traceability signals weigh 15. Identical metadata always yields an identical band (see golden fixtures). function slopBandFor(slopRisk: number): SlopBand { if (slopRisk <= 0) return "clean"; if (slopRisk < 25) return "low"; diff --git a/test/unit/slop.test.ts b/test/unit/slop.test.ts index 600dacbebe..32e8f9c32a 100644 --- a/test/unit/slop.test.ts +++ b/test/unit/slop.test.ts @@ -10,6 +10,8 @@ import { buildSlopAssessment, buildTrivialWhitespaceChurnFinding, buildUnfilledIssueTemplateFinding, + type SlopAssessmentInput, + type SlopBand, ISSUE_SLOP_WEIGHTS, SLOP_RUBRIC_MARKDOWN, SLOP_WEIGHTS, @@ -405,3 +407,46 @@ describe("buildNonSubstantivePaddingFinding (#561 path-matcher signal)", () => { expect(JSON.stringify(result)).not.toMatch(FORBIDDEN); }); }); + +describe("slop golden fixtures & determinism (#565)", () => { + const goldenFixtures: Array<{ name: string; input: SlopAssessmentInput; slopRisk: number; band: SlopBand; codes: string[] }> = [ + { name: "clean — no metadata", input: {}, slopRisk: 0, band: "clean", codes: [] }, + { name: "low — generic commit subject", input: { commitMessages: ["wip"] }, slopRisk: 15, band: "low", codes: ["low_quality_commit_message"] }, + { name: "low — no linked issue and no rationale", input: { hasLinkedIssue: false }, slopRisk: 15, band: "low", codes: ["no_linked_issue_without_rationale"] }, + { + name: "elevated — code change without test evidence", + input: { changedFiles: [{ path: "src/svc.ts", additions: 12, deletions: 3 }], description: "Add retry logic to the sync client." }, + slopRisk: 30, + band: "elevated", + codes: ["missing_test_evidence"], + }, + { + name: "elevated — untested code change inside a duplicate cluster", + input: { changedFiles: [{ path: "src/svc.ts", additions: 12, deletions: 3 }], description: "Add retry logic to the sync client.", inDuplicateCluster: true }, + slopRisk: 45, + band: "elevated", + codes: ["duplicate_cluster_membership", "missing_test_evidence"], + }, + { + name: "high — whitespace churn, untested code, and empty description", + input: { changedFiles: [{ path: "src/x.ts", additions: 2, deletions: 1 }, { path: "src/state.snap", additions: 60, deletions: 40 }], description: "" }, + slopRisk: 75, + band: "high", + codes: ["empty_pr_description", "missing_test_evidence", "trivial_whitespace_churn"], + }, + ]; + + it.each(goldenFixtures)("scores the $name fixture to its documented band", (fixture) => { + const result = buildSlopAssessment(fixture.input); + expect(result.slopRisk).toBe(fixture.slopRisk); + expect(result.band).toBe(fixture.band); + expect(result.findings.map((finding) => finding.code).sort()).toEqual(fixture.codes); + expect(JSON.stringify(result)).not.toMatch(FORBIDDEN_PUBLIC_TERMS); + }); + + it("returns identical slopRisk and findings for identical metadata (determinism)", () => { + for (const fixture of goldenFixtures) { + expect(buildSlopAssessment(fixture.input)).toEqual(buildSlopAssessment(fixture.input)); + } + }); +});