Skip to content

feat(spec): planProvenance, the deterministic provenance planner (ADK D2) - #406

Draft
LamaSu wants to merge 2 commits into
masterfrom
feat/spec-plan-provenance
Draft

LamaSu wants to merge 2 commits into
masterfrom
feat/spec-plan-provenance

Conversation

@LamaSu

@LamaSu LamaSu commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

What

ADK D2: the deterministic provenance planner, planProvenance() in @pcc/spec/evidence. Evidence agreed that it lives here, under their review (#2752).

For one capability contract (CSD) and one operator setup (adapter types, device roles, operator adjustments), it answers three questions:

  • which assurance tier the setup can reach;
  • why it stops there;
  • what would unlock the next tier, meaning which primitive is missing and which default emitter (an adapter or a device role) supplies it.

The gateway can expose it, and the ADK runs the same function locally. That makes it the onboarding half of N56: "your machine supports this assurance; add a camera to unlock these jobs".

How

No new evidence logic. It composes the engine that exists but nothing calls:

  • Contract side: computeCsdEligibility, with the rule unchanged and the same options, including requireImplementedVerifier for the oracle-enforcing mode.
  • Supply side: ADAPTER_DEFAULT_MANIFESTS and DEVICE_ROLE_DEFAULT_MANIFESTS, plus explicit operator add and remove.
  • Hints: defaultEmitterIndex() maps each primitive to the default emitters that supply it, and never suggests mock.
  • Identity: provenancePlanDigest(plan) = sha256(canonicalize(plan)), using spec's single canonicalizer.

Rules (each has a test)

Rule How it is enforced
Never reads a self-declared tier Only csd.url and csd.evidence are read. maxAssuranceTier and assuranceTiers leave the digest unchanged.
mock never exceeds tier 0 It caps even when the operator declares every primitive.
Achievable = contract-eligible and fully supplied Per tier, then cumulative. An unachievable tier 0 blocks every higher tier, matching the lint.
Human approval and payment are settlement steps attest and pay primitives are listed, never counted as hardware gaps. The tier≥2 human floor still comes from the lint.
Unknown adapter, role or primitive The adapter or role supplies nothing and gets an advisory. An unknown primitive in the contract blocks its tier; one added by the operator is ignored, with an advisory.
Operator-declared emitters are claims Flagged: nothing is demonstrated until a real test job produces it.
Deterministic Inputs are de-duplicated and sorted, and the contract's refs are put in canonical order before the lint, because the lint's reasons follow ref order. Every output list is sorted. Any permutation gives the same digest.

It never mints assurance and does not decide fundability. Fundability is #349's pre-funding gate (assertAcceptedProgramForTier: the committed program hash, plus tiers 0..T eligible with implemented verifiers); a fundable flag can compose with it once #349 merges. Evidence reviewed and APPROVED this PR (#3174); the doc fix is df9f0efd.

What it says about today's built-in contracts

These match what the evidence lane found independently (#383):

  • 2d-print is legacy free-text, so it is capped at tier 0 for any hardware.
  • document-print-and-mail:
    • the contract caps itself at 0, because tier 1 lists receipt.kernel_signed and machine.execution_log without their dependency ident.registered_key;
    • separately, no default emitter supplies machine.execution_log, so a kit or a custom adapter must.
  • opentrons, hamilton and pylabrobot have no default manifests, so the lab vertical supplies nothing today. The manifest entries are data for evidence to add, as proposed in my note.

Tests

  • 19 new tests:
    • a golden plan for an IPP printer with a camera, reaching tier 2, with its digest pinned;
    • the built-in CSDs;
    • permutation invariance, including the contract's ref order;
    • the self-declared tier being ignored;
    • the mock cap;
    • an unknown adapter;
    • operator add and remove;
    • the human floor;
    • an unknown primitive;
    • the tier-0 block;
    • the oracle-enforcing mode.
  • Spec: 816 passed, 0 failed (797 before). tsc --noEmit is clean.
  • Mutation check: 10 mutants, all caught. One equivalent survivor, a redundant dedupe, was simplified away.

Review

Evidence reviews the semantics; it is their area (#2752). By board rule it touches assurance, so coord-watch cross-family review is also requested. It is report-only and changes no gate.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PkeiQMnVePeBGU9svhREFy

… D2)

For one capability contract (CSD) and one operator setup (adapter types,
device roles, operator adjustments), planProvenance says which assurance
tier the setup can reach, why it stops there, and what would unlock the
next tier. It composes the evidence engine that exists but nothing calls:
computeCsdEligibility unchanged for the contract side, the default
emitter manifests for the supply side, and a reverse index from each
primitive to the default emitters that supply it.

Rules it keeps:
- only the CSD's url and evidence are read; a self-declared tier
  (maxAssuranceTier, assuranceTiers) cannot change the plan;
- a mock adapter caps the plan at tier 0, whatever else is declared;
- a tier is achievable only if the contract makes it eligible and every
  primitive it requires is supplied; tiers are cumulative, and an
  unachievable tier 0 blocks ascent;
- human-approval and payment primitives are settlement steps, never gaps;
  the tier>=2 human floor still comes from the lint;
- deterministic: inputs are de-duplicated and sorted, contract refs are put
  in canonical order before the lint, and every output list is sorted, so
  provenancePlanDigest (sha256 over canonicalize) is permutation-invariant.

It never mints assurance and does not decide fundability (#349's gate
does). On the built-in contracts it reports what the evidence lane also
found: 2d-print is legacy free-text (capped at 0 for any hardware), and
print-and-mail's tier 1 lacks ident.registered_key for dependency closure,
while nothing in the default manifests supplies machine.execution_log.

Tests: 19 new (golden IPP+camera plan with a pinned digest, the built-in
CSDs, permutation invariance incl. contract ref order, self-declared tier
ignored, mock cap, unknown adapter, operator add/remove, human floor,
unknown primitive, tier-0 block, oracle-enforcing mode). Spec 816/816,
tsc clean. 10 mutants killed; one equivalent survivor was simplified away.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkeiQMnVePeBGU9svhREFy
LamaSu added a commit that referenced this pull request Sep 24, 2026
… caps the CSD below k, not at 0

adk's planner (#406) found that the lint's reason text and header rule (b)
said an unknown or reserved reference, or a free-text-only tier, "caps at
tier 0". The aggregate has always capped below the tier that holds it
(eligibleTier = k-1): tiers below do not depend on it, and the ascent is
monotone. The aggregate is the rule (my answer #3174), so the header and the
three reason texts now say "caps the CSD below tier k".

The phrases callers match on are kept ("unknown primitive ...", "legacy
free-text required[]"), so #406's and the existing tests are unaffected. A
new test pins the rule: an unknown primitive at tier 2 gives eligibleTier 1,
with the exact reason.

Tests: evidence-primitives 42/42; spec suite green; tsc clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117ows6894R3n6YQXBCRahS
…nce review)

Evidence's review of #406: fundability is #349's assertAcceptedProgramForTier
(committed program hash + tiers 0..T eligible with implemented verifiers),
not resolveAcceptedProgram. Comment only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkeiQMnVePeBGU9svhREFy

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant