How attestations flow through meld compositions. The meld edge is both spatial grouping and reactive data pipeline: dragging elements together declares the subscription.
See AXIOMS.md for the attestation flow axioms.
[ax: contact] → [py: enrich] → [prompt: summarize {{subject}}]
[se: contact] → [py: enrich] → [prompt: summarize {{subject}}]
[se: raw] → [se: filtered] → [py: process]
When the user melds these three elements, two subscriptions compile eagerly:
ax→py / se→py: AX and SE are filters, always live. Their query becomes the subscription filter. Any new attestation matching that filter triggers py.py→prompt: Py is a producer. The subscription filter is actor == element:{py_element_id}. When py calls attest(), the resulting attestation triggers prompt.| Source element | Subscription filter | Why |
|---|---|---|
| ax (filter) | The AX element's query filter directly | AX is a pure filter — it doesn't create attestations, it selects them. The filter definition IS the subscription. |
| py / prompt (producer) | actor == element:{upstream_id} | Producers create new attestations tagged with their element ID. The edge watches for attestations from that specific element. |
1. User melds [ax: contact] → [py: enrich] → [prompt: summarize]
Subscriptions compile immediately:
- ax→py: filter = {subjects: ["contact"]}
- py→prompt: filter = {actor: "element:{py_id}"}
2. Attestation enters the system matching "contact"
(via CLI, another element, API — any source)
3. ax→py subscription fires
4. py element executes with that ONE attestation as `upstream`
5. py code runs, calls attest() with enriched data
actor: element:{py_element_id}
6. py→prompt subscription fires
7. prompt element executes with that ONE attestation
8. {{subject}}, {{predicate}}, etc. resolve from the attestation
9. LLM runs, result attestation created
10. Attestation element appears below prompt
Each step is one attestation in, one execution, zero or more attestations out.
AX has no play button. It is always running — this is the current state. When ax→py melds, the subscription starts delivering immediately. No backfill step, no manual trigger. The edge cursor initializes at meld time; attestations matching the AX filter from that point forward flow through.
Existing attestations from before the meld are not retroactively delivered. The subscription is forward-looking from the moment of assembly. The edge cursor marks the boundary.
No pending(). No query(). The attestation is injected as a variable into the execution context, like attest() is today:
# `upstream` is the single attestation that triggered this execution
# injected by the runtime, like attest() is
print(upstream.subjects) # ["alice@example.com"]
print(upstream.predicates) # ["contact"]
print(upstream.contexts) # ["crm"]
print(upstream.attributes) # {"phone": "555-1234", ...}
# Do work with it
enriched = lookup_something(upstream.subjects[0])
# Produce output attestation (triggers downstream if melded)
attest(
subjects=upstream.subjects,
predicates=["enriched"],
contexts=["pipeline"],
attributes={"original": upstream.id, "enriched_data": enriched}
)
When the py element is NOT in a meld (standalone), upstream is None. The element works as it does today — user writes code, clicks play, it runs.
The prompt element has no user code. Variable resolution is automatic. The incoming attestation's fields map to {{template}} placeholders per the existing template syntax in ats/so/actions/prompt/doc.go:
{{subject}} / {{subjects}}{{predicate}} / {{predicates}}{{context}} / {{contexts}}{{actor}} / {{actors}}{{attributes.key}}{{id}}Same delivery mechanism (one attestation triggers one execution), different interface (template interpolation vs code).
The watcher engine's existing OnAttestationCreated hook fires once per new attestation. Since each attestation has a unique ASID and is immutable, the natural deduplication is: fire once per ASID per edge. The edge either has or hasn't seen a given ASID.
A (composition_id, edge_from, edge_to, last_processed_asid, last_processed_timestamp) table tracks what each edge has consumed. This is the per-edge analog of the prompt handler's TemporalCursor.
On system restart or composition reconstruction, the cursor ensures we don't re-fire for attestations already processed.
AX doesn't create attestations. It queries existing ones. When AX is the root of a meld, its query filter becomes the subscription filter for the outgoing edge. The attestations that flow downstream are the original attestations from the store — not copies, not wrappers.
This means: