Files
deepseek-harness/docs/rfc/README.md
Tianyi Cui 0ebb86e70f Implement mandatory app-attribution headers per the RFC
dsh-llm owns the vocabulary (attribution.ts): AppIdentity with the version
read from the package manifest, userAgent(), and attributionHeaders(target,
identity) over a closed AttributionTarget union ('generic' | 'openrouter').
Both adapters send the headers on every provider request — llm-deepseek in
its fetch headers, llm-pi-ai through pi-ai's StreamOptions.headers — behind
an explicit attributionTarget config (never inferred from baseURL), with
mock-server tests asserting exact wire arrival and the absence of the
OpenRouter set by default.

The RFC moves to implemented/ amended with the settled identity (the
deepseek-harness token, the DeepSeek Harness title, the planned
deepseek-ai/deepseek-harness-sdk URL behind a FIXME until that repo exists)
and the explicit-config OpenRouter decision.
2026-07-04 18:14:42 +08:00

19 KiB

RFCs

One kind of design doc lives here. An RFC records a decision or proposal that shapes this codebase — the why and what we gave up, the parts code and docs can't carry. (Earlier this split into separate "ADR" and "RFC" trees; they were unified, since most ADRs were simply implemented RFCs.)

Layout and naming

Every RFC has two axes, both encoded in its path{lifecycle}/{class}/yyyy-mm-dd-topic-title.md:

  • Lifecycle (the top-level folder) is the RFC's status, and an RFC moves between folders as that status changes:
    • proposed/ — proposals reviewed before implementation; not yet built (or only partly).
    • implemented/ — the decision shipped. The file records what was decided and what was rejected, and is kept current with what actually shipped: when the code later moves a file, renames a package, or changes a key/default, the RFC is updated in the same change to match (facts only — paths, names, structure — not the decision itself). See implemented/AGENTS.md.
    • rejected/ — the proposal was considered and declined. Kept for the record so the rejection isn't re-litigated.
  • Class (the nested folder) is the kind of decision — see Classification below.

The date in the filename is when the topic was first proposed (per git history). Cross-references between RFCs use relative markdown links ([topic](../../implemented/architecture/2026-…-….md)) — never bare prose or numbers — so they are mechanically checkable and survive moves between folders.

Classification

