From 2549a32bad3d5414dbae4d0f42de32880ff3381b Mon Sep 17 00:00:00 2001 From: garrytan Date: Tue, 27 Oct 2026 00:46:55 +0000 Subject: [PATCH] test(docsync): accept the cap gate's ~/.claude/skills/gstack form the section prints (pass 9 Red Team note) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- test/docsync-command-grammar.test.ts | 6 +++--- test/helpers/docsync-observer.ts | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/test/docsync-command-grammar.test.ts b/test/docsync-command-grammar.test.ts index ca1cb60d3..87dc5c829 100644 --- a/test/docsync-command-grammar.test.ts +++ b/test/docsync-command-grammar.test.ts @@ -515,13 +515,13 @@ test('Step 14.5 housekeeping commands and the docs record are declared exactly a const skills = fixture.skills.split(path.sep).join('/'); const caps = docsCapsCommand(skills); const interfaceText = docsNativeInterface(fixture); - for (const command of [DOCS_TMP_EXCLUDE, DOCS_AUDIT_RESET, caps]) { - expect(interfaceText).toContain(command); + for (const command of [DOCS_TMP_EXCLUDE, DOCS_AUDIT_RESET, caps, docsCapsCommand('~/.claude/skills/gstack')]) { + if (command === caps) expect(interfaceText).toContain(command); expect(docsCommandAllowed(command, fixture)).toBe(true); expect(docsToolFailures(nativeResult(command, 'DOCS_CAP: none declared'), fixture, [], true)).toEqual([]); } for (const command of [DOCS_TMP_EXCLUDE.replace("'/.gstack/tmp/'", "'/.gstack/'"), `${DOCS_TMP_EXCLUDE}; git add -A`, 'rm -f .gstack/tmp', 'rm -rf .gstack/tmp/docs-audit.json', - `${skills}/bin/gstack-ship-receipt docs-caps --base feature/docs`, `${skills}/bin/gstack-ship-receipt write --docs @${DOCS_AUDIT_RECORD}`, `${skills}/bin/gstack-ship-receipt docs-caps --base main --json`]) { + `${skills}/bin/gstack-ship-receipt docs-caps --base feature/docs`, '~/.claude/skills/other/bin/gstack-ship-receipt docs-caps --base main', `${skills}/bin/gstack-ship-receipt write --docs @${DOCS_AUDIT_RECORD}`, `${skills}/bin/gstack-ship-receipt docs-caps --base main --json`]) { expect(docsCommandAllowed(command, fixture)).toBe(false); } const write = (file: string): SkillTestResult => { diff --git a/test/helpers/docsync-observer.ts b/test/helpers/docsync-observer.ts index d87514927..8a7833107 100644 --- a/test/helpers/docsync-observer.ts +++ b/test/helpers/docsync-observer.ts @@ -230,7 +230,9 @@ function unwrapHarmlessRead(text: string, fixture: ReturnType, scripts: string[] = []): boolean { const text = command.trim(); if (docsPreambleCommands(fixture).some(block => block.trim() === text)) return true; - if (text === DOCS_TMP_EXCLUDE || text === DOCS_AUDIT_RESET || text === docsCapsCommand(fixture.skills.split(path.sep).join('/'))) return true; + // The section prints the cap gate with the ~/.claude/skills/gstack prefix; in the fixture HOME is fixture.home, so that form names the same installed binary. + if (text === DOCS_TMP_EXCLUDE || text === DOCS_AUDIT_RESET || text === docsCapsCommand(fixture.skills.split(path.sep).join('/')) || + (text === docsCapsCommand('~/.claude/skills/gstack') && fixture.skills === path.join(fixture.home, '.claude/skills/gstack'))) return true; if (readOnlyCommandAllowed(text, docsReadOnlyScope(fixture)) || docsComposedReadAllowed(text, fixture)) return true; const insert = /^cat (['"]?)([^\s'"]+)\1 >> (['"]?)([^\s'"]+)\3$/.exec(text); if (insert) return [insert[2], insert[4]].every(file => path.isAbsolute(file) && /\.md$/i.test(file) &&