Skip to content

Commit 8641195

Browse files
committed
fix: resolve merge conflicts with main
2 parents 2e2b7cb + c9efaca commit 8641195

5 files changed

Lines changed: 125 additions & 24 deletions

File tree

.claude/hooks/guard-git.sh

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ if [ -z "$COMMAND" ]; then
2222
fi
2323

2424
# Act on git and gh commands (may appear after cd "..." &&)
25-
if ! echo "$COMMAND" | grep -qE '(^|\s|&&\s*)(git|gh)\s+'; then
25+
if ! echo "$COMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)(git|gh)[[:space:]]+'; then
2626
exit 0
2727
fi
2828

2929
# Normalize: strip `git -C "<path>"` / `git -C <path>` so downstream subcommand
30-
# patterns (git\s+push, git\s+commit, …) match regardless of whether `-C` is
30+
# patterns (git[[:space:]]+push, git[[:space:]]+commit, …) match regardless of whether `-C` is
3131
# present. detect_work_dir still inspects the raw $COMMAND to find the target.
3232
# The unquoted pattern requires a non-quote first char so it does not mis-match
3333
# the opening `"` of a quoted path (which would leave a trailing `path"` in
@@ -53,34 +53,34 @@ deny() {
5353
# --- Block dangerous commands ---
5454

5555
# git add . / git add -A / git add --all (broad staging)
56-
if echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)git\s+add\s+(\.\s*$|-A|--all)'; then
56+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+add[[:space:]]+(\.[[:space:]]*$|-A|--all)'; then
5757
deny "BLOCKED: 'git add .' / 'git add -A' stages ALL changes including other sessions' work. Stage specific files instead: git add <file1> <file2>"
5858
fi
5959

6060
# git reset (unstaging / hard reset)
61-
if echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)git\s+reset'; then
61+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+reset'; then
6262
deny "BLOCKED: 'git reset' can unstage or destroy other sessions' work. To unstage your own files, use: git restore --staged <file>"
6363
fi
6464

6565
# git checkout -- <file> (reverting files)
66-
if echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)git\s+checkout\s+--'; then
66+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+checkout[[:space:]]+--'; then
6767
deny "BLOCKED: 'git checkout -- <file>' reverts working tree changes and may destroy other sessions' edits. If you need to discard your own changes, be explicit about which files."
6868
fi
6969

7070
# git restore (reverting) — EXCEPT git restore --staged (safe unstaging)
71-
if echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)git\s+restore'; then
72-
if ! echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)git\s+restore\s+--staged'; then
71+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+restore'; then
72+
if ! echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+restore[[:space:]]+--staged'; then
7373
deny "BLOCKED: 'git restore <file>' reverts working tree changes and may destroy other sessions' edits. To unstage files safely, use: git restore --staged <file>"
7474
fi
7575
fi
7676

7777
# git clean (delete untracked files)
78-
if echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)git\s+clean'; then
78+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+clean'; then
7979
deny "BLOCKED: 'git clean' deletes untracked files that may belong to other sessions."
8080
fi
8181

8282
# git stash (hides all changes)
83-
if echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)git\s+stash'; then
83+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+stash'; then
8484
deny "BLOCKED: 'git stash' hides all working tree changes including other sessions' work. In worktree mode, commit your changes directly instead."
8585
fi
8686

@@ -158,21 +158,21 @@ validate_branch_name() {
158158

159159
# --- Branch name validation on push ---
160160

161-
if echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)git\s+push'; then
161+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+push'; then
162162
validate_branch_name push
163163
fi
164164

165165
# --- Branch name validation on gh pr create ---
166166

167-
if echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)gh\s+pr\s+create'; then
167+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)gh[[:space:]]+pr[[:space:]]+create'; then
168168
# `gh pr create` does not use `git -C`; detect_work_dir falls through to the
169169
# `cd` path or cwd. No subcommand hint to pass.
170170
validate_branch_name
171171
fi
172172

173173
# --- Commit validation against edit log ---
174174

175-
if echo "$NCOMMAND" | grep -qE '(^|\s|&&\s*)git\s+commit'; then
175+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+commit'; then
176176
# Resolve the target worktree so the edit log and staged-file listing come
177177
# from the same repo the commit targets (e.g. `git -C <pr-worktree> commit`).
178178
WORK_DIR=$(detect_work_dir commit)

.github/workflows/ci.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ jobs:
2121
with:
2222
node-version: 22
2323

24+
# Runs before `npm install` because npm 11 silently strips the `libc`
25+
# field from optional-dependency lockfile entries (see #1160). If the
26+
# check ran post-install we'd flag every CI run instead of the PRs that
27+
# actually introduce the regression.
28+
- name: Verify lockfile libc discriminators
29+
run: node scripts/verify-lockfile-libc.mjs
30+
2431
- name: Install dependencies
2532
timeout-minutes: 20
2633
shell: bash
@@ -277,6 +284,18 @@ jobs:
277284
CODEGRAPH_PARITY: '1'
278285
run: npx vitest run tests/engines/ tests/integration/build-parity.test.ts --reporter=verbose
279286

