gstack/test/founder-resources-optout.te...

78 lines
3.0 KiB
TypeScript

/**
* #538: the office-hours founder-resources pitch takes no for an answer.
*
* The reporter found that memory instructions telling the agent to stop
* showing the 34-resource pool kept being overridden on every update. The
* fix is a config key — `founder_resources` — that session context cannot
* override: `false` skips the entire section silently, forever, until the
* user re-enables it.
*
* Pins: (1) the config key's default, persistence, and validation through
* the real bin/gstack-config subprocess; (2) the generated section gates on
* the key BEFORE any resource content; (3) the opt-out write is verified
* before the skill may promise "never again" (R6 — a failed write must not
* produce a false promise).
*/
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
import { execFileSync } from "child_process";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
const ROOT = path.resolve(import.meta.dir, "..");
const CONFIG_BIN = path.join(ROOT, "bin", "gstack-config");
const SECTION = path.join(ROOT, "office-hours", "sections", "design-and-handoff.md");
let tmpHome: string;
beforeEach(() => { tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "gstack-538-")); });
afterEach(() => { fs.rmSync(tmpHome, { recursive: true, force: true }); });
function cfg(args: string[]): string {
return execFileSync(CONFIG_BIN, args, {
encoding: "utf-8",
env: { ...process.env, GSTACK_HOME: tmpHome },
}).trim();
}
describe("founder_resources config key (#538)", () => {
test("defaults to true (pitch stays on for everyone who never opted out)", () => {
expect(cfg(["get", "founder_resources"])).toBe("true");
});
test("set false persists and reads back — the write-verify contract", () => {
cfg(["set", "founder_resources", "false"]);
expect(cfg(["get", "founder_resources"])).toBe("false");
});
test("invalid values are rejected to the default, never persisted as-is", () => {
execFileSync(CONFIG_BIN, ["set", "founder_resources", "banana"], {
encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"],
env: { ...process.env, GSTACK_HOME: tmpHome },
});
expect(cfg(["get", "founder_resources"])).toBe("true");
});
});
describe("office-hours section gates on the key (#538)", () => {
const src = fs.readFileSync(SECTION, "utf-8");
test("the opt-out check precedes any resource content", () => {
const gate = src.indexOf("gstack-config get founder_resources");
const pool = src.indexOf("Resource Pool");
expect(gate).toBeGreaterThan(-1);
expect(pool).toBeGreaterThan(-1);
expect(gate).toBeLessThan(pool);
});
test("skip is silent and permanent — never means never", () => {
expect(src).toContain("skip this entire section silently");
expect(src).toContain("gstack-config set founder_resources true");
});
test("the opt-out write is verified before any promise (R6)", () => {
expect(src).toContain("VERIFY the write");
expect(src).toMatch(/read back\s*\n?`false`/);
});
});