Skip to content

Instruction Bundles

An instruction bundle collects the project, home, and SASE instructions for one agent invocation. Its manifest records which sections were included and their digests, so you can inspect the intended context and compare it with what a provider actually loaded.

The current implementation records bundles for diagnostics. With the default instruction_shadow_render flag enabled, root provider invocations render a bundle into the run's artifacts and export SASE_INSTRUCTIONS_FILE for that provider call, then restore the previous value. Current providers do not read that variable. Their argv, prompts, and the instruction files they already load stay the same. A successful render therefore does not prove delivery: use sase instructions verify to check observed loads. This stage is called E2 in the implementation and migration inventory; later delivery stages have not replaced the current path. Shadow-render failures never fail an invocation. The env var and the shadow files are described under Shadow manifests.

See also Verifying instruction delivery.

Inspecting your instructions

Start with these read-only commands from your project checkout:

sase instructions list                 # Current instruction files and provider shims
sase instructions render -s            # Intended bundle sections from current sources
sase instructions verify -c            # Observed loads and manifest coverage
sase instructions verify -a AGENT      # Compare a recorded run's intent with its loads

Replace AGENT with a name from sase agent list -a. render -a AGENT instead uses that agent's facts to compile a fresh preview from today's memory; its digest may differ from the historical bundle. verify looks at the newest 20 runs per provider from the last 7 days unless you pass -n (maximum 200) or --since. Delivery problems still exit 0. A bad --since or --until value exits 2. Inspect the rows rather than treating exit code 0 as proof that delivery is correct.

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 1). 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; no content in E2
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; no content in E2

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 preview-only in E2; the hook renders roots only.

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; there is no truncation and no budget gate in E2.

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, never pruned in E2). 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).

Shadow manifests

Every root provider invocation routes through one boundary, sase.llm_provider._instruction_boundary.invoke_with_instructions, which shadow-renders the bundle the agent would get and records it without delivering anything. With the instruction_shadow_render sunset flag on and an artifacts dir, each invocation writes <artifacts>/instructions/NN-<provider>.md (the bundle) and NN-<provider>.json (the normalized manifest, indent=2, sorted keys), where NN is a two-digit per-run sequence allocated with O_EXCL. The agent_meta.json instructions summary counts the manifests and points at the latest (seq, provider, purpose, sha256, common_digest, manifest), and SASE_INSTRUCTIONS_FILE holds the bundle path during the call and is restored afterwards. The manifest's attempt is 1 + count(attempts/*).

Failure posture: the shadow render never fails an invocation. Any shadow exception is caught, logged once as a warning, recorded best-effort as NN-<provider>.error.json (exception type, message, rendered_at), and the call proceeds. Kill switch: SASE_INSTRUCTIONS_FILE is never exported and no shadow file is written with sase flag disable instruction_shadow_render (or no artifacts dir); remove the flag when E3 delivers the rendered bundle or the readout shows zero shadow failures and warm p95 within budget for 7 days.

Previewing bundles

sase instructions render previews the bundle for the current project and home sources without delivering anything:

sase instructions render | head -40                      # what a root agent here would get
sase instructions render -f provider=codex -s            # sections instead of Markdown
sase instructions render -j                               # preview manifest as JSON
sase instructions render -p                               # parity against AGENTS.md files

Options (alphabetical): -a/--agent NAME starts from one agent's facts — the provider from its run record, or its latest recorded manifest when one exists — while sources stay the current project and home; stderr then reports whether the fresh bundle sha256 matches the agent's recorded bundle and lists changed section ids. -f/--fact KEY=VALUE is repeatable and accepts comma-separated pairs; mode=interactive|export implies actor=interactive, and a conflicting explicit actor is an error. -j/--json prints the normalized manifest with delivery.status: preview. -N/--no-cache bypasses the cache (cache: bypass). -s/--sections prints a Rich table of id, layer, status or reason, lifecycle, bytes, tokens_est, and source. -p/--parity additionally checks the bundle against the legacy root and home AGENTS.md files: every legacy core, reference, and web path must map to an included or shadowed bundle section (the legacy sase.md maps to the pkg.sase.* sections), every legacy repository name must appear in pkg.sase.repos, and exactly one included section must contain the SASE Final Declaration marker. The parity table goes to stderr and the exit code is 1 on any gap; extra bundle sections (for example pkg.provider.*) are reported as additions, not failures. The default output is raw bundle Markdown on stdout plus one summary line on stderr (sha256 and common_digest prefixes, section count, bytes, tokens_est, cache, and milliseconds).