Files
gstack/test/transcript-gate-values.test.ts
T
garrytan b730e63a9c W6 T1, T3, T4 (DX): two-question consent gates and transcript docs
Both gates recognize every stored value form (unrecognized values are asked
as not recognized by this version), ask the window (recent, all, new, off)
and then the repo scope, and store nothing until both answers are known.
setup-gbrain/memory.md#transcripts documents every value form, examples,
clearing the allowlist, multi-root merging, what the purge removes and the
downgrade steps.
2026-10-03 21:14:47 +00:00

75 lines
3.6 KiB
TypeScript

/**
* Both transcript consent gates (setup-gbrain's transcript-gate section and
* /sync-gbrain Step 1.6) run their rendered check block against every stored
* value form: each valid form is recognized so a stored choice is never asked
* again, and anything else is asked as "not recognized by this version".
*/
import { describe, expect, test } from 'bun:test';
import { spawnSync } from 'child_process';
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
const ROOT = join(import.meta.dir, '..');
const CONFIG_BIN = join(ROOT, 'bin', 'gstack-config');
const GATES = {
'setup-gbrain transcript gate': join(ROOT, 'setup-gbrain', 'sections', 'transcript-gate.md'),
'sync-gbrain Step 1.6': join(ROOT, 'sync-gbrain', 'SKILL.md'),
};
const TS = '2026-10-03T17:00:00Z';
const RECOGNIZED = ['recent', 'all', 'off', 'recent+repos', 'all+repos', `new@${TS}`, `new@${TS}+repos`, ' Recent ', `NEW@${TS.toLowerCase()}+REPOS`];
const UNRECOGNIZED = ['A', 'incremental', 'new', 'off+repos', `recent@${TS}`, 'new@2026-10-03', `new@${TS}x`, 'yes'];
function gateBlock(file: string): string {
const text = readFileSync(file, 'utf-8');
const block = text.split('```bash\n').map((b) => b.split('\n```')[0]).find((b) => b.includes('has transcript_ingest_mode'));
if (!block) throw new Error(`no transcript gate check block in ${file}`);
return block.replaceAll('~/.claude/skills/gstack/bin/gstack-config', CONFIG_BIN);
}
function runGate(block: string, yaml: string | null): string {
const root = mkdtempSync(join(tmpdir(), 'gstack-gate-'));
try {
if (yaml !== null) writeFileSync(join(root, 'config.yaml'), yaml);
const r = spawnSync('bash', ['-c', block], { encoding: 'utf-8', timeout: 30_000, env: { ...process.env, GSTACK_STATE_ROOT: root } });
expect(r.status).toBe(0);
return r.stdout.trim();
} finally {
rmSync(root, { recursive: true, force: true });
}
}
describe('transcript consent gates recognize every value form', () => {
for (const [name, file] of Object.entries(GATES)) {
const block = gateBlock(file);
test(`${name}: every stored grammar form is a choice, never re-asked`, () => {
for (const value of RECOGNIZED) {
const out = runGate(block, `transcript_ingest_mode: ${value}\ntranscript_repos: github.com/acme/app\n`);
expect({ value, out }).toEqual({ value, out: `TRANSCRIPT_MODE: ${value.trim()} (repos: github.com/acme/app)` });
}
});
test(`${name}: anything else is asked as not recognized by this version; absent is asked as not set`, () => {
for (const value of UNRECOGNIZED) {
const out = runGate(block, `transcript_ingest_mode: ${value}\n`);
expect({ value, out }).toEqual({ value, out: `TRANSCRIPT_MODE: ask (stored value '${value}' is not recognized by this version)` });
}
expect(runGate(block, null)).toBe('TRANSCRIPT_MODE: ask (not set)');
});
test(`${name}: two questions, nothing stored until both are answered, scope before mode`, () => {
const text = readFileSync(file, 'utf-8');
expect(text).toContain('`new`');
expect(text).toMatch(/second, separate question/i);
expect(text).toMatch(/only this repo/i);
expect(text).toMatch(/store nothing until\s+both answers are known/i);
const scopeAt = text.indexOf('gstack-config set transcript_repos');
const modeAt = text.indexOf('gstack-config set transcript_ingest_mode', scopeAt);
expect(scopeAt).toBeGreaterThan(0);
expect(modeAt).toBeGreaterThan(scopeAt);
expect(text).toContain('gstack-config unset transcript_repos');
});
}
});