Skip to content

Artifact References

Artifact references are typed @<kind>:<argument> citations in launch prompts. They are not macros: SASE resolves them late in prompt preprocessing, after macro expansion and command substitution, using the project context for that prompt segment. Each successful reference expands to prompt text, records a per-agent use row, and can publish as a numbered Markdown reference link.

References name artifacts. Typed Artifact Links record why two artifacts are related and render on the artifact markdown file; prompt references record that an agent consumed an artifact in launch context.

Prompt Grammar

Use the unquoted form when the argument contains no spaces:

@plan:202608/artifact_ref_contract.md
@bead:sase-js.9
@stitch:sase@f499469

Use quotes when the argument contains spaces:

@patch:"artifact references adoption"
@file:"~/bob/Research Inbox.md"

Fragments such as #L10-L20, #page=2, and #t=30 stay attached to the reference when the target kind supports them. Inline code, fenced code, and disabled macro regions stay literal. Unknown @kind: text remains prose; malformed or missing references for a known kind stop launch with a diagnostic.

Live Kinds

Canonical live categories are:

Form Meaning
@stitch:<sha> Commit in the prompt segment's project repository
@stitch:<repo>@<sha> Commit in a named primary, linked, or sidecar repo
@patch:<name> Patch in the prompt segment's project
@bead:<id> Bead by full id or unambiguous shorthand
@agent:<name> Published agent page and related transcript context
@goal:<id> Goal in the prompt segment's project (a citation that never binds)
@goal:<project>@<id> Goal in a named project
@file:<path> Allow-listed local file captured by content digest
@file:<source>:<digest> Indexed file, where source is explicit or default
@<document-kind>:<repo-path> Document in a configured artifact sidecar, such as plan or research

The built-in plans sidecar exposes @plan:<path> through ref: {use: builtin@plan}. The sase-research-artifacts plugin exposes @research:<path> for the research content sidecar. Other artifact sidecars can add their own document kind with an inline ref: spec or with ref.use: <plugin>@<provider> from an installed provider plugin.

Compatibility readers preserve older persisted references. @commit: canonicalizes to @stitch: permanently. @plans: and the bare plans: machine-field spelling used by SDD plan references are both read-only compatibility spellings of plan — kept because commit trailers and bead event streams that already carry them are immutable — and neither is ever emitted again; new references always render as @plan: in prose and plan: in machine fields. Historical @chat: and @bug: references remain archive readers and are not offered for new authoring. The retired #ref/<kind>:<argument> macro renderer syntax is not accepted.

Project Context

Short references resolve from the prompt segment, not from the current working directory. A segment's leading +<project> project tag, #git:, #gh:, or other VCS workflow tag supplies the project context. If a segment has no explicit tag, the caller's launch identity supplies it.

Context affects ambiguity:

  • @stitch:<sha> searches the selected project's primary repo; qualify with @stitch:<repo>@<sha> when needed.
  • @patch:<name> searches the selected project's active Patch store, then its archive.
  • A short @bead:<suffix> searches the selected project first, then rejects cross-project collisions instead of guessing.
  • @agent:<local-name> canonicalizes to the durable global agent identity.
  • @goal:<id> cites the segment project's goal ledger; qualify with @goal:<project>@<id> when needed. A citation never binds (binding is a later epic's %goal); citing a settled goal is allowed.

On-demand document sidecars

During final preprocessing of each agent prompt at launch, a well-formed reference for a path-bound document kind (see Expansion) in a workspace-backed prompt segment can materialize its configured sidecar when that role has a recorded remote but no local clone. For example, the first live citation of an explicitly path-bound custom kind can clone its sidecar, refresh that segment's project context, and then resolve the document. SASE prints the role it is materializing. A clone failure stops launch with the remote's error and an explicit sase repo path <role> --ensure retry command.

Pointer document kinds, including @plan:... and @research:..., never trigger this: their expansion does not depend on a local checkout, so citing one never clones its sidecar.

This write is launch-only. Validation, macro display and expansion previews, editor catalogs, and other discovery paths remain read-only and never clone a missing sidecar. Materialize the role explicitly when one of those surfaces needs a local inventory — sase repo path <role> --ensure is the CLI form, and typing @<kind>:: in sase's TUI prompt (empty payload, immediately after the kind's colon) is the in-prompt equivalent: it syncs or first-clones that kind's sidecar, rebuilds the completion catalog, and reopens the payload menu with the newly-arrived rows badged. Pointer kinds such as @plan: and @research: never auto-materialize at launch (see above), but they are reachable this way — @research:: clones the sidecar on first use. See sase's TUI for the full gesture, its status row, and its ref_sync_gesture feature flag. References inside inline code, fenced code, or disabled macro regions stay literal and do not trigger materialization.

Allow-Listed Files

Path-backed @file: references are opt-in. Configure roots in sase.yml:

artifact_refs:
  file:
    roots:
      - name: bob
        path: ~/bob
        path_globs: ["**/*.md"]

The root name becomes the portable logical identity in published metadata. path must be absolute or ~/ rooted. path_globs are root-relative POSIX globs with ! exclusions. SASE accepts regular files that stay under exactly one effective root, pass the glob policy, and fit the configured capture size limit.

At launch, SASE reads the bytes once, hashes them with SHA-256, stores the captured object, and expands the prompt to the <captured-path> file. Later source edits do not change what the agent received.

Provider Specs

Document artifact providers are declarative. A project can start from an installed provider:

repos:
  sidecar:
    custom:
      research:
        ref:
          use: sase-research-artifacts@research

or define the same policy inline:

repos:
  sidecar:
    custom:
      design:
        ref:
          kind: design
          icon: ◆
          expansion_format:
            "the {repo_relative_path} file in the {sidecar_role} sidecar repo"
          properties: {}
          detail: {}
          identity: {}
          inventory:
            globs: ["**/*.md", "!drafts/**"]
          publication:
            link: vcs_permalink
            referenced_by: markdown_table

use and inline fields normalize to the same spec. An assembled reference provider spec must include ref.icon, the Artifacts tab mark; installed providers supply it, and inline specs declare it directly. Scalar values replace, mappings deep-merge, and lists replace. Missing providers fail soft during launch and surface as sase doctor -C config.repos findings. A linked repo or cloned sidecar is not an installed Python distribution; the provider package must be installed so its entry points are visible.

A sidecar's reference kind is independent of its role name. ref.kind (or the kind inherited through ref.use) is the @<kind>: prefix agents author; the sidecar role is storage identity only — the repo checkout path, sase repo open <role>, and similar plumbing. The builtin roles ship with kinds that differ from their role names on purpose: role plans writes kind plan, role beads writes kind bead, and role agents writes kind agent. Any sidecar, builtin or custom, can set its own ref.kind independently of its role.

Expansion

A document reference expands through its provider spec's expansion_format, a template drawn from a subset of the shared placeholder vocabulary:

Placeholder Value
kind The reference's kind, such as research.
argument The authored argument, fragment stripped.
canonical_argument The canonical <kind>:<argument> reference text.
repo_relative_path The sidecar-relative POSIX path; identical to argument.
display_label The argument's filename.
sidecar_role The sidecar role backing this document kind.
checkout_path The resolved absolute path in a local checkout.

A format that uses checkout_path is path-bound: expansion resolves the reference to a local file, materializing a missing auto_clone sidecar when needed, and fails the launch with a diagnostic when the document cannot be found there. Path-bound expansion is an opt-in compatibility capability for an explicit custom provider. Those providers should render prose such as the {checkout_path} file, not an @-prefixed filesystem path.

A format that uses no path placeholder is a pointer: expansion renders straight from the reference itself, with no resolution dependency. A pointer reference never clones its sidecar and never fails a launch — an unresolvable pointer still expands, using whatever prose the format declares. @plan:, @research:, and every unconfigured document sidecar use the default pointer format "the {repo_relative_path} file in the {sidecar_role} sidecar repo", so @plan:202608/foobar.md expands to "the 202608/foobar.md file in the plans sidecar repo" whether or not the plans sidecar is cloned.

