WebService exposed an observation surface nothing in production observes: the web/providers-change event (declared, emitted on every provider registration/disposal, rollback-yield ordered before the emit solely so a throwing change listener unwinds the registration) and the aggregated searchStatus()/fetchStatus() query with its WebCapabilityStatus union. dsh-tool-web executes through ctx.web.search()/fetch() and routes on the structured WebError codes selection throws at execution time; tool registration follows product enablement, not provider availability. The only listeners/callers were the web packages' own tests, and the tool-web README / architecture.md prose claiming the tool 'reads only the aggregated searchStatus()/fetchStatus()' had drifted from the call sites. Remove the event declaration, both emits, and the rollback-before-emit machinery (the plain ctx.effect disposer keeps HMR cleanup, matching LlmService.registerAdapter). Remove searchStatus()/fetchStatus(), resolveStatus(), and WebCapabilityStatus; the provider-private status() stays as the execution-time selection input. Delete the listener-throw rollback test, and rewrite every event/status assertion across the web packages' tests onto caller-observable behavior: a successful search()/fetch() or the structured WEB_PROVIDER_* codes. Regenerate the cordis catalog; update the web/tool-web READMEs, the architecture.md web paragraph, core-data-structures/web.md, and the type-equiv manifest; amend the web capability seam RFC's facts to the shipped surface. This follows the llm/adapter-change precedent: a boot-time backend-registry signal and an availability probe distinct from executing both sit on the cut side of its keep/cut criterion. RFC: docs/rfc/implemented/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md
4.8 KiB
RFC: Drop the unconsumed web observation surface — the providers-change event and the status methods
Status: implemented (proposed and accepted 2026-07-04)
Problem
WebService exposes an observation surface no production code observes:
web/providers-change(packages/web/web/src/index.ts) is declared and emitted on every provider registration and disposal, and each registration effect's rollback yield is ordered BEFORE the emit solely so a throwing change listener unwinds the registration. No listener exists outside the package's own two unit tests (one of which exists to pin that rollback ordering).searchStatus()/fetchStatus()and theWebCapabilityStatusunion (same package) have zero production callers:dsh-tool-webexecutes directly throughctx.web.search()/fetch()and surfaces unavailability as the structuredWebErrorcodes the seam throws at execution time (packages/web/tool-web/src/search.ts,packages/web/tool-web/src/fetch.ts); the only status callers are the web packages' own tests. The prose inpackages/web/tool-web/README.mdand architecture.md claims the tool "reads only the aggregatedsearchStatus()/fetchStatus()" — drift that survives only because nothing checks prose against call sites.
The seam's own design starves both surfaces of consumers: tool registration follows product ENABLEMENT, not provider availability (packages/web/tool-web/src/index.ts), and provider selection resolves at execution time, never cached — so there is no cache to invalidate, no registration set to recompute, and no caller that needs an availability probe distinct from executing and routing the structured error. HMR cleanup is carried by the effect disposers themselves.
This mirrors drop the unconsumed llm/adapter-change event, which removed the same notification shape, the same rollback-before-emit machinery, and the same listener-throw test from LlmService. That RFC's keep/cut criterion — keep tools/change for its plausible user-facing tool-list consumer, cut the boot-time backend-registry signal — puts a web-provider registry squarely on the cut side; the status methods are the same judgment applied to a pull surface instead of a push one.
Proposal
Delete the event declaration, both emits, and the rollback-before-emit ordering (the plain ctx.effect disposer keeps HMR cleanup). Delete searchStatus()/fetchStatus()/WebCapabilityStatus — the provider-private status() stays, since it feeds execution-time selection. Delete the listener-throw rollback test that exists solely for the removed event, and rewrite the emission assertions and every status-based assertion onto the behavior a real caller observes (a successful search()/fetch(), or the structured WebError codes for unavailable/ambiguous/misconfigured provider sets). Run pnpm run gen-cordis-catalog; update packages/web/web/README.md, packages/web/tool-web/README.md (the drifted reads-status sentence), web.md, and the web paragraph in architecture.md. Amend the web capability seam RFC's facts (it specified the event and the status aggregation) per implemented/AGENTS.md.
Why not keep it?
The web seam RFC specified both deliberately — the event as a minimal HMR-visibility signal, the status methods as the tool's aggregated diagnostics — and a future provider-status panel is imaginable. But the same RFC's other choices starved them: derived-on-call selection and enablement-based registration leave no consumer that CAN need either, the shipped tool demonstrates the real pattern (execute and route the structured error), and the drifted README sentence shows the promised consumer never materialized. Per AGENTS.md "RFCs are proposals, not golden truth", these are the parts of that proposal the code has since shown to over-reach; a future observer reintroduces the smallest signal or query it actually consumes, shaped by that consumer.
Acceptance criteria
- No
providers-change,searchStatus,fetchStatus, orWebCapabilityStatusspelling outside RFC history; the catalog is regenerated and fresh (verify-cordis-cataloggreen). - Registration/disposal HMR-safety tests prove cleanup through execution behavior rather than the removed surfaces.
packages/web/tool-web/README.mdand the architecture paragraph describe the execution-time error-routing contract the tool actually has.
Risks
A future provider-picker UI or diagnostics panel wants change notifications or a status query — it re-adds the smallest surface it consumes; the identical judgment, and its reversal condition, is already recorded on the llm precedent.