Skip to content

feat: attribute registry calls made through host Fastify routes - #166

Merged
aoede3 merged 2 commits into
mainfrom
feat/host-route-attribution
Sep 6, 2026
Merged

aoede3 merged 2 commits into
mainfrom
feat/host-route-attribution

Conversation

@aoede3

@aoede3 aoede3 commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Host-route attribution

In development, τjs now observes registry-backed work performed through ordinary Fastify route handlers, without taking ownership of those routes. A handler that calls the service registry through callServiceMethod with no explicit recorder is correlated to a host-observed episode carrying the route's method and path, the registry calls made in it, and the response outcome.

The seam is Fastify's documented fastify.request.handler tracing channel with a bound AsyncLocalStorage. Nothing is added to the caller's Fastify instance, no application handler changes, and nothing binds outside development.

@taujs/server (minor)

  • EpisodeRecorder: routeMatched gains a required kind: 'page' | 'host' and an optional method; sent gains a host arm without a render mode; failed carries the response status with an optional error.
  • Observations schema version 2; host rows carry appId: null and identity host:<method> <path>; page identities are unchanged.
  • One episode per request, decided at runtime; request-ID collisions across instances are refused with one warning per boot; calls after the response has finished are recorded nowhere; every seam is wrapped so a fault degrades to no attribution, never a changed response.
  • Failed page responses now record the status the client actually received.
  • A failed boot releases the channel binding, including when no registry was supplied.

@taujs/mcp (minor)

  • Reads observations version 2; episodes.ndjson is a paired read with observations.json and fails closed when the pairing is missing, unreadable or mismatched.
  • taujs_who_calls_service reports hostObserved callers separately from declared and observed.
  • taujs_explain_route answers for an observed host path from a live boot only, preserves a reader refusal, and says "no observation" rather than implying no request occurred.

Limits, stated

Not route discovery, not API ownership, not complete request monitoring. Validation failures before the first registry call, unexercised routes, calls from hooks, and calls outside the request's async continuation leave no episode. "No observation" means unknown.

Verification

Full workspace gates green (build, test, typecheck, check-format, check-exports).

In development, tau-js now observes registry-backed work performed
through ordinary Fastify route handlers without taking ownership of
those routes. A handler that calls the service registry through
callServiceMethod with no explicit recorder is correlated to a
host-observed episode carrying the route method and path, the registry
calls made in it and the response outcome. The seam is Fastify's own
documented request-handler tracing channel with a bound
AsyncLocalStorage; nothing is added to the caller's instance and
nothing runs in production.

Recorder events gain an explicit episode kind, a status on failed and a
host arm on sent; observations schema moves to version 2 and the mcp
reader gates episodes as a paired read. taujs_who_calls_service reports
host-observed callers separately from declared and observed edges, and
taujs_explain_route can answer for an observed host path from a live
boot, saying "no observation" rather than implying no request occurred.
Three correctness fixes on top of b4bc618, each with regression cells.

taujs_explain_route now gates on discovery.mode === 'active' rather than
the presence of devJson.bootId: a stale boot deliberately retains its own
devJson, so a crashed or expired boot could otherwise still answer from
its episodes.

CreateServer no longer releases host attribution by re-deriving the
registry from opts.serviceRegistry, which is undefined when the caller
supplied none. SSRServer now hands back an idempotent disposer closure
that already captures the exact registry and introspection it acquired
with, so a boot that fails after acquiring never leaks the binding.

The paired episode read no longer fails open: a missing or unreadable
observations document now refuses episodes.ndjson explicitly, rather than
letting an undefined probed version pass through unguarded. Fixtures that
wrote episodes without their paired observations document are corrected.
taujs_explain_route preserves a reader refusal instead of reporting it as
"no observation".
@aoede3
aoede3 merged commit abe6240 into main Sep 6, 2026
5 checks passed
@aoede3
aoede3 deleted the feat/host-route-attribution branch September 6, 2026 19:41
@github-actions github-actions Bot mentioned this pull request Sep 6, 2026
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