Skip to content

fix(providers): report a reason code when the GJC watcher fails - #37

Merged
devswha merged 1 commit into
mainfrom
fix/gjc-watcher-failure-reason
Aug 13, 2026
Merged

fix(providers): report a reason code when the GJC watcher fails#37
devswha merged 1 commit into
mainfrom
fix/gjc-watcher-failure-reason

Conversation

@devswha

@devswha devswha commented Aug 13, 2026

Copy link
Copy Markdown
Owner

What changed

  • fail() now takes a reason from a fixed vocabulary and attaches it as Error.cause
  • child exit reports its numeric exit code and signal name
  • reportFailure logs that cause together with the consecutive-failure run length, reset once a start succeeds
before:  GJC native session watcher failed.
after:   GJC native session watcher failed. (child-exit code=1 signal=none; consecutive 47)

Why

A watcher restart loop logs one indistinguishable line per cycle. On a live host it ran 1752 cycles over 14.5 hours and left no record of the cause — the failure was only cleared by an unrelated process kill, and by then the evidence was gone.

Raw stderr is not the fix: watcher diagnostics deliberately never expose transcript paths, an invariant locked by contains callback diagnostics and continues without exposing event paths. A closed reason vocabulary carries no session data and still identifies the failure. Exit code and signal name are numeric/enumerated, so they are safe to attach.

Restart backoff already exists (1s doubling to a 30s cap) and is unchanged; only the run length is new, so a stuck loop is visible without counting timestamps.

Tests

  • each failure mode reports its own reason code and nothing from the frame
  • ready timeout and child exit stay distinguishable, exit status included
  • failure diagnostics name the reason so a restart loop is diagnosable from logs alone
  • existing path-leak assertions still pass unchanged
  • npm run typecheck, npm run lint, npm run check:identity, npm test (server 907, client 262) all green

Every failure logged one fixed string, so a restart loop produced thousands of
identical, unactionable lines: a 14-hour loop on a live host left no record of
what broke it. Watcher diagnostics deliberately withhold transcript paths, so
the fix is a closed failure vocabulary rather than raw stderr.

- fail() takes a reason from a fixed set and attaches it as Error.cause
- child exit carries its numeric exit code and signal name
- reportFailure logs that cause plus the consecutive-failure run length
- the run length resets once a watcher start succeeds

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@devswha
devswha merged commit 7a6e5da into main Aug 13, 2026
3 of 4 checks passed
@devswha
devswha deleted the fix/gjc-watcher-failure-reason branch August 13, 2026 06:27
devswha added a commit that referenced this pull request Aug 13, 2026
The repeated-interrupt assertion slept 100ms and then required exactly two
recorded interrupts, so a slower runner failed it: PR #37 went red on Node 24
while Node 22 passed on the same commit, and a rerun went green.

waitForInterrupt now takes an expected count and polls to the harness deadline,
so the test waits on the event it asserts. The equality check still pins
exactly two, so an extra interrupt remains a failure.

Verified by running the file three times in a row after the change.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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