deepseek-harness/docs/postmortem/README.md

15 lines
1.5 KiB
Markdown
Raw Normal View History

fix(acp): server crashed on connect — drop `export default`, read optional service cwd-independently Two independent bugs made the ACP server crash the moment an editor (Zed) connected, despite 178 green unit tests at 100% coverage: 1. `session/new` threw `cannot get property "agents" without inject`. Root cause: a stray `export default apply` made the cordis Loader's `unwrapExports` (`exports.default ?? exports`) collapse the module to the bare `apply` function, discarding the sibling `inject`/`name`/`Config` named exports. The plugin fiber was built with empty `inject`, so every `ctx.<service>` read in `apply` threw at load. Fix: remove the default export so the Loader uses the namespace. 2. `session/load` threw `cannot get property "sessionPersistence" without inject`. `AgentLoop.resume` read `this.ctx.sessionPersistence` (a service it deliberately does NOT inject); the property proxy's ancestor-only fiber walk fails through the bridge's traceable shadow. Fix: read it via `this.ctx.get('sessionPersistence', false)`, the topology-independent global-store lookup. Why the suite missed both: every test mounted the plugin by hand (`ctx.plugin({name,inject,apply})`), bypassing `unwrapExports` entirely, and the only test driving these RPCs was key-gated (skipped in CI). Added a no-key `session/new` e2e that boots the real example through the real Loader — it fails loudly on bug #1 without an API key. Set `TSX_TSCONFIG_PATH` in the e2e spawn so the subprocess resolves workspace `paths` from a temp cwd (it was silently falling back to a stale built `lib/`). Docs: post-mortem 0001; AGENTS.md "line coverage is not behavior coverage" + with-key/smoke-test philosophy; packages/AGENTS.md plugin-export-shape and ctx.get rules; dsh-code-review SKILL checks.
2026-06-18 03:12:37 +08:00
# Post-mortems
Incident write-ups: a bug reached a place it shouldn't have (a real user, a merged PR, a release), and the interesting part is *why our process let it through*, not just the one-line fix.
2026-07-19 22:50:49 +08:00
A post-mortem is NOT an [Agent Note](../../.agents/notes/README.md) (which records a deliberate design decision and its rejected alternatives, or proposes future work). It is a backward-looking record of a failure: what broke, the mechanism, why every safety net missed it, and the concrete guardrails added so the same class of bug fails loudly next time.
fix(acp): server crashed on connect — drop `export default`, read optional service cwd-independently Two independent bugs made the ACP server crash the moment an editor (Zed) connected, despite 178 green unit tests at 100% coverage: 1. `session/new` threw `cannot get property "agents" without inject`. Root cause: a stray `export default apply` made the cordis Loader's `unwrapExports` (`exports.default ?? exports`) collapse the module to the bare `apply` function, discarding the sibling `inject`/`name`/`Config` named exports. The plugin fiber was built with empty `inject`, so every `ctx.<service>` read in `apply` threw at load. Fix: remove the default export so the Loader uses the namespace. 2. `session/load` threw `cannot get property "sessionPersistence" without inject`. `AgentLoop.resume` read `this.ctx.sessionPersistence` (a service it deliberately does NOT inject); the property proxy's ancestor-only fiber walk fails through the bridge's traceable shadow. Fix: read it via `this.ctx.get('sessionPersistence', false)`, the topology-independent global-store lookup. Why the suite missed both: every test mounted the plugin by hand (`ctx.plugin({name,inject,apply})`), bypassing `unwrapExports` entirely, and the only test driving these RPCs was key-gated (skipped in CI). Added a no-key `session/new` e2e that boots the real example through the real Loader — it fails loudly on bug #1 without an API key. Set `TSX_TSCONFIG_PATH` in the e2e spawn so the subprocess resolves workspace `paths` from a temp cwd (it was silently falling back to a stale built `lib/`). Docs: post-mortem 0001; AGENTS.md "line coverage is not behavior coverage" + with-key/smoke-test philosophy; packages/AGENTS.md plugin-export-shape and ctx.get rules; dsh-code-review SKILL checks.
2026-06-18 03:12:37 +08:00
Write one when a bug is **subtle** (the mechanism is non-obvious and a careful engineer would re-derive it the hard way), **systemic** (the reason it escaped is a gap in tests/tooling/conventions, not a one-off typo), and **costly to rediscover** (it cost real debugging time, and would cost it again). Link the guardrails (tests, AGENTS.md rules, ADRs) the post-mortem motivated.
Every post-mortem opens with an **Executive summary**: one short paragraph a busy reader can absorb in thirty seconds — what broke, the root cause in plain terms, why it escaped, and the durable lesson — before the detailed Summary / Timeline / Root cause / Guardrails sections that follow.
| # | Title |
|---|---|
| [0001](0001-acp-default-export-drops-inject.md) | ACP server crashed on connect: `export default` dropped the plugin's `inject` |
| [0002](0002-js-expression-disabled-filesystem-tools.md) | Filesystem snapshot tools were permanently disabled by a literal `!!js` object |