Skip to content

perf(windows): Node hot paths for the check-inbox hook and CLI entries (implements #449) - #463

Draft
koujimatsumoto76-spec wants to merge 1 commit into
fujibee:mainfrom
koujimatsumoto76-spec:perf/windows-node-hot-paths
Draft

perf(windows): Node hot paths for the check-inbox hook and CLI entries (implements #449)#463
koujimatsumoto76-spec wants to merge 1 commit into
fujibee:mainfrom
koujimatsumoto76-spec:perf/windows-node-hot-paths

Conversation

@koujimatsumoto76-spec

Copy link
Copy Markdown

What

Implements the proposal from #449. On Windows, the measured hot hook / CLI entries today
walk bash -> many short-lived processes (sed / tr / sqlite3 / identity walk),
which costs 40-80 s per invocation on a stock Git Bash install (measured
breakdown in #449). This PR adds first-class Node implementations for the hot
paths and dispatches to them where it pays:

  1. Identity resolver module (scripts/lib/identity-resolver.js) — pure
    lookup: project + type -> identity candidates. Reused by every entry below;
    replaces the per-call identities.sh spawn (~70 s of a 77 s SessionStart).
  2. check-inbox.js — the Stop-hook path (cooldown, actas-lock respect,
    unread select, read_at on displayed rows, block/status JSON) via
    node:sqlite.
  3. Fast CLI (agmsg-fast.js) — team / send / history / inbox /
    whoami, byte-parity with the bash output contracts (raw-compared; see
    Behavior notes). Only commands with an existing upstream .sh entry are
    included — no upstream-unused code.
    (Native SessionStart is deliberately NOT in this PR: open PR feat(windows): native Codex hook transport (queue + out-of-sandbox dispatcher) #305 already
    reworks the Windows hook transport across the same files
    (hooks-json/delivery/_delivery). Our measurements strongly support that
    direction — the Git Bash entry alone costs 6-9 s, and our local native
    SessionStart cut 77 s to ~1-2 s — so we would rather bring those numbers and
    review energy to feat(windows): native Codex hook transport (queue + out-of-sandbox dispatcher) #305 than collide with it. If feat(windows): native Codex hook transport (queue + out-of-sandbox dispatcher) #305 stalls, a follow-up PR
    can carry it.)

Dispatch contract (uniform): Node path only on Windows; each entry
(check-inbox.js and the fast CLI) probes node:sqlite and falls back to
the existing bash path only on a failed probe (exit 78), i.e. before any
side effect. The gating is
deliberately narrow: the bash paths remain the contract of record on every
platform, so the Node paths can be extended to other OSes later without this
PR taking that risk now. node:sqlite is still marked experimental upstream
in Node; because every native entry is probe-gated, an API change degrades to
the bash path instead of breaking anything. After
the Node implementation has started, a non-zero exit is loud and does NOT
fall back (a mid-run bash retry could double side effects such as a send
INSERT or read_at updates). Non-Windows platforms are untouched.

Measured (Windows 11, Git Bash/MSYS2, Node v25)

path before after
Stop hook check-inbox 50-80 s per turn 1.4-2.1 s
team / history / inbox / whoami 8-90 s (timeouts common) 0.3-1.6 s
send 8.1 s 0.47 s

Behavior notes

  • Parity is enforced by 19 golden contract fixtures plus raw byte comparison
    of representative outputs (LF-normalized). read_at is set only on the
    rows actually displayed — this matches current main (check-inbox.sh /
    inbox.sh already restrict to displayed IDs), so no behavior divergence
    against the tree this PR targets.
  • The package keeps its engines contract (node >= 14): the native paths are
    probe-gated, so an older Node simply keeps the bash path.
  • node:sqlite needs Node >= 22.5; the capability probe (not a version
    compare) decides, and older/absent Node keeps the bash path.
  • In restricted environments where MSYS2 cannot create shared memory
    (CreateFileMapping error 5), the native path is an availability fix, not
    just performance.

Not in this PR

Structure (patchless boundary, against main 3f87d60)

This PR does NOT port our local patch artifacts (full-file replacements /
anchor edits were derived against v1.1.6 and are not portable — e.g. the
request file grew from 5 local fields to the current 3-field contract, with
identity authority moved into the launcher). Instead it implements the same
behavior first-class in the current tree:

  • scripts/lib/identity-resolver.js + scripts/check-inbox.js +
    scripts/agmsg-fast.js as new files (no runtime deps, node:sqlite only);
  • OS/type-aware dispatch added where the entry points already branch:
    hook generation (commandWindows) and the .sh entry heads;
  • launcher internals, role-session/thread authority, watcher, and bridge are
    NOT modified.

Minimal seam (5 change areas — 4 runtime files + tests — per on-tree verification against 3f87d60):

file change
scripts/lib/identity-resolver.js (new) shared identity lookup
scripts/check-inbox.js (new) + scripts/check-inbox.sh head Stop-hook Node path + Windows-only dispatch
scripts/agmsg-fast.js (new) + the 5 CLI .sh entry heads team/send/history/inbox/whoami Node paths + dispatch
Node units + bats (Linux/macOS legs) contract fixtures, dispatch exit-78 semantics

No changes to hooks-json.sh / delivery.sh / _delivery.sh — zero overlap with
open PR #305 (Windows hook transport), by design.

Fallback semantics: diagnostics to stderr and read-only probing are allowed
before falling back (exit 78); once the Node implementation has started, a
non-zero exit is loud and never falls back — a bash retry could double side
effects (a second send INSERT, repeated read_at updates).

Validation gate (before submission): the implementation is built and verified
on an isolated clone of current main. Nothing is transplanted from our
v1.1.6-based local install. Per-leg time boxes (a run is terminated at 5 min
of silence or its cap, and switched to an alternative gate instead of
retried): Node units <= 3 min; real commandWindows PowerShell smokes <= 60 s
each; static checks / bash -n <= 1 min; full Bats on Linux/macOS (CI or an
equivalent environment) <= 10 min. Windows Bats suites are NOT part of the
gate — measured on this machine they cost 4-7 min for 3 cases (delivery
subset aborted at 15 min), which is the very spawn tax this PR addresses;
the Windows delivery behavior is covered by the PowerShell smokes instead,
and that substitution is recorded openly.
Compatibility gates: shared-path shell code is bash-3.2-safe (macOS ships
3.2); a fixture pins the POSIX command field byte-unchanged when
commandWindows is added; full Bats pass on the macOS leg.

🤖 Generated with Claude Code

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