Skip to content

Artifact Links

Artifact links are typed relationships between SASE artifacts. They answer "why are these durable records connected?" without overloading prompt citations, bead dependencies, or free-text notes.

An artifact is any durable record addressable by an artifact reference: a plan, research report, bead, completed agent, Patch, stitch, or indexed file. Each artifact has a canonical <kind>:<argument> identity. An artifact markdown file is the Markdown document or generated page where SASE renders that artifact's typed links.

Relation Registry

The registry is closed. Use one of these slugs exactly:

Relation Inverse Directed Written by
cites cited-by yes prompt references and structured header derivation
read read-by yes sase artifact read
related related no CLI / plan inlet; RELATED: migration
supersedes superseded-by yes CLI / plan inlet
implements implemented-by yes CLI / plan inlet; plan, agent, and stitch projections
derives-from derived-into yes CLI / plan inlet; research lineage derivation
produced-by produced yes projected from a stitch's recorded agent or an epic's creating agent
launched launched-by yes projected from a configured job and its published agents
awaits awaited-by yes projected from a published agent's wait_for_beads

blocks and depends-on are reserved. Use sase bead dep for scheduling and blocking relationships instead of storing those as artifact links.

Run sase artifact link relation list to inspect the closed registry, or sase artifact link relation show <slug> for one relation's direction, positive and negative examples, and recommended endpoint kinds. Both forms accept -j/--json. Direction matters: the replacement supersedes the old artifact, a plan or agent implements a bead, a stitch is produced-by an agent, a job launched an agent, a waiting agent awaits a bead, and a derived report derives-from its source. related is undirected. Only related, supersedes, implements, and derives-from are writable by the CLI; cites and read are observational rows, while produced-by, launched, and awaits are read-only projections from other durable evidence.

Commands

Create or update a deliberate link:

sase artifact link add <source-ref> <relation> <target-ref> "<why>"

The source is always explicit; it never defaults to the current agent. References may include their prompt-only leading @. <why> must be one non-empty line of at most 240 characters. Repeating an identical edge is an unchanged success rather than a second row.

List one artifact's neighborhood, or the current project's recent links:

sase artifact link list <ref> -d both
sase artifact link list -l 20 -R related

Without a reference, list shows the current project's newest 50 rows. With one, it shows that artifact's neighborhood. -d in|out|both, -R/--relation, and -o/--origin (manual, migrated, prompt_ref, read, derived, or projected) narrow it; -l 0 is unlimited and -j emits a stable JSON array. The default --source index reads the rebuildable machine-local aggregate and includes computed projections. --source store recomputes rows from durable document and bead truth plus pending event outbox entries, and excludes computed projections. Before the legacy cutover is complete, that durable truth also includes the frozen document-sidecar links/ indexes; after cutover it comes from immutable events and bead records.

Ask SASE for write-free, hard-evidence suggestions before adding a deliberate edge:

sase artifact link suggest
sase artifact link suggest plan:202608/example.md -l 20

Suggestions come only from deterministic filename lineage, a shared bead or epic, overlapping audited-read sets, or an audited read-log candidate. Existing persisted links are excluded (related is compared in either direction). Each row names its signal and evidence. This command never writes graph rows, companion files, or commits; review a suggestion and use link add yourself when it expresses the intended meaning.

Remove links between a pair:

sase artifact link rm <source-ref> <target-ref>
sase artifact link rm <source-ref> <target-ref> -R implements

Read an artifact as context with an audited reason:

sase artifact read <ref> "<why you need this context>"

read strips leading frontmatter plus managed Links and Referenced By blocks before printing Markdown. Inside a SASE agent run with an agent identity, it also records a read edge from the agent to the artifact. Outside an agent run it still prints the artifact and writes the audit row, but warns that no graph edge was recorded. show, path, and open remain silent reads.

After the body, read prints a Links: footer to stderr with up to five one-hop neighbors, semantic links first, followed by (+N more) when needed. Reading an artifact that is the target of supersedes also emits a direct warning naming its replacement.

link add, link rm, and migrate-notes --apply publish immutable operations. A removal is a tombstone over the active operation IDs the command observed; it does not delete history. The shared Rust reducer folds those operations into the current graph rows that link list displays.

Projected relationships

