# Task 1736 — retire `mcp-stderr-tee`

**Date:** 2026-07-17
**Task:** [`.tasks/1736-*.md`](../../../../.tasks/1736-nothing-sets-stream-log-path-so-the-per-conversation-mcp-stream-log-does-not-exist.md)
**Verdict:** retire the module, its call sites, and every doc claim that a per-conversation
MCP stream log exists.

## Why retire, precisely

`mcp-stderr-tee` reads `STREAM_LOG_PATH` and mirrors MCP stderr into a per-conversation
stream log. Nothing sets that variable, so `initStderrTee` takes its skip path on every
production spawn and 21 call sites wire up a no-op.

The reason to retire is **not** that the surface was deliberately retired elsewhere. It is
that `mcp-spawn-tee` already serves the same consumer, strictly better:

- It is a shim Claude Code runs *in place of* each server's entry, so it replaces
  `process.stderr.write` before the real entry imports. It captures every stderr byte with
  no cooperation from the plugin — including module-load throws that happen before
  `initStderrTee` would ever run.
- It writes `${LOG_DIR}/mcp-<name>-<SESSION_ID>.log`, keyed by session, which *is* the
  conversation. Per-conversation correlation already exists.
- It handles the sessionless spawn explicitly (`mcp-<name>-nosession.log`).
- It uses `appendFileSync`, which survives an immediate `process.exit`. `mcp-stderr-tee`
  used an async `createWriteStream`, which does not.

The two modules were never complements. Task 1721's docstring records that they wrote the
same file and "every line landed twice". 1721 removed the duplication and left the loser
holding a destination nothing produces.

## Correcting the record

The task file and its LANES row currently argue "retire because Tasks 287 and 288
dismantled the consumers." That is wrong and it is load-bearing: 287 retired the
`script_stream` surface — shell scripts narrating phase lines into the chat UI. It never
evaluated the MCP-stderr surface, whose consumer is an investigator grepping a file. That
consumer still exists; it is simply already served by `mcp-spawn-tee`.

Both records get the correct argument before the task archives. A wrong reason in an
archive licenses the next removal that the reason does not actually cover.

## What changes

**Code.**

- Delete `platform/lib/mcp-stderr-tee/`.
- Delete its two `tsc -p lib/mcp-stderr-tee/tsconfig.json` entries in
  `platform/package.json` (`build` and `build:lib`).
- Remove 21 `initStderrTee` call sites — 20 plugin MCP entry files plus
  `platform/lib/graph-mcp/src/index.ts`. Each loses one import line and one call line.
  `graph-mcp` is inside the 21, not additional. Premium plugins have zero call sites.
- Remove the now-dead `STREAM_LOG_PATH` assignment in
  `platform/plugins/memory/mcp/scripts/boot-smoke.sh` — the last assignment in the tree once
  the reader is gone.
- Strip the dangling module name from two comment-only references
  (`services/claude-session-manager/src/rc-daemon.ts`, `platform/scripts/logs-rotate.sh`).
  Neither imports it.
- `packages/create-maxy-code/scripts/bundle.js` names `mcp-stderr-tee` as the example in its
  lib-rewrite comment. The mechanism is generic and collects whatever premium plugins import;
  the example just becomes stale. Point it at a lib that is actually imported.

**Docs.** `platform/plugins/docs/references/plugins-guide.md` loses every claim that a
per-conversation MCP stream log exists:

- the `initStderrTee` opt-in block;
- "How the tee decides which file to write to" (asserts the platform sets `STREAM_LOG_PATH`
  on every spawn — it does not);
- the tee-state markers paragraph (`[mcp-tee-attach]` / `[mcp-tee-skip]` /
  `[mcp-tee-detach]` "land in the stream log");
- the `logs-read { type: "system", sessionId }` → grep `[mcp:<name>]` retrieval instruction;
- the "Main-subprocess stderr" `[subproc-stderr]` paragraph, which describes the retired
  admin server;
- the `initStderrTee` `createWriteStream` write-race paragraph in the failure-path contract;
- the premium-plugins paragraph's `mcp-stderr-tee` bundler example.

What replaces them is short: a plugin author calls `console.error`; the `mcp-spawn-tee` shim
captures every byte into `mcp-<name>-<SESSION_ID>.log`; that file is where you grep. No
import, no call, no opt-in.

`platform/plugins/admin/skills/platform-architecture/SKILL.md` is generated from that
reference doc. Regenerate it; never hand-edit.

## Scope boundaries

**In scope:** the `STREAM_LOG_PATH` contract — the module, its 21 call sites, its build
entries, and the doc claims asserting the surface works.

**Out of scope:**

- The raw sink. Task 1721 settled it and this removal does not touch it.
- `mcp-spawn-tee`'s stale marker names (`[mcp-spawn-tee-attached]` / `[mcp-spawn-tee-exit]`,
  retired by Task 989 in favour of `[mcp-helper] op=spawn|boot|exit`) and its stale
  two-process description. Owned by [`.tasks/1737-*.md`](../../../../.tasks/1737-mcp-spawn-tee-docs-promise-lifecycle-markers-and-a-child-process-that-task-989-removed.md),
  which is designed but unimplemented on branch `worktree-task-1737-mcp-spawn-tee-docs`.
  This sprint must not restate those names when it points authors at the sink, or it
  re-introduces the exact bug 1737 exists to fix.
- Reviving `claude-agent-stream-<id>.log` or any per-conversation agent stream file. It has
  no producer in the PTY architecture; building one is a different task with a different
  premise.
- The `script_stream` surface and its banned-token gate. Tasks 287/288 settled it.

## Open question to resolve during implementation

`logs-read { type: "system", sessionId }` is documented as returning the conversation's
stream log for `[mcp:<name>]` grepping. The doc instruction goes either way — grepping
`[mcp:<name>]` on that log returns nothing, so the sentence is false regardless of what the
tool does internally.

What is unresolved is the *tool*. If `logs-read type:"system"` reads a file with no producer,
that is a live bug and not this task's doc defect. Establish which during implementation. If
it is a bug, file a `.tasks/NNN-*.md` for it — deleting the doc sentence while leaving a tool
that silently returns nothing would hide the same false-clean shape one layer down, which is
the exact failure this task exists to close.

## Verification

- `npm run build` at `maxy-code/platform/` is clean after the tsconfig entries are removed.
- The 21 affected MCP packages typecheck.
- `initStderrTee` and `mcp-stderr-tee` return zero hits outside `.tasks/archive/` and
  `docs/superpowers/specs/`.
- The architecture-skill regeneration gate passes with the regenerated `SKILL.md` committed.
- `STREAM_LOG_PATH` returns zero hits in the live tree.

## Deployment

Every one of the 21 call sites ships in the bundled `create-maxy-code` payload, so this
requires an installer publish to reach the fleet. Per the sprint gating rule, publish awaits
an explicit operator command.
