Instruction bundle internals¶
This page documents the storage and delivery contracts behind instruction bundles.
Bundle and manifest¶
A bundle is one Markdown document composed from memory: package, home, and project layers under compiler-generated frame headings. A manifest is the JSON sidecar describing that bundle: schema version, compiler identity, host-set facts, bundle digests and size, per-layer budgets, the ordered section table (included sections with byte offsets, excluded sections with reasons), delivery identity, and observation status.
The manifest wire contract — closed vocabulary, validation invariants, and the
common_digest definition — is owned by sase-core (instruction_manifest module,
schema version 2). Markdown composition stays in Python (sase.instructions), which
reuses the legacy memory renderer through its structured units API. Only Python needs
composition today (launch and CLI preview); if a non-Python front end ever needs to
preview a render, composition moves into Rust rather than growing a second copy.
Layers and section ids¶
The fixed layer order is package → plugin → home → project → launch, with frame
sections as document scaffolding. Every byte of a bundle belongs to exactly one section:
| Layer | Section ids | Source |
|---|---|---|
frame |
frame.title, frame.core, frame.reference, frame.webs |
Compiler headings and the intro paragraphs |
package |
pkg.sase.*, pkg.root.final_declaration, pkg.helper.contract, pkg.provider.<provider> |
Contract template, helper template, adapter directive |
plugin |
plugin.<dist>.<slug> |
Reserved; currently empty |
home |
home.core.<stem>, home.ref.<stem>, home.web.<stem> |
Home memory at Path.home() |
project |
proj.core.<stem>, proj.ref.<stem>, proj.web.<stem> |
The project root's memory |
launch |
launch.<slug> |
Reserved; currently empty |
The compiler renders the contract template once — with the project name and the merged
linked entries (project entries first, home-only entries tagged as home configuration) —
and splits it on H2 headings into the fixed ids. Unknown H2 headings in an override
template get pkg.sase.<slug of heading> and are lifecycle-neutral. Generated
sase/memory/sase.md notes (project and home) are superseded input: excluded with
reason superseded_input. A project note shadows a same-path home note, recorded as
shadowed plus shadowed_by.
Bundle layout: # title (plus a Home: line when the home layer contributes),
## Core Memory with the package sections then home and project core notes,
## Reference Memory with one entry per note, and ## Memory Webs with one block per
web. Empty groups omit their frame section. Section bytes are
text.rstrip("\n") + "\n\n", concatenated, so offsets are contiguous. Bundle bytes
never embed absolute paths, workspace names, timestamps, or hostnames: identical inputs
from any workspace give identical bytes.
Overlays and facts¶
Each included section carries lifecycle: neutral, root, or helper. Facts are set
by the host — project content cannot set them:
| Fact | Values |
|---|---|
actor |
sase_root | native_helper | interactive |
mode |
runtime | interactive | export |
purpose |
ordinary | declaration_recovery | conflict_repair |
provider |
execution provider name |
project |
project memory name or null |
host |
short hostname |
vcs |
VCS provider name or null |
The root render (runtime + sase_root) carries pkg.root.final_declaration and
pkg.provider.<provider>; the helper render carries pkg.helper.contract instead.
Interactive and export renders carry neither; export additionally carries no home.*
content. Only the root render contains the SASE Final Declaration heading. Rendering
for native_helper is compiled at the boundary when the subagent slot is explicit.
common_digest is the sha256 of the canonical JSON array [[id, sha256], …] over
included sections with provider_specific == false, in bundle order. Equal
common_digest across providers means "same instructions except the provider section".
Section budgets record bytes and tokens_est (ceil(len/4)) per section and per layer;
the compiler does not truncate or reject a bundle for size. An adapter can separately
limit the delivered text and fall back when that limit is exceeded.
Cache and store¶
The bundle bytes live once in a content-addressed store:
<sase home>/instructions/bundles/<sha[:2]>/<sha>.md (mode 0444, written atomically,
currently not pruned). The render cache (<sase home>/instructions/cache/<key>.json)
holds the compiled section table keyed by the sha256 over the compiler version, the sase
version, a per-process code fingerprint, the content-changing facts, the provider
directive file, and the sorted content hashes of every input-file class (project and
home memory, project configs, global config and overlays, resolved templates, the helper
template, the project AGENTS.md, and the task-type plugin distributions). A missing or
corrupt entry or blob is a miss. The hit path imports none of the heavy composition
modules, which keeps warm renders within the 250 ms p95 budget; every manifest records
render_ms and cache (hit, miss, or bypass).
Delivery¶
Every root provider invocation routes through one boundary,
sase.llm_provider._instruction_boundary.invoke_with_instructions. With an artifacts
dir it resolves a delivery plan, renders the root bundle, optionally renders a helper
bundle, writes v2 manifests, and exposes the active delivery to adapters. With no
artifacts dir (for example the TUI fix-hook path) it invokes directly: no plan and no
active delivery.
Plan hook and slots¶
Providers implement llm_instruction_delivery (firstresult) and return
{"root": SLOT, "subagent": SLOT}. Each SLOT is
{"status", "channel"?, "max_bytes"?, "reason"?}. status is one of explicit,
inherits_native, inherits_root, or none. The root slot may only be explicit or
inherits_native. A missing hook, a hook error, or an invalid return resolves to
legacy: both slots inherits_native with reason provider_legacy. Claude, Codex, Grok,
and Muse return explicit root slots when their sunset flags are on, and flag_disabled
legacy when off. Antigravity (agy), Qwen, and OpenCode stay provider_legacy on native
AGENTS.md discovery; see
The Committed AGENTS.md Stays A Readable Projection.
sase.llm_provider.instruction_delivery.resolve_delivery_plan(provider) validates the
hook result and never raises. Objects without instruction_delivery also resolve to
legacy, so test fakes keep working.
Marker¶
The delivered text is the compiler bundle bytes plus one trailing marker line:
<!-- sase-instructions bundle=<sha256[:12]> invocation=<invocation_id> -->
The bundle .md and its sha256 stay exactly the compiler's bytes. The marker is
trailing so every byte before it stays cacheable. Helper delivered text carries the
helper bundle's sha and the root invocation id.
Helper manifests¶
When the subagent slot is explicit, the boundary also writes NN-<provider>.helper.md
and NN-<provider>.helper.json with the same NN as the root. The helper manifest has
facts.actor = native_helper, delivery.status = explicit, and parent_invocation_id
set to the root invocation id. A failed helper render or validation sends helpers back
to the packaged static helper template. A root bundle is never substituted for an
explicit helper bundle; Codex's declared inherits_root slot is a separate capability.
Fallback and notifications¶
These switch the root slot to legacy delivery for that invocation:
- render failure (
.error.jsongains"delivery_fallback": "legacy") - invalid root bundle (
invalid_bundle) - delivered text over the slot's
max_bytes(oversize) - adapter materialization failure (
delivery_failed)
render_failed, invalid_bundle, oversize, and delivery_failed raise one
notification, deduplicated per host, provider, reason, and day (+1 on repeats).
provider_legacy and flag_disabled are declared states and never notify. A plan-hook
error selects legacy delivery; a bundle-preparation error also attempts to write an
.error.json record. If the final manifest rewrite fails after the provider call, SASE
logs a warning without retrying the call, so session IDs or fallback details may be
missing from the recorded manifest. These protections do not catch failures of the
provider invocation itself, and its legacy path can also fail. KeyboardInterrupt and
SystemExit propagate.
Codex currently has a fallback limitation: its shadow-home filtering follows the
codex_explicit_instructions flag, even when an invocation falls back or has no active
delivery plan. SASE-owned global instruction copies and the ~/AGENTS.md fallback can
therefore remain absent from that shadow home. See
Codex instruction delivery
before treating a legacy manifest as proof that all native instructions were restored.
Manifest v2 fields¶
Root manifests record delivery.status (explicit, or inherits_native plus
fallback), channel, provider_cli_version (cached probe, null on failure),
session_ids (registered by the adapter, written back after the call; Codex stores the
thread id, which matches the rollout filename stem suffix), and the subagent slot.
shadow is no longer written; readers still accept it for E2-era runs. The manifest is
written before the call and atomically rewritten afterwards with session ids and any
adapter-reported fallback. A killed process keeps the pre-call manifest. Bundle and
manifest files are mode 0644. The agent_meta.json instructions summary adds
delivery_status and helper_manifest.
%dispatch launch requests do not forward SASE_INSTRUCTIONS_FILE. The target renders
with its own code; no instruction bytes cross the wire. See
Remote Dispatch.
Adapter contract¶
Without an active explicit delivery, adapters use their legacy directive and delivery path. The Codex shadow-home limitation above still applies.
An adapter whose slot is explicit must do three things together:
- deliver the slot's delivered text once through its channel
- suppress SASE-owned native instruction files
- drop its legacy directive, because the bundle carries
pkg.provider.<p>
Adapters read the live delivery through
sase.llm_provider.instruction_delivery.current_instruction_delivery(). They call
record_session for every session id they start, report_fallback when materialization
fails, and materialize(slot) for a private 0600 file that is removed after the call.
Plugin authors: see LLM Plugins.
Each invocation writes <artifacts>/instructions/NN-<provider>.md (the bundle) and
NN-<provider>.json (the normalized v2 manifest, indent=2, sorted keys), where NN
is a two-digit per-run sequence allocated with O_EXCL. SASE_INSTRUCTIONS_FILE holds
the bundle path during the call and is restored afterwards. The manifest's attempt is
1 + count(attempts/*). With no artifacts dir, SASE_INSTRUCTIONS_FILE is never
exported and no delivery files are written.
Related¶
- Instruction bundles — preview and user-facing behavior.
- External provider plugins — adapter hooks.
_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.