Prompt Assembly and Context Injection
Section registry, ordering, scope, tool schemas, and dynamic context
Conclusion: the prompt is a per-step, log-reconstructable program
DeepSeek Harness does not build one system string at session creation. For every proposed model step it re-evaluates scoped prompt providers, canonicalizes visible tool schemas, projects volatile runtime facts, admits sourced context messages, and records the effective request state before model I/O.
This architecture serves three goals at once: per-Agent composition, deterministic replay, and provider prefix-cache stability. It deliberately separates stable instructions from volatile facts rather than calling every model-visible input a “system prompt.”
1. Four model-input lanes with different semantics
| Lane | Representation | Durable form | Typical content | Change behavior |
|---|---|---|---|---|
| Stable instructions | PromptSection[] | request/header.system | Harness identity, persona, plan policy, tool usage guidance | Re-render every step; write a new full header only when the value changes |
| Tool interface | ToolSchema[] | request/header.tools | Names, descriptions, JSON parameters visible in this Agent scope | Canonicalize every step; compare in order |
| Volatile runtime facts | PromptContext[] | Sourced user/message snapshot | Sandbox mode, approval policy, delegation status | Append only when the complete rendered snapshot changes |
| Context plugins | agent/pre-step messages | Sourced user/message | Workspace instructions, current time, tmux location, explicit session references | Each plugin owns refresh, ordering, bounds, and provenance |
All four lanes become reconstructable before provider dispatch: stable system/tool state is captured in a request-header event, while message-shaped context enters the session surface. The Loop's invariant rebuilds both sides from the log. ROOT-MODEL-LOGGEDLOOP-REQUEST-INVARIANT
2. The registry has five reversible contribution types
SystemPrompt effect-registers sections, contexts, runtime-context suppressors, tool-schema providers, and variables. Registration and disposal emit system-prompt/change; duplicate named entries are rejected within one layer. The built-in identity at order -100 and deployment persona at order 0 are ordinary sections, not hidden string concatenation. PROMPT-REGISTRY-CONTRACT
| Registration | Identity | Evaluation | Teardown |
|---|---|---|---|
section({name, order, text, complete?}) | Name unique per layer | Text provider once per assembly; interpolation later | Exact entry removed with owner effect |
context({name, order, text}) | Name unique per layer | Skipped entirely when runtime context is suppressed | Exact entry removed |
suppressRuntimeContext() | Anonymous independent token | Any active suppressor in the scope chain wins | Only that suppressor is removed |
tools(provider) | Anonymous provider membership | Returns visible schemas and optional pre-restriction known names | Provider removed |
variable(name, provider) | Strict lower-case identifier | Resolved once per assembly; may yield undefined | Exact name removed |
The default Agent Loop contributes three variables—provider, model, and cwd—from the Agent and its Session. This is an ownership choice: templates reference values, while the runtime objects remain their source.
3. Scope shadowing occurs before provider evaluation
The assembly context sets both agent and scope to the same Agent. Global contributions are overlaid by the farthest ancestor and finally by the nearest scope; the nearest same-name section, context, or variable wins. DESIGN-SCOPED-LAYERSPROMPT-STEP-BOUNDARY
A scoped deployment:persona replaces the global persona only for that scope. More importantly, the shadowed global text provider is never evaluated—the effective map is formed before text execution. PROMPT-SCOPE-SHADOW
Shadowing is not post-render string replacement. It is provider selection. A child Agent can inherit its parent's composition and override one named slot without executing the displaced provider or copying the entire prompt.
4. One assembly has a precise evaluation order
Resolve scope and suppression
Collect the existing scope chain and determine whether any global or inherited suppressor is active.
Evaluate variables
Global first, then ancestor-to-nearest scope; a nearer provider overwrites the same name.
Materialize named entries
Build effective section and context maps before text providers run.
Snapshot tool providers
Freeze membership for this generation, invoke each provider, and clone schema parameters.
Order and evaluate
Stable-sort sections/contexts by numeric order, enforce at most one complete section, evaluate text, and canonicalize tools.
Run expert waterfall
system-prompt/assemble may cooperatively rewrite the mutable assembly or short-circuit it.
Reapply hard constraints
Restore the original evaluated complete section as the sole section and/or clear contexts when suppression is active.
This order is implemented directly in assemble(). Registry output is deterministic before the waterfall; the returned waterfall value is authoritative except for complete-section and suppression enforcement. PROMPT-ASSEMBLY-PIPELINE
Tool-provider membership is snapshotted, so a provider registered during provider evaluation appears only in the next assembly. Variables use live map iteration, so a variable registered by an earlier variable provider may appear in the same generation. Every assembly receives fresh section/context/tool containers, and tool parameters are structurally cloned. PROMPT-ASSEMBLY-MUTATION-TESTPROMPT-VARIABLE-TEST
5. complete narrows sections, not the entire assembly
One effective complete: true section does not skip variable, context, tool, or waterfall work. The waterfall still runs; afterward the registry restores the originally evaluated complete section as the sole section. Two effective complete sections make assembly fail. PROMPT-ASSEMBLY-PIPELINEPROMPT-ASSEMBLY-MUTATION-TEST
- A listener cannot replace the complete section's name or evaluated text.
- A listener can still alter variables, contexts, and tools.
- Because interpolation happens later, changing a referenced variable can still change the final rendered complete template.
- Runtime-context suppression clears contexts even if a waterfall re-adds them.
complete is a section-authority mechanism, not an unmodifiable whole-request mode. That preserves tool and context composition, but callers requiring a byte-fixed complete prompt must also control the variables it references.
6. Rendering is strict and intentionally late
Section and context providers return uninterpolated text. Rendering accepts only {{name}} where the name matches [a-z][a-z0-9_]*; unknown, undefined, and malformed references fail loudly. Own-property lookup blocks prototype names unless explicitly registered, replacement values are not scanned again, empty entries disappear, and sections join with blank lines. PROMPT-STRICT-RENDERPROMPT-VARIABLE-TEST
| Failure point | What has already been logged | What never happens |
|---|---|---|
| Assembly/provider/tool-order failure | turn/start | No step/start, request header, or adapter call |
| Runtime-context interpolation failure | turn/start | No step opens |
agent/pre-step rejects | turn/start; claimed inbox splice is already durable | No step or model request; Turn ends blocked |
| Initial accepted message list is empty | Turn boundary and claim effects | No step; Turn ends completed |
| System-section interpolation failure | turn/start, step/start, entered user messages | No request dispatch; step/end still closes in finally |
Cancellation or owner disposal while assembly is blocked produces an aborted Turn with no step, chunks, assistant message, or adapter request. PROMPT-CANCEL-TEST
7. Tool schemas are part of prompt determinism
Without explicit configuration, schemas use locale-independent code-unit lexicographic order. An explicit toolOrder must contain exactly one <unlisted-tools> marker and no duplicates. Unknown configured names fail; names known before scope restriction may be absent; unlisted visible tools sort within the marker position. PROMPT-TOOL-ORDER
Schema visibility and usage guidance are distinct registrations. Hiding a tool schema does not automatically remove an independently contributed section that discusses that tool. A package requiring those to move together must make its guidance provider inspect current scope visibility.
Canonical order is simultaneously a correctness, replay, and cache concern. The registry guarantees order before the expert waterfall; a listener that adds or reorders tools afterward owns the resulting determinism. Equal-name schemas are not silently deduplicated at this layer.
8. Assembly frequency: per proposed step, not per retry
preStep() claims inbox input, assembles the scoped prompt, renders dynamic context, proposes a snapshot, and then enters agent/pre-step. Only an accepted, non-empty decision opens step/start and carries that assembly into step(). PROMPT-STEP-BOUNDARY
step() renders the system string once before its provider-retry loop. A retry rebuilds request config, folded header, and durable messages in the same numbered Turn and Step, but reuses that Step's system and tool assembly. PROMPT-RETRY-BOUNDARYLOOP-REQUEST-ERROR-TEST
| Event | Reassemble? | Why |
|---|---|---|
| Tool result requires another model request | Yes | It is a new proposed Step; scoped providers and runtime facts may have changed |
| Next user Turn | Yes | Every proposed Step evaluates current composition |
| Provider error with retry action | No | Retry is another request attempt inside the same Step |
| Prompt registration changes after assembly | Next Step only | The current Step already owns a detached assembly snapshot |
| Rejected or empty proposed Step | Already assembled | Admission is decided after assembly and dynamic-context projection |
9. Dynamic runtime context is an append-only full snapshot
Runtime context renders named sections under a fixed “supersedes earlier snapshots” preamble. RuntimeContextProjection restores the newest owned snapshot still retained in the Session surface, proposes a sourced user message only when the complete value changes, emits an explicit clear marker when context disappears, and forgets a snapshot when a replacement removes it from the surface. LOOP-RUNTIME-CONTEXT
No prior value + empty current value
Emit nothing; there is no old semantic state to clear.
Same retained text
Emit nothing; history already carries the effective snapshot.
Changed non-empty value
Propose one full user-role snapshot with plugin and named-section provenance.
Value becomes empty
Propose a clear tombstone so an older snapshot no longer remains semantically active.
Compaction replaces retained snapshot
Reset retained state; the next Step reasserts current context even if its text did not change.
The candidate is not self-committing. It is appended only if the final agent/pre-step decision enters it. A listener may remove, rewrite, reorder, or reject it; if removed, projection state does not falsely advance and the next Step can propose it again.
10. Other context plugins use the pre-step lane deliberately
| Plugin | Refresh/admission rule | Placement and provenance |
|---|---|---|
| Agent instructions | Composes workspace files and tracks tool-touched paths; preserves pending context when an initial Step will not run | After the claimed batch and before driver-added runtime context PROMPT-CONTEXT-AGENT-INSTRUCTIONS |
| Time context | Per Step unless a configured refresh interval still covers the last injection; resolves browser or fallback timezone | Appends a sourced snapshot after downstream pre-step decisions PROMPT-CONTEXT-TIME |
| Tmux context | Step 1 only; requires shell, refresh due, successful location query, and changed state | Prepends its sourced snapshot to the decision PROMPT-CONTEXT-TMUX |
| Session reference | Explicit host preparation; snapshots bounded referenced Session surfaces in mention order | Returns one aggregated recall message for the caller to enqueue |
These inputs are not mislabeled system sections because their refresh, message position, source attribution, and retention rules differ. The pre-step waterfall is powerful but distributed: final message order depends on listener nesting and each plugin's before/after-next() behavior.
11. Request headers make the stable half replayable
The header holds resolved call config, which fields came from adapter defaults, rendered system text, and ordered tool schemas. Empty system/tool collections canonicalize to absent fields; equality compares config, adapter-default markers, system bytes, and schemas in order. Offline replay folds the latest full snapshot. PROMPT-HEADER-CANONICAL
The first request writes initial or resume. Later requests write a full change snapshot only when canonical equality fails. Tests show that per-Step reassembly of an unchanged prompt produces no extra header, while a real section change does. LOOP-REQUEST-BUILDPROMPT-HEADER-CHANGE-TEST
The frozen provider request is then formed from the current header plus Session.deriveMessages(). This avoids a second hidden conversation store: both stable instructions and message history have durable anchors.
12. KV-cache reuse is emergent, not a Harness cache
The direct DeepSeek adapter serializes system text as the first wire message and then appends the derived history. It always requests streaming usage; this path does not set an explicit cache-control directive. PROMPT-DEEPSEEK-WIRE
Cache reuse follows from a byte-stable prefix: stable system/config/tools, append-only message history, and volatile facts appended as user-role snapshots rather than rewriting the beginning. DeepSeek Harness does not maintain its own KV cache in this path.
A key-gated upstream E2E expects every request after the first to report positive provider cache-read tokens across a tool round trip and a later Turn. Dependencies and credentials were not installed for this study, so this is a reviewed test contract—not a locally observed result. PROMPT-CACHE-E2E
13. Strengths, costs, and unresolved edges
| Choice | Benefit | Cost or edge |
|---|---|---|
| Reassemble every proposed Step | No stale change-driven cache; current scope is always evaluated | Every Step pays provider and assembly work |
| Full header snapshots | Simple, local reconstruction and equality | Larger log entries than deltas when a large tool catalog changes |
| Append volatile context | Preserves stable provider-cache prefixes and provenance | Snapshots accumulate until surface replacement/compaction |
| Strict variables/tool order | Typos fail before a malformed request reaches the model | Some configuration errors appear only at first assembly |
| Expert waterfall | Maximum composability without Loop edits | Post-waterfall ordering, uniqueness, and consistency belong to listeners |
| Stable numeric section order | Simple deterministic ordering across distinct values | Equal orders fall back to registration order, which is composition-sensitive |
| Per-Step assembly, per-attempt request build | Retry sees newly durable history/config while preserving Step semantics | Prompt changes during a retry wait for the next Step |
The strongest idea is not prompt templating but semantic placement. Stable policy belongs in a reconstructable header; volatile policy belongs in sourced snapshots; situational context belongs in independently governed messages. Replay and cache behavior become consequences of the same data model rather than separate optimizations.
Chapter verification checklist
- Traced all five registry contribution APIs and their scoped effect ownership.
- Reconstructed the exact assembly order, including membership snapshots and live variable iteration.
- Verified scope shadowing occurs before displaced provider evaluation.
- Separated section, tool-schema, runtime-snapshot, and pre-step message lanes.
- Verified the proposed-Step boundary and the same-Step provider-retry boundary.
- Traced strict rendering failures to their exact durable Turn/Step outcomes.
- Verified header canonicalization, change suppression, request freezing, and log reconstruction.
- Reviewed—but did not claim execution of—the key-gated DeepSeek cache E2E.
My Learning Notes
Autosaved only in this browser. Nothing is uploaded or committed. Export Markdown whenever you want to keep a copy.