Files
deepseek-harness/docs/rfc/implemented/process/2026-07-12-package-model-experience-contract.md
T

7.3 KiB

RFC: Package Model Experience contract

Status: implemented

Problem

A package README can explain APIs and runtime mechanics without answering the question that dominates an agent harness's behavior and cost: what from this package reaches a model request, under which conditions, and how long those tokens remain. The omission is especially hard to audit in a plugin architecture. A consumer may turn a backend result into a tool message, a policy plugin may replace success with an error, compaction may remove old history, and an agent-scoped registration may change one agent's prompt or schemas while leaving every other agent unchanged. Reading only the nominally model-facing packages therefore misses real context effects, while reading source across every dependency is too expensive for routine review.

Decision

Every workspace package README ends with the canonical Model Experience section, immediately before ## Known Limitations and Deferred Work; a package on the no-limitations allowlist ends with Model Experience itself. Packages with direct, multi-surface, conditional, capped, or lifetime effects use one H3 block per context surface. Each block says what the relevant model literally receives and when under **What the model sees**:, then classifies the token effect under **Token effect**:; the structured section grounds at least one surface with inline code, a nested markdown block, or an anchored catalog link. Every stable system-prompt paragraph, including a one-liner, follows those fields inside the owning H3 as a titled H4 plus markdown fence; the H3 title contains system prompt. Other short stable source literals remain inline with named placeholders only for interpolated values; other long non-generated literals use the same nested H4 form. Model Experience subsections do not link to each other because physical nesting owns the literal. Tool-schema surfaces use schema in their H3 and link the relevant anchored package section of the generated tool schema catalog rather than copying default descriptions or JSON Schema, then state only configuration or composition deltas absent from that catalog. A runtime-only definition outside the catalog's stated scope links that scope and explains the exception before reproducing its stable text. Summaries are reserved for data-dependent payloads and provider-owned text. The default subject is the conversation model; a package that invokes an auxiliary model, such as a summarizer or search provider, names that request separately. Agent-scoped visibility is stated where it changes which agent receives a contribution. Prompt text and tool schemas are described separately whenever configuration or scoping can hide one without the other.

Every package participates. A package with no model-context effect, or one simple effect rendered entirely by another package, uses the verifier's audited sentence allowlist. It carries exactly one sentence beginning None, as or Indirectly, through instead of expanding a negative fact into a structured block. Pure routing, transport, and keyless test-support packages use the none form when they create no model-bound content. A provider backend whose single context path is formatted and inserted entirely by a named consumer uses the indirect sentence even when it caps or filters data before returning it; wiring bundles do the same when every model effect belongs to named children. The indirect form names that consumer only to locate this package's contribution and does not restate the consumer's implementation. Structured blocks likewise document only package-owned inputs, transformations, and deltas. Packages that own model input, output shaping, multiple context paths, or an auxiliary request keep context-surface blocks even when they add zero direct prompt tokens.

verify-package-readme-model-experience discovers packages from packages/*/*/package.json, requires one sibling README and the canonical final-section order, and validates one of two package-classified bodies. An allowlisted package carries exactly one sentence with its assigned prefix; every other package carries at least one H3 context surface with the two exact, non-empty fields and one blank line between each element plus at least one inline literal, nested block, or catalog link across the section. Optional verbatim literals follow those fields inside that surface, each as an H4 title paired to one non-empty markdown fence. Local subsection links are rejected, every system-prompt surface requires at least one nested block, and every tool-schema surface links an existing H2 section in the generated tool catalog. The check runs in doc-sync and the parallel gate runner. It owns package classification, structural presence, concrete-literal evidence, nested-block shape, catalog-link shape, and order; implementation review owns coverage, link relevance, and the truth of the prose.

Alternatives considered

  • Document only packages that register prompts or tools — rejected because backends, policy plugins, adapters, persistence, scoping, and compaction change the content or lifetime of tokens without owning a model-facing schema.
  • Generate one central context-cost catalog from source — rejected because an AST can find registrations but cannot infer semantic conditions such as history retention, output truncation, parent-versus-child visibility, or an auxiliary model boundary. The package README is the implementation-local contract; a central copy would add another drift surface.
  • Require numeric token counts — rejected because exact counts depend on the selected model tokenizer, adapter serialization, configuration, and runtime data. The stable contract is the growth shape: fixed per request, conditional per call, retained, replaced, capped, or zero-direct.
  • Use a three-column table — rejected because exact source text and conditional result shapes make cells dense and difficult to scan. Repeated subsections give each context surface readable vertical space while preserving the same fields.
  • Allow zero-impact packages to omit the section — rejected because absence is ambiguous between an audited zero and forgotten documentation. One explicit sentence is cheap and mechanically distinguishable.
  • Require the full structured form for audited zero or simple indirect packages — rejected because it repeats labels around one fact. A gated sentence preserves explicit coverage without the ceremony.
  • Convention without a gate — rejected because a repo-wide contract must also cover every future package; review memory cannot reliably detect an omitted README section.

Consequences

A reviewer can start at any package and see its contribution to the conversation model, child models, and auxiliary calls without reconstructing the full plugin graph. Token-budget work can distinguish repeated request overhead from data-dependent history, and agent-scoped changes have an explicit documentation checkpoint. Package authors maintain one or more compact context-surface blocks or one classified sentence whenever model-visible behavior changes. The structured fields do not promise provider-exact token counts; measurements remain model- and workload-specific, while the documented growth and visibility contract stays stable.