287+
# Isolated regression canary for #1147 — runs only the dropped-language-gap
288+
# suite so a failure surfaces under a clear step name and can't be masked
289+
# by pollution from neighbouring tests in the larger `test` job. The gap
290+
# repair path (#1083) is load-bearing for engine parity but easy to break
291+
# silently — the bug shape is "node row never re-inserted", which only
292+
# this test exercises directly.
293+
- name: Dropped-language gap regression guard (#1147)
294+
shell: bash
295+
env:
296+
CODEGRAPH_PARITY: '1'
297+
run: npx vitest run tests/integration/dropped-language-gap.test.ts --reporter=verbose
298+
280299
rust-check:
281300
runs-on: ubuntu-latest
282301
name: Rust compile check

package-lock.json

Lines changed: 34 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/verify-lockfile-libc.mjs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env node
2+
// Verifies that @optave/codegraph-linux-* entries in package-lock.json declare
3+
// the `libc` discriminator. npm 11 silently strips this field when generating
4+
// the lockfile on non-Linux hosts (and sometimes on Linux too), even though the
5+
// published packages declare it. Without it, npm cannot disambiguate
6+
// linux-x64-gnu vs linux-x64-musl when resolving from the lockfile and may
7+
// install (or load) the wrong native binary on Alpine/musl hosts.
8+
//
9+
// Run via `npm run lint` (or directly) in CI to catch silent regressions from
10+
// Dependabot bumps and contributor `npm install` runs.
11+
import { readFileSync } from 'node:fs';
12+
import { dirname, resolve } from 'node:path';
13+
import { fileURLToPath } from 'node:url';
14+
15+
const EXPECTED_LIBC = {
16+
'@optave/codegraph-linux-arm64-gnu': 'glibc',
17+
'@optave/codegraph-linux-x64-gnu': 'glibc',
18+
'@optave/codegraph-linux-x64-musl': 'musl',
19+
};
20+
21+
// Resolve relative to this script's location so it works regardless of CWD
22+
// (e.g. running `node scripts/verify-lockfile-libc.mjs` from the `scripts/`
23+
// subdirectory still finds the repo-root lockfile).
24+
const __dirname = dirname(fileURLToPath(import.meta.url));
25+
const lockfilePath = resolve(__dirname, '..', 'package-lock.json');
26+
const lock = JSON.parse(readFileSync(lockfilePath, 'utf8'));
27+
const failures = [];
28+
29+
for (const [pkgName, expectedLibc] of Object.entries(EXPECTED_LIBC)) {
30+
const entry = lock.packages?.[`node_modules/${pkgName}`];
31+
if (!entry) {
32+
failures.push(`${pkgName}: missing from package-lock.json`);
33+
continue;
34+
}
35+
const libc = entry.libc;
36+
if (!Array.isArray(libc) || !libc.includes(expectedLibc)) {
37+
failures.push(
38+
`${pkgName}: expected libc=["${expectedLibc}"], got ${JSON.stringify(libc)}`,
39+
);
40+
}
41+
}
42+
43+
if (failures.length > 0) {
44+
console.error('package-lock.json libc discriminator check failed:\n');
45+
for (const f of failures) console.error(` - ${f}`);
46+
console.error(
47+
'\nnpm install may have stripped the libc field. Restore it by editing\n' +
48+
'package-lock.json so each @optave/codegraph-linux-* entry includes\n' +
49+
'its libc field (see expected values above). Tracked in #1160.',
50+
);
51+
process.exit(1);
52+
}
53+
54+
console.log('package-lock.json libc discriminators OK');

tests/unit/snapshot.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,16 +149,16 @@ describe('snapshotSave', () => {
149149
// snapshotSave. better-sqlite3 is synchronous, so Promise-based
150150
// concurrency would queue two sequential microtasks — only separate
151151
// threads exercise the TOCTOU race.
152-
const nodeMajor = Number(process.versions.node.split('.')[0]);
153152
const raceWorkerPath = path.join(__dirname, 'snapshot-race-worker.mjs');
154153
// --import requires a URL (file://…) or a bare/relative specifier, not a
155154
// drive-letter path on Windows. Use the file:// URL directly.
156155
const loaderUrl = new URL('../../scripts/ts-resolve-loader.js', import.meta.url).href;
157-
const raceExecArgv = [
158-
nodeMajor >= 23 ? '--strip-types' : '--experimental-strip-types',
159-
'--import',
160-
loaderUrl,
161-
];
156+
// `--experimental-strip-types` is accepted by Worker execArgv across all
157+
// supported Node versions (≥ 22.6); the unprefixed `--strip-types` flag
158+
// is not in the Worker allowlist on Node 24 and causes
159+
// ERR_WORKER_INVALID_EXEC_ARGV. On Node ≥ 23.6 type stripping is on by
160+
// default, so the flag is a harmless no-op there.
161+
const raceExecArgv = ['--experimental-strip-types', '--import', loaderUrl];
162162
const spawnSaveWorker = (workerData: {
163163
dbPath: string;
164164
name: string;

0 commit comments

Comments
 (0)