mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 06:28:59 +02:00
fix(setup): --no-team sweep keeps the opt-in gstack-memorable hook
`./setup --no-team` finishes its teardown with `prune-stale --all`, which removes every KNOWN_HOOKS item. The Memorable bridge hook is a user-registered opt-in unrelated to team mode, exactly like verify-gate, so it joins the sweep exclusion list. The verify-gate pin now accepts the comma-extended list; a schema-aware case proves the exclusion keeps both opt-ins (tagged or tag-stripped) while the uninstall sweep still removes them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
05b97dbe6d
commit
6aa9cd9e35
@@ -3010,10 +3010,10 @@ if [ "$NO_TEAM_MODE" -eq 1 ] && [ -x "$SETTINGS_HOOK" ]; then
|
||||
"$SETTINGS_HOOK" remove-source --source plan-tune-cathedral >/dev/null || true
|
||||
"$SETTINGS_HOOK" remove-source --source auq-error-fallback >/dev/null || true
|
||||
"$SETTINGS_HOOK" remove-source --source gstack-timeline-stop >/dev/null || true
|
||||
# verify-gate is a user-registered opt-in unrelated to team mode -- turning
|
||||
# team mode off must not delete it (uninstall still sweeps it, correctly,
|
||||
# because there the binary itself is being removed).
|
||||
GSTACK_SWEEP_EXCLUDE_SOURCES="verify-gate" "$SETTINGS_HOOK" prune-stale --all >/dev/null || true
|
||||
# verify-gate and gstack-memorable are user-registered opt-ins unrelated to
|
||||
# team mode -- turning team mode off must not delete them (uninstall still
|
||||
# sweeps both, correctly, because there the binaries themselves are removed).
|
||||
GSTACK_SWEEP_EXCLUDE_SOURCES="verify-gate,gstack-memorable" "$SETTINGS_HOOK" prune-stale --all >/dev/null || true
|
||||
fi
|
||||
|
||||
# ─── Redact pre-push guard consent (#1946) ───────────────────────────────────
|
||||
|
||||
@@ -927,6 +927,32 @@ describe('remove-source: identity-aware (tag OR table)', () => {
|
||||
expect(backups()).toEqual([]);
|
||||
});
|
||||
|
||||
test('setup --no-team sweep: GSTACK_SWEEP_EXCLUDE_SOURCES keeps verify-gate AND gstack-memorable (tagged or tag-stripped), sweeps timeline', () => {
|
||||
fs.writeFileSync(settingsFile, JSON.stringify({
|
||||
hooks: {
|
||||
Stop: [
|
||||
{ _gstack_source: 'verify-gate', hooks: [{ type: 'command', command: '/x/bin/gstack-verify-gate' }] },
|
||||
{ _gstack_source: 'gstack-timeline-stop', hooks: [{ type: 'command', command: '/x/hosts/claude/hooks/timeline-stop-hook' }] },
|
||||
],
|
||||
UserPromptSubmit: [
|
||||
{ _gstack_source: 'gstack-memorable', hooks: [{ type: 'command', command: memo }] },
|
||||
{ hooks: [{ type: 'command', command: `bash "${memo}"` }] }, // tag stripped by Claude Code
|
||||
],
|
||||
},
|
||||
}, null, 2));
|
||||
const r = runIso(['prune-stale', '--all'], { GSTACK_SWEEP_EXCLUDE_SOURCES: 'verify-gate,gstack-memorable' });
|
||||
expect(r.exitCode).toBe(0);
|
||||
expect(r.stdout).toMatch(/removed 1 /);
|
||||
const s = settings();
|
||||
expect(s.hooks.Stop).toHaveLength(1);
|
||||
expect(s.hooks.Stop[0]._gstack_source).toBe('verify-gate');
|
||||
expect(s.hooks.UserPromptSubmit).toHaveLength(2);
|
||||
// and WITHOUT the exclusion (uninstall) the memorable items go too
|
||||
const r2 = runIso(['prune-stale', '--all']);
|
||||
expect(r2.stdout).toMatch(/removed 3 /);
|
||||
expect(settings().hooks).toBeUndefined();
|
||||
});
|
||||
|
||||
test('a tagged legacy stray (single item, no table row) is still removed', () => {
|
||||
fs.writeFileSync(settingsFile, JSON.stringify({
|
||||
hooks: { UserPromptSubmit: [{ _gstack_source: 'gstack-memorable', hooks: [{ type: 'command', command: '/legacy/anything' }] }] },
|
||||
|
||||
@@ -356,7 +356,9 @@ describe('opt-in contract (adapted from the fork: NOT registered by default)', (
|
||||
expect(mentions.length).toBeGreaterThan(0); // the exclusion itself is pinned
|
||||
for (const line of mentions) {
|
||||
const t = line.trim();
|
||||
const allowed = t.startsWith('#') || t.includes('GSTACK_SWEEP_EXCLUDE_SOURCES="verify-gate"');
|
||||
// The exclusion list may name other user-registered opt-ins beside
|
||||
// verify-gate (gstack-memorable); it must still start with verify-gate.
|
||||
const allowed = t.startsWith('#') || /GSTACK_SWEEP_EXCLUDE_SOURCES="verify-gate(,[a-z-]+)*"/.test(t);
|
||||
expect(allowed).toBe(true);
|
||||
expect(t).not.toContain('add-event');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user