diff --git a/plugins/ultraplan-local/tests/commands/ultracontinue.test.mjs b/plugins/ultraplan-local/tests/commands/ultracontinue.test.mjs index c7c2c61..491d413 100644 --- a/plugins/ultraplan-local/tests/commands/ultracontinue.test.mjs +++ b/plugins/ultraplan-local/tests/commands/ultracontinue.test.mjs @@ -15,10 +15,12 @@ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync } from 'nod import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { runHook } from '../helpers/hook-helper.mjs'; const HERE = dirname(fileURLToPath(import.meta.url)); const ROOT = join(HERE, '..', '..'); const COMMAND_FILE = join(ROOT, 'commands', 'ultracontinue-local.md'); +const PRE_BASH = join(ROOT, 'hooks', 'scripts', 'pre-bash-executor.mjs'); function readCommand() { return readFileSync(COMMAND_FILE, 'utf8'); @@ -154,3 +156,39 @@ test('ultracontinue Bug 1 sub — Phase 1 emits SC-2 diagnostic for .md position 'Phase 1 must detect .md positional arg (case for SC-2)', ); }); + +// --------------------------------------------------------------- +// Step 4 — Bug 2 regression tests (SC-3) +// --------------------------------------------------------------- + +test('ultracontinue Bug 2 — pre-bash-executor ALLOWS resolved validator invocation', async () => { + // (d-1) Sanity-check that the planned Phase 2 Bash form (validator + // invocation with a concrete absolute path) is not blocked by the + // marketplace pre-bash-executor hook chain. + const cmd = "node lib/validators/session-state-validator.mjs --json /tmp/fixture-not-real/.session-state.local.json"; + const { code } = await runHook(PRE_BASH, { tool_name: 'Bash', tool_input: { command: cmd } }); + assert.strictEqual(code, 0, 'pre-bash-executor must not block resolved validator invocations'); +}); + +test('ultracontinue Bug 2 — Phase 2 contains no {state-file-path} or any {curly-template} placeholder', () => { + // (d-2) Pattern D structure test. The fix must eliminate the + // {state-file-path} placeholder and any other {anything} curly-brace + // template syntax from Phase 2 — substitution failures are the + // root cause of the path-guard hook crash. + const phase2 = extractPhase(readCommand(), '## Phase 2 '); + assert.equal( + phase2.includes('{state-file-path}'), + false, + 'Phase 2 must not contain the {state-file-path} placeholder', + ); + assert.doesNotMatch( + phase2, + /\{[a-z][a-z0-9-]*\}/, + 'Phase 2 must not contain any {lowercase-template} curly-brace placeholder', + ); + assert.match( + phase2, + /Read tool/, + 'Phase 2 must document the deterministic Read tool flow', + ); +});