fix: close what an independent review found in the trial 2 fixes
- The pre-compact hook read only a checkpoint that started with `git commit`,
so every plan in the new `git add … && git commit` form rebuilt nothing, and
an empty match also skipped the session-state refresh. It now finds the
commit anywhere in the span and always refreshes; first test for the hook.
- A sub-bullet that carries its own command stays a criterion (folding it
left the command unrun); the shallowest bullet sets the base indent.
- Only a MINOR/SUGGESTION /code-review finding outside the range is released
from forbidding ALLOW: a MAJOR one may be a break cited at an unchanged
caller.
- `reuse` is an improvement category; the session-spec template stages too;
a negated bash 3.2 constraint, an env prefix and `bash -c` are handled.
- Stale comment, Declined-to-Judge wording and rule-key counts brought in line.
Tests fail on 7c11c2a first (9 of 24).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7c11c2aa9f
commit
b0d96a2d53
13 changed files with 194 additions and 54 deletions
72
tests/hooks/pre-compact-flush.test.mjs
Normal file
72
tests/hooks/pre-compact-flush.test.mjs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// tests/hooks/pre-compact-flush.test.mjs
|
||||
// The PreCompact hook rebuilds progress.json from git and refreshes the
|
||||
// session state before compaction. An independent review (2026-10-08) found
|
||||
// that the checkpoint form `git add … && git commit -m "…"` (plan template,
|
||||
// lekerepo trial 2 point A7) matched nothing, and that an empty match skipped
|
||||
// the session-state refresh too. No test covered the hook until now.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import { strict as assert } from 'node:assert';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const HOOK = join(HERE, '..', '..', 'hooks', 'scripts', 'pre-compact-flush.mjs');
|
||||
|
||||
function git(cwd, ...args) {
|
||||
const r = spawnSync('git', args, { cwd, encoding: 'utf8' });
|
||||
assert.equal(r.status, 0, `git ${args.join(' ')}: ${r.stderr}`);
|
||||
return r.stdout.trim();
|
||||
}
|
||||
|
||||
function project(checkpoint, { stateStatus = 'in_progress' } = {}) {
|
||||
const repo = mkdtempSync(join(tmpdir(), 'voyage-precompact-'));
|
||||
git(repo, 'init', '-q');
|
||||
git(repo, 'config', 'user.email', 't@example.invalid');
|
||||
git(repo, 'config', 'user.name', 'test');
|
||||
writeFileSync(join(repo, 'a.txt'), 'a\n');
|
||||
git(repo, 'add', 'a.txt');
|
||||
git(repo, 'commit', '-q', '-m', 'base');
|
||||
const base = git(repo, 'rev-parse', 'HEAD');
|
||||
writeFileSync(join(repo, 'a.txt'), 'b\n');
|
||||
git(repo, 'add', 'a.txt');
|
||||
git(repo, 'commit', '-q', '-m', 'feat(a): change a');
|
||||
const dir = join(repo, '.claude', 'projects', 'p');
|
||||
mkdirSync(dir, { recursive: true });
|
||||
writeFileSync(join(dir, 'plan.md'), ['# Plan', '', '### Step 1: Change a', '', `- **Checkpoint:** ${checkpoint}`, ''].join('\n'));
|
||||
writeFileSync(join(dir, 'progress.json'), JSON.stringify({ status: 'in_progress', current_step: 0, steps: {}, session_start_sha: base }));
|
||||
writeFileSync(join(dir, '.session-state.local.json'), JSON.stringify({ status: stateStatus, updated_at: '2000-01-01T00:00:00.000Z' }));
|
||||
return { repo, dir };
|
||||
}
|
||||
|
||||
function run(repo) {
|
||||
return spawnSync(process.execPath, [HOOK], { input: '{}', encoding: 'utf8', env: { ...process.env, CLAUDE_PROJECT_DIR: repo } });
|
||||
}
|
||||
|
||||
const progress = (dir) => JSON.parse(readFileSync(join(dir, 'progress.json'), 'utf8'));
|
||||
const state = (dir) => JSON.parse(readFileSync(join(dir, '.session-state.local.json'), 'utf8'));
|
||||
|
||||
for (const [name, cp] of [
|
||||
['the old form', '`git commit -m "feat(a): change a"`'],
|
||||
['the template form with git add', '`git add a.txt && git commit -m "feat(a): change a"`'],
|
||||
]) {
|
||||
test(`pre-compact: ${name} -> progress.json is rebuilt from git`, () => {
|
||||
const { repo, dir } = project(cp);
|
||||
try {
|
||||
assert.equal(run(repo).status, 0);
|
||||
assert.equal(progress(dir).current_step, 1);
|
||||
} finally { rmSync(repo, { recursive: true, force: true }); }
|
||||
});
|
||||
}
|
||||
|
||||
test('pre-compact: a plan with no recognisable checkpoint still refreshes the session state', () => {
|
||||
const { repo, dir } = project('`make commit`');
|
||||
try {
|
||||
assert.equal(run(repo).status, 0);
|
||||
assert.equal(progress(dir).current_step, 0);
|
||||
assert.notEqual(state(dir).updated_at, '2000-01-01T00:00:00.000Z');
|
||||
} finally { rmSync(repo, { recursive: true, force: true }); }
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue