Skip to content

test: make the memory stall timing assertion load-tolerant - #516

Merged
LeXwDeX merged 37 commits into
devfrom
test/512-leftover-hardening
Sep 2, 2026
Merged

test: make the memory stall timing assertion load-tolerant#516
LeXwDeX merged 37 commits into
devfrom
test/512-leftover-hardening

Conversation

@LeXwDeX

@LeXwDeX LeXwDeX commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Closes #512
Closes #513
Closes #514
Closes #515

Why

Close the four actionable findings left over from the v1.0.38 train review: a load-dependent flake in the memory stall test, the open high-severity mysql2 credential-leak advisory (dependabot 110), a seam-only TUI cleanup test that did not protect the #502 production change, and a diverging duplicate of the platform group-kill primitive flagged by the #500-504 review.

What changed

Evidence

Checklist

  • Why, What changed, and Evidence are filled in.
  • specgit finish exits 0.

resolveSpecPath now strips the synthetic builtin:// marker and resolves by
name through the library chain (project shadows global shadows builtin),
so the path list advertises round-trips as a spec_path input. Unknown
builtin names fail as a library lookup instead of a cwd-joined extension
error.
Workflow blocks compile coding/prototype to build and plan blocks to
plan, but the generated worker_type catalog filtered every primary agent
out, so the model could never name the defaults explicitly. The workflow
catalog now lists native primaries (build/plan); user-defined primary
modes and hidden agents stay excluded and the task catalog is unchanged.
Four guide/schema mismatches from the 2026-09-02 verification: replan and
extend auto-resume a paused workflow (manual control(resume) after a
successful replan dies on InvalidTransitionError), the exhaustive node
field table missed review, the tool reference missed draft/guide/validate,
and the name rule missed the leading-dot and control-character exclusions
plus the builtin:// marker round-trip.
The SpecGit Acceptance verdict requires required-check runs that started
at or after the draft-to-ready transition. Both CI gates only listened
to the default pull_request types, so a ready transition never produced
fresh runs and the verdict timed out waiting for them.
spec_path now mentions the builtin:// marker round-trip; the review field
states the input_mapping wiring validateReviewLifecycle enforces for diff
reviews; timeout_ms documents that the budget runs from admission (queue
wait counts, an expired queued node fails without spawning).
validateDiffReview enforces seven wiring checks beyond the two node ids
(transitive dependency chain, three input_mapping bindings, PASS-gated
condition, verdict+fingerprint output_schema), and validateReviewLifecycle
only turns them into authoring errors in deep mode — standard mode warns.
The previous description understated both.
Regenerate the parameters snapshot after the description changes (the CI
unit-test matrix never exercised parameters.test.ts, so the stale snapshot
only failed locally). Align blocks.ts with validation.ts (expired-queued-node
clause), add the capped-escalation semantics of the deadline watcher and the
deep-only final-gate requirement to the timeout/review descriptions, and sync
the worker_config guide row.
…fresh

The replay list still named specgit@^0.5.0 and omitted three specializations
the refreshed harness actually carries (45/40-minute timeout split, node 22,
hand-parsed policy.yaml), so the next re-init would replay the wrong version
and drop them.
fix: accept builtin:// spec_path returned by list
…ldren cannot hang triggers

A timed-out command hook only SIGTERM'd the shell wrapper; grandchildren
keeping the stdio pipes open meant the close event never fired and the
hook trigger hung forever. Exit and stream-drain are now awaited
separately with a bounded grace, then the whole group (detached, negative
pid on POSIX; taskkill /T /F on Windows) is SIGKILL'd and reaped.
watch() discarded every Unsubscribe, so each instance dispose/remount
cycle leaked five permanent EventV2 listeners holding the instance
context. The finalizer now unsubscribes before closing the scope.
Three event.on handlers discarded their unsubscribe functions and
accumulated in the app-level SDK handler set on every route transition,
retaining the opentui editor and renderer trees.
…nt shutdown

stop() on POSIX returned right after SIGTERM, so servers ignoring it
stayed alive as orphans while instance finalizers reported success. It
now waits a bounded grace, escalates to SIGKILL, and awaits exit; the SDK
copy adapts the same escalation synchronously with an unref'd timer. MCP
client shutdown reaps the whole process tree through a shared
shutdownClient used by the state finalizer, closeClient, and the create
rollback path.
Every memory commit copied the full topic set into a new generations
directory that was never removed; keep the latest 3 plus orphan-staging
sweep, best-effort so a cleanup failure never fails a commit. Heap
snapshots (hundreds of MB each) now rotate to the latest 2 in the log
directory.
Redundant toString on stream text, .resolves on a synchronous prune
helper, and one intentional wire-shape JSON assertion (disabled inline
with reason, matching the schema-contract test precedent).
heap-<pid>-<ts> sorted lexicographically orders snapshots by pid across
runs (digit-count changes, wraparound), so pruning could delete the newest
snapshot and keep stale ones. Sort by the timestamp segment instead, surface
non-ENOENT readdir failures as warnings, fix the always-true missing-dir
assertion (unawaited async), and add a cross-pid regression test.
The finally-block process.exit() cut off pending async cleanup (instance
finalizers, MCP/process stop escalation) — the #503 clause the first pass
skipped. An unref'd 5s force-exit timer (matching the stop escalation
budget) keeps the hanging-subprocess protection while a clean drain exits
immediately with all cleanup completed.
The escalates-to-SIGKILL child wrote "ready" before registering its trap,
so a parent preempted between the two statements delivered SIGTERM to the
default handler and the child exited SIGTERM instead of escalating —
observed as a load-dependent failure on the linux runner (262ms, signalCode
SIGTERM). Ordering the trap first makes the ready signal prove the handler
is installed.
fix: hook command grandchildren hold stdio pipes and hang triggers forever
Series file for the next stable derived from graphagent-v1.0.37: the #505
resource-leak fix wave and the #509 DAG workflow-tool consistency pass,
including the review-round fixes and the reverted bounded-exit-window
attempt. Render verified locally with release-notes.ts (exit 0).
The 200ms bound around a 40ms connectTimeout flaked at 288ms on a loaded
linux runner (raw setTimeout, no TestClock possible). 2000ms still separates
fail-fast from hang — a hang trips the test timeout instead — and the
Stalled error-class assertion remains the semantic check.
Closes the dependabot high advisory (alert 110): mysql2 < 3.22.0 leaks
plaintext credentials on an auth-plugin downgrade to mysql_clear_password.
The RouteProbe seam test mirrors the production subscription shape, so
removing an onCleanup wrapper from the real Session/Prompt routes left it
green. Mount the actual route components inside the production provider
stack and assert the SDK event bus returns to its baseline listener count
after unmount; verified red when either wrapper is removed. The two bridge
lint suppressions carry reasons (restoration point; overloaded on widened
through bind).
win32 taskkill /T /F vs negative-pid kill existed as diverging copies in
util/process.stop and the hook execShell killGroup. Extract killGroupPid
(POSIX group signal; win32 awaits the taskkill exit code and throws, leaving
fallback and logging to callers) and reuse it in both. The SDK sync copy
stays as documented in #503. No behavior change: same signals, ordering, and
grace constants; the #500 process-group regression suite passes.
@LeXwDeX
LeXwDeX marked this pull request as ready for review September 2, 2026 12:58
@LeXwDeX
LeXwDeX merged commit 94f5471 into dev Sep 2, 2026
8 checks passed
@LeXwDeX
LeXwDeX deleted the test/512-leftover-hardening branch September 2, 2026 14:04
LeXwDeX added a commit that referenced this pull request Sep 2, 2026
Series file for the next stable derived from graphagent-v1.0.38: the
leftover-hardening train (#512-#515) accepted on dev via PR #516. Render
verified locally with release-notes.ts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant