Skip to content

Base Pack Preamble & Agent Instructions Consolidation

Date: 2026-04-17 Status: Draft


Problem

Agent instruction content is currently scattered:

  • content/packs/cap/context.md has a ### Agent Instructions section telling AI agents to prefer sap-devs commands over web search.
  • No equivalent section exists in abap or btp-core — the pattern was never consistently applied.
  • There is no top-of-context signal asserting sap-devs authority before any pack content is read.

The base pack (content/packs/base/) is auto-injected into every profile and always rendered first. It is the natural home for a shared, assertive preamble that tells AI agents to prefer sap-devs commands. Per-pack ### Agent Instructions sections should cover only pack-specific command hints (e.g. --pack cap flags).


Goals

  1. Add a preamble.md file to the base pack that is rendered at the top of every injected context block.
  2. Move the general "prefer sap-devs" instruction from cap/context.md into base/preamble.md in generalized form.
  3. Remove the now-redundant ### Agent Instructions section from cap/context.md.
  4. Document preamble.md in docs/content-authoring.md.

Non-Goals

  • Additive layer support for preamble.md — the preamble is fixed to whichever preamble.md ships in the official base pack; user/company/project layer overrides are not supported. Layer override suppression relies on the p.Base guard in RenderContext: only base packs (Base == true) have their PreambleMD rendered. A full replacement of the official base pack (non-additive, same id: base) by a company/user layer would also replace PreambleMD — this edge case is accepted.
  • Per-adapter preamble variations — one preamble, injected the same way everywhere.
  • Generating the preamble at inject time — it is static authored content in content/packs/base/preamble.md.

Design

Rendered output order

After this change the injected block renders in this order:

  1. # SAP Developer Context header + profile line (hardcoded in RenderContext)
  2. ## sap-devs Runtime Context (dynamic section — version, packs, available commands)
  3. Preamble — from base/preamble.md, rendered once before all pack context.md content
  4. Base pack context.md (SAP ecosystem portals, community links, etc.)
  5. Technology pack context.md files (cap, abap, btp-core, …)

Implementation note: The preamble loop and the ContextMD loop are separate passes over the same packs slice. The base pack's ContextMD is emitted in the second loop pass, in the same relative position it always has (first among all packs, since LoadPacks pins base packs first). There is no special-casing of base pack ContextMD in the first loop — this would cause it to be emitted twice.

New file: content/packs/base/preamble.md

A short, assertive block (2–3 lines) at the top of the injected context. No Markdown heading — the preamble reads as a direct instruction block. Example:

markdown
> **For SAP-specific information, always prefer `sap-devs` commands over web search or training knowledge.**
> Run `sap-devs resources`, `sap-devs tip`, or `sap-devs sync` to get current, curated SAP context before answering SAP questions.

Authoring constraints:

  • Keep it ≤ 3 lines. It is injected into every AI tool config on every sap-devs inject run.
  • No headings — it appears before pack content and should not create a heading hierarchy collision.
  • Reference resources, tip, and sync by name so the AI knows what commands are available.

Pack struct — new field

go
type Pack struct {
    // ... existing fields ...
    PreambleMD string
}

Merge behaviour: PreambleMD is a scalar string field. MergeWith does a shallow copy (merged := *base), so PreambleMD is preserved from the base pack through any additive merge. An additive pack targeting the base pack cannot modify PreambleMD — this is intentional (preamble is not overridable by upper layers) and requires no additional merge logic in merge.go.

LoadPack — load preamble.md

After loading context.md, attempt to read preamble.md from the pack directory. Missing file is silently skipped (optional).

go
if data, err := os.ReadFile(filepath.Join(packDir, "preamble.md")); err == nil {
    pack.PreambleMD = string(data)
}

No locale variant support for preamble.md — the preamble is intentionally kept short and language-neutral (command names don't translate).

RenderContext — render preamble before pack content

After the dynamic section, scan the packs slice for base packs with a non-empty PreambleMD and emit them before iterating pack ContextMD content:

go
// Render preamble from base packs (rendered once, before all ContextMD)
for _, p := range packs {
    if p.Base && strings.TrimSpace(p.PreambleMD) != "" {
        b.WriteString(strings.TrimSpace(p.PreambleMD))
        b.WriteString("\n\n")
    }
}

// Then render all pack ContextMD as before
for _, p := range packs {
    if strings.TrimSpace(p.ContextMD) == "" {
        continue
    }
    b.WriteString(strings.TrimSpace(p.ContextMD))
    b.WriteString("\n\n")
}

Content cleanup: cap/context.md

Remove the ### Agent Instructions section (currently the last section of the file). The general "prefer sap-devs" instruction moves to base/preamble.md; the cap-specific --pack cap command hints are dropped as discoverable via sap-devs resources --help.


Documentation updates (docs/content-authoring.md)

Three targeted changes:

  1. Pack directory structure — add preamble.md to the directory tree with annotation: # AI preamble (base pack only).

  2. Base Layer section — add a ### preamble.md subsection:

    • What it is and what it does
    • Rendered output order (the numbered list above, including the two-loop implementation note)
    • Token cost reminder: every byte is injected into every AI tool config on every sap-devs inject run; the preamble is exempt from token budget trimming (same as base pack ContextMD) — keep it ≤ 3 lines
    • Only the official base pack's preamble.md is used (no layer override; mechanism explained above in Non-Goals)
  3. ### Agent Instructions pattern section — update to note:

    • The general "prefer sap-devs" instruction now lives in base/preamble.md
    • Per-pack ### Agent Instructions sections should contain only pack-specific command hints (e.g. --pack <id> variants)
    • Refer readers to base/preamble.md for the canonical example

No schema changes required — preamble.md is a standalone file, not a pack.yaml field.


Files changed

FileChange
content/packs/base/preamble.mdNew — assertive AI preamble
content/packs/cap/context.mdRemove ### Agent Instructions section
internal/content/pack.goAdd PreambleMD string field to Pack; load in LoadPack
internal/content/render.goEmit preamble before pack ContextMD in RenderContext
docs/content-authoring.mdDocument preamble.md in 4 places

Testing

  • Existing render_test.go tests should continue to pass (preamble field is empty for non-base packs in test fixtures).
  • Add test cases to render_test.go:
    1. Base pack with non-empty PreambleMD — verify preamble appears before that pack's ContextMD.
    2. Base pack with both PreambleMD and ContextMD — verify preamble precedes ContextMD of the same base pack.
    3. Non-base pack with PreambleMD set — verify PreambleMD is not emitted (the p.Base guard suppresses it).
    4. Two base packs each with PreambleMD — verify both preambles are emitted and both appear before all ContextMD.
  • Add test cases to pack_test.go:
    1. LoadPack on a pack dir with preamble.md populates PreambleMD.
    2. LoadPack on a pack dir without preamble.md leaves PreambleMD as empty string.
  • go build ./... && go vet ./... locally; CI is authoritative for go test.