Some relationships are computed into the machine-local read model instead of being stored as link sidecars:

  • a published agent's bead_id, epic_bead_id, or phase_bead_id projects agent:<name> implements bead:<id>;
  • a primary-repository commit with a SASE_BEAD trailer projects stitch:<sha> implements bead:<id>;
  • a commit with a SASE_AGENT trailer (or legacy AGENT) projects stitch:<sha> produced-by agent:<name>; and
  • a published agent's created_epic_ids (recorded by sase bead work) projects bead:<epic> produced-by agent:<name>, and an epic-tier plan bead whose created_by names an agent projects the same edge, so unpublished planners get the edge too; and
  • a published job-agent name that resolves against the live AXE configuration projects job:<routine>/<job> launched agent:<name>; and
  • a published agent's wait_for_beads (from %wait(bead=<id>)) projects agent:<name> awaits bead:<id>.

Projected rows carry origin: projected and a created_by: projection:<rule> marker. They appear in sase artifact link list, sase artifact doctor, and sase's TUI alongside durable links, but they are recomputed read-model data: link rm and sase's TUI remove action cannot delete them. Change the durable source evidence instead, then refresh or repair the aggregate. The health check compares the aggregate against both durable sidecar truth and these expected projections.

Rendering

SASE renders deliberate manual and migrated links near the top of the artifact markdown file in a managed ## Links table. Automatic prompt citations and audited reads render at the bottom in ## Referenced By.

When a prompt expands a document artifact reference, SASE may append up to five one-hop, directed semantic neighbors as (linked: …). Only implements, derives-from, and supersedes participate; the expansion is never transitive, and it omits the broad related relation plus observational cites / read rows.

Markdown document artifacts are their own artifact markdown file. Non-Markdown files use a sibling <stem>.md companion created lazily on the first link. Beads, agents, and Patches use generated pages, so agents should update their underlying stores with SASE commands and never hand-edit those generated pages. Stitches have no page of their own; links to a stitch render on the other artifact.

When the selected Agent, Artifact, or AXE job has links, sase's TUI shows a contextual link rail. Press $ to arm it, then $ again for the first link, 1-9 for a numbered link, or 0 for the complete links panel. A projected group may occupy one rail entry; choosing it opens a panel scoped to that group instead of guessing which member to follow.

The panel explains relation direction, provenance, rationale, and missing targets, and warns when the aggregate is stale. a-z follow the first 26 rows directly, Enter follows the highlighted row, and arrows or Ctrl+N/Ctrl+P move the highlight. - removes a writable durable link; projected rows are read-only. Cross-tab follows record a 32-hop trail: Ctrl+O walks backward and Ctrl+Shift+O walks forward, restoring the prior tab, pane, project scope, query, selection, and supported fold state. Ordinary navigation starts a new trail.

An agent may attach deliberate links while authoring a plan by adding a transient links: list to its YAML frontmatter:

---
tier: tale
title: Implement the artifact browser
goal: Make historical artifacts searchable
size: small
links:
  - ref: bead:sase-123
    relation: implements
    description: This plan implements the accepted task
---

Each entry requires string ref, relation, and description values. sase plan propose validates the entire list before mutating the proposal, restricts it to the four CLI-writable relations, and enforces the registry's recommended direction and endpoint kinds. It then removes links: from the archived plan, persists the rows with manual origin, and refreshes the managed ## Links block. The list is an authoring inlet, not retained plan metadata.

Automatic derivation and repair

SASE durably derives only relationships backed by deterministic structured evidence:

  • plan:<path> implements bead:<id> from a plan's bead_id: frontmatter, when the bead is known to the readable store.
  • agent:<name> cites plan:<path> by following a plan's canonical PROMPT header to the archived prompt's canonical AGENTS entries, after the agent page is published.
  • A research lead derives-from its on-disk __a and __b research-swarm siblings.

Derivation runs on relevant plan/archive and sidecar-commit paths. The built-in hourly AXE artifact_link_backfill job covers older documents in bounded, checkpointed batches, drains queued read rows for agents that have since published, recomputes projected relationships, reconciles the machine-local aggregate, and repairs dangling references from Git rename history. See Default routines.

Beads

Use artifact links for relationship context between beads:

sase artifact link add bead:<new-task-id> related bead:<other-bead-id> "<why>"

Use bead dependencies only for scheduling:

sase bead dep add <blocked-bead-id> <blocking-bead-id>

Historical RELATED: notes remain in bead history. sase artifact link migrate-notes dry-runs the conversion, and --apply writes typed related edges plus MIGRATED: notes without deleting the original text.

Legacy index cutover

Existing installations may still have mutable per-artifact indexes under document sidecar links/ trees. Upgrade every SASE machine that can write those sidecars, then preview the one-time import:

sase artifact link import-indexes

The preview requires clean Git document sidecars, freezes each current HEAD and links/ tree digest, and reports the unique and duplicate rows, legacy or invalid queued outbox entries, deterministic baseline-event path, enrolled machine names, and a capability attestation. Review that fleet list before applying: the marker fences current binaries only, and older binaries do not understand it.

