fix: brief template teaches runnable criteria; commands call validators through the plugin root
The skills reading (2026-10-06) found two defects the operator decided to fix once wave 1 was merged: - the brief template's Success Criteria examples used a pipe and npx, which the criteria runner refuses (and the brief validator now warns on); - trekcontinue and trekexecute called lib/ scripts relative to the working directory, which is the user's repo, not the plugin. Tests fail on the old text first (tests/lib/reading-defects.test.mjs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5f9faeed4a
commit
8094b00861
4 changed files with 55 additions and 8 deletions
|
|
@ -106,7 +106,7 @@ token in the Bash command — never a template placeholder — same
|
|||
anti-substitution rule as Phase 2:
|
||||
|
||||
```
|
||||
node --input-type=module -e "import {cleanupProject} from './lib/util/cleanup.mjs'; const [, dir, mode] = process.argv; const r = cleanupProject(dir, {dryRun: mode !== 'confirm', confirm: mode === 'confirm'}); console.log(JSON.stringify(r, null, 2)); process.exit(r.valid ? 0 : 1)" '<RESOLVED-PROJECT-DIR>' '<MODE>'
|
||||
node --input-type=module -e "import {cleanupProject} from '${CLAUDE_PLUGIN_ROOT}/lib/util/cleanup.mjs'; const [, dir, mode] = process.argv; const r = cleanupProject(dir, {dryRun: mode !== 'confirm', confirm: mode === 'confirm'}); console.log(JSON.stringify(r, null, 2)); process.exit(r.valid ? 0 : 1)" '<RESOLVED-PROJECT-DIR>' '<MODE>'
|
||||
```
|
||||
|
||||
Substitute `<RESOLVED-PROJECT-DIR>` with the literal `positional[0]`
|
||||
|
|
@ -185,7 +185,7 @@ After resolving the project directory and state-file path, look for two
|
|||
literal tokens (no template substitution at the Bash boundary):
|
||||
|
||||
```
|
||||
node lib/validators/next-session-prompt-validator.mjs --json --consistency <RESOLVED-PATH-A> <RESOLVED-PATH-B>
|
||||
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/next-session-prompt-validator.mjs --json --consistency <RESOLVED-PATH-A> <RESOLVED-PATH-B>
|
||||
```
|
||||
|
||||
Replace `<RESOLVED-PATH-A>` and `<RESOLVED-PATH-B>` with the two concrete
|
||||
|
|
@ -233,7 +233,7 @@ exact same string you just passed to the Read tool. The validator accepts
|
|||
`--json <path>` and prints a `{valid, errors, warnings}` JSON record:
|
||||
|
||||
```
|
||||
node lib/validators/session-state-validator.mjs --json <RESOLVED-ABSOLUTE-PATH-FROM-PHASE-1>
|
||||
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/session-state-validator.mjs --json <RESOLVED-ABSOLUTE-PATH-FROM-PHASE-1>
|
||||
```
|
||||
|
||||
Replace `<RESOLVED-ABSOLUTE-PATH-FROM-PHASE-1>` with the actual path string at
|
||||
|
|
@ -250,7 +250,7 @@ from Phase 1; emit the value, not a placeholder for it.
|
|||
- **Validator exit code != 0 OR `valid: false` in JSON output:** print the
|
||||
structured `errors[]` (each `[code] message` on its own line) and exit. Do
|
||||
not proceed to narration. Suggest running the validator directly for
|
||||
follow-up: `node lib/validators/session-state-validator.mjs <path>`.
|
||||
follow-up: `node ${CLAUDE_PLUGIN_ROOT}/lib/validators/session-state-validator.mjs <path>`.
|
||||
- **`valid: true` AND any warning has `code: SESSION_STATE_NOT_RESUMABLE`**
|
||||
(i.e. `status: completed`): print "no further sessions to resume; project
|
||||
complete" and exit cleanly.
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ clean the working tree before resuming. Write atomically alongside `progress.jso
|
|||
}
|
||||
```
|
||||
|
||||
Verify with `node lib/validators/session-state-validator.mjs --json
|
||||
Verify with `node ${CLAUDE_PLUGIN_ROOT}/lib/validators/session-state-validator.mjs --json
|
||||
{project_dir}/.session-state.local.json`. On validator failure, emit a warning
|
||||
to stderr but do NOT block the stop; `progress.json` is still authoritative.
|
||||
|
||||
|
|
@ -2162,7 +2162,7 @@ reaches Phase 8 must produce or refresh the state file. Schema:
|
|||
```
|
||||
|
||||
Use `lib/util/atomic-write.mjs` (`atomicWriteJson`) — same crash-safety as
|
||||
`progress.json`. Then verify: `node lib/validators/session-state-validator.mjs
|
||||
`progress.json`. Then verify: `node ${CLAUDE_PLUGIN_ROOT}/lib/validators/session-state-validator.mjs
|
||||
--json {project_dir}/.session-state.local.json`. On validator failure, warn
|
||||
to stderr but do NOT block — Phase 8 must always reach the final report.
|
||||
|
||||
|
|
|
|||
|
|
@ -109,8 +109,13 @@ Quantified where possible.*
|
|||
verifiable by running a specific command or observing a specific system behavior.*
|
||||
|
||||
- {criterion — e.g., "All existing tests pass: `npm test` exits 0"}
|
||||
- {criterion — e.g., "New endpoint returns 200: `curl -s localhost:3000/api/health | jq .status` → `"ok"`"}
|
||||
- {criterion — e.g., "No TypeScript errors: `npx tsc --noEmit` exits 0"}
|
||||
- {criterion — e.g., "New endpoint answers ok: `bash tests/health.sh` exits 0 (the script calls the endpoint and checks the status field)"}
|
||||
- {criterion — e.g., "No TypeScript errors: `npm run typecheck` exits 0"}
|
||||
|
||||
One command per criterion, from the runner's allowlist (`npm test`, `npm run <script>`,
|
||||
`node --test`, `pytest`, `bash tests/<script>.sh`, …), with no pipe or `&&`.
|
||||
Anything else is wrapped in a script under `tests/`; otherwise the review
|
||||
reports the criterion as NOT RUN.
|
||||
|
||||
Do NOT write vague criteria:
|
||||
- "It should work" (not testable)
|
||||
|
|
|
|||
42
tests/lib/reading-defects.test.mjs
Normal file
42
tests/lib/reading-defects.test.mjs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// tests/lib/reading-defects.test.mjs
|
||||
// Defects the skills reading found (docs/skillsanalyse.local.md § 6, 2026-10-06);
|
||||
// the operator decided they are fixed in a small delivery once wave 1 was
|
||||
// merged. Each test fails on the code the reading measured.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import { strict as assert } from 'node:assert';
|
||||
import { readFileSync, readdirSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { runnableCheck } from '../../lib/verification/criteria-runner.mjs';
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = join(HERE, '..', '..');
|
||||
const read = (p) => readFileSync(join(ROOT, p), 'utf8');
|
||||
|
||||
// --- 1. the brief template teaches only criteria the runner can run ---------
|
||||
|
||||
test('brief template: every example command under Success Criteria is one the criteria runner accepts', () => {
|
||||
const md = read('templates/trekbrief-template.md');
|
||||
const start = md.indexOf('## Success Criteria');
|
||||
const section = md.slice(start, md.indexOf('\n## ', start + 1));
|
||||
const examples = section.split('\n').filter((l) => l.startsWith('- {criterion')).join('\n');
|
||||
const commands = [...examples.matchAll(/`([^`\n]+)`/g)].map((m) => m[1]).filter((c) => !/^"/.test(c));
|
||||
assert.ok(commands.length >= 3, `found ${commands.length} example commands`);
|
||||
for (const command of commands) {
|
||||
const r = runnableCheck({ command });
|
||||
assert.equal(r.runnable, true, `${command}: ${r.reason}`);
|
||||
}
|
||||
});
|
||||
|
||||
// --- 2. commands reach Voyage's own scripts through the plugin root ---------
|
||||
|
||||
test('commands: no Voyage script is called relative to the working directory (it is the user\'s repo)', () => {
|
||||
const offenders = [];
|
||||
for (const f of readdirSync(join(ROOT, 'commands')).filter((n) => n.endsWith('.md'))) {
|
||||
read(`commands/${f}`).split('\n').forEach((line, i) => {
|
||||
if (/(?:node |from ['"]|import\(['"])(?:\.\/)?lib\//.test(line)) offenders.push(`commands/${f}:${i + 1}`);
|
||||
});
|
||||
}
|
||||
assert.deepEqual(offenders, []);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue