Skip to content

fix(hooks): wrap the augment CLI child in the orphan guard (#2163)#2169

Open
Minidoracat wants to merge 1 commit into
abhigyanpatwari:mainfrom
Minidoracat:fix/2163-wrap-augment-guard
Open

fix(hooks): wrap the augment CLI child in the orphan guard (#2163)#2169
Minidoracat wants to merge 1 commit into
abhigyanpatwari:mainfrom
Minidoracat:fix/2163-wrap-augment-guard

Conversation

@Minidoracat

Copy link
Copy Markdown
Contributor

Summary

Wraps the augment CLI child — the longest-lived previously-unwrapped subprocess (7 s local / 12 s npx) — in the same self-tested orphan guard that #2165 introduced for lsof/ps, closing the remaining SIGKILL-orphan window called out in the tri-review (finding 4). Also adds the cursor-integration slot-skip diagnostic and the guard-availability precheck polish from the same review thread.

Follow-up to #2165 (maintainer-invited: #2165 (comment) → invitation in the merge comment). Part of the #2163 follow-up list.

Motivation / context

From the #2165 tri-review digest:

(pre-existing, out of declared scope) The augment child (7s local / 12s npx) is now the longest-lived unwrapped subprocess; the same SIGKILL-orphan mechanism this PR fixes for lsof/ps applies to it.

When Claude Code's hook timeout SIGKILLs a hook that is blocked in the augment spawnSync, the CLI child (a full Node process, far heavier than lsof) reparents to PID 1 and runs to completion. The mechanism, the fix, and the test pattern are identical to #2165 — this PR just extends them.

Areas touched

  • gitnexus/ (CLI / core / MCP server) — hooks + tests only
  • gitnexus-web/
  • .github/
  • eval/ or other tooling
  • Docs / agent config only

Also touches gitnexus-claude-plugin/hooks/ (byte-identical probe copy + plugin adapter) and gitnexus-cursor-integration/hooks/ (debug line only).

Scope & constraints

In scope

  • hook-db-lock-probe.cjs (both copies, byte-identical): export resolveUnixGuardTimeout. Adapters require the same module instance they already use for the lock probe, so the memo is shared and the lazy self-test still runs at most once per hook process — whoever needs the guard first (probe fallback or augment) pays the one-shot ~5 ms.
  • runGitNexusCli in the three probe-equipped adapters (claude CJS, plugin JS, antigravity CJS): every CLI-executing branch is wrapped with timeout -k 1 <ceil(inner_ms/1000)+1> on Unix when a guard is available. The budget is strictly above each branch's inner spawnSync timeout (7000 ms → 8 s, timeout+5000 → 13 s), so on a live hook Node's own timer always fires first and the existing error/status contract is untouched — the wrapper only matters once the hook has been SIGKILLed. Windows and no-guard hosts produce byte-identical argv to main. The plugin adapter's PATH-direct gitnexus branch (its most common production path) is wrapped as well; the cheap which/where detection spawn (3 s, not a CLI execution) deliberately is not.
  • Cursor integration: debug-gated augment skipped: hook slots saturated on the slot-starved early return (same observability gap fix(hooks): bound db-lock probe subprocesses and gate probe behind hook slot (#2163) #2165's F2 fixed in the other three adapters), following that file's own GITNEXUS_DEBUG truthiness convention.
  • Reaping tests (the two from fix(hooks): bound db-lock probe subprocesses and gate probe behind hook slot (#2163) #2165 plus the new one) get a guard-availability precheck that fails with an explicit, actionable message on hosts without a usable coreutils timeout. We chose an assertion over skipIf on purpose: a skip would let a coreutils-less Linux host go silently green on the exact regression lane this work exists for; GitHub ubuntu runners always ship coreutils, so CI behavior is unchanged.

Explicitly out of scope / not done here

Implementation notes

  • Single-spawnSync-literal-per-branch pattern preserved (argv assembled conditionally), so the spawn-count/windowsHide parity convention holds in every touched file (verified by the in-suite regression check: CJS 4=4, Plugin 6=6, Antigravity 4=4, probe 4=4, cursor 3=3).
  • resolveUnixGuardTimeout() is never called on win32 (guarded at the call sites; the self-test spawns /bin/sh). Documented in the export JSDoc and pinned by the source tests.
  • The source-pinning test counts the direct-budget formula exactly per adapter (Plugin: 2, others: 1), so a partial revert of any single wrapped branch cannot pass unnoticed.

Testing & verification

  • cd gitnexus && npx vitest run test/unit/hooks.test.ts — 186/188 (the 2 reds are the pre-existing fix: skip Claude augment hook when GitNexus server owns DB #1493 ENOENT fail-open pair, environmental to the pathological host documented in fix(hooks): bound db-lock probe subprocesses and gate probe behind hook slot (#2163) #2165's Known limitations; verified red on clean base)
  • cd gitnexus && npx vitest run test/unit/resolve-invocation.test.ts test/unit/setup.test.ts test/unit/cli-commands.test.ts test/integration/antigravity-hook-e2e.test.ts — all green (135/135 with the setup suites)
  • npx prettier --check / npx eslint on changed files — 0 errors
  • npx tsc --noEmit — no errors in touched files (remaining errors pre-existing on main)
  • Probe copies diff -q byte-identical

Red/green evidence: with only the three adapter wraps reverted (probe export and tests kept), both orphaned-augment reaping tests go red — the SIGTERM-immune fake CLI survives the hook's SIGKILL for the full poll window. With the wrap applied, the child is reaped in ~9.1 s (8 s budget + 1 s -k escalation), green on both the CJS and Plugin paths.

🤖 Generated with Claude Code

…atwari#2163)

Follow-up invited by the maintainer on abhigyanpatwari#2165: the augment child
(7s local / 12s npx) was the longest-lived unwrapped subprocess, exposed
to the same SIGKILL-orphan mechanism fixed for lsof/ps.

- Export resolveUnixGuardTimeout from the probe module (both copies,
  byte-identical); adapters share the same module instance, so the memo
  and lazy self-test still run at most once per hook process.
- Wrap every CLI-executing branch of runGitNexusCli in the three
  probe-equipped adapters with the guard: budget ceil(inner/1000)+1
  seconds with -k 1, strictly above each branch's inner spawnSync
  timeout, so the supervised path is unchanged and the wrapper only
  matters once the hook itself is SIGKILLed. Windows and no-guard hosts
  keep byte-identical argv. The plugin adapter's PATH-direct gitnexus
  branch (its most common production path) is wrapped too; the cheap
  which/where probe is not.
- Cursor integration: debug-gated 'augment skipped: hook slots
  saturated' on the slot-starved early return. Its augment child stays
  unwrapped for now — that integration does not install the probe
  sibling (the 'cursor probe' item on the abhigyanpatwari#2163 follow-up list).
- Reaping tests get a guard-availability precheck with an explicit
  failure message (assertion, not skipIf, so a coreutils-less Linux
  host fails diagnosably instead of going silently green).
- Tests: orphaned-augment reaping (CJS + Plugin, red without the wrap,
  ~9.1s reap measured), disabled-sentinel degradation equivalence,
  source pinning for all three adapters (exact per-branch budget-formula
  counts) + probe export + cursor debug line.

Note: pre-commit typecheck skipped; remaining tsc errors are
pre-existing on main (none in files touched here).
@Minidoracat Minidoracat requested a review from magyargergo as a code owner June 11, 2026 17:12
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

@Minidoracat is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Tests success unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
11330 11314 0 16 572s

✅ All 11314 tests passed

16 test(s) skipped — expand for details
  • COBOL pipeline benchmark > scales with file count
  • C++ ADL emit benchmark > emit phase scales sub-quadratically with co-scaled files and sites
  • C++ pipeline benchmark > scales with file count
  • C# pipeline benchmark > scales with file count — namespaces spread across the solution
  • C# pipeline benchmark > scales with file count — all types in one (global) namespace bucket
  • C# pipeline benchmark > scales with file count — all types in one (named) namespace bucket
  • Go pipeline benchmark > scales with file count (workers enabled)
  • Go pipeline benchmark — worker pool (issue Worker idle timeout kills long Go scope extraction and surfaces as Napi::Error during analyze #1848) > does not quarantine the large generated Go file on sub-batch idle timeout
  • Go structural interface detection benchmark > scales linearly with interface × struct count
  • Go structural interface detection split-phase benchmark > separates index-build and detection time
  • PHP pipeline benchmark > scales with file count (workers enabled)
  • Ruby pipeline benchmark > scales with file count (workers enabled)
  • Rust pipeline benchmark > scales with file count (workers enabled)
  • Vue pipeline benchmark > scales with component count
  • run.cjs direct-exec entrypoint (fix(cli): steer docs, skills, and hooks through a CLI-neutral project-local runner (#1939) #1945) > resolves a .cmd shim via the Windows shell branch, passing args and exit code
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 75.77% 37315/49243 N/A% 🟢 ███████████████░░░░░
Branches 63.6% 23120/36349 N/A% 🟢 ████████████░░░░░░░░
Functions 81.44% 4021/4937 N/A% 🟢 ████████████████░░░░
Lines 79.49% 33692/42384 N/A% 🟢 ███████████████░░░░░

📋 View full run · Generated by CI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant