summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.dispatch/rules/extension-logging.md24
-rw-r--r--ORCHESTRATOR.md9
-rw-r--r--tasks.md21
3 files changed, 50 insertions, 4 deletions
diff --git a/.dispatch/rules/extension-logging.md b/.dispatch/rules/extension-logging.md
new file mode 100644
index 0000000..14dce2d
--- /dev/null
+++ b/.dispatch/rules/extension-logging.md
@@ -0,0 +1,24 @@
+# Rule: extension logging + self-redaction
+
+Use the injected `host.logger` (and the `ctx.log` / span you're handed) — never
+`console.*`, never a hand-rolled logger, never your own correlation ids. `extensionId`
+is auto-stamped by the host: do NOT set it or invent an id scheme.
+
+- **Self-redact your OWN secrets, in your OWN code, at the log call-site.** There is no
+ shared `redact()` and nothing in the kernel does it for you — you alone know what is
+ secret and how to censor it (isolation over DRY). Mask BEFORE the record is built so a
+ raw secret never reaches the sink/journal.
+- **Redaction = partial mask, keep the field present + diffable** (never drop it): reveal
+ first+last real chars with a fixed-width `…redacted…` middle, graduated by length —
+ `≥13`→reveal 3 each side · `11–12`→2 · `8–10`→1 · `≤7`→full mask. Reimplement this
+ locally per extension; declare your own secret fields/headers (e.g. the `authorization`
+ header + any vault-injected body field).
+- **Edge I/O (providers/transports): capture verbatim, post-transform, at the fetch edge**
+ (the bytes that hit the wire = ground truth), self-redacting secret headers/fields. Put
+ large verbatim payloads in the span `body`, not in attributes.
+- **Attributes are flat scalars** (string/number/boolean/null) — nest → stringify. Spans:
+ open at the work's start, `end()` at its finish, so a crashed turn is reconstructable.
+- **Do NOT log token deltas / per-chunk streaming** — high-frequency and redundant (the
+ chunk log already has the final text).
+- **Logs are one-way**: never read another extension's logs; cross-feature reaction is a
+ hook/service, not a log. Logging never blocks or fails a turn — emit and move on.
diff --git a/ORCHESTRATOR.md b/ORCHESTRATOR.md
index 128823e..a20dab9 100644
--- a/ORCHESTRATOR.md
+++ b/ORCHESTRATOR.md
@@ -156,10 +156,11 @@ Keep it scoped (P6): state only the project-specific, non-inferable task — the
- **Kernel unit:** `kernel-purity.md` + `pure-core.md` + `no-internal-mocks.md`.
- **Pure-core unit:** `pure-core.md` + `no-internal-mocks.md`.
- **Any extension coupling via hooks/services:** `typed-handles.md`.
-- **Any extension that emits logs/spans (≈ all of them):** `extension-logging.md`
- *(pending — authored with the observability substrate, see
- `notes/observability-design.md` §9; keystone: each extension self-redacts its OWN
- secrets in its OWN code — NO shared redaction helper).*
+- **Every extension (≈ all of them — they all log):** `extension-logging.md`. Use the
+ injected `host.logger`/`ctx.log`; keystone: each extension self-redacts its OWN secrets
+ in its OWN code — NO shared redaction helper (design rationale:
+ `notes/observability-design.md` §9). Include this on EVERY extension summon (an
+ extension that never logs is a coverage gap, not an exemption).
- **Frontend units** are summoned from the SEPARATE `../dispatch-web` repo using ITS
OWN harness (`package-agent.md` + `frontend-*.md` rules) + ITS OWN scoping map — NOT
these backend rules. See that repo's `ORCHESTRATOR.md`.
diff --git a/tasks.md b/tasks.md
index a3b9faa..5e10126 100644
--- a/tasks.md
+++ b/tasks.md
@@ -283,6 +283,27 @@ independent of the SQLite trace-store; the lib is redaction-free (caller self-re
Summons: prompts/phase-a-{kernel-logging,journal-sink}.md;
reports/phase-a-{kernel-logging,journal-sink}.md.
+### Logging-coverage audit (post FE-Slice-2 backend work)
+The core turn round-trip is well-instrumented (kernel turn/step/tool-call/prompt spans +
+provider-openai-compat `provider.request` D5 capture + session-orchestrator per-turn childing).
+But a survey found per-extension/edge coverage thin, and a HARNESS gap as the root cause:
+- [x] **#3 ROOT CAUSE FIXED — `.dispatch/rules/extension-logging.md` authored** (was "(pending)"
+ in ORCHESTRATOR §3 for the whole substrate's life, so EVERY extension summon — incl. this
+ session's conversation-store/transport-http/transport-ws/credential-store — was built blind to
+ it). Rule now exists (self-redaction in own code, no shared helper, §6 tiers; use injected
+ `host.logger`/`ctx.log`; flat scalar attrs; no token-delta logging; one-way; edge verbatim
+ capture). ORCHESTRATOR §3 row updated to "Every extension — include on EVERY extension summon."
+ Future extensions now get logging guidance by construction.
+- [ ] **#1 INSTRUMENTATION DEBT — `reconcile.repair` span (conversation-store).** It has ZERO
+ logger refs and `createConversationStore(storage)` receives no logger, so a load-time history
+ repair (the §3.4 / bug-catalog "API rejected corrupted history" class) leaves NO trace. Inject a
+ logger + emit a `reconcile.repair` span. Address when conversation-store is next touched (or as
+ a dedicated pass).
+- [ ] **#2 INSTRUMENTATION DEBT — transport edges.** transport-http has 0 logger refs (a `/chat`
+ 500 / malformed request / the new `GET /conversations` read is invisible); transport-ws logs
+ minimally. Add request/error logging at the edges.
+- D8 `prompt.assembly` segments remain deferred-by-design (await the context-filter chain).
+
---
## ROADMAP — what's next (user-decided, §5.2)