Files
suaveplan/openspec/archive/2026-05-25-api-logger-worker-proxy/proposal.md
2026-05-27 23:29:53 +01:00

3.2 KiB

@suaveplan/api-logger-worker-proxy — worker-side proxy to main-process logger

Package: @suaveplan/api-logger-worker-proxy Status: PROPOSED Type: new-package Stage: 3 (Worker isolation infrastructure)

Why

Per design doc §5.2a, every capability ships a proxy surface. Logging is special in two ways: (a) it's bidirectional — the worker emits log calls that travel to the main-process logger AND the main host MAY disable worker logs during shutdown so the worker doesn't keep producing records after the sink closes; (b) it's high-volume — even with batching, a sustained worker producing a record per request can quickly saturate the proxy. So this package combines an outbound proxy (worker → main) with a control channel (main → worker, "stop logging now"), plus batching + backpressure to bound the wire cost.

What changes

Net-new package.

ADDED exports:

  • ./client — createLoggerProxyClient(port): WorkerProxyOf<LoggerLike>. info / warn / error / debug / trace calls marshalled to main; pre-tagged with plugin name + version per design doc §5.1.
  • ./batching — batched send (flushIntervalMs default 50, maxBatchSize default 100); flush on error / fatal level immediately. Bounded outstanding-batch budget enforces backpressure.
  • ./control-channel — main → worker disable / enable messages. After disable, worker-side log.info(...) becomes a no-op (records dropped); after enable, normal flow resumes. Used by main host during plugin shutdown so the worker stops producing during dispose.
  • ./error-codes — LOGGER_PROXY_* codes (LOGGER_PROXY_PORT_CLOSED, LOGGER_PROXY_BACKPRESSURE_DROP, LOGGER_PROXY_DISABLED).
  • ./testing — paired MessageChannel + stub main-side sink.

Out of scope

  • Concrete log sinks — @suaveplan/logger's job (and api-audit-* for audit records).
  • Trace correlation — span/trace ids are populated main-side; this package preserves them but doesn't compute them.
  • Log format — main-side logger owns the wire/disk format.

Source mapping

Genesis-original. Design doc sections: §5.2a (worker-RPC variants), §5.1 (pre-tagged logger in PluginContext), §12.3 (logs emitted on span close).

Requirement Design doc section
Proxy over LoggerLike from @suaveplan/logger §5.1
Bidirectional control channel (main can disable worker logs) Implied by §5.5 disposal (logger disposed last)
Batching to bound wire cost §5.2a (backpressure)
Pre-tagged with plugin name + version §5.1

Verification approach

  • src/client/client.test.ts — every level round-trips; record carries plugin tags.
  • src/batching/batching.test.ts — batch fills before flush interval → flush; idle → flush at interval; error level flushes immediately.
  • src/batching/backpressure.test.ts — bounded budget enforced; over-budget records emit LOGGER_PROXY_BACKPRESSURE_DROP counter rather than throwing (logging should be best-effort, not application-breaking).
  • src/control-channel/control-channel.test.ts — main sends disable → worker log.info becomes no-op; enable restores.

Migration impact

None — additive only.