Apply with the exact token printed by the preview:

sase artifact link import-indexes --apply <attestation>

Apply first commits a link-events/STORE.json fence to every document sidecar, converts legacy outbox rows, publishes the same deterministic baseline event to every sidecar, marks the import complete, and rebuilds the aggregate. The operation is idempotent and resumable: rerunning preview and apply with the reported token completes an interrupted import rather than creating a second baseline. Once every marker says imported, current readers ignore the frozen links/ indexes. Do not edit or delete that legacy tree by hand; sase doctor reports post-import stragglers.

Health and recovery

sase artifact doctor reports link health alongside the file index: dangling or unpublished agent references, stale rendered tables, missing or orphaned companions, aggregate drift from the expected durable-plus-projected rows, audited reads versus durable read rows, immutable-event validation or reduction failures, orphaned tombstones, pending-event age, publication retries, cutover state, queued and dropped outbox rows, derived-link coverage, and counts by origin and relation. When drift exists, the report breaks out missing and extra rows by relation, origin, and endpoint. It exits 1 for unhealthy state; unpublished agent references are informational because a queued publication may still resolve them.

An orphaned tombstone can arrive before the event version it removes. The reducer keeps that tombstone, suppresses a matching stale legacy row, and continues serving unrelated durable links; the warning converges away when the predecessor event arrives. The current top-level doctor report still counts an orphaned tombstone as unhealthy and exits 1 while it is present.

sase artifact doctor --fix rebuilds the aggregate and managed projections from durable truth, repairs references whose files can be followed through Git rename history, and performs the ordinary artifact-index enrichment pass. It does not infer graph state by parsing hand-authored Markdown, run the legacy-index import, or fabricate missing events.

The project-level sase doctor -C project.artifact_link_cutover check reports incomplete or inconsistent cutover markers and any post-import links/ tree changes. Resume an incomplete import with the attested import-indexes --apply command printed by the diagnostic.

sase doctor -C project.primary_sidecar_link_dirt flags uncommitted links/ dirt in sidecar clones nested under a project's primary (human) checkout. That dirt blocks pull-based sidecar auto-sync. sase doctor -R / --fix-primary-sidecar-links restores stranded canonical link-index deletions rather than committing them: durable deletions must land via the machine write lane (hidden host-owned sidecar clones) and reach the primary through auto-sync.

Storage lifecycle

Artifact-link truth lives in several places with different durability:

Path Role Versioned?
Sidecar link-events/v1/<shard>/<digest>.json Content-addressed immutable operation objects; the reducer derives the current graph from their union. Yes. Each owning document sidecar commits the exact event bytes.
Sidecar link-events/STORE.json Fleet-safety fence and legacy-import progress marker (fenced or imported). Yes. One canonical marker in every document sidecar.
Sidecar links/**/*.json Frozen legacy schema-v2 indexes, read only before cutover and ignored after every marker is imported. Historical compatibility data only; never a current write target.
~/.sase/projects/<key>/link-events/v1/... Immutable local receipt history for an event with no document owner, including bead-only events. No. Machine-local SASE state.
~/.sase/projects/<key>/artifact-links.json Rebuildable aggregate of reduced store rows plus projected relationships, with its lock. No. Machine-local SASE state.
~/.sase/projects/<key>/artifact-link-outbox.jsonl Replay queue of canonical event payloads awaiting all required durability receipts. No. Machine-local; retried by publication and hourly housekeeping.
~/.sase/projects/<key>/artifact-link-outbox-dropped.jsonl Audit trail for stale terminal-agent observations that could not become publishable. No. Machine-local.

sase artifact link add publishes an edge-put; rm publishes an edge-remove tombstone. One canonical event is installed in every document sidecar that owns either endpoint, and one command creates at most one chore(artifact-links): persist link events commit per affected repository. Bead ownership is acknowledged through the bead event store. Events with neither a document nor bead owner use the machine-local event root as their durability receipt; bead-only events are installed there as immutable history before their bead receipt is accepted. SASE updates the aggregate only after the operation has every required durability receipt, so an ephemeral checkout cannot report success while holding the only copy. An unchanged put or a removal with no active edge writes no new event, but still verifies that every document-owner sidecar is durably published. The no-op retry therefore fails when a required owner root is unresolved or its event commits have not reached the configured remote.

An audited agent read updates the local reduced view and appends a replayable event to the outbox until the agent's published identity can satisfy document ownership. The commit workflow and hourly backfill retry those events. Rows for terminal agents that remain unpublished for 90 days leave the live queue and enter the dropped audit. Publication commits isolate only canonical event and marker paths, leaving unrelated or pre-existing sidecar dirt for the normal declaration workflow.