mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
fix: pre-landing review fixes (round 2)
- offersDefer: the wait/hold-off-for-now paraphrase never counts inside a
browser drive option, accepts an em dash without spaces and a curly
apostrophe in let's; negative and positive controls added (adversarial
review on #3472).
- docsync-authority: the interface assertion expects the rendered
`<home>/remote.git` form so it holds on Windows; the captured Linux
replay is POSIX-only like the late-result replay (outside voice P1).
- touchfiles: register the two new docsync captures with the paid cases
they replay (ship-docsync-store, ship-docsync-completion), which
test/free-fixtures.test.ts requires (remote gate red at 470ac55).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
470ac550c2
commit
8c106d642b
4 files changed
+22
-8
No files matched your search
@@ -42,17 +42,27 @@ test('docs write authority permits the authored doc and private JSON/Markdown ar
|
||||
// saved section and the compare target under {home}/state, gstack's own state directory
|
||||
// (GSTACK_HOME), which the interface never named. The authority is right to refuse the
|
||||
// state directory; the interface now names every refused root beside the permitted place.
|
||||
test('the declared interface names every refused private-artifact root, and the captured state-dir run stays refused', () => {
|
||||
test('the declared interface names every refused private-artifact root', () => {
|
||||
const fixture = fixtureDocs('updated');
|
||||
try {
|
||||
const interfaceText = docsNativeInterface(fixture, [path.join(fixture.home, 'fixture-publish.ts')], false, true);
|
||||
const refused = [fixture.skills.split(path.sep).join('/'), fixture.env.CLAUDE_CONFIG_DIR, fixture.env.GSTACK_HOME, path.join(fixture.home, 'remote.git')];
|
||||
// The interface renders the remote root as `${home}/remote.git` (the same literal form the agent reads), not path.join.
|
||||
const refused = [fixture.skills.split(path.sep).join('/'), fixture.env.CLAUDE_CONFIG_DIR, fixture.env.GSTACK_HOME, `${fixture.home}/remote.git`];
|
||||
const sentence = interfaceText.split(/(?<=\.)\s+/).find(part => part.startsWith('A private JSON/Markdown artifact'));
|
||||
expect(sentence).toBeDefined();
|
||||
for (const root of refused) expect(sentence).toContain(`${root}`);
|
||||
expect(sentence).toContain(`directly under ${fixture.home} or in a new directory you create there`);
|
||||
expect(sentence).toContain('never inside the repository');
|
||||
expect(fixture.env.GSTACK_HOME).toBe(path.join(fixture.home, 'state'));
|
||||
} finally { fixture.clean(); }
|
||||
});
|
||||
|
||||
// The captured transcript is a Linux run: its Bash commands carry POSIX paths, which the literal
|
||||
// command grammar rejects once a Windows home is substituted, so the replay is POSIX-only like the
|
||||
// late-result replay below.
|
||||
test.skipIf(process.platform === 'win32')('the captured state-dir run stays refused, and passes once relocated under the home', () => {
|
||||
const fixture = fixtureDocs('updated');
|
||||
try {
|
||||
const captured = JSON.parse(fs.readFileSync(path.join(import.meta.dir, 'fixtures/docsync-state-dir-artifacts-public.json'), 'utf8')) as
|
||||
{ calls: SkillTestResult['toolCalls'] };
|
||||
const fill = (text: string) => text.replaceAll('{home}', fixture.home);
|
||||
|
||||
@@ -50,11 +50,13 @@ export const offersDriveOption = (text: string): boolean => letteredOptions(text
|
||||
* The defer option: a lettered option that defers, postpones or sets the step aside for now. "Wait for now" and
|
||||
* "hold off for now" count only as the option's own opening or sentence (gate 40290033102: "D) Wait for now. I'll
|
||||
* mark this step as blocked on you."), never as a drive option's "I wait while you sign in": the phrase must open the
|
||||
* option or a sentence and end it, so "I wait for now while you sign in" and "wait for now and do it yourself" do not count.
|
||||
* option or a sentence and end it, and the option must not be a browser drive, so "I wait for now while you sign in",
|
||||
* "wait for now and do it yourself" and "I drive it in Aside. I wait for now." do not count.
|
||||
*/
|
||||
export const offersDefer = (text: string): boolean => letteredOptions(text).some(option =>
|
||||
/\b(?:defer\w*|postpone\w*|put (?:this|it)(?: step)? off|skip (?:this|it)(?: step)? for now|come back to (?:this|it)(?: step)? later|leave (?:this|it)(?: step)? for (?:now|later))\b/i.test(option)
|
||||
|| /(?:^|[.!?;:]\s+)(?:(?:we|I|let's) )?(?:wait|hold off)(?: on (?:this|it)(?: step)?)? for now\b(?=\s*(?:[.!?;:]|[-\u2013\u2014]\s|$))/i.test(option));
|
||||
|| !/drive|browse|Aside/i.test(option)
|
||||
&& /(?:^|[.!?;:]\s+)(?:(?:we|I|let['\u2019]s) )?(?:wait|hold off)(?: on (?:this|it)(?: step)?)? for now\b(?=\s*(?:[.!?;:]|[-\u2013\u2014]|$))/i.test(option));
|
||||
|
||||
/** The manual option: "manual", or one sentence offering instructions or steps the user follows themselves. */
|
||||
export function offersManualSteps(text: string): boolean {
|
||||
|
||||
@@ -814,10 +814,10 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'ship-triage': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'bin/gstack-repo-mode', 'test/skill-e2e-triage.test.ts', 'test/helpers/ship-triage-labels.ts',
|
||||
'scripts/resolvers/testing.ts'
|
||||
],
|
||||
'ship-docsync-completion': ['bin/gstack-docs-candidate', 'bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/read-only-commands.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts', 'bin/gstack-context-recovery'],
|
||||
'ship-docsync-completion': ['bin/gstack-docs-candidate', 'bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/read-only-commands.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts', 'bin/gstack-context-recovery', 'test/fixtures/docsync-state-dir-artifacts-public.json'],
|
||||
'ship-docsync-current': ['bin/gstack-docs-candidate', 'bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/read-only-commands.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts', 'bin/gstack-context-recovery', 'test/fixtures/docsync-candidate-not-found-public.json'],
|
||||
'ship-docsync-failure': ['bin/gstack-docs-candidate', 'bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/read-only-commands.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts', 'bin/gstack-context-recovery'],
|
||||
'ship-docsync-store': ['bin/gstack-docs-candidate', 'bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/read-only-commands.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts', 'bin/gstack-context-recovery'],
|
||||
'ship-docsync-store': ['bin/gstack-docs-candidate', 'bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/read-only-commands.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts', 'bin/gstack-context-recovery', 'test/fixtures/docsync-help-not-found-public.json'],
|
||||
// #2733 behavioral proof: the JSON contract survives a firing gate inside a
|
||||
// spawned-marked subagent. Deps name every behavior under test — the
|
||||
// session-kind override, the skill-start gates, both hooks + the shared
|
||||
|
||||
@@ -546,7 +546,8 @@ describe('Opus consent renders (gate captures)', () => {
|
||||
'- **D)** We skip it for now and come back to it later.', '> **D)** Defer: I mark the step blocked on you.',
|
||||
'D) Postpone this step.', 'D) Leave it for later.', "D) Wait for now. I'll mark this step as blocked on you.",
|
||||
'- **D)** Hold off for now; the step stays blocked on you.', 'D) Not yet: we wait on this step for now.',
|
||||
"D) Let's hold off on it for now."]) expect(offersDefer(option), option).toBe(true);
|
||||
"D) Let's hold off on it for now.", "D) Wait for now\u2014I'll mark this step as blocked on you.", 'D) Let\u2019s hold off on it for now.'])
|
||||
expect(offersDefer(option), option).toBe(true);
|
||||
});
|
||||
|
||||
test('defer counts only as a lettered option', () => {
|
||||
@@ -560,7 +561,8 @@ describe('Opus consent renders (gate captures)', () => {
|
||||
"B) I drive it in gstack's own browser and wait for your go-ahead for now.", 'A) You wait for now; I drive it in Aside.',
|
||||
'D) Do not wait for now; I drive it in Aside at once.', 'A) I drive it in Aside; I wait for now while you sign in.',
|
||||
'A) I drive it in your Aside browser. At the sign-in wall I stop. Wait for now while you sign in, then I continue.',
|
||||
'B) Manual instructions: wait for now and do it yourself.', "C) I do it in gstack's own browser. We wait for now only if sign-in is needed."])
|
||||
'B) Manual instructions: wait for now and do it yourself.', "C) I do it in gstack's own browser. We wait for now only if sign-in is needed.",
|
||||
'A) I drive it in Aside. I wait for now.', 'B) I drive it in your Aside browser; hold off for now.'])
|
||||
expect(offersDefer(text), text).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user