For compliance reviewers: every entity created or mutated by an accepted MCP proposal carries six
x-ai-*frontmatter fields naming the source, the decision trace, the model, the prompt hash, the accept time, and the accepter. The metadata is C2PA-aligned, lives on the markdown itself, and isgit diff-readable. The escape hatch is[provenance] inline = false; the audit trail in.synapse/audit.ndjsonkeeps the same metadata regardless.
1. The six fields
After syn pending accept <id> of a capture_fact or
update_status proposal, the resulting entity carries:
x-ai-source: mcp:capture_fact
x-ai-decision-trace-id: pending:7
x-ai-model: anthropic/claude-opus-4-7
x-ai-prompt-hash: sha256:f8e2c1aa…
x-ai-accepted-at: 2026-05-25T15:30:00Z
x-ai-accepted-by: romulo@febasi.com.br
| Field | Meaning |
|---|---|
x-ai-source | How the proposal entered the pending queue. Conventional prefixes: mcp:, cli:. Examples: mcp:capture_fact, cli:capture (SYN-124). |
x-ai-decision-trace-id | Stable trace identifier. v0.3 uses pending:<row-id> because the row is monotonic per workspace and matches what syn pending show shows. |
x-ai-model | Best-effort model id. Set when the MCP client carries x-ai-model or model in the optional context snapshot field of the write tool. Omitted when unknown. |
x-ai-prompt-hash | sha256:<hex> of the canonical proposal payload at enqueue time. Identical to the payload_sha256 field of the matching audit.ndjson row: they link the file to the trail. |
x-ai-accepted-at | RFC-3339 timestamp of the acceptance. |
x-ai-accepted-by | Identifier of the accepter: [provenance] accepted_by config wins; falls back to $USER; finally to "unknown". |
The fields live under the x-ai- namespace so existing YAML readers,
the doctor lint (BR-9.1), and any third-party tool treat them as
forward-compatible extensions. BR-3.4 preserves insertion order, so
the stamps land after the caller-supplied per-type fields and read
as a trailing banner.
2. What does NOT carry stamps
Only mutators that produce or modify a markdown entity stamp it:
capture_fact→ stamps the new entity.update_status→ stamps the existing entity in place.
Mutators that touch auxiliary data do not stamp anything:
link_entitymutates thelinkstable.add_workspace_topic/add_workspace_tagmutateSYNAPSE.md, the manifest is not an entity in the BR-1.4 sense and the attribution lives in the audit trail instead.
The audit trail records every decision regardless of whether the markdown got stamped.
3. The escape hatch: inline = false
Workspaces in regulated industries that need clean entity files for publication / export can disable inline stamping while keeping the audit trail intact:
[provenance]
inline = false
accepted_by = "compliance-team@org" # explicit override of $USER
When inline = false:
- The five (or six)
x-ai-*fields do not land on the entity. .synapse/audit.ndjsonstill records the same metadata.syn audit show <id>is the single source of truth for the decision.
4. Worked example
Starting workspace contains nothing. The user via Claude Code captures a decision:
You: "Capture a decision: we picked PKCE for OAuth on Cliente Alfa. Tag it `auth`."
Claude calls capture_fact with type=note, kind=decision,
title=PKCE for Cliente Alfa OAuth. The proposal lands at pending:1.
The user reviews:
$ syn pending show 1
id 1
kind capture_fact
status pending
proposed_at 2026-05-25T15:00:00Z
source mcp:capture_fact
reason conversation 2026-05-25, decision-making call
payload:
{
"type": "note",
"title": "PKCE for Cliente Alfa OAuth",
"body": "We picked PKCE for OAuth on Cliente Alfa…",
"tags": ["auth"],
"fields": {"kind": "decision"}
}
syn pending accept 1 writes:
---
type: note
id: pkce-for-cliente-alfa-oauth
title: PKCE for Cliente Alfa OAuth
kind: decision
x-ai-source: mcp:capture_fact
x-ai-decision-trace-id: pending:1
x-ai-prompt-hash: sha256:f8e2c1aa…
x-ai-accepted-at: 2026-05-25T15:30:00Z
x-ai-accepted-by: romulo@febasi.com.br
tags:
- auth
created: 2026-05-25T15:30:00Z
updated: 2026-05-25T15:30:00Z
---
We picked PKCE for OAuth on Cliente Alfa…
And .synapse/audit.ndjson gets a single new line:
{"id":1,"kind":"capture_fact","decision":"accepted","proposed_at":"2026-05-25T15:00:00Z","decided_at":"2026-05-25T15:30:00Z","source":"mcp:capture_fact","reason":"conversation 2026-05-25, decision-making call","payload_sha256":"sha256:f8e2c1aa…"}
The entity and the audit row are now linked by payload_sha256 and
x-ai-prompt-hash (same SHA).
5. Reading the stamps from outside
Any tool that can read YAML frontmatter can read the stamps. Common patterns:
# How many entities in this workspace are AI-authored?
grep -rl '^x-ai-source:' projects/ people/ entities/ apis/ tasks/ notes/
# Which decisions came from cli:capture vs mcp:?
ag '^x-ai-source: ' --no-numbers | sort -u
# Strip stamps before exporting (e.g. for public publication)
syn export --as html --out /tmp/site # already strips by design
# the static site exposes body, not frontmatter
The stamps are part of the markdown; git diff shows them naturally
and they survive in any clone of the workspace.
6. What does NOT make the cut
- No PGP / per-entity signature. Disproportionate to the threat model; ed25519 is reserved for the portable identity bundle (ADR-014) where the threat model warrants it.
- No chained hash between stamps and audit log. Slot reserved for a future ADR if a downstream tool asks for it.
- No
x-ai-historyof previous stamps. When an entity is mutated twice via the pending queue the second accept overwrites the stamps; full history lives in the audit trail. Slot reserved for v0.4 if demand justifies.
7. References
- ADR-012: the load-bearing decision behind every field above.
- AUDITABILITY.md: companion document focused on the workspace-wide audit trail.
- BUSINESS_RULES.md §12: normative rules around AI safety, federation, freshness.
- MCP.md: the MCP write tools that originate every stampable proposal.