Each RFC is filed under exactly one class — the kind of decision it records. The class is encoded in the path (the folder is the label, so a file's location declares its class) and the set is closed: scripts/verify-rfc-classification.ts rejects any folder outside the set and asserts this index lists every RFC under the heading matching its path. Adding a new class means amending that gate and this section, not just dropping a new folder. See the classification RFC for why the taxonomy is path-encoded and gated.

Class What it covers
feature A new user- or model-facing capability.
bug-fix Corrects a defect or closes a gap a postmortem surfaced.
simplification Removes code, behavior, or surface area without adding a capability.
architecture A structural decision about the shipped source — how packages relate, what the runtime vocabulary is.
process Tooling, policy, or workflow around the code — gates, the package manager, vendoring — not runtime behavior.
testing Test infrastructure and strategy.

The architecture / process line: architecture is about the source we ship; process is the surrounding tooling and workflow. (refactor is deliberately absent — it overlaps simplification, whose discriminator, "does observable behavior change?", already covers it.)

When to write one

Write an RFC when a decision is durable (it shapes the codebase beyond a single function or package), contested (there was a real alternative a reasonable engineer might have chosen), and surprising (a future reader would otherwise ask "why on earth is it done this way?"). A proposal for substantial future work starts in proposed/; a decision already made starts in implemented/. Pick the class folder that matches the decision (see Classification).

Do NOT write one for a mechanical or local choice (a variable name, a one-file refactor), for anything already enforced and explained by a gate or a convention in AGENTS.md, or for a still-provisional decision tagged TODO(...) in the code — record those as TODOs and promote to an RFC only once they settle. An RFC is never edited into a different decision: supersede it with a new one and cross-link. (Editing an implemented/ RFC to track where its already-made decision now lives — a moved file, a renamed package — is not a different decision and is required, not forbidden; see implemented/AGENTS.md.)

Proposed

Feature

Title First proposed
Agent Client Protocol (ACP) support for external editors 2026-06-14
Multiplex concurrent ACP sessions over one connection 2026-06-14
Optional Code Mode — model writes TypeScript against an SDK of all tools 2026-06-15
Pre-tool input rewrite — a consistent design 2026-06-30

Simplification

Title First proposed
Unify the agent id and the session id 2026-06-20
Prune producer-less vocabulary variants (block cache hints, the agent message source, the continuation turn trigger) 2026-07-04
Drop GenerateOptions.prefill and ToolSchema.strict — request knobs with no working end-to-end path 2026-07-04
Drop the unconsumed web observation surface — the providers-change event and the status methods 2026-07-04
Drop the image content block until a path can honor it 2026-07-04
Prune write-only fields and a dead routing knob from the fs seam 2026-07-04
Trim unreachable ACP bridge surface — the branding knobs and the kind-sniffing fallback 2026-07-04
Prune dead core-spine surface — SurfaceManager.invalidate(), the loop-internal exports, ToolExecutionResult.callId 2026-07-04
Share the app bins' boot glue instead of maintaining twin copies 2026-07-04
Remove the agent/steering mirror emit 2026-07-04
Tighten the hook-protocol contract — dialect, discarded fields, double defaults, and lib-owned hook/result semantics 2026-07-04
Fold the stdio UI helper into the stdio app 2026-07-04

Architecture

Title First proposed
Runtime schemas for the event vocabulary (Zod vs the merge-extensible-map pattern) 2026-06-16
Extract a generic long-running tool runtime 2026-06-20

Process

Title First proposed
Architectural conformance — dependency rules and the adapter kit 2026-06-11
API extractor reports 2026-06-11
Supply chain checks and vendor drift verification 2026-06-11
Discover package inventories instead of maintaining static lists 2026-06-20
Generate the RFC index tables 2026-07-04

Testing

Title First proposed
Mutation testing as the coverage counterweight 2026-06-11
Deterministic tests, the replay invariant fixture, and race stress 2026-06-11
Single-source the acp-agent replay config 2026-07-04

Implemented

Feature

Title First proposed
Filesystem tool schemas — model-facing read/write/edit shapes 2026-06-17
Rich ACP bash rendering — the terminal card (_meta) and command classification 2026-06-18
Compaction as a capability seam (abstract contract + basic backend) 2026-06-18
Subagent capability seam 2026-06-21
ACP subagent backend (out-of-process delegation) 2026-06-22
The todo_write tool — model task list as event-sourced session state 2026-06-29
Interception seams — the typed-Decision surface a hook programs against 2026-06-30
Subagent lifecycle enrichment — lastAssistantMessage (observe-only) 2026-06-30
dsh-hook-protocol — the shared Claude Code / Codex hook wire-protocol core 2026-06-30
dsh-hooks-claude + dsh-hooks-codex — the Claude Code / Codex hook bridges 2026-06-30

Simplification

Title First proposed
Drop the mutable session summary 2026-06-19
Drop unconsumed assembled LLM convenience surfaces 2026-06-20
Drop the unconsumed llm/adapter-change event 2026-06-20
Prune dead methods from the persistence seam 2026-06-20
Keep one public stop primitive 2026-06-20
Fold trace-only session facts into load-bearing events 2026-06-20
Stop mirroring durable boundaries as agent events 2026-06-20
Split the filesystem seam — provider text mutations plus the dsh-fs-policy plugin 2026-06-26
Stop mirroring the token stream as an agent event 2026-07-02

Architecture

Title First proposed
Microkernel: extension via Cordis event taxonomy, one concrete loop 2026-06-11
Event-sourced sessions with derived message history 2026-06-11
Provider-neutral content-block vocabulary owned by dsh-llm 2026-06-11
Custom typed tool-schema DSL instead of schemastery 2026-06-11
Tool schemas are part of the system-prompt assembly 2026-06-11
Runtime arg validation at the model boundary 2026-06-11
Dev-mode invariants over compile-time deep-readonly 2026-06-11
Structured error taxonomy 2026-06-11
Capability seams — interface / implementation / consumer split 2026-06-13
Two LLM adapters as a design-verification twin 2026-06-13
Session persistence as an abstract service over SessionEvent 2026-06-14
Every session event is enclosed in a turn 2026-06-15
Filesystem capability seam — ctx.fs, local backend, and model-facing filesystem tools 2026-06-17
Shared persistence write coordinator 2026-06-18
Agent lifecycle and ownership seams 2026-06-18
Session surface — a linked list over the event log for LLM message derivation 2026-06-18
Reorganize packages into a modular hierarchy 2026-06-20
Branded IDs everywhere they belong 2026-06-20
Extract example apps into packages 2026-06-20
Mandatory app-attribution headers for provider requests 2026-06-21
Web capability seam — provider registry and model-facing web tools 2026-06-24
Make dsh-fs-policy an event-gate plugin, not a method interface 2026-06-26
Event-domain semantics — session is the fact log, agent is the live surface 2026-06-30
stdin + extra env on the bash seam 2026-06-30
Resolve filesystem paths against the caller's session cwd 2026-07-02
Tagged render-intent union for tool-call presentation 2026-07-02
Result-time applied-hunk diffs for file mutations 2026-07-02
Add direct directory listing to the filesystem seam 2026-07-03

Process

Title First proposed
Vendor Cordis as source, not npm dependencies 2026-06-11
Mechanical quality gates over prose guidelines 2026-06-11
tsdown for JS bundling instead of dumble 2026-06-11
Doc-sync enforcement 2026-06-11
pnpm as the package manager instead of Yarn 4 2026-06-16
TSC-first build and one tsconfig 2026-06-17
Markdown cross-link validity linting 2026-06-18
Core-data-structures catalog and the ts type-equiv drift gate 2026-06-20
Generated cordis events + services catalog 2026-06-20
Classify RFCs by kind via path-encoded subdirectories 2026-06-20
Generated tool-schema catalog (boot-and-harvest) 2026-07-02
Bilingual documentation via paired sibling files and a pairing gate 2026-07-02

Testing

Title First proposed
Property-based testing for protocol-shaped code 2026-06-11
ACP snapshot tests — record-once / replay-deterministic 2026-06-19
Real-API e2e in CI against the external DeepSeek API 2026-06-19
Use session.jsonl as the only snapshot session-log artifact 2026-06-20
Per-session snapshot replay for nested agents 2026-06-22
Persist the seed boundary so fork-child replay routes correctly 2026-06-22
Record fork and mixed spawn+fork snapshot scenarios 2026-06-22
Hook snapshot matrix — end-to-end goldens for both bridges 2026-07-04

Rejected

Simplification

Title First proposed
Persist assembled assistant messages, not stream chunks 2026-06-20
Drop ACP session/load until resume has a product shape 2026-06-20
Drop ACP terminal _meta rendering 2026-06-20
Drop bash full-output spill files 2026-06-20
Drop durable step boundary events 2026-06-20
Drop unused session lineage metadata 2026-06-20
Fold the persistence interface into dsh-session 2026-06-20
Collapse tool-owned UI presentation 2026-06-20
Retire mid-turn steering 2026-06-20
Return the ACP bridge to one live session per connection 2026-06-20
Truncate interrupted final turns on load 2026-06-20
Prune the unimplemented subagent seam vocabulary 2026-07-04

Architecture

Title First proposed
Deep-readonly public surfaces 2026-06-11
Make the shared example base providerless 2026-06-20