From de2aa2939e5352fd4200e4c135c655b162b7b3be Mon Sep 17 00:00:00 2001 From: Jayesh Betala Date: Wed, 3 Jun 2026 11:34:06 +0530 Subject: [PATCH] test(retro): static invariants for the #1834 recommendation follow-through loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the #1624 static-invariant convention: asserts the persist → read-back loop in retro/SKILL.md.tmpl (and the regenerated SKILL.md) stays intact — Step 13 always writes recommendations, Step 12 reads them back and scores addressed/partial/open with a backward-compat skip, and the "3 Things to Improve" narrative records them. Fails the build if any leg regresses. Verified 8/8 fail without the template fix and pass with it. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...ression-1834-retro-recommendations.test.ts | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 test/regression-1834-retro-recommendations.test.ts diff --git a/test/regression-1834-retro-recommendations.test.ts b/test/regression-1834-retro-recommendations.test.ts new file mode 100644 index 000000000..5ebc3974a --- /dev/null +++ b/test/regression-1834-retro-recommendations.test.ts @@ -0,0 +1,108 @@ +/** + * Regression tests for #1834 — /retro generated "3 Things to Improve" as + * throwaway prose and never persisted them, so the next run had no structured + * record of what it recommended and could not detect follow-through. A week + * spent acting on retro feedback got mischaracterized as a generic metric swing. + * + * The fix lives in retro/SKILL.md.tmpl: + * - Step 13 snapshot schema gains a `recommendations` array (always written). + * - Step 13 prose mandates populating it from the "3 Things to Improve" items. + * - Step 12 reads a prior snapshot's `recommendations` back and classifies each + * as addressed / partial / open, with a backward-compat skip for older + * snapshots that predate the field. + * - The "3 Things to Improve" narrative is wired to the persisted array. + * + * These are static invariants against the template body (and the regenerated + * SKILL.md). They fail the build if any leg of the persist → read-back loop is + * dropped, so the follow-through capability can't silently regress. + */ +import { describe, expect, test } from "bun:test"; +import * as fs from "node:fs"; +import * as path from "node:path"; + +const ROOT = path.resolve(import.meta.dir, ".."); +const RETRO_TMPL = path.join(ROOT, "retro", "SKILL.md.tmpl"); +const RETRO_MD = path.join(ROOT, "retro", "SKILL.md"); + +function readTmpl(): string { + return fs.readFileSync(RETRO_TMPL, "utf-8"); +} + +function readMd(): string { + return fs.readFileSync(RETRO_MD, "utf-8"); +} + +describe("#1834 retro recommendations — Step 13 snapshot persists them", () => { + test("schema block carries a recommendations array with category + text shape", () => { + const body = readTmpl(); + const schemaStart = body.indexOf("Use the Write tool to save the JSON file with this schema:"); + expect(schemaStart).toBeGreaterThan(-1); + // The recommendations array must live inside the Step 13 JSON schema, not + // somewhere else in the doc. + const schema = body.slice(schemaStart, schemaStart + 2000); + expect(schema).toMatch(/"recommendations"\s*:\s*\[/); + expect(schema).toMatch(/"category"\s*:/); + expect(schema).toMatch(/"text"\s*:/); + }); + + test("recommendations are mandatory, not optional like greptile/backlog/test_health", () => { + const body = readTmpl(); + expect(body).toMatch(/\*\*Always include the `recommendations` array\.\*\*/); + }); + + test("Step 13 ties the array to the 3 Things to Improve items", () => { + const body = readTmpl(); + const anchor = body.indexOf("**Always include the `recommendations` array.**"); + expect(anchor).toBeGreaterThan(-1); + const para = body.slice(anchor, anchor + 700); + expect(para).toMatch(/3 Things to Improve/); + expect(para).toMatch(/follow-through/i); + }); +}); + +describe("#1834 retro recommendations — Step 12 reads them back and scores follow-through", () => { + test("Step 12 has a Recommendation follow-through block before Step 13", () => { + const body = readTmpl(); + const follow = body.indexOf("**Recommendation follow-through.**"); + const step13 = body.indexOf("### Step 13: Save Retro History"); + expect(follow).toBeGreaterThan(-1); + expect(step13).toBeGreaterThan(-1); + expect(follow).toBeLessThan(step13); + }); + + test("follow-through classifies each prior recommendation addressed/partial/open", () => { + const body = readTmpl(); + const follow = body.indexOf("**Recommendation follow-through.**"); + const block = body.slice(follow, follow + 1400); + expect(block).toMatch(/`addressed`/); + expect(block).toMatch(/`partial`/); + expect(block).toMatch(/`open`/); + }); + + test("follow-through is backward compatible with snapshots that predate the field", () => { + const body = readTmpl(); + const follow = body.indexOf("**Recommendation follow-through.**"); + const block = body.slice(follow, follow + 600); + // Older snapshots have no recommendations array — the block must skip, not crash. + expect(block).toMatch(/if it's absent, skip this block/i); + }); +}); + +describe("#1834 retro recommendations — 3 Things to Improve narrative persists the loop", () => { + test("narrative instructs recording the 3 items into the snapshot array", () => { + const body = readTmpl(); + const heading = body.indexOf("### 3 Things to Improve"); + expect(heading).toBeGreaterThan(-1); + const section = body.slice(heading, heading + 900); + expect(section).toMatch(/recommendations` array/); + }); +}); + +describe("#1834 retro recommendations — regenerated SKILL.md carries the loop", () => { + test("generated SKILL.md is not stale relative to the template", () => { + const md = readMd(); + expect(md).toMatch(/\*\*Always include the `recommendations` array\.\*\*/); + expect(md).toMatch(/\*\*Recommendation follow-through\.\*\*/); + expect(md).toMatch(/"recommendations"\s*:\s*\[/); + }); +});