Built-in non-document kinds follow the same portable-prose rule. @file: becomes the <captured-or-materialized-path> file; @bead:<id> becomes the <canonical-id> bead in the <project> project; @agent:<name> becomes the <canonical-name> agent in the <project> project; @stitch: / @commit: become the <full-sha> stitch in the <repository> repo; @patch:<name> becomes the <name> Patch in the <project> project; @goal:<id> becomes the one-line goal citation (goal ⌖<id> "<title>" in the <project> project (<status>) with outcome and criteria). Historical @chat: becomes the <resolved-path> file, and historical @bug: becomes issue #<number> in the <project> project (<resolved-url>). Authored citations remain @<kind>:<argument>; none of these expansions inject an @ sigil in front of a filesystem path. Fragments such as (lines 10-20) are appended after that pointer.

Publication

Published prompts rewrite live references as stable Markdown reference links:

Read [@research:202608/report.md][1] and [@file:~/bob/gtd.md][2].

[1]: https://github.com/sase-org/sase--research/blob/<revision>/202608/report.md
[2]: ../../files/objects/sha256/ab/<sha256>

Clean repository-backed documents link to the captured revision. Dirty or untracked documents and local files link to the captured object. Tracking does not depend on linkability: an unlinkable reference still records a use row and remains visible in the published prompt metadata. A pointer document reference that never resolved to a local file — no clone of its sidecar was ever made — publishes the same way: unlinked, but with its use row intact.

Artifact repos that opt into referenced_by: markdown_table get a managed Referenced By section at the bottom of cited Markdown documents. That section is a projection of recorded use rows, not part of the document's semantic content version. Each row names the publishing agent, project, canonical reference, publication date, and use count; the agent name links to its published page when SASE can build that URL. Repeated publication of the same agent revision and document is idempotent, while multiple citations of the document in that prompt increase the row's use count.

The write-back workflow runs in this order:

  1. SASE finishes prompt-archive publication in the agents sidecar, including its push when the sidecar has changes or is ahead of its remote.
  2. It queues one durable request per cited provider document, then synchronously tries to drain the project's queue before the publishing command returns.
  3. The drain groups requests by sidecar role, pulls each artifact repository with rebase, updates only the managed Markdown block and its tracked links/ index (links/<artifact-relpath>.json, original extension preserved), and prepares any changed document and index files. The sidecar is the provider, so the index path does not repeat a referenced-by/<provider>/ prefix, and the JSON is committed with the projection rather than written under .sase/.
  4. When the refresh changes files, SASE creates a local Update artifact link projections commit and starts a detached push. The first refresh in a sidecar also installs the rooted /links/**/*.lock ignore rule in that same commit so lock sentinels never become a later lock-only cleanup. A successful refresh, including an idempotent no-op, acknowledges the request without waiting for that push to finish.

A failure before a successful local artifact-sidecar refresh leaves the request queued for a later mutating sase agent sync. The same --retry-quarantined and --drop-retired controls used for agent publication also operate on queued Referenced By requests; --retry-retired revives retired agent-publication requests only. See Agent Hood Synchronization. Once the local refresh succeeds, however, the request is no longer in that outbox. A later detached-push failure is recorded in the managed SDD sync log and is not retried from the Referenced By outbox.

The write-back attempt can delay the publishing command's return, but it begins only after the prompt archive has been pushed and cannot roll that publication back. Because the managed block is stripped when SASE hashes a clean Markdown input, adding a back-reference does not make the original citation appear to have changed. These commits use the non-user file-hook cause artifact_links, so ordinary file hooks ignore the managed write unless they explicitly opt in with filters.causes.

Deliberate relationships render separately as a top Links table, while prompt-reference and read rows use the unified link graph behind the same Referenced By projection. See Artifact Links for the relation registry and CLI.