Provenance Consent Rides the Settings Hierarchy, and Refusal Wins
ADR-0005: Provenance Consent Rides the Settings Hierarchy, and Refusal Wins
Section titled “ADR-0005: Provenance Consent Rides the Settings Hierarchy, and Refusal Wins”Status
Section titled “Status”Accepted
Context
Section titled “Context”Background and Problem Statement
Section titled “Background and Problem Statement”The mif-provenance helper observes sessions: hooks record which files a session touches so provenance can be witnessed instead of asserted (mif-docs-plugin#63). Observation demands a consent surface, and that surface had two open questions: where the configuration lives (a plugin-invented file? environment variables? Claude Code’s settings?) and how conflicting scopes resolve (a project that wants capture on versus a person who wants it off, and the reverse).
Current Limitations
Section titled “Current Limitations”- The plugin previously had no configuration surface at all — hooks were either shipped-on or absent, with no per-user or per-project say.
- Claude Code’s settings precedence is designed for preferences, where the most specific scope wins. Applied naively to consent, a project-local file could re-enable observation a person had explicitly refused — precedence semantics are the wrong shape for refusal.
- A malformed settings file resolves to “no value” in most config systems, which under a default-on design (or a carelessly merged default) could enable observation nobody agreed to.
Decision Drivers
Section titled “Decision Drivers”Primary Decision Drivers
Section titled “Primary Decision Drivers”- Consent before observation: nothing may be captured or stamped without an explicit, inspectable opt-in; refusal must be absolute from any scope.
- Fail-closed misconfiguration: a configuration error must only ever produce less observation, never more.
- No novel config surface: users should express consent where they already express Claude Code configuration, inspectable with the tools they already have.
Secondary Decision Drivers
Section titled “Secondary Decision Drivers”- Determinism: identical file contents must resolve to an identical effective config (the suite’s mif-validate precedent, applied to config).
- Auditability: one grep-able key name across all scopes.
Considered Options
Section titled “Considered Options”Option 1: One namespaced key in Claude Code’s settings hierarchy, with a refusal-wins carve-out
Section titled “Option 1: One namespaced key in Claude Code’s settings hierarchy, with a refusal-wins carve-out”Description: A single mifProvenance key (capture: bool, stamp:
auto/ask/off, both defaulting to off) read from the user scope
(settings.json and settings.local.json under $CLAUDE_CONFIG_DIR,
defaulting to ~/.claude/) and the project scope (.claude/settings.json
and .claude/settings.local.json), following Claude Code’s own precedence
for non-refusal values only. The carve-out: an explicit capture: false or
stamp: "off" at ANY scope defeats enablement at every other scope. Any
parse error or wrong-shaped value resolves the affected scope to explicit
refusal. The plugin-local settings file pattern
(.claude/mif-docs.local.md-style carriers) is the documented fallback for
hosts where the settings hierarchy is unavailable.
Advantages:
- Consent lives where users already look; no new file format, no env-var side channel.
- Refusal semantics match consent’s real shape: revocation from anywhere, no scope that can overrule a “no”.
- Fail-closed by construction — an unreadable consent surface disables the whole feature rather than riding over it.
Disadvantages:
- Diverges from vanilla settings-precedence intuition (a project-local enable does NOT beat a user disable); must be documented plainly.
- Four file reads on every hook invocation (measured: well under the 50 ms disabled-path budget).
Risk Assessment:
- Technical Risk: Low. A ~100-line dependency-free resolver with the carve-out pinned by unit tests in both directions.
- Ecosystem Risk: Low. The key is namespaced; collisions with Claude Code’s own settings vocabulary are structurally avoided.
Option 2: Plugin-owned config file (e.g. .mif-provenance.json)
Section titled “Option 2: Plugin-owned config file (e.g. .mif-provenance.json)”Description: A dedicated file at the repository root, owned and parsed solely by this plugin.
Advantages:
- Full control of semantics; no interaction with host precedence rules.
Disadvantages:
- A novel surface users must discover; consent hidden from the place they configure everything else.
- No natural user-scope story (a personal “never observe me” would need a
second invented file in
$HOME). - Encourages committing the file, making one contributor’s consent look like everyone’s.
Disqualifying Factor: fails the no-novel-surface driver and has no clean per-user refusal, the exact case consent exists for.
Risk Assessment:
- Technical Risk: Low mechanically, but the consent-model gap is a design defect, not an implementation one.
- Ecosystem Risk: Medium. Two config surfaces for one plugin family.
Option 3: Environment variables
Section titled “Option 3: Environment variables”Description: MIF_PROVENANCE_CAPTURE=1-style toggles read by each hook.
Advantages:
- Trivial to implement; naturally per-invocation.
Disadvantages:
- Invisible and unauditable: nothing on disk records what was consented to, and a stray export in a shell profile becomes silent standing consent.
- No scope model at all — no way to express “this project yes, this user no”, let alone refusal-wins between them.
Disqualifying Factor: unauditable standing consent contradicts the consent-before-observation driver.
Risk Assessment:
- Technical Risk: Low to build, high to operate — the misconfiguration surface is every shell profile on the machine.
Decision
Section titled “Decision”We adopt Option 1. Consent rides Claude Code’s own settings hierarchy under
one namespaced mifProvenance key; precedence orders only non-refusal
values, and an explicit disable at any scope is absolute. Configuration
errors fail closed: a malformed or unreadable settings file, or a
wrong-shaped value, contributes an explicit refusal for its scope — the whole
feature disables rather than riding over a consent surface it cannot read.
The resolver (scripts/lib/provenance-config.mjs) is the single reader; no
hook or verb consults the settings files directly.
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- Refusal is absolute and auditable: one grep for
mifProvenanceshows every scope’s word, and any “no” anywhere is the final answer. - Misconfiguration only ever de-escalates: every malformed-input path resolves toward off, pinned by tests in both carve-out directions.
Negative
Section titled “Negative”- Surprising precedence for enables: users accustomed to “most specific wins” must learn that refusal is scope-less; mitigated by stating it in the SKILL.md, the reference page, and this ADR.
- A corrupt but irrelevant settings file disables the feature entirely (strictest reading of fail-closed); accepted — silent partial consent is the worse failure.
Neutral
Section titled “Neutral”- Hosts without the settings hierarchy fall back to the documented plugin-local settings file pattern; the resolver’s semantics are carrier independent.
Decision Outcome
Section titled “Decision Outcome”The decision achieves its objective — observation strictly gated on
inspectable, revocable consent — measured by: the resolver’s unit tests
pinning both refusal directions and the malformed-input postures; the capture
hooks’ disabled path writing nothing and emitting nothing; and no code path
outside the resolver reading the mifProvenance key.
Related Decisions
Section titled “Related Decisions”- ADR-0004: Node Engine Stays Authoritative, Convergence Proven by a Parity Gate — the same no-model-in-the-path determinism discipline this decision applies to configuration resolution.
- The session ledger: format contract — the artifact capture produces once consent is given.
- Skill reference: mif-provenance — the consumer-facing statement of the consent model and trust ceiling.
More Information
Section titled “More Information”- Date: 2026-07-11
- Source: the mif-provenance Epic (mif-docs-plugin#63, Story #64)
- Related ADRs: ADR-0004
2026-07-11
Section titled “2026-07-11”Status: Compliant
Findings:
| Finding | Files | Lines | Assessment |
|---|---|---|---|
| Resolver + refusal-wins carve-out + fail-closed tests | - | - | accepted |
Summary: Decision accepted with the resolver, both carve-out directions and all malformed-input postures pinned by unit tests, and the capture hooks consulting the resolver before any observation I/O.
Action Required: Re-audit if Claude Code’s settings hierarchy gains new scopes (an enterprise layer would need an explicit place in the refusal ordering — which, per this decision, is “refusal wins from there too”).