Agent Provenance ADR Amendment
Tracker reference: pm-oskdmu. Implementation lineage: pm-itsjf0, pm-0zcwz6, pm-1zhfls, pm-pwq0g5, and pm-te6elw, with bounded automatic resolution and patch-free historical reads implemented by pm-ffz0a9, pm-v8gfi7, and pm-3yxwv5. Stable Claude session discovery across child working directories is tracked by pm-l7m7k5. Cross-harness adapters and operator-managed probe/vocabulary controls are tracked by pm-c0lrdm and pm-yds9dt.
Status: accepted amendment to pm-qwuber. The original stable-author contract remains authoritative; this amendment replaces only its fixed model-only provenance shape.
Decision
Mutation history may retain bounded descriptive agent provenance independently
from the stable mutation author. agent_provenance is a string-keyed map whose
values are either { value, source } observations or null when a detected
harness declares a dimension but cannot expose a value.
The default runtime understands model, effort, role, topic, and
version. Harness
descriptors and trusted embedded hosts may add dimensions such as topic
without a storage migration. The legacy agent_model and
agent_model_source fields remain populated from agent_provenance.model for
backward-compatible readers.
The precedence for each dimension is:
- explicit
PM_AGENT_<DIMENSION>override; - the selected harness descriptor's environment keys;
- MCP client provenance;
- trusted embedding-host provenance;
- bounded argv values.
- an explicitly declared bounded local resolver.
detectAgentIdentity() and detectHarnessIdentity() use ambient invocation
signals when called with no argument. An explicitly supplied signal object
remains isolated from ambient state. SDK hosts that need async-safe scoping use
runWithHarnessDetectionSignals().
Privacy and security boundary
Provenance values are descriptive context, never authentication or
authorization principals. Values are trimmed, length-bounded, and obtained only
from literal descriptor keys, trusted caller data, or a named bounded resolver.
Detection does not spawn processes, traverse process trees, evaluate user
regexes, or access the network. The built-in Claude and Codex resolvers read
only bounded windows of the current session's harness-owned JSONL file. Claude
resolves an explicitly supplied transcript first, then the direct workspace
coordinate, then performs a deterministic scan of at most 512 immediate
project directories for the unique stable session id. This keeps provenance
attached when the invocation cwd moves into a child package. No match is
reported as resolver_input_missing; multiple matches are ambiguous and remain
resolver_failed instead of selecting an arbitrary transcript. Claude uses the
bounded session window to recover its recorded model/version. Codex uses the
recent tail plus a bounded initial-head fallback for oversized sessions to
recover allow-listed turn_context.model and turn_context.effort values.
Both cap traversal, file bytes, lines, and line length, ignore all other fields,
never follow symlinks, and fail closed. agent_identity.probes_enabled or
PM_AGENT_PROBES=off disables every local resolver without disabling ordinary
environment, argv, client, or host detection.
Raw session identifiers remain transient and are never written to history or
telemetry. When a harness and session are both present, history may retain only
the existing domain-separated, truncated agent_instance digest. Public
telemetry continues to use installation-scoped hashes or presence booleans; it
does not export raw provenance values.
Health counts resolvers with an available input, plus failed or ambiguous resolution attempts, as attempts. A sustained attempted-but-zero-success series remains advisory: it truthfully identifies parser, filesystem, ambiguity, or provider-shape drift without treating missing descriptive metadata as authorization or item-state corruption. Missing-input and probes-disabled events are classified separately and do not inflate that warning.
Compatibility
All new history fields are optional. Readers must accept:
- legacy entries with no agent fields;
- model-only entries using
agent_modelandagent_model_source; - new entries with
agent_provenance; - explicit
model: null,effort: null, orrole: nullobservations meaning the dimension was declared but unavailable for a detected harness.
Modern MCP clients may supply a bounded
io.modelcontextprotocol/clientInfo.provenance map in each request's _meta.
The server retains only valid dimension names and trimmed values, then resolves
that map inside the invocation-scoped identity context for that request. The
bounded initialize-era adapter accepts the same map during legacy initialize.
A missing MCP signal is recorded as explicit null; it is never confused with
a legacy entry that predates the dimension.
Unknown provenance dimensions are preserved as data and do not change author resolution. Removing a dimension from a descriptor never rewrites existing history.
Historical disposition
The repository snapshot measured at the start of the 2026-08-01 migration
contains 42,679 immutable history entries. Privacy-safe aggregation by the
presence and shape of agent_provenance.effort found 549 observed values, 166
explicitly unavailable values, and 41,964 entries with no effort key. No raw
provenance value, session identifier, or instance digest was exported during
that measurement.
The missing group is retained as legacy_missing; it is never backfilled from
nearby mutations or inferred from another surface. The unavailable group
means the dimension was declared at write time but could not be observed. The
observed group remains the only group suitable for effort-based evaluation.
Repository history does not persist the CLI-versus-MCP transport as an
identity field, and the current snapshot contains no committed
agent_instance group mixing an observed effort with a legacy-missing effort.
Historical MCP records from the original isolated reproduction therefore keep
the same immutable disposition: missing means unknown, not a change in effort.
Consumers grouping one session must treat legacy_missing, explicit
unavailability, and an observed value as three distinct states. Rewriting the
hash chain would erase the evidence that motivated this amendment and could
fabricate precision that was never captured.
SDK and completeness contracts
The aggregate and core SDK entrypoints export:
AGENT_PROVENANCE_DIMENSIONS;BUILTIN_AGENT_PROVENANCE_ADAPTERS,listAgentProvenanceAdapters(), andregisterAgentProvenanceAdapters();normalizeAgentProvenanceAdapterValue()for stable model-family and effort vocabulary projections that retain the bounded raw observation;detectAgentIdentity()anddetectHarnessIdentity();analyzeAgentProvenanceDescriptorCoverage()for the descriptor capability matrix and negative controls;summarizeAgentModelProvenance()for observed, unavailable, legacy-missing, and inert-capture reporting;analyzeSdkCliParameterCompleteness()for a derived bidirectional CLI flag and strict SDK parameter matrix.projectHistoryProvenance(),compileHistoryProvenanceMatcher(), andsummarizeHistoryProvenance()for immutable, patch-free reads and bounded completeness reporting.
pm history <id>, pm activity, and pm events share --provenance,
--provenance-summary, repeatable --harness, repeatable --agent-instance,
and repeatable --provenance-filter dimension=value. The provenance projection
never returns JSON Patch operations or document hashes. History rows retain
their original one-based stream version after filtering. Events use the same
predicates in the durable derived index, so consumers do not need to scan raw
history payloads.
Legacy author interpretation is workspace-owned data under
agent_identity.identity_vocabulary. It contains a monotonically managed
version and exact literal-to-harness aliases. Reads disclose both the
version and whether a harness was recorded, resolved by vocabulary, or
remains unresolved; immutable authors and hashes are never rewritten.
pm config get agent-identity-vocabulary, config list, and config export
publish only its version and alias_count. Typed mutations use the existing
config transport:
pm config set agent-identity-probes-enabled false
pm config set agent-identity-vocabulary --policy preview-add \
--value "Legacy Codex=codex" --criterion "Legacy Codex" --criterion "Alice"
pm config set agent-identity-vocabulary --policy add \
--value "Legacy Codex=codex"
pm config set agent-identity-vocabulary --policy remove --value "Legacy Codex"
preview-* policies perform no write and report the exact residual unique
author count without returning author spellings. Real add/remove/clear changes
bump the vocabulary revision once; identical adds and absent removes are
idempotent. Aliases for already canonical harness:<name> authors fail closed.
Every built-in interactive harness has a contract-versioned adapter with an implementation version, priority, covered dimensions, source classes, normalization revisions, confidence, waivers, and immutable probe bounds. Packages may register a new namespace or explicitly replace a built-in only at a higher priority. Equal-priority ambiguity and descriptor mismatch fail closed, and the disposer restores the prior adapter.
The SDK/CLI matrix classifies every input as shared, positional, transport,
presentation, local adapter, scope selector, compatibility alias, or SDK-native.
An unknown CLI flag fails closed as unclassified. Committed test baselines cap
every waiver category and the representative behavioral-envelope corpus, so
coverage may expand and waivers may shrink without returning to a curated
hand-picked list.
Operational guidance
Use provenance to answer context questions such as which harness/model/effort
performed a mutation or whether a capture source is inert. Do not use it to
decide who is allowed to mutate an item. Use author, ownership, and explicit
project policy for authorization and coordination.