Skip to content

refactor(events): report IPC claim and endpoint teardown failures instead of ignoring them - #516

Merged
ScriptedAlchemy merged 2 commits into
mainfrom
refactor/effect-ipc-teardown
Sep 4, 2026
Merged

refactor(events): report IPC claim and endpoint teardown failures instead of ignoring them#516
ScriptedAlchemy merged 2 commits into
mainfrom
refactor/effect-ipc-teardown

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Summary

Effect-conformance cleanup 5 of the prior audit (events/ipc.ts): split the inode lock (kept raw, now documented as such) from the Effect orchestration around it, and stop swallowing claim/socket teardown failures with Effect.ignore — they are captured with Effect.exit and reported, last failure wins. The lock itself, its open(…, 'wx') + handle stat identity, and the identity-matched removals are untouched; nothing moved to FileSystem.

What changed (packages/agent-bundle/src/events/ipc.ts)

  • Inode lock stays raw and says so. tryClaimEndpoint (audit rows :415–437, 443–456) now carries the doc comment explaining why the exclusive-create lock, the owner record written through the same handle, and the handle-bound dev/ino identity remain node:fs inside one liftPromise: Effect FileSystem.stat follows links and has no handle inode, so the identity cannot be expressed there (docs/effect-conventions.md § Effect platform services). No code change in the lock.
  • releaseEndpointRecoveryGate (:532) — dropped a redundant Effect.ignore on an infallible Effect.callback.
  • releaseEndpointClaim (:609) — was liftPromise(close + removeIfIdentityMatches).pipe(Effect.ignore); now Effect<void, EventRuntimeTransportError> (runtime-failed, "Unable to release the event runtime endpoint claim."). In openServer, the Effect.acquireUseRelease(claimEndpoint, listen, releaseEndpointClaim) became an explicit exit sequence under Effect.uninterruptibleMask: acquire the claim, Effect.exit(restore(listenUnderClaim)), Effect.exit(releaseEndpointClaim(claim)); a release failure wins over the listen outcome and, when the listener had come up, shutdownServer runs first so the failure never leaks a listening socket. (Why not keep acquireUseRelease with a fallible release: in rc.112 a failing release after a failed use is combined into the cause behind the use failure — combineFinalizerCausecauseCombine(useCause, releaseCause) — so the boundary's Cause.squash would report the use failure, not last-failure-wins.)
  • removeOwnedEndpoint (:768) — was .pipe(Effect.ignore); now Effect<void, EventRuntimeTransportError> (runtime-failed, "Unable to remove the event runtime endpoint."). The old closeServer (server close + Effect.ensuring(removeOwnedEndpoint)) is split: shutdownServer (destroy sockets, stop listening — idempotent and infallible) is the Layer scope finalizer; closeServer = shutdownServer then removeOwnedEndpoint is what EventRuntimeServer.close() runs explicitly through the boundary runtime before disposing it (runtime.run(closeServer(service)).finally(() => runtime.close()), memoized). The scope finalizer therefore only ever repeats the idempotent shutdown, and the one teardown step that can fail reports to the caller instead of to a finalizer that would have to swallow it.
  • Two test seams on EventRuntimeServerTestHooks (beforeEndpointClaimRelease, beforeEndpointRemoval) stand in for failing releases in tests; production paths are unchanged when they are absent.

Observable change (the point of the cleanup)

close() can now reject with EventRuntimeTransportError runtime-failed when the owned socket path cannot be removed (a non-ENOENT stat/rm failure), and createEventRuntimeServer can fail with the same class when the claim lock cannot be released — both previously silent. mcp-server-runtime.ts already surfaces events.close() rejections through its documented "teardown error surfaces once they are closed" chain. Changeset: .changeset/effect-ipc-teardown-failures.md (agent-bundle: patch). Docs: the shared-IPC section of website/docs/{en,zh}/guide/authoring/hooks.mdx now records the fail-closed shutdown / claim-release contract; pnpm docs:site:build green (language parity checked).

Idioms and citations

Idiom Doc repos/effect/LLMS.md / repo section
Capture an outcome with Effect.exit, run cleanup, re-raise (explicit sequence, not a scope finalizer) Error Management → Two Types of Errors ("At application boundaries, Effect.exit … can be used to inspect or report them") docs/effect-conventions.md Stage 3 "Hurt": "Teardown contracts that propagate cleanup failures … must be explicit effect sequences — capture the attempt's Exit, run the cleanup, then unwrap — never scope finalizers"
Effect.acquireRelease finalizer stays infallible and idempotent Resource Management → Scope → acquireRelease agent-patterns/effect-scope.md "Resource lifecycle"; rc.112 acquireRelease release type Effect<unknown, never, R2>
Interruption-safe acquire→release pairing via uninterruptibleMask Concurrency → Fibers → Interruption Model rc.112 acquireUseRelease is uninterruptibleMask((restore) => …) itself (internal/effect.ts:4207)
Typed class error on the fail channel instead of Effect.ignore Error Management → Expected Errors agent-patterns/effect-errors.md

Tests

  • New in tests/event-ipc.test.ts: reports a failed endpoint removal from close() after the server has stopped listening (typed error with cause, endpoint no longer answers, repeated close() returns the same settled rejection, removal attempted once); reports a failed claim release from open() and shuts down the server it had started (typed error with cause, endpoint not live afterwards).
  • Existing 16 IPC tests (claim races, orphan reclamation, stale sockets, zombie owners) unchanged and green.
  • pnpm typecheck, pnpm lint green; pnpm test:unit green (3173 passed); projection tests/projection/contract-matrix.test.ts (the other createEventRuntimeServer consumer) green (21 passed).

Review status

No PR comments are posted by the author; every review thread is answered in this section.

Head Review Threads
2014e93 Codex completed (2 P1) .changeset/effect-ipc-teardown-failures.md:5 "Add the required PR reference" → fixed in dbeb653 (summary ends with (#516)). ipc.ts:864 "Document the new shared-runtime shutdown failure behavior" → fixed in dbeb653: paragraph added to the shared-IPC section of hooks.mdx in both locales; pnpm docs:site:build green. CI on 2014e93: Verify (Node 24) failed only in packages/workbench/tests/overview.e2e.test.ts ("offers the host-owned MCP playground handoff…", a Playwright waitForRequest timeout) — a Workbench e2e untouched by this PR; the same job's unit (3173), route-unit, and projection pools passed.
dbeb653 pending

…tead of ignoring them

The endpoint claim lock stays raw node:fs (exclusive create, handle-bound
inode identity) and is documented as such. Around it, the two Effect.ignore
sites become typed failures: releasing the claim is an explicit exit
sequence under uninterruptibleMask whose failure wins over the listen
outcome (after shutting a started listener down), and removing the owned
socket path is the fallible half of an explicit close() that runs through
the boundary runtime before the scope's idempotent shutdown finalizer.
@changeset-bot

changeset-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dbeb653

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agent-bundle Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T03:21:13.170970Z 2014e93 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 4, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@516
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@516
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@516

commit: dbeb653

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2014e938f6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"agent-bundle": patch
---

Report shared event runtime teardown failures instead of swallowing them: `createEventRuntimeServer(...).close()` now rejects with `EventRuntimeTransportError` (`code: 'runtime-failed'`, "Unable to remove the event runtime endpoint.") when the owned socket path cannot be removed, and opening a server fails with the same error class ("Unable to release the event runtime endpoint claim.") — after shutting the just-started listener down — when the endpoint claim lock cannot be released. Successful opens and closes are unchanged, and the generated MCP runtime's shutdown surfaces the close error the way it already surfaces other teardown failures.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required PR reference to the changeset

The changeset summary does not end with the required (#PR) attribution, so the generated changelog would publish this entry without the repository-mandated pull-request reference. Add the applicable PR suffix before merging.

AGENTS.md reference: AGENTS.md:L102-L105

Useful? React with 👍 / 👎.

Comment on lines +862 to +864
close: () => {
closing ??= runtime.run(closeServer(service, testHooks)).finally(() => runtime.close());
return closing;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document the new shared-runtime shutdown failure behavior

This changes the generated shared event runtime's observable shutdown contract because close() can now reject with EventRuntimeTransportError, but the matching shared-IPC sections in website/docs/en/guide/authoring/hooks.mdx and website/docs/zh/guide/authoring/hooks.mdx were not updated. Document the endpoint-removal failure behavior in both locales so users know shutdown can fail and leave a stale endpoint.

AGENTS.md reference: AGENTS.md:L71-L77

Useful? React with 👍 / 👎.

…laim release (#516)

The changeset now ends with its PR reference, and the shared-IPC section of
the hooks guide (en + zh) records that a shutdown which cannot remove its
own socket path, or a startup which cannot release its claim lock, reports
EventRuntimeTransportError runtime-failed instead of exiting silently.
@ScriptedAlchemy
ScriptedAlchemy merged commit b8e9390 into main Sep 4, 2026
13 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the refactor/effect-ipc-teardown branch September 4, 2026 20:02
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