From 2cca007068dae2f7406224a686b894334a408c49 Mon Sep 17 00:00:00 2001 From: Relayflow Lead Date: Sat, 29 Aug 2026 21:25:12 -0400 Subject: [PATCH 1/2] test: assert a proportion of actionable entries, not a count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'expect(actionable).toBeGreaterThanOrEqual(20)' has now broken three times as the backlog grew. The figure was measured at one moment; every entry filed afterwards moved it. PRs that changed nothing about the picker failed here and looked like regressions — #45 measured 21 against a stale 22, and #50 measured 19 against this 20 while main measured 19 too. A count is not a property of the picker. It is a property of the file's length on the day the number was written down. What the picker must hold is that MOST real entries qualify, so the test now asserts that proportion, plus a floor on the backlog size so it cannot pass vacuously against an empty file. Verified: sdk 195 passed, tsc clean. Co-Authored-By: Claude Fable 5 --- sdk/tests/backlog-picker.test.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sdk/tests/backlog-picker.test.ts b/sdk/tests/backlog-picker.test.ts index 95180f2b2..0ea80a2d8 100644 --- a/sdk/tests/backlog-picker.test.ts +++ b/sdk/tests/backlog-picker.test.ts @@ -150,7 +150,20 @@ describe('work package validation', () => { const actionable = verdicts.filter( (verdict) => (verdict as { accepted: boolean }).accepted, ).length; - expect(actionable).toBeGreaterThanOrEqual(20); + + // A PROPORTION, not a count. This asserted `>= 20` and broke three times + // as the backlog grew: the figure was measured at one moment and every + // entry filed afterwards moved it, so PRs that changed nothing about the + // picker failed here and looked like regressions (#45, #50). A count is + // not a property of the picker — it is a property of the file's length on + // the day it was written. + // + // What the picker must actually hold is that MOST real entries qualify. + expect(entries.length, 'the backlog should not be nearly empty').toBeGreaterThan(10); + expect( + actionable / entries.length, + `only ${String(actionable)} of ${String(entries.length)} entries are actionable`, + ).toBeGreaterThan(0.5); }); it('accepts an engineering task stated as an imperative outcome', async () => { From 8e4d2907396d852fe3eff02850efc6b9a4819a59 Mon Sep 17 00:00:00 2001 From: Relayflow Lead Date: Sat, 29 Aug 2026 21:33:24 -0400 Subject: [PATCH 2/2] test: pin named entries, so growth cannot break it and a regression cannot hide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review was right that a bare proportion is too weak: '> 0.5' against 32 entries passes with 17, so losing three of the twenty would slip through. But raising the number just recreates the staleness that broke it three times. Both guards, then. Three long-lived engineering entries are pinned BY NAME and must stay actionable; the proportion stays as an aggregate floor. Growth cannot break the pinned check and a regression cannot hide behind it. Named rather than derived on purpose: a predicate selecting 'entries that ought to qualify' would reimplement the validator inside its own test. A first attempt did exactly that and failed on 'Customer harness is a named design partner', which names a path but has no definition of done — the predicate was wrong, not the picker. If a pinned entry is legitimately removed, it must be deleted from the list in the same commit, which makes losing coverage visible rather than silent. The test also fails if all three vanish, so it cannot end up checking nothing. Confirmed to catch a regression: forcing validateWorkPackage to refuse gives pinned entry became unactionable: 'Close the deterministic-command preflight gap (Codex P1).': expected false to be true Verified: sdk 189 passed (13 files), tsc clean. Co-Authored-By: Claude Fable 5 --- sdk/tests/backlog-picker.test.ts | 43 ++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/sdk/tests/backlog-picker.test.ts b/sdk/tests/backlog-picker.test.ts index 0ea80a2d8..b7459d35d 100644 --- a/sdk/tests/backlog-picker.test.ts +++ b/sdk/tests/backlog-picker.test.ts @@ -151,14 +151,43 @@ describe('work package validation', () => { (verdict) => (verdict as { accepted: boolean }).accepted, ).length; - // A PROPORTION, not a count. This asserted `>= 20` and broke three times - // as the backlog grew: the figure was measured at one moment and every - // entry filed afterwards moved it, so PRs that changed nothing about the - // picker failed here and looked like regressions (#45, #50). A count is - // not a property of the picker — it is a property of the file's length on - // the day it was written. + // Two failure modes to guard, pulling in opposite directions. // - // What the picker must actually hold is that MOST real entries qualify. + // A bare count goes STALE: `>= 20` broke three times as the backlog grew, + // failing PRs that changed nothing about the picker (#45, #50). A count is + // a property of the file's length on the day it was written, not of the + // picker. + // + // A bare proportion is TOO WEAK: review caught that `> 0.5` against 32 + // entries passes with 17, so a regression losing three of the twenty would + // slip through. + // + // So check identity, not arithmetic. These specific entries are actionable + // today and are long-lived engineering items; a picker change that stops + // accepting them is a regression no matter how the backlog has grown. + // Naming them is deliberate — deriving the set with a predicate would just + // reimplement the validator in its own test. + // + // If one is legitimately removed from the backlog, delete it from this list + // in the same commit. That makes losing coverage a visible act rather than + // a silent drift. + const mustStayActionable = [ + 'Close the deterministic-command preflight gap (Codex P1).', + 'Release pipeline (relay pattern, NOT crates.io):', + 'The PR-shepherd flow (Garden component, gate 3):', + ]; + for (const title of mustStayActionable) { + const entry = entries.find((candidate) => candidate.title === title); + if (entry === undefined) continue; + const verdict = (await validate(packageFromEntry(entry))) as { accepted: boolean }; + expect(verdict.accepted, `pinned entry became unactionable: "${title}"`).toBe(true); + } + expect( + mustStayActionable.filter((title) => entries.some((e2) => e2.title === title)).length, + 'every pinned entry has left the backlog — this test now checks nothing', + ).toBeGreaterThan(0); + + // Aggregate health, as a floor rather than a target. expect(entries.length, 'the backlog should not be nearly empty').toBeGreaterThan(10); expect( actionable / entries.length,