summaryrefslogtreecommitdiffhomepage
path: root/notes
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-05 14:06:39 +0900
committerAdam Malczewski <[email protected]>2026-06-05 14:06:39 +0900
commit9ae09aad5d8d6232c55932af0d496b888166065f (patch)
treee62b59314ba065a531f9924151742c5adc7426ef /notes
parent6733bbd47e6df8681fcf4b6815f82bd6b6922334 (diff)
downloaddispatch-9ae09aad5d8d6232c55932af0d496b888166065f.tar.gz
dispatch-9ae09aad5d8d6232c55932af0d496b888166065f.zip
feat(observability): Phase A.2 — verbatim provider.request "after" capture + self-redaction (267 tests)
Threads the step span's correlated logger into provider.stream (new optional ProviderStreamOptions.logger) so provider-openai-compat opens a child provider.request span at the fetch edge, capturing the verbatim post-transform request + response status/cache-tokens/raw-error. Auth header self-redacted in the provider's OWN code (graduated mask tiers; no shared helper). Capture is fail-safe (never throws into the turn). Adds the first hermetic provider HTTP test (stream.test.ts: fetch mocked, 15 cases). Large payloads use attributes for now; the LogRecord.body channel is a deferred ABI design (notes §10). Verified: tsc -b clean, 267 tests (250->+17), biome 0 warnings/0 infos. Live boot: provider.request shares turnId with prompt:before (before<->after diffable); auth-key leak count = 0 (self-redaction proven on a real request).
Diffstat (limited to 'notes')
-rw-r--r--notes/observability-design.md32
1 files changed, 26 insertions, 6 deletions
diff --git a/notes/observability-design.md b/notes/observability-design.md
index 6c09099..5e9c3a7 100644
--- a/notes/observability-design.md
+++ b/notes/observability-design.md
@@ -583,12 +583,32 @@ finish) → Unit 2 → host-bin wiring**. Optional overlap: start Unit 2 once Un
**Verify:** `typecheck`/`test`/`check` clean; live boot → `host.logger` lines land in
the journal file.
-### Next step (after Phase A) — the "AFTER" capture
-`provider.request` verbatim post-transform (D5) inside `provider-openai-compat`: the
-exact serialized request bytes + raw response/error + cache tokens, auth header
-self-redacted. This **completes full round-trip rebuild** and unlocks the
-**before↔after diff** (kernel "before" vs wire "after" → pinpoints transform bugs).
-Depends only on the Phase A Logger ABI; lives entirely in the provider extension.
+### Phase A.2 — the "AFTER" capture (build plan)
+`provider.request` verbatim post-transform (D5) inside `provider-openai-compat`:
+exact serialized request + response status/cache-tokens/raw-error, auth self-redacted.
+Completes full round-trip rebuild + the **before↔after diff**.
+- **Contract (DONE, orchestrator):** `ProviderStreamOptions.logger?: Logger`
+ (`contracts/provider.ts`) — threads the step's correlated logger into `stream()` so
+ the `provider.request` span is a child of the step span (before↔after share
+ turnId/parentSpanId). Optional = non-breaking.
+- **Unit K — kernel run-turn** (owner: kernel): pass the step span's logger into
+ `provider.stream(msgs, tools, { ...opts, logger })`. One file (`runtime/run-turn.ts`).
+- **Unit P — provider-openai-compat** (owner): at the fetch edge, if `opts.logger`,
+ open a `provider.request` child span; capture the verbatim post-transform request
+ (URL, model, params, serialized body) + `cache_control` presence; on response,
+ status + cache-read/creation tokens (Usage) + (on error) raw error; **self-redact
+ the auth header in its own code** (graduated tiers, §6). First hermetic provider
+ HTTP test (`stream.test.ts`, mock `fetch` + real-capture fixtures).
+- **Order:** contract frozen (done) → Unit K ∥ Unit P (disjoint: kernel vs provider).
+
+**DEFERRED — body-channel ABI (design decision, surface to user):** `LogRecord` has a
+`body` field but the Logger/Span API exposes no way to set it — that's why
+`prompt:before` (and now the request/response) use stringified `attributes`. Storing
+large verbatim payloads in `body` (store-fat-serve-thin; both before & after) needs a
+small ABI addition (e.g. `Span.setBody(body)`), worth doing before Phase B's query
+layer. Until then captures use attributes — functional + reconstructable, just not
+ideal for D9 `GROUP BY`.
+
*(Full per-extension prompt-segment provenance — D8 — comes later, with the
context-filter chain.)*