From fd17e857159243b0cca522210ba6705b7231077e Mon Sep 17 00:00:00 2001 From: Nader Helmy Date: Wed, 15 Jul 2026 17:25:31 -0500 Subject: [PATCH] feat: promote P051 to D165 with cost-policy scope vocabulary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D165 records the orchestration wiring shipped 2026-07-14 (live baton/ join proof at log indices 77509-77512, 77548-77550, closure 77559), adds the subagent role term to the D142 vocabulary (SKILL.md v0.6.0), and gives §6.7.1 capability envelopes / §1.11 certificate scopes an optional cost_policy sub-field (model_tiers allowlist, max_tokens budget). checkCostPolicy in @atrib/verify evaluates caller-supplied usage facts; the record-only walk produces no cost_policy mismatch, the max_amount/counterparties posture. New walk-scope-cost-policy conformance case with pinned usage vectors; existing corpus cases regenerate byte-identical. Also moves the misplaced Pending decisions header below D164 where a renumber merge had left it. --- .changeset/d165-cost-policy-scope.md | 5 + CLAUDE.md | 9 +- DECISIONS.md | 57 +++++----- atrib-spec.md | 16 ++- ...ate-conformance-delegation-certificates.ts | 74 ++++++++++++- packages/verify/src/delegation.ts | 68 +++++++++++- packages/verify/src/index.ts | 3 + ...onformance-delegation-certificates.test.ts | 25 +++++ packages/verify/test/delegation.test.ts | 52 +++++++++ skills/atrib/SKILL.md | 8 +- .../cases/walk-scope-cost-policy.json | 103 ++++++++++++++++++ .../delegation-certificates/manifest.json | 6 +- 12 files changed, 381 insertions(+), 45 deletions(-) create mode 100644 .changeset/d165-cost-policy-scope.md create mode 100644 spec/conformance/delegation-certificates/cases/walk-scope-cost-policy.json diff --git a/.changeset/d165-cost-policy-scope.md b/.changeset/d165-cost-policy-scope.md new file mode 100644 index 00000000..3723a0eb --- /dev/null +++ b/.changeset/d165-cost-policy-scope.md @@ -0,0 +1,5 @@ +--- +'@atrib/verify': minor +--- + +Add the [D165](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d165-orchestration-wiring-with-routing-and-cost-accounting) `cost_policy` capability-envelope sub-field (`model_tiers`, `max_tokens`) to `DelegationScope`, with `checkCostPolicy(policy, usage)` for caller-supplied usage facts. Signal-only per [§6.7.3](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#673-out-of-envelope-is-a-signal-not-invalidation); the record-only [§1.11.4](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#1114-verifier-walk) walk produces no `cost_policy` mismatch. New conformance case `walk-scope-cost-policy` with pinned usage vectors. diff --git a/CLAUDE.md b/CLAUDE.md index 65565d00..e322a535 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,7 +47,7 @@ atrib/ CLAUDE.md # THIS FILE: hub doc, conventions, invariants DESIGN.md # Product design system source of truth: current state, target state, tokens, components, surface backlog atrib-spec.md # The single source of truth for the protocol - DECISIONS.md # Architectural decision log (D001-D164; D070 accepts the Record Body Archive Layer per §2.12; D073 remains the placeholder ADR for the `handoff` event_type byte; D071 codifies spec writing conventions; D072 governs orphan handling; D074 + D075 capture the git-trailer integration and compose-not-override hook config patterns; D076 introduces the opt-in long-lived atrib-emit daemon; D077 codifies pass^k as the primary Track B reporting metric, k=3 default, promoted from P019 on 2026-05-10; D081 introduces emitInProcess for hook-class producers (signing in-process, byte-identical to MCP-signed records); D082 supersedes D081's integration shape by shipping `atrib-emit-cli` from `@atrib/emit` and having the hook helper spawn the binary, so the hook source directory never becomes an npm workspace; D083 extends D078 with a harness session-id discovery registry in `@atrib/mcp` so cognitive-primitive MCP servers spawned by Claude Code, and future harnesses, derive `context_id` from the parent's session_id env var without operator-side config (v2 2026-05-23 adds optional file-fallback to the registry for startup-spawn harnesses like Claude Code whose MCP children pre-date the per-session env; SessionStart hook writes `~/.claude/state/active-session-id-` and `resolveEnvContextId` reads it when env unset); D084 ships per-event instrumentation jsonl files under `~/.atrib/state/` for read primitives (Surface 6, via `logReadPrimitiveCall` in `@atrib/mcp@0.10.0`), SessionStart (Surface 7), cli-spawn transport (Surface 8), plus a unified analyzer (Surface 9, host-side) that joins all four jsonl pillars + the signed-record mirror so the loop-closure question can be answered against data instead of inferred from one source; `@atrib/recall` compact-mode response now always carries `record_hash` so callers can chain other primitives from any result; D085/D086 tune recall calibration and BM25 content indexing; D087 formalizes signed diagnostic outcome + trace replay as the canonical repair/refinement pattern; D088 updates AP2 transaction detection to successful CheckoutReceipt / PaymentReceipt as the current v0.2 hook while keeping the v0.1 PaymentMandate DataPart fallback; D089 adds verifier-side AP2 / Verifiable Intent evidence checks in `@atrib/verify`; D090 adds async AP2 receipt JWT verification with `jose`, trusted JWKS, and verifier metadata; D091 adds async AP2 / VI SD-JWT conformance with OpenWallet `sd-jwt-js`; D092 adds typed AP2 / VI mandate constraint evaluation; D093 makes the AP2 / VI fixture directory the local verifier corpus; D094 attaches AP2 / VI evidence to verifier results as a tiered block; D095 adds an AP2 Path 2 content_id receipt identity ladder; D096 adds a pinned offline AP2 / VI crypto conformance corpus and named verifier hardening for JOSE, JWKS, SD-JWT, and clock edges; D097 adds an opt-in AP2 live interop artifact harness in `@atrib/integration`; D098 keeps AP2 receipt signatures as external evidence and makes Path 2 transaction records carry an agent `signers[]` entry via `signTransactionRecord`; D099 commits explicit emit content through default `args_hash` and gives direct CLI emits a default mirror path; D100 lets MCP middleware sign and run `onRecord` with log submission disabled for offline tests and local-mirror-only hosts; D101 adds the substrate-wide adversarial conformance corpus for §1.4 signing, §3.2.4 full edge derivation, D067 race vectors, and D052 creator-signer separation; D102 requires sandboxed producers to keep Ed25519 signing keys outside the sandbox through a host signer proxy; D103 adds optional SSE and JSON Feed subscription surfaces over commitment-visible fields; D104 codifies `ATRIB_PARENT_RECORD_HASH` parent-child threading through `informed_by`; D105 adds verifier-side Pattern 3 handoff claim acceptance in `@atrib/verify`; D106 promotes `@atrib/verify-mcp` as cognitive primitive #7 after two independent Pattern 3 receiving flows; D107 adds AP2 counterparty attestation over atrib transaction bytes and makes `cross_attestation.signers_valid` count distinct verified keys; D108 makes OpenTelemetry/OpenInference span trees an intake/correlation layer and puts detailed observability fields in local sidecar content for recall, trace, and summarize; D109 adds generic tiered authorization evidence blocks, MCP/OAuth evidence checks, and resolved capability facts to `@atrib/verify`; D110 closes producer-side MCP/OAuth evidence capture, DPoP proof checks, the local harness, and the §5.5.6 OAuth corpus; D111 adds the archive evidence API, producer-side archive submission, HTTP-backed DPoP replay-cache adapter, and host-owned OAuth introspection helper; D112 adds `@atrib/memory-tool` as an Anthropic Memory Tool handler wrapper that signs memory commands while leaving storage host-owned; D113 makes producers omit unvalidated `informed_by` refs by default while keeping `allow_unresolved_informed_by` for deliberate dangling claims; D114 adds the Google ADK Python plugin proof at the Python tool callback boundary; D115 defines the three-signal same-session agent-to-subagent env bundle; D116 adds source-aware producer-side `informed_by` validation; D117 classifies demo/proof record treatment by execution surface; D118 makes the explorer primary trace path a presentation rule over trace plus chain while keeping human-attested approval as separate signed evidence; D119 keeps AAuth as verifier-side authorization evidence with a producer capture helper and conformance corpus; D120 accepts the optional local substrate coordinator pattern while keeping startup-spawn signing, sidecars, and outbound context wrapper-owned; D121 accepts runtime-log proof manifests for host-owned run windows while keeping raw runtime logs outside the public log and trace systems as projections; D122 keeps host runtime adapters distinct from agent framework adapters and pins one signing owner per host event; D123 makes critical-path content recall require complete evidence or explicit fallback; D124 makes base recall context scope explicit; D125 makes complete content recall coverage-first; D126 adds the durable content index; D127 gates primitive runtime health on recall contract freshness; D128 makes host-owned primitive runtime updates a build, restart, and direct-probe command; D129 gates primitive runtime health on every mounted primitive package and tool surface; D130 gates primitive runtime health on deterministic non-mutating behavioral probes while write primitives stay skipped until validate-only contracts exist; D131 adds the Google ADK decision-ledger proof at the before-tool authority boundary; D132 keeps x401 proof requirements as verifier-side authorization evidence rather than payment detection; D133 adds `@atrib/action-gate` as the host-owned decision and outcome proof package for high-impact actions; D134 adds x401 producer capture, local proof-gate and multi-endpoint E2E, sanitized archive projection, Explorer propagation, optional origin, trust, proof-payment binding facts, released Proof SDK native interop, Proof VC credential-verifier evidence, and the Proof upstream open-thread map; D135 threads delegated-builder atrib context via orchestrator-injected explicit `context_id` + `informed_by` rather than ambient env/file, since the builder's atrib runtime is a shared persistent process; D136 adds the consolidated client SDKs — `@atrib/sdk` with attest/recall verbs, daemon-first with in-process fallback and no third signing implementation, plus the Python `atrib` distribution as the first non-TypeScript §1/§5 implementation, held byte-identical through the shared conformance corpora and a cross-implementation determinism harness, both in-repo with an extraction-ready layout; D137 makes the universal evidence envelope the single attachment model for external evidence per §5.5.7; D138 generalizes cross-log replication to anchor plurality per §2.11.7-§2.11.13; D139 adds the session_checkpoint event type extension-first per §1.2.10 with byte 0x08 reserved; D140 adds delegation certificates, principal keys certifying ephemeral run keys, per §1.11; D141 publishes the dev.atrib/attribution MCP extension v0.1 per §1.5.4.1 and docs/extensions/dev.atrib-attribution/; D142 codifies orchestration-topology baton-pass and join records as conventional attest content shapes and registers the continuation-packet evidence profile, the ninth atrib-maintained §5.5.7 profile; D143 evaluates authority as verifier-side policy over informed_by lineage with minimum-along-path propagation, closing the laundering gap without adding record fields or edge types; D144 adds the pre-action elevation gate in @atrib/action-gate composing D143 authority evaluation with the D052/D107 cross-attestation distinctness rule; D145 adds action-bound single-use authorization tokens over a JCS-plus-SHA-256 five-field binding with a host-owned atomic consumption store; D146 widens mirror inheritance from the effective file to the corpus-scoped directory, deterministic cross-file tie-breaks by signed timestamp then canonical hash, with a deletable advisory tail index; D147 spins the payment-rail material out of the spec core into the independently versioned payments profile at docs/payments-profile.md, leaving stable tombstone anchors behind and registering the payments-detection and payments-settlement evidence profiles; D148 promotes the primitive runtime to atribd, the public stateless-native local daemon, built behind a transport adapter with per-context write serialization and the seven-family conformance corpus at spec/conformance/atribd/; D149 composes cross-attestation with a caller-supplied trust set so untrusted co-signers surface as sybil_suspected instead of counting as authority; D150 generalizes attestation as corroboration off transactions to any signed target, extension-first per §8.7.6; D151 makes own signed content win over chain-derived text in rendering; D152 makes handoff verdicts receiver-computed; D153 gives chain expansion a reserved budget share; D154 separates memory retrieval selection from expansion; D155 makes write-primitive refusals error-shaped; D156 makes tool-call score suppression liftable per query; D157 renders revision lineage as an ordered connected chain; D158 renders dropped chain members as a compact ordered path line; D159 lets the lineage tail win budget admission; D160 carries temporal provenance on rendered memory lines; D161 links cross-topic revisions under a stricter content bar; D162 keeps factual values untruncated in rendered memory; D163 ships the session-transcript runtime-log source that binds harness transcript windows (Claude-Code-style session JSONL, the Session Traces Format shape; D164 collapses the primitive surface to the attest/recall verbs with the fifteen legacy tool names as permanent aliases, byte-identical records, and the alias-window migration rules) to signed records through manifest receipts, fork and compaction bindings, and durable-body refs; v2 adds the auto-detected codex-rollout-jsonl profile). A "Pending decisions" section at the end (forward-looking pattern) tracks forward-looking decisions awaiting action (P002, P004, P005, P008, P009, P010, P012, P013, P016-P018, P021, P024, P026, P027, P036-P040, P047, P051, P052). P012, P013, P016, and P017 cover remaining runtime and sandboxing patterns. P018 and P021 cover eval-framework adoption and benchmark publication. P024, P026, and P027 cover spec hosting, multi-creator SessionStart, and host-side hook deployment. P036-P040 cover the support/RCA implication set from the Autumn support-investigation case study: cross-harness continuation packets, skill/context provenance, hosted-agent diagnostics, the support/RCA demo wedge, and Mastra source verification. P047 remains from the 2026-07 redesign upgrade-path candidate set (attest/recall rename; full drafts under `docs/adr-draft-p04x-*.md`, plan in `docs/redesign-upgrade-path.md`); P042-P045 and P049 were promoted to D137-D141 on 2026-07-06; P050 was promoted to D142 on 2026-07-08; P048 was promoted to D147 and P046 to D148 on 2026-07-10. P051 covers wiring the D142 records into the orchestration infrastructure itself, with per-agent model/effort/token-spend accounting and D140 cost-policy scopes. P052 holds the transcript recall corpus composition-first: manifests and extraction receipts are the interface to memory tools, with explicit attribution triggers for ever revisiting. + DECISIONS.md # Architectural decision log (D001-D165; D070 accepts the Record Body Archive Layer per §2.12; D073 remains the placeholder ADR for the `handoff` event_type byte; D071 codifies spec writing conventions; D072 governs orphan handling; D074 + D075 capture the git-trailer integration and compose-not-override hook config patterns; D076 introduces the opt-in long-lived atrib-emit daemon; D077 codifies pass^k as the primary Track B reporting metric, k=3 default, promoted from P019 on 2026-05-10; D081 introduces emitInProcess for hook-class producers (signing in-process, byte-identical to MCP-signed records); D082 supersedes D081's integration shape by shipping `atrib-emit-cli` from `@atrib/emit` and having the hook helper spawn the binary, so the hook source directory never becomes an npm workspace; D083 extends D078 with a harness session-id discovery registry in `@atrib/mcp` so cognitive-primitive MCP servers spawned by Claude Code, and future harnesses, derive `context_id` from the parent's session_id env var without operator-side config (v2 2026-05-23 adds optional file-fallback to the registry for startup-spawn harnesses like Claude Code whose MCP children pre-date the per-session env; SessionStart hook writes `~/.claude/state/active-session-id-` and `resolveEnvContextId` reads it when env unset); D084 ships per-event instrumentation jsonl files under `~/.atrib/state/` for read primitives (Surface 6, via `logReadPrimitiveCall` in `@atrib/mcp@0.10.0`), SessionStart (Surface 7), cli-spawn transport (Surface 8), plus a unified analyzer (Surface 9, host-side) that joins all four jsonl pillars + the signed-record mirror so the loop-closure question can be answered against data instead of inferred from one source; `@atrib/recall` compact-mode response now always carries `record_hash` so callers can chain other primitives from any result; D085/D086 tune recall calibration and BM25 content indexing; D087 formalizes signed diagnostic outcome + trace replay as the canonical repair/refinement pattern; D088 updates AP2 transaction detection to successful CheckoutReceipt / PaymentReceipt as the current v0.2 hook while keeping the v0.1 PaymentMandate DataPart fallback; D089 adds verifier-side AP2 / Verifiable Intent evidence checks in `@atrib/verify`; D090 adds async AP2 receipt JWT verification with `jose`, trusted JWKS, and verifier metadata; D091 adds async AP2 / VI SD-JWT conformance with OpenWallet `sd-jwt-js`; D092 adds typed AP2 / VI mandate constraint evaluation; D093 makes the AP2 / VI fixture directory the local verifier corpus; D094 attaches AP2 / VI evidence to verifier results as a tiered block; D095 adds an AP2 Path 2 content_id receipt identity ladder; D096 adds a pinned offline AP2 / VI crypto conformance corpus and named verifier hardening for JOSE, JWKS, SD-JWT, and clock edges; D097 adds an opt-in AP2 live interop artifact harness in `@atrib/integration`; D098 keeps AP2 receipt signatures as external evidence and makes Path 2 transaction records carry an agent `signers[]` entry via `signTransactionRecord`; D099 commits explicit emit content through default `args_hash` and gives direct CLI emits a default mirror path; D100 lets MCP middleware sign and run `onRecord` with log submission disabled for offline tests and local-mirror-only hosts; D101 adds the substrate-wide adversarial conformance corpus for §1.4 signing, §3.2.4 full edge derivation, D067 race vectors, and D052 creator-signer separation; D102 requires sandboxed producers to keep Ed25519 signing keys outside the sandbox through a host signer proxy; D103 adds optional SSE and JSON Feed subscription surfaces over commitment-visible fields; D104 codifies `ATRIB_PARENT_RECORD_HASH` parent-child threading through `informed_by`; D105 adds verifier-side Pattern 3 handoff claim acceptance in `@atrib/verify`; D106 promotes `@atrib/verify-mcp` as cognitive primitive #7 after two independent Pattern 3 receiving flows; D107 adds AP2 counterparty attestation over atrib transaction bytes and makes `cross_attestation.signers_valid` count distinct verified keys; D108 makes OpenTelemetry/OpenInference span trees an intake/correlation layer and puts detailed observability fields in local sidecar content for recall, trace, and summarize; D109 adds generic tiered authorization evidence blocks, MCP/OAuth evidence checks, and resolved capability facts to `@atrib/verify`; D110 closes producer-side MCP/OAuth evidence capture, DPoP proof checks, the local harness, and the §5.5.6 OAuth corpus; D111 adds the archive evidence API, producer-side archive submission, HTTP-backed DPoP replay-cache adapter, and host-owned OAuth introspection helper; D112 adds `@atrib/memory-tool` as an Anthropic Memory Tool handler wrapper that signs memory commands while leaving storage host-owned; D113 makes producers omit unvalidated `informed_by` refs by default while keeping `allow_unresolved_informed_by` for deliberate dangling claims; D114 adds the Google ADK Python plugin proof at the Python tool callback boundary; D115 defines the three-signal same-session agent-to-subagent env bundle; D116 adds source-aware producer-side `informed_by` validation; D117 classifies demo/proof record treatment by execution surface; D118 makes the explorer primary trace path a presentation rule over trace plus chain while keeping human-attested approval as separate signed evidence; D119 keeps AAuth as verifier-side authorization evidence with a producer capture helper and conformance corpus; D120 accepts the optional local substrate coordinator pattern while keeping startup-spawn signing, sidecars, and outbound context wrapper-owned; D121 accepts runtime-log proof manifests for host-owned run windows while keeping raw runtime logs outside the public log and trace systems as projections; D122 keeps host runtime adapters distinct from agent framework adapters and pins one signing owner per host event; D123 makes critical-path content recall require complete evidence or explicit fallback; D124 makes base recall context scope explicit; D125 makes complete content recall coverage-first; D126 adds the durable content index; D127 gates primitive runtime health on recall contract freshness; D128 makes host-owned primitive runtime updates a build, restart, and direct-probe command; D129 gates primitive runtime health on every mounted primitive package and tool surface; D130 gates primitive runtime health on deterministic non-mutating behavioral probes while write primitives stay skipped until validate-only contracts exist; D131 adds the Google ADK decision-ledger proof at the before-tool authority boundary; D132 keeps x401 proof requirements as verifier-side authorization evidence rather than payment detection; D133 adds `@atrib/action-gate` as the host-owned decision and outcome proof package for high-impact actions; D134 adds x401 producer capture, local proof-gate and multi-endpoint E2E, sanitized archive projection, Explorer propagation, optional origin, trust, proof-payment binding facts, released Proof SDK native interop, Proof VC credential-verifier evidence, and the Proof upstream open-thread map; D135 threads delegated-builder atrib context via orchestrator-injected explicit `context_id` + `informed_by` rather than ambient env/file, since the builder's atrib runtime is a shared persistent process; D136 adds the consolidated client SDKs — `@atrib/sdk` with attest/recall verbs, daemon-first with in-process fallback and no third signing implementation, plus the Python `atrib` distribution as the first non-TypeScript §1/§5 implementation, held byte-identical through the shared conformance corpora and a cross-implementation determinism harness, both in-repo with an extraction-ready layout; D137 makes the universal evidence envelope the single attachment model for external evidence per §5.5.7; D138 generalizes cross-log replication to anchor plurality per §2.11.7-§2.11.13; D139 adds the session_checkpoint event type extension-first per §1.2.10 with byte 0x08 reserved; D140 adds delegation certificates, principal keys certifying ephemeral run keys, per §1.11; D141 publishes the dev.atrib/attribution MCP extension v0.1 per §1.5.4.1 and docs/extensions/dev.atrib-attribution/; D142 codifies orchestration-topology baton-pass and join records as conventional attest content shapes and registers the continuation-packet evidence profile, the ninth atrib-maintained §5.5.7 profile; D143 evaluates authority as verifier-side policy over informed_by lineage with minimum-along-path propagation, closing the laundering gap without adding record fields or edge types; D144 adds the pre-action elevation gate in @atrib/action-gate composing D143 authority evaluation with the D052/D107 cross-attestation distinctness rule; D145 adds action-bound single-use authorization tokens over a JCS-plus-SHA-256 five-field binding with a host-owned atomic consumption store; D146 widens mirror inheritance from the effective file to the corpus-scoped directory, deterministic cross-file tie-breaks by signed timestamp then canonical hash, with a deletable advisory tail index; D147 spins the payment-rail material out of the spec core into the independently versioned payments profile at docs/payments-profile.md, leaving stable tombstone anchors behind and registering the payments-detection and payments-settlement evidence profiles; D148 promotes the primitive runtime to atribd, the public stateless-native local daemon, built behind a transport adapter with per-context write serialization and the seven-family conformance corpus at spec/conformance/atribd/; D149 composes cross-attestation with a caller-supplied trust set so untrusted co-signers surface as sybil_suspected instead of counting as authority; D150 generalizes attestation as corroboration off transactions to any signed target, extension-first per §8.7.6; D151 makes own signed content win over chain-derived text in rendering; D152 makes handoff verdicts receiver-computed; D153 gives chain expansion a reserved budget share; D154 separates memory retrieval selection from expansion; D155 makes write-primitive refusals error-shaped; D156 makes tool-call score suppression liftable per query; D157 renders revision lineage as an ordered connected chain; D158 renders dropped chain members as a compact ordered path line; D159 lets the lineage tail win budget admission; D160 carries temporal provenance on rendered memory lines; D161 links cross-topic revisions under a stricter content bar; D162 keeps factual values untruncated in rendered memory; D163 ships the session-transcript runtime-log source that binds harness transcript windows (Claude-Code-style session JSONL, the Session Traces Format shape; D164 collapses the primitive surface to the attest/recall verbs with the fifteen legacy tool names as permanent aliases, byte-identical records, and the alias-window migration rules) to signed records through manifest receipts, fork and compaction bindings, and durable-body refs; v2 adds the auto-detected codex-rollout-jsonl profile; D165 wires D142 baton/join signing into the operator's orchestration routing plane with per-leg routing facts in content/sidecar, adds the `subagent` role term, and gives §6.7.1 envelopes / §1.11 certificate scopes an optional cost_policy (model_tiers, max_tokens) as a signal-only signed budget grant). A "Pending decisions" section at the end (forward-looking pattern) tracks forward-looking decisions awaiting action (P002, P004, P005, P008, P009, P010, P012, P013, P016-P018, P021, P024, P026, P027, P036-P040, P047, P052). P012, P013, P016, and P017 cover remaining runtime and sandboxing patterns. P018 and P021 cover eval-framework adoption and benchmark publication. P024, P026, and P027 cover spec hosting, multi-creator SessionStart, and host-side hook deployment. P036-P040 cover the support/RCA implication set from the Autumn support-investigation case study: cross-harness continuation packets, skill/context provenance, hosted-agent diagnostics, the support/RCA demo wedge, and Mastra source verification. P047 remains from the 2026-07 redesign upgrade-path candidate set (attest/recall rename; full drafts under `docs/adr-draft-p04x-*.md`, plan in `docs/redesign-upgrade-path.md`); P042-P045 and P049 were promoted to D137-D141 on 2026-07-06; P050 was promoted to D142 on 2026-07-08; P048 was promoted to D147 and P046 to D148 on 2026-07-10. P051 was promoted to D165 on 2026-07-15. P052 holds the transcript recall corpus composition-first: manifests and extraction receipts are the interface to memory tools, with explicit attribution triggers for ever revisiting. ARCHITECTURE.md # Technical architecture overview: trust model, protocol layers, design decisions PRIOR-ART.md # Prior art & standards map: every spec/protocol atrib builds on, organized by layer METRICS.md # Tiered metrics framework + lifecycle states + quarterly evolution review for the dogfood experiment docs/publishing-new-npm-package.md # Runsheet for creating and publishing a new public npm package. @@ -359,7 +359,7 @@ Every normative MUST in the spec must have a corresponding test. The spec's test Multi-agent orchestration (workflows, subagent fleets) must compose with model routing; "token cost is not a constraint" directives change scope, never tier policy. Rules, learned from the 2026-07-06 redesign session (a multi-million-token -single-day subagent spend on the top tier; see [P051](DECISIONS.md#p051-orchestration-infrastructure-dogfood-wiring-with-cost-and-routing-accounting)): +single-day subagent spend on the top tier; see [D165](DECISIONS.md#d165-orchestration-wiring-with-routing-and-cost-accounting), promoted from P051): 1. **Tier by task class, not by inheritance.** Mechanical agents (corpus generation/regeneration, linkification, doc placement, test refactors, @@ -398,8 +398,9 @@ single-day subagent spend on the top tier; see [P051](DECISIONS.md#p051-orchestr each agent rediscover it. 5. **Account for spend.** Report per-fleet token totals and tier choices in the integration summary so the operator never needs transcript archaeology - to answer "where did the budget go." (P051 makes this a signed sidecar - fact once implemented, including harness identity for cross-harness legs.) + to answer "where did the budget go." ([D165](DECISIONS.md#d165-orchestration-wiring-with-routing-and-cost-accounting) wires this + as signed baton/join routing facts, including harness identity for + cross-harness legs.) Files in this repo describe orchestration infrastructure in role terms (orchestrator, relay, executor harness, loop layer) rather than naming local diff --git a/DECISIONS.md b/DECISIONS.md index 6196ca6f..f6067d10 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -7681,14 +7681,14 @@ Full design document: [docs/adr-draft-p049-mcp-extension.md](docs/adr-draft-p049 - [D140](#d140-delegation-certificates-principal-keys-certify-ephemeral-run-keys) certificates are what make the receiving side's authority verifiable: the profile's `verified` tier binds `target_principal` to a [§1.11.4](atrib-spec.md#1114-verifier-walk) walk. That dependency is why P050 waited for [D140](#d140-delegation-certificates-principal-keys-certify-ephemeral-run-keys) before landing. - Role-term facts compose with the repository's public-vocabulary rule: packet contents that name operator infrastructure stay in the private body, reachable only through mirror/archive retrieval, never in signed content or envelope facts. - No signed byte, event_type, or graph change anywhere. [§3.2.4](atrib-spec.md#324-edge-derivation-rules) derivation is untouched; the records are ordinary observations whose structure the existing edge types already carry. -- Per-agent model/effort/token-spend accounting on these records is [P051](#p051-orchestration-infrastructure-dogfood-wiring-with-cost-and-routing-accounting)'s scope, not this entry's; this entry supplies the record shapes P051 wires into the orchestration infrastructure. +- Per-agent model/effort/token-spend accounting on these records was [D165](#d165-orchestration-wiring-with-routing-and-cost-accounting)'s scope (queued as P051 when this entry landed), not this entry's; this entry supplies the record shapes [D165](#d165-orchestration-wiring-with-routing-and-cost-accounting) wires into the orchestration infrastructure. **Implementation (2026-07-08):** skill section with both code shapes ([skills/atrib/SKILL.md](skills/atrib/SKILL.md) v0.4.0), [docs/evidence-profiles/continuation-packet.md](docs/evidence-profiles/continuation-packet.md), spec [§5.5.7](atrib-spec.md#557-universal-evidence-envelope) post-initial registry addition, `spec/conformance/evidence-envelope/` continuation-packet family (4 cases: valid baton envelope, profile-level hash mismatch, withheld public projection, signed-baton-record via the `ref.record_hash` sibling rule), and the `@atrib/verify` registry plus reference tests (envelope suites at 57 tests). **Cross-references.** - [P036](#p036-cross-harness-continuation-packet-for-supportrca-investigations), the packet shape the baton carries. -- [P051](#p051-orchestration-infrastructure-dogfood-wiring-with-cost-and-routing-accounting), the infrastructure wiring and cost accounting these shapes feed. +- [D165](#d165-orchestration-wiring-with-routing-and-cost-accounting), the infrastructure wiring and cost accounting these shapes feed (promoted from P051). - [D067](#d067-multi-producer-chain-composition-precedence-contract) / [D041](#d041-informed_by-linking-primitive-and-informed_by-edge-type), the topologies as graph structure. - [D135](#d135-delegated-builder-atrib-context-threads-via-orchestrator-injected-explicit-args), orchestrator-injected context for the receiving side. - [D137](#d137-universal-evidence-envelope-as-the-single-protocol-level-attachment-model), the envelope this profile registers under; [D140](#d140-delegation-certificates-principal-keys-certify-ephemeral-run-keys), the authority binding. @@ -8171,11 +8171,6 @@ No new `event_type` byte ([D036](#d036-bar-for-promoting-an-extension-uri-to-atr **Alternatives considered.** (a) Sign each transcript event under the agent's key: rejected here; transcripts are bulk host-owned bodies, the [D121](#d121-runtime-log-proof-manifests-verify-host-owned-run-windows) window commitment is the boundary, and per-event signing remains [P013](#p013-new-runtime-integration-pattern---hosted-runtime-adapter-sign-events-stored-by-hosted-runtimes-like-anthropic-managed-agents)'s hosted-runtime question. (b) A public `@atrib/session-transcript` package: rejected per [D122](#d122-host-runtime-adapters-stay-distinct-from-agent-framework-adapters) until repeated implementations prove the boundary. (c) Per-harness adapters (one for Claude Code, one for Codex, one for Pi): rejected; the lenient mapping covers the interchange shape directly and degrades cleanly, and harness-specific parsing can layer on later without changing any manifest bytes. (d) Indexing transcript content into recall in the same change: deferred to its own decision per the research doc; it crosses the [D086](#d086-bm25-corpus-extended-from-annotations-to-per-event_type-record-content) unit-of-indexing boundary and the signed-scope boundary, and it should not ride in on a manifest adapter. **v2 (2026-07-14): Codex rollout profile.** The example gains a second line profile, `codex-rollout-jsonl/v1`, selected explicitly via the source `format` option or auto-detected by `manifestSessionTranscriptFile` when a file's first line is a `session_meta` object. The profile maps Codex rollout lines at full fidelity: event ids from `payload.id`, kinds as `.` (for example `response_item.function_call`), session identity and CLI version from the `session_meta` payload. The claude-code profile, its fixtures, and all v1 manifest bytes are unchanged; per alternative (c) the profile layers on the same adapter without touching manifest structure. Verified against a real 208-event rollout during development. - -# Pending decisions - -These will get full ADRs when we act on them. Recorded here so they remain findable and don't silently drop. Per the global Deferred Decision Logging convention, this section uses the forward-looking pattern (forward-looking decisions that will become numbered ADRs when codified). - ## D164: attest/recall verb rename and primitive-surface collapse **Date:** 2026-07-14 @@ -8219,6 +8214,35 @@ These will get full ADRs when we act on them. Recorded here so they remain finda **Cross-references.** [D079](#d079-the-six-core-cognitive-primitives-atribs-agent-facing-surface) / [D106](#d106-verify-is-promoted-to-cognitive-primitive-7) (superseded surface enumeration), [D080](#d080-primitive-lifecycle--extensions-first-dedicated-mcps-upon-promotion) (lifecycle gate, untouched), [D084](#d084-read-primitive-instrumentation-for-empirical-loop-closure-measurement) (retirement instrument), [D099](#d099-explicit-emit-records-commit-local-content-through-default-args_hash) (content commitment carried over), [D113](#d113-unvalidated-informed_by-refs-are-omitted-by-default) (informed_by defaults carried over), [D148](#d148-atribd-is-the-public-stateless-native-local-daemon-for-the-primitive-runtime) (the alias mounting vehicle), [§5.8](atrib-spec.md#58-degradation-contract) (binding on every shim, alias mount, forwarding bin, instrumentation writer, and the optional-peer loader). +## D165: Orchestration wiring with routing and cost accounting + +**Date:** 2026-07-15 + +**Status:** Accepted (implemented 2026-07-14 in the operator's private tree; promoted from P051) + +**Promoted from:** P051. The original deferred-decision entry is removed from the Pending decisions section by this ADR; see git history prior to this commit for the source text. + +**Context.** [D142](#d142-orchestration-topology-baton-pass-and-join-records-as-attest-conventions) defined the baton-pass and join record shapes but nothing wired them into the orchestration layers themselves, and a concrete economic forcing function stood open: one working day of multi-agent orchestration spent a multi-million-token subagent budget on the top model tier, and "where did the budget go, and why was this task class on this tier" had no answer short of transcript archaeology. Routing capability existed in the harness; routing policy and accounting did not. + +**Decision.** + +1. **The routing plane is the operator's existing orchestration infrastructure, wired to sign what it routes.** A dispatch wrapper in the operator's private tree signs a [D142](#d142-orchestration-topology-baton-pass-and-join-records-as-attest-conventions) baton-pass record per dispatched leg and a join record per integration, threading [D135](#d135-delegated-builder-atrib-context-threads-via-orchestrator-injected-explicit-args)/[D115](#d115-agent-to-subagent-handoff-uses-a-three-signal-producer-bundle) child-context injection automatically. Acceptance evidence is live on the public log: baton/join pairs at indices 77509 to 77512 (external-executor leg) and 77548 to 77550 (in-harness leg), with the closure record at 77559. +2. **Per-leg accounting rides content and sidecar, never signed fields.** Baton and join content carries `routing` facts (model, effort tier, token spend, spend source) per the [D084](#d084-read-primitive-instrumentation-for-empirical-loop-closure-measurement) instrumentation-pillar pattern; detailed accounting stays in the local sidecar. "Which agent, on which model, spent what, returned what, and was it accepted" becomes a recall query. +3. **The [D142](#d142-orchestration-topology-baton-pass-and-join-records-as-attest-conventions) role vocabulary gains `subagent`** for the in-harness fleet leg (a worker the orchestrating session spawns and joins inside one harness), alongside `successor-session`, `relay-executor`, and `loop-layer`. Canonical shapes in [skills/atrib/SKILL.md](skills/atrib/SKILL.md) v0.6.0. +4. **[§6.7.1](atrib-spec.md#671-identity-claim-extension) capability envelopes gain an OPTIONAL `cost_policy` sub-field** (`model_tiers`: allowlist of host-defined tier labels; `max_tokens`: total token budget), carried verbatim into [§1.11](atrib-spec.md#111-delegation-certificates) certificate scopes so a principal can grant a delegated run a compute budget as signed, offline-verifiable material. Verifier posture per [§6.7.3](atrib-spec.md#673-out-of-envelope-is-a-signal-not-invalidation): signals, never invalidation. `cost_policy` is evaluable only against caller-supplied usage facts (`checkCostPolicy` in `@atrib/verify`); the record-only [§1.11.4](atrib-spec.md#1114-verifier-walk) walk produces no `cost_policy` mismatch, the same posture `max_amount` and `counterparties` pin. Enforcement stays host-side: the protocol records the grant, the orchestrator enforces it. + +**Boundary, stated plainly.** No signed-byte change, no event_type, no graph or calculation change anywhere. Items 1 to 3 are conventions plus host-side wiring per [§7](atrib-spec.md#7-harness-integration-patterns); item 4 is one optional field in the already-optional capability envelope schema, in the signal-only capability layer ([D051](#d051-capability-scoped-records-via-directory-published-envelopes) lineage). The [payments profile](docs/payments-profile.md) invariant that atrib does not decide what contributions are worth is untouched: `cost_policy` states what a delegation permits, not what work is valued at. + +**Conformance.** `walk-scope-cost-policy` joins the [spec/conformance/delegation-certificates/](spec/conformance/delegation-certificates/) verifier-walk family: a cost-policy-bearing certificate resolves depth-1 with no record-only mismatch, and pinned usage vectors exercise within-grant and tier-plus-budget-exceeded outcomes through `checkCostPolicy`. Reference tests in `@atrib/verify` (unit plus conformance). + +**Alternatives rejected.** Signing spend into record fields (violates the sidecar posture and bloats the canonical form for a host-variable fact). A parallel in-harness orchestration plane (duplicates the operator's relay; rejected by P051's own framing). Making `cost_policy` a certificate-only schema fork (breaks the one-schema-two-carriers rule [§1.11.1](atrib-spec.md#1111-certificate-format) pins). Verifier-side budget enforcement (a thumb on the scale; [§6.7.3](atrib-spec.md#673-out-of-envelope-is-a-signal-not-invalidation) signal posture instead). + +**Cross-references.** [D142](#d142-orchestration-topology-baton-pass-and-join-records-as-attest-conventions), the record shapes wired in. [D140](#d140-delegation-certificates-principal-keys-certify-ephemeral-run-keys), the certificate the cost grant rides. [D135](#d135-delegated-builder-atrib-context-threads-via-orchestrator-injected-explicit-args) / [D115](#d115-agent-to-subagent-handoff-uses-a-three-signal-producer-bundle), context injection. [D084](#d084-read-primitive-instrumentation-for-empirical-loop-closure-measurement), the accounting-sidecar pattern. [P036](#p036-cross-harness-continuation-packet-for-supportrca-investigations), the packet the baton carries. + +# Pending decisions + +These will get full ADRs when we act on them. Recorded here so they remain findable and don't silently drop. Per the global Deferred Decision Logging convention, this section uses the forward-looking pattern (forward-looking decisions that will become numbered ADRs when codified). + ## P009: middleware orphan-flagging consistency with [D072](#d072-orphan-handling--synthesize-fresh-never-inherit-from-mirror-tail) **Source:** Audit pass 2026-05-09 after [D072](#d072-orphan-handling--synthesize-fresh-never-inherit-from-mirror-tail) shipped. The middleware's MCP-traffic-handling path in `packages/mcp/src/middleware.ts` synthesizes a `context_id` (random or `stableContextId` under `autoChain`) when no inbound atrib token, no `traceparent`, and no caller value are available. This path produces real per-call records but does NOT mark them with `inheritedFrom = 'fresh-orphan'` because the middleware's signing path doesn't go through `inheritChainContext`. @@ -8701,25 +8725,6 @@ Candidate demo: **ADR number** will be assigned when the decision is acted on. Do not pre-allocate. -## P051: Orchestration-infrastructure dogfood wiring with cost and routing accounting - -**Date queued:** 2026-07-06. **Origin:** two converging observations from the redesign-analysis session. First, the deeper half of the operator's orchestration-topology observation was never captured: [D142](#d142-orchestration-topology-baton-pass-and-join-records-as-attest-conventions) (promoted from P050) defines the baton-pass/join record *shapes*, but nothing commits to wiring them into the orchestration layers themselves — harness workflow and loop-layer runs emitting atrib records, spawned agents receiving orchestrator-injected context automatically (generalizing [D135](#d135-delegated-builder-atrib-context-threads-via-orchestrator-injected-explicit-args)), and the [P036](#p036-cross-harness-continuation-packet-for-supportrca-investigations) continuation packet becoming the machine-readable baton instead of hand-written markdown briefs. Second, the same session produced a concrete economic forcing function: one working day of multi-agent orchestration consumed a multi-million-token subagent spend on the most expensive model tier at maximum effort, and the operator could not answer "where did the budget go, and why was this task class on this tier" without transcript archaeology — despite the harness exposing per-agent model/effort routing knobs and per-workflow budget accounting that the orchestrating agent simply did not exercise. Routing capability existed; routing *policy* and *accounting* did not. - -Proposal: a dogfood integration layer whose routing plane is the operator's existing orchestration infrastructure — the operator's cross-harness relay (a premium-tier orchestrator, planner, and judge with external executor legs on a separate budget pool) as the handoff/routing surface, and the operator's loop-command infrastructure as the loop layer — rather than a parallel in-harness orchestration plane. In-harness fleets (workflow/ultracode) decompose mechanical self-contained subtasks into executor work-packages (task-kind, executable acceptance gates, target branch) routed through that relay, keeping only warm-context and judgment work in-session; the relay's routing decides harness and model per package, and its receipts carry the accounting. ([P002](#p002-agent-bridge-on-atrib-substrate)'s agent-bridge, if revived on atrib substrate, is a candidate transport for the same packages, not the routing plane itself.) Within that frame: (a) fan-out and join events are signed per the [D142](#d142-orchestration-topology-baton-pass-and-join-records-as-attest-conventions) conventions, carrying per-agent model, effort tier, and token spend as local sidecar content — so "which agent, on which model, spent what, returned what, and was it accepted" becomes a recall query rather than archaeology; (b) spawned agents receive orchestrator-injected `context_id` + parent `informed_by` automatically; (c) [D140](#d140-delegation-certificates-principal-keys-certify-ephemeral-run-keys) scope objects gain an OPTIONAL cost-policy vocabulary (model tier ceiling, token budget) so a run certificate can scope not just what a worker may do but what it may spend — verifier-side signal-not-block, consistent with [§6.7.3](atrib-spec.md#673-out-of-envelope-is-a-signal-not-invalidation). No protocol change beyond the [D140](#d140-delegation-certificates-principal-keys-certify-ephemeral-run-keys) scope vocabulary extension; everything else is conventions plus host-side wiring per [§7](atrib-spec.md#7-harness-integration-patterns). - -**Likely outcome (not committed):** accept now that the [D142](#d142-orchestration-topology-baton-pass-and-join-records-as-attest-conventions) convention write-up has landed; implement first against this repo's own multi-session workflow (the redesign/SDK/website relay) as the dogfood, since it is the pattern's richest live instance. - -**Cross-references.** - -- [D142](#d142-orchestration-topology-baton-pass-and-join-records-as-attest-conventions), the record shapes this wires in. -- [P036](#p036-cross-harness-continuation-packet-for-supportrca-investigations), the packet that becomes the baton. -- [D135](#d135-delegated-builder-atrib-context-threads-via-orchestrator-injected-explicit-args), orchestrator-injected context, generalized here. -- [D140](#d140-delegation-certificates-principal-keys-certify-ephemeral-run-keys), the scope object the cost-policy vocabulary extends. -- [D084](#d084-read-primitive-instrumentation-for-empirical-loop-closure-measurement), the instrumentation-pillar pattern the accounting sidecars follow. - -**ADR number** will be assigned when the decision is acted on. Do not pre-allocate. - - ## P052: transcript recall corpus stays composition-first until attributed paraphrase-gap misses exist **Source:** The 2026-07-14 traces deep-dive ([docs/traces-integration-research.md](docs/traces-integration-research.md), option O4) and its follow-up analysis. [D163](#d163-session-transcript-runtime-log-source-binds-harness-transcripts-to-signed-records) closed the binding half (transcript windows commit to manifests; manifests bind signed records). Host-side, the operator's machine now runs continuous manifests (SessionEnd, PreCompact, two-harness sweep) and signed memory-extraction receipts that link claude-mem observation batches to manifest records through `informed_by`. The remaining question is retrieval. diff --git a/atrib-spec.md b/atrib-spec.md index ffbb54dd..d4493cda 100644 --- a/atrib-spec.md +++ b/atrib-spec.md @@ -1343,12 +1343,12 @@ A delegation certificate is a JSON object, canonicalized with JCS (RFC 8785, sam "not_before": 1767225600000, // OPTIONAL; Unix ms; default 0 when absent "principal_key": "iojj3XQJ8ZX9UtstPLpdcspnCb8dlBIb83SIAbQPb1w", // MUST; base64url 32-byte Ed25519 principal public key "run_pubkey": "gTl3Dqh9F19Wo1Rmw0x-zMuNipG07jeiXfYPW4_Js5Q", // MUST; base64url 32-byte Ed25519 run public key - "scope": { "tool_names": ["search", "read_email"], "max_amount": { "currency": "USD", "value": 100 } }, // OPTIONAL; §6.7.1 capability envelope schema, verbatim + "scope": { "tool_names": ["search", "read_email"], "max_amount": { "currency": "USD", "value": 100 }, "cost_policy": { "model_tiers": ["standard"], "max_tokens": 500000 } }, // OPTIONAL; §6.7.1 capability envelope schema, verbatim "signature": "iMouU-GLdyyISG2S6fRZEEYE5brlP1x6ycuidxY4dIhneSCEKMD_irR3hrHMDg9QfK_2LNct6xPuqm0yQj2pDA" // MUST; Ed25519 by principal_key } ``` -JCS lexicographic field order is exactly as listed: `cert_type` < `context_id` < `not_after` < `not_before` < `principal_key` < `run_pubkey` < `scope` < `signature`. Optional fields MUST be omitted, not null, when absent — presence/absence changes the canonical form and therefore the signature, mirroring the `session_token` rule ([§1.3](#13-canonical-serialization)). The `scope` object reuses the [§6.7.1](#671-identity-claim-extension) capability envelope schema **verbatim** (`tool_names`, `max_amount`, `counterparties`, `event_types`, `expires_at`); one schema, two carriers — directory-published (per-key, identity-claim cadence) and certificate-carried (per-run, issuance cadence). When `scope.expires_at` is present, the effective expiry is `min(not_after, scope.expires_at)`. +JCS lexicographic field order is exactly as listed: `cert_type` < `context_id` < `not_after` < `not_before` < `principal_key` < `run_pubkey` < `scope` < `signature`. Optional fields MUST be omitted, not null, when absent — presence/absence changes the canonical form and therefore the signature, mirroring the `session_token` rule ([§1.3](#13-canonical-serialization)). The `scope` object reuses the [§6.7.1](#671-identity-claim-extension) capability envelope schema **verbatim** (`tool_names`, `max_amount`, `counterparties`, `event_types`, `cost_policy`, `expires_at`); one schema, two carriers — directory-published (per-key, identity-claim cadence) and certificate-carried (per-run, issuance cadence). When `scope.expires_at` is present, the effective expiry is `min(not_after, scope.expires_at)`. #### 1.11.2 Signing, Certificate Hash, and Depth @@ -1389,7 +1389,7 @@ Given record `R` and available certificate set `C`, the walk is offline and dete 2. Select certificates `c ∈ C` with `c.run_pubkey === R.creator_key`. No covering certificate → **depth 0**: attribute to `R.creator_key` as today. If `R`'s context genesis (signed by `R.creator_key`) carries `delegation_cert_hash` but no valid covering certificate resolved, surface `delegation_unresolved: true` — signal, not invalidation, the [D113](DECISIONS.md#d113-unvalidated-informed_by-refs-are-omitted-by-default) posture. A covering certificate that is invalid per [§1.11.2](#1112-signing-certificate-hash-and-depth) is rejected as evidence: the walk reports its `cert_hash`, `cert_valid: false`, and the rejection error, and falls back to depth 0. 3. For a valid matching `c`, evaluate: `(c.not_before ?? 0) <= R.timestamp <= c.not_after` → `in_window`; `c.context_id` absent → `context_bound: null`, else `c.context_id === R.context_id` → `context_bound`; when the context genesis was signed by `R.creator_key` AND carries `delegation_cert_hash`, `genesis.delegation_cert_hash === cert_hash(c)` → `cert_bound`, otherwise `cert_bound: null` (the standing state for run keys that joined a multi-producer context, [§1.11.6](#1116-multi-producer-contexts)). 4. Consult the directory ([§6.3](#63-verifier-consultation-algorithm)) for `c.principal_key`, not the run key. Run keys never enter the directory; the expected lookup result for a run key is a non-membership proof, and a directory claim found *for the run key itself* is surfaced as the structural anomaly `run_key_in_directory: true`. -5. Scope check per [§6.7.2](#672-verifier-semantics) semantics against `c.scope`: `in_scope` with a `mismatches[]` list naming the failed constraints (`tool_names`, `event_types`, `max_amount`, `counterparties`). When the principal's directory envelope is available, `attenuation_ok` reports whether `c.scope` is a subset of it (a certificate granting what the principal's own envelope excludes sets `attenuation_ok: false`); when no directory envelope is supplied, `attenuation_ok` is `null`. All scope outputs are signals, never invalidation, per [§6.7.3](#673-out-of-envelope-is-a-signal-not-invalidation). +5. Scope check per [§6.7.2](#672-verifier-semantics) semantics against `c.scope`: `in_scope` with a `mismatches[]` list naming the failed constraints (`tool_names`, `event_types`, `max_amount`, `counterparties`, `cost_policy.model_tiers`, `cost_policy.max_tokens`). `cost_policy` constraints are evaluable only against caller-supplied usage facts per [§6.7.2](#672-verifier-semantics); the record-only walk produces no `cost_policy` mismatch. When the principal's directory envelope is available, `attenuation_ok` reports whether `c.scope` is a subset of it (a certificate granting what the principal's own envelope excludes sets `attenuation_ok: false`); when no directory envelope is supplied, `attenuation_ok` is `null`. All scope outputs are signals, never invalidation, per [§6.7.3](#673-out-of-envelope-is-a-signal-not-invalidation). 6. Revocation: scan for [§1.9](#19-key-rotation-and-revocation) `key_revocation` records retiring either the run key ([§1.11.5](#1115-run-key-revocation)) or the principal. A revoked principal cascades: its certificates are invalid as delegation evidence for records at `log_index >= R` per [§1.9.3](#193-verifier-semantics) semantics. The verifier output is an optional `delegation` block: @@ -1449,7 +1449,7 @@ Producers that opt in stamp `delegation_cert_hash` on the genesis record *only w #### 1.11.11 Conformance -Implementations MUST pass all vectors in [`spec/conformance/delegation-certificates/`](spec/conformance/delegation-certificates/). The corpus covers six case families: certificate canonical form and signing (full/minimal optional-field forms, self-certificate, wrong-signer), the [§1.11.4](#1114-verifier-walk) verifier walk (valid, expired, scope mismatch as signal, wrong principal signature, run-key mismatch with `delegation_unresolved`), depth-0 byte-identity against [`spec/conformance/1.4/signing-vectors.json`](spec/conformance/1.4/signing-vectors.json), `delegation_cert_hash` lex-slotting with distinct signatures for presence vs. absence, run-key revocation extending [`spec/conformance/1.9/`](spec/conformance/1.9/) (authorized rule-3 revocation and the not-covering rejection), and the [D067](DECISIONS.md#d067-multi-producer-chain-composition-precedence-contract) `cert_bound: null` posture. The generator is `packages/log-dev/scripts/generate-conformance-delegation-certificates.ts`; the reference implementation is `packages/verify/test/conformance-delegation-certificates.test.ts`. Two implementations given the same record and certificate set MUST produce identical `delegation` blocks. +Implementations MUST pass all vectors in [`spec/conformance/delegation-certificates/`](spec/conformance/delegation-certificates/). The corpus covers six case families: certificate canonical form and signing (full/minimal optional-field forms, self-certificate, wrong-signer), the [§1.11.4](#1114-verifier-walk) verifier walk (valid, expired, scope mismatch as signal, cost-policy scope with pinned usage vectors, wrong principal signature, run-key mismatch with `delegation_unresolved`), depth-0 byte-identity against [`spec/conformance/1.4/signing-vectors.json`](spec/conformance/1.4/signing-vectors.json), `delegation_cert_hash` lex-slotting with distinct signatures for presence vs. absence, run-key revocation extending [`spec/conformance/1.9/`](spec/conformance/1.9/) (authorized rule-3 revocation and the not-covering rejection), and the [D067](DECISIONS.md#d067-multi-producer-chain-composition-precedence-contract) `cert_bound: null` posture. The generator is `packages/log-dev/scripts/generate-conformance-delegation-certificates.ts`; the reference implementation is `packages/verify/test/conformance-delegation-certificates.test.ts`. Two implementations given the same record and certificate set MUST produce identical `delegation` blocks. #### 1.11.12 What This DOES NOT Cover @@ -4300,12 +4300,17 @@ The [§6.1](#61-identity-claim-format) identity claim format gains an OPTIONAL ` "https://atrib.dev/v1/types/tool_call", "https://atrib.dev/v1/types/observation", ], + "cost_policy": { + // optional compute-spend scope for delegated runs (D165) + "model_tiers": ["economy", "standard"], // optional allowlist of host-defined tier labels + "max_tokens": 500000, // optional total token budget for the certified run + }, "expires_at": 1761000000000, // optional; envelope rotates with the identity claim }, } ``` -All capability sub-fields are individually optional. A claim with `capabilities: {}` declares no scope (equivalent to omitting the field). A claim with some sub-fields and not others applies only the present constraints. +All capability sub-fields are individually optional. A claim with `capabilities: {}` declares no scope (equivalent to omitting the field). A claim with some sub-fields and not others applies only the present constraints. `cost_policy` scopes what a key may spend rather than what it may do: `model_tiers` is an allowlist of host-defined tier labels (free-form strings, the `tool_names` idiom) and `max_tokens` caps total token spend. Its primary carrier is the [§1.11](#111-delegation-certificates) certificate scope, where a principal grants a delegated run a compute budget; the protocol records the grant and never enforces it. #### 6.7.2 Verifier semantics @@ -4316,6 +4321,7 @@ A verifier that has resolved a record's `creator_key` to an identity claim with - If `tool_names` is present, the record's `tool_name` MUST be in the list (for tool_call records). The verifier MAY use a `tool_name` field disclosed on the record or caller-supplied facts resolved from the local record body or upstream protocol event. If no tool name is available, the verifier MUST flag the check as `unresolvable: true`. - If `event_types` is present, the record's `event_type` URI MUST be in the list. - For transaction records, if `max_amount` and/or `counterparties` are present, the verifier MUST resolve the transaction amount and counterparty from the protocol-specific transaction event the record commits to (per [§1.7](#17-transaction-event-hooks)'s payment-protocol definitions: ACP order envelope, UCP envelope, x402 PAYMENT-RESPONSE header, MPP Payment-Receipt, AP2 CheckoutReceipt or PaymentReceipt, a2a-x402 receipts). The resolved amount MUST NOT exceed `max_amount`; the resolved counterparty MUST be in the `counterparties` allowlist. When the protocol-specific event is not available out-of-band or through caller-supplied resolved facts, the verifier MUST flag the check as `unresolvable: true` rather than passing or failing silently. + - If `cost_policy` is present, the constraint is evaluable only against caller-supplied usage facts (model tier label, tokens spent): signed records carry neither, since spend accounting lives in local sidecar and join-record content per the [D142](DECISIONS.md#d142-orchestration-topology-baton-pass-and-join-records-as-attest-conventions) orchestration conventions. With usage facts, the claimed tier MUST be in `model_tiers` and claimed spend MUST NOT exceed `max_tokens`; mismatches are named `cost_policy.model_tiers` / `cost_policy.max_tokens`. Without usage facts the check produces no mismatch (nothing checkable is claimed). - If `expires_at` is present and the record's timestamp is after it, the envelope is expired (treated as having no constraint and flagged separately). 3. Surface the result as `capability_check: { envelope: CapabilityEnvelope | null, in_envelope: bool, mismatches: string[], unresolvable: bool }` on the verification output. diff --git a/packages/log-dev/scripts/generate-conformance-delegation-certificates.ts b/packages/log-dev/scripts/generate-conformance-delegation-certificates.ts index 05e76fb6..89ec36a9 100644 --- a/packages/log-dev/scripts/generate-conformance-delegation-certificates.ts +++ b/packages/log-dev/scripts/generate-conformance-delegation-certificates.ts @@ -91,6 +91,7 @@ interface CapabilityScope { counterparties?: string[] event_types?: string[] expires_at?: number + cost_policy?: { model_tiers?: string[]; max_tokens?: number } } /** Delegation certificate per §1.11.1. Optional fields omitted (not null) when absent. */ @@ -493,6 +494,76 @@ async function main(): Promise { }, }) + // ── Case: walk-scope-cost-policy ────────────────────────────────── + const ctxC9 = 'c9'.repeat(16) + const certC9 = await signCertificate( + { + cert_type: 'atrib/delegation-cert/v1', + context_id: ctxC9, + not_after: CERT_NOT_AFTER, + not_before: CERT_NOT_BEFORE, + principal_key: principalKey, + run_pubkey: runKey, + scope: { + tool_names: ['search', 'read_email'], + cost_policy: { model_tiers: ['economy', 'standard'], max_tokens: 500_000 }, + }, + }, + PRINCIPAL_SEED, + ) + const walkCostPolicyRecord = await runKeyGenesis( + ctxC9, + 'c9', + REFERENCE_TIME_MS + 60_000, + 'search', + certC9, + RUN_SEED, + runKey, + ) + writeCase('walk-scope-cost-policy', { + name: 'walk-scope-cost-policy', + spec_section: '1.11', + description: + 'A certificate whose scope carries the D165 cost_policy sub-field (model_tiers allowlist, max_tokens budget). From the record alone the walk reports no cost_policy mismatch: signed records carry neither model tier nor token spend, so cost_policy is evaluable only against caller-supplied usage facts (checkCostPolicy, §6.7.2). The usage_vectors pin that evaluation: within-grant usage is in scope; a tier outside the allowlist plus spend over the budget produce both mismatches. Signals, never invalidation (§6.7.3): the record signature and depth-1 resolution are unaffected.', + input: { + record: walkCostPolicyRecord, + genesis_record: walkCostPolicyRecord, + certificates: [certC9], + usage_vectors: [ + { + name: 'within-grant', + usage: { model_tier: 'standard', tokens_spent: 120_000 }, + expected: { in_scope: true, mismatches: [] }, + }, + { + name: 'tier-and-budget-exceeded', + usage: { model_tier: 'premium', tokens_spent: 500_001 }, + expected: { + in_scope: false, + mismatches: ['cost_policy.model_tiers', 'cost_policy.max_tokens'], + }, + }, + ], + }, + expected: { + record_signature_valid: true, + record_hash: recordHash(walkCostPolicyRecord), + signal_not_block: true, + delegation: { + depth: 1, + principal_key: principalKey, + cert_hash: certHash(certC9), + cert_valid: true, + in_window: true, + context_bound: true, + cert_bound: true, + scope_check: { in_scope: true, attenuation_ok: null, mismatches: [] }, + revoked: false, + errors: [], + }, + }, + }) + // ── Case: walk-wrong-principal-signature ────────────────────────── const ctxB4 = 'b4'.repeat(16) const certB4Bad = await signCertificate( @@ -1059,6 +1130,7 @@ async function main(): Promise { 'walk-valid', 'walk-expired', 'walk-scope-mismatch', + 'walk-scope-cost-policy', 'walk-wrong-principal-signature', 'walk-run-key-mismatch', 'depth0-identity', @@ -1085,7 +1157,7 @@ async function main(): Promise { rogue_pubkey: rogueKey, }, note: - 'Six case families: certificate canonical form + signing (4), verifier walk (5), ' + + 'Six case families: certificate canonical form + signing (4), verifier walk (6), ' + 'depth-0 byte-identity against spec/conformance/1.4/signing-vectors.json (1), ' + 'delegation_cert_hash genesis lex-slotting (1), run-key revocation extending ' + 'spec/conformance/1.9/ (2), D067 multi-producer cert_bound: null posture (1). ' + diff --git a/packages/verify/src/delegation.ts b/packages/verify/src/delegation.ts index 023deab4..c6b5c009 100644 --- a/packages/verify/src/delegation.ts +++ b/packages/verify/src/delegation.ts @@ -46,6 +46,27 @@ export interface DelegationScope { max_amount?: { currency: string; value: number } counterparties?: string[] expires_at?: number + cost_policy?: DelegationCostPolicy +} + +/** + * §6.7.1 `cost_policy` sub-field (D165): the compute-spend scope of a + * delegated run. `model_tiers` is an allowlist of host-defined tier labels + * (free-form strings, same idiom as `tool_names`); `max_tokens` caps the + * certified run's total token spend. Both sub-fields are individually + * optional; absence means no constraint. Like every §6.7 constraint the + * outputs are signals, never invalidation (§6.7.3), and enforcement stays + * host-side: the protocol records the grant, the orchestrator enforces it. + */ +export interface DelegationCostPolicy { + model_tiers?: string[] + max_tokens?: number +} + +/** Caller-supplied usage facts for {@link checkCostPolicy}. */ +export interface CostPolicyUsage { + model_tier?: string + tokens_spent?: number } /** @@ -246,10 +267,12 @@ export async function delegationCertErrors(cert: DelegationCertificate): Promise * * Checks the constraints resolvable from the record alone: `tool_names` * (when the record discloses §8.2 `tool_name`) and `event_types`. - * `max_amount` / `counterparties` require protocol-event facts the - * compact signed record does not carry; they produce no mismatch here - * (same posture the conformance corpus pins). `attenuation_ok` is `null` - * until a caller supplies the principal's directory envelope. + * `max_amount` / `counterparties` / `cost_policy` require facts the + * compact signed record does not carry (transaction amounts, counterparty + * identity, model tier and token spend); they produce no mismatch here + * (same posture the conformance corpus pins). Callers holding usage facts + * evaluate `cost_policy` with {@link checkCostPolicy}. `attenuation_ok` + * is `null` until a caller supplies the principal's directory envelope. */ export function checkDelegationScope( record: DelegatedRecord, @@ -269,6 +292,43 @@ export function checkDelegationScope( return { in_scope: mismatches.length === 0, attenuation_ok: null, mismatches } } +/** + * §6.7.2 cost-policy check (D165). Evaluable only when the caller holds + * usage facts: signed records do not carry model tier or token spend + * (accounting lives in local sidecar and join-record content per the + * orchestration conventions), so this never runs inside the record-only + * §1.11.4 walk. A host verifying a join, or a receiver deciding whether a + * leg stayed inside its certified grant, supplies the claimed usage and + * reads the mismatch list. Signals only, never invalidation (§6.7.3): + * an over-budget leg's records remain valid; the mismatch is a fact for + * the accepting party to weigh. + * + * Absent constraints and absent usage facts both produce no mismatch: + * a policy without `max_tokens` caps nothing, and usage without + * `tokens_spent` claims nothing checkable. + */ +export function checkCostPolicy( + policy: DelegationCostPolicy, + usage: CostPolicyUsage, +): { in_scope: boolean; mismatches: string[] } { + const mismatches: string[] = [] + if ( + policy.model_tiers && + usage.model_tier !== undefined && + !policy.model_tiers.includes(usage.model_tier) + ) { + mismatches.push('cost_policy.model_tiers') + } + if ( + policy.max_tokens !== undefined && + usage.tokens_spent !== undefined && + usage.tokens_spent > policy.max_tokens + ) { + mismatches.push('cost_policy.max_tokens') + } + return { in_scope: mismatches.length === 0, mismatches } +} + function certWindow(cert: DelegationCertificate): { from: number; to: number } { return { from: cert.not_before ?? 0, to: cert.not_after } } diff --git a/packages/verify/src/index.ts b/packages/verify/src/index.ts index c053b182..079d9885 100644 --- a/packages/verify/src/index.ts +++ b/packages/verify/src/index.ts @@ -293,6 +293,7 @@ export { graphLabelFromEventTypeUri } from './types.js' // never affects record validity. export { checkDelegationScope, + checkCostPolicy, delegationCertErrors, delegationCertHash, delegationCertSignatureVerifies, @@ -306,6 +307,8 @@ export type { DelegationCertificate, DelegationOutcome, DelegationScope, + DelegationCostPolicy, + CostPolicyUsage, DelegationScopeCheck, EvaluateDelegationOptions, KeyRevocationRecordLike, diff --git a/packages/verify/test/conformance-delegation-certificates.test.ts b/packages/verify/test/conformance-delegation-certificates.test.ts index 3655c508..5d1c3938 100644 --- a/packages/verify/test/conformance-delegation-certificates.test.ts +++ b/packages/verify/test/conformance-delegation-certificates.test.ts @@ -40,14 +40,17 @@ import { type AtribRecord, } from '@atrib/mcp' import { + checkCostPolicy, delegationCertErrors, delegationCertHash, delegationCertSignatureVerifies, delegationCertSigningInput, evaluateDelegation, evaluateRevokerAuthorization, + type CostPolicyUsage, type DelegatedRecord, type DelegationCertificate, + type DelegationCostPolicy, type DelegationOutcome, type KeyRevocationRecordLike, } from '../src/delegation.js' @@ -210,6 +213,28 @@ describe('spec §1.11 conformance: delegation certificates', () => { expect(delegation.scope_check?.in_scope).toBe(false) }) + it('walk-scope-cost-policy: cost_policy scope is usage-fact-evaluable, signal-only (D165)', async () => { + await assertWalkCase('walk-scope-cost-policy') + const c = loadCase('walk-scope-cost-policy') + expect((c.expected as { signal_not_block: boolean }).signal_not_block).toBe(true) + // From the record alone the walk reports no cost_policy mismatch. + const delegation = (c.expected as { delegation: DelegationOutcome }).delegation + expect(delegation.scope_check).toEqual({ in_scope: true, attenuation_ok: null, mismatches: [] }) + // The pinned usage vectors evaluate through checkCostPolicy. + const cert = (c.input.certificates as DelegationCertificate[])[0]! + const costPolicy = (cert.scope as { cost_policy?: DelegationCostPolicy }).cost_policy + expect(costPolicy).toBeDefined() + const vectors = c.input.usage_vectors as Array<{ + name: string + usage: CostPolicyUsage + expected: { in_scope: boolean; mismatches: string[] } + }> + expect(vectors.length).toBeGreaterThan(0) + for (const v of vectors) { + expect(checkCostPolicy(costPolicy!, v.usage), v.name).toEqual(v.expected) + } + }) + it('walk-wrong-principal-signature: invalid cert falls back to depth 0', async () => { await assertWalkCase('walk-wrong-principal-signature') }) diff --git a/packages/verify/test/delegation.test.ts b/packages/verify/test/delegation.test.ts index 9f4a5145..686182e2 100644 --- a/packages/verify/test/delegation.test.ts +++ b/packages/verify/test/delegation.test.ts @@ -29,6 +29,7 @@ import { sha512 } from '@noble/hashes/sha2.js' import { base64urlEncode, genesisChainRoot } from '@atrib/mcp' import { checkDelegationScope, + checkCostPolicy, delegationCertErrors, delegationCertHash, delegationCertSignatureVerifies, @@ -504,4 +505,55 @@ describe('degradation: the walk never throws', () => { 'tool_names', ]) }) + + it('cost_policy in a cert scope produces no mismatch from the record alone (D165)', async () => { + const record = runRecord(await pub(R1_SEED), T0 + 1_000) + const check = checkDelegationScope(record, { + cost_policy: { model_tiers: ['economy'], max_tokens: 1 }, + }) + expect(check).toEqual({ in_scope: true, attenuation_ok: null, mismatches: [] }) + }) +}) + +describe('checkCostPolicy (§6.7.2 / D165)', () => { + it('passes usage inside the grant', () => { + expect( + checkCostPolicy( + { model_tiers: ['economy', 'standard'], max_tokens: 500_000 }, + { model_tier: 'standard', tokens_spent: 120_000 }, + ), + ).toEqual({ in_scope: true, mismatches: [] }) + }) + + it('flags a tier outside the allowlist', () => { + expect( + checkCostPolicy({ model_tiers: ['economy'] }, { model_tier: 'premium' }).mismatches, + ).toEqual(['cost_policy.model_tiers']) + }) + + it('flags spend over the token budget', () => { + expect( + checkCostPolicy({ max_tokens: 100_000 }, { tokens_spent: 100_001 }).mismatches, + ).toEqual(['cost_policy.max_tokens']) + }) + + it('spend equal to the budget is inside the grant', () => { + expect(checkCostPolicy({ max_tokens: 100_000 }, { tokens_spent: 100_000 }).in_scope).toBe(true) + }) + + it('absent constraints and absent usage facts produce no mismatch', () => { + expect(checkCostPolicy({}, { model_tier: 'premium', tokens_spent: 9e9 }).in_scope).toBe(true) + expect( + checkCostPolicy({ model_tiers: ['economy'], max_tokens: 1 }, {}).in_scope, + ).toBe(true) + }) + + it('reports both mismatches together, signals only', () => { + const check = checkCostPolicy( + { model_tiers: ['economy'], max_tokens: 10 }, + { model_tier: 'premium', tokens_spent: 11 }, + ) + expect(check.in_scope).toBe(false) + expect(check.mismatches).toEqual(['cost_policy.model_tiers', 'cost_policy.max_tokens']) + }) }) diff --git a/skills/atrib/SKILL.md b/skills/atrib/SKILL.md index b40d9a3b..5505c4f9 100644 --- a/skills/atrib/SKILL.md +++ b/skills/atrib/SKILL.md @@ -1,6 +1,6 @@ --- name: atrib -version: 0.5.0 +version: 0.6.0 description: | Use atrib as the verifiable substrate for memory, reasoning, and getting sharper over time, not as instrumentation that observes you from the @@ -478,9 +478,9 @@ await attest({ Discipline for both shapes: - **Verify before you join.** Results from other signers pass through the `verification` parameter first (previous section); only accepted hashes enter `join.accepted` and `informed_by`. Rejected results are routing facts in content, never influence claims in `informed_by`. -- **Role terms in `baton` facts.** `target_harness_role` uses role vocabulary (`successor-session`, `relay-executor`, `loop-layer`); local tool names belong in the packet body, not in signed content or envelope facts. +- **Role terms in `baton` facts.** `target_harness_role` uses role vocabulary (`successor-session`, `relay-executor`, `loop-layer`, `subagent`); local tool names belong in the packet body, not in signed content or envelope facts. `subagent` is the in-harness fleet leg: a worker the orchestrating session spawns and joins inside one harness, as opposed to `relay-executor` (a separate harness on its own budget pool) and `successor-session` (a later session of the same agent). - **Authority, when it matters.** For cross-harness or sandboxed receivers, pair the baton with a [§1.11](../../atrib-spec.md#111-delegation-certificates) delegation certificate; the profile's `verified` tier binds `target_principal` to the certificate walk. -- Per-agent model/effort/token-spend accounting on these records is [P051](../../DECISIONS.md#p051-orchestration-infrastructure-dogfood-wiring-with-cost-and-routing-accounting)'s scope, pending, not yet convention. +- **Spend accounting rides content and sidecar; budget grants ride certificates** ([D165](../../DECISIONS.md#d165-orchestration-wiring-with-routing-and-cost-accounting)). Baton and join content carries per-leg routing facts (`routing: { model, effort, tokens_spent, spend_source }`) so which agent, on which tier, spent what, and was it accepted is a recall query instead of transcript archaeology; detailed accounting stays in the local sidecar. The budget a leg was granted belongs in its [§1.11](../../atrib-spec.md#111-delegation-certificates) certificate scope `cost_policy` (`model_tiers`, `max_tokens`), a signed offline-verifiable grant; verifiers surface claimed-spend-versus-grant mismatch as a signal, never invalidation. ## Multi-producer composition (the density picture) @@ -613,4 +613,4 @@ These are honest gaps in the verification stack and producer-side cognitive surf - **Warning-only**: [§6.3](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#63-verifier-consultation-algorithm) verifier-consultation steps 1, 3, 4, 5, 7 surface explicit `IMPLEMENTATION-GAP` warnings rather than silently passing. These cover anchor freshness, witness coverage, directory checkpoint signature, append-only consistency, and AKD lookup proof validation. - **Not yet implemented**: cross-log replication / equivocation detection ([D050](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d050-cross-log-replication-for-equivocation-defense) / [§2.11](https://github.com/creatornader/atrib/blob/main/atrib-spec.md#211-cross-log-replication)), HKDF sub-agent identity derivation, periodic directory anchoring, emergency-key compromise path, and archive retrieval inside the verification path. The `log.atrib.dev` SSE / JSON Feed subscription surface is implemented per [D103](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#d103-log-subscriptions-use-sse-plus-json-feed-over-commitment-visible-fields); an embedded spec viewer at `atrib.dev` is queued at [P024](https://github.com/creatornader/atrib/blob/main/DECISIONS.md#p024-embedded-spec-viewer-at-atribdev-auto-updated-from-spec-source). -The skill is the practice; the substrate is the mechanism. Both evolve. When this skill version (v0.5.0) feels stale, rewrite it again. +The skill is the practice; the substrate is the mechanism. Both evolve. When this skill version (v0.6.0) feels stale, rewrite it again. diff --git a/spec/conformance/delegation-certificates/cases/walk-scope-cost-policy.json b/spec/conformance/delegation-certificates/cases/walk-scope-cost-policy.json new file mode 100644 index 00000000..149f155e --- /dev/null +++ b/spec/conformance/delegation-certificates/cases/walk-scope-cost-policy.json @@ -0,0 +1,103 @@ +{ + "name": "walk-scope-cost-policy", + "spec_section": "1.11", + "description": "A certificate whose scope carries the D165 cost_policy sub-field (model_tiers allowlist, max_tokens budget). From the record alone the walk reports no cost_policy mismatch: signed records carry neither model tier nor token spend, so cost_policy is evaluable only against caller-supplied usage facts (checkCostPolicy, §6.7.2). The usage_vectors pin that evaluation: within-grant usage is in scope; a tier outside the allowlist plus spend over the budget produce both mismatches. Signals, never invalidation (§6.7.3): the record signature and depth-1 resolution are unaffected.", + "input": { + "record": { + "spec_version": "atrib/1.0", + "content_id": "sha256:c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9", + "creator_key": "gTl3Dqh9F19Wo1Rmw0x-zMuNipG07jeiXfYPW4_Js5Q", + "chain_root": "sha256:f68aa2dbeac17e81c6fc8a96138128731465ac20d7a296ee7f17b43983ce6e51", + "event_type": "https://atrib.dev/v1/types/tool_call", + "context_id": "c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9", + "timestamp": 1767225660000, + "tool_name": "search", + "signature": "EB103fMYnKH3khtwpKR572zQtKuv4DRo28jpBgjFYFpKJYjWvG_uHKVXDUU3eBqz1p4zcPB3R2jFKBAE6c9kAw", + "delegation_cert_hash": "sha256:48435d55996b9451da2a2ad19d81490babb9d5f586fd0ab6e8e25a021ccfd3f6" + }, + "genesis_record": { + "spec_version": "atrib/1.0", + "content_id": "sha256:c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9", + "creator_key": "gTl3Dqh9F19Wo1Rmw0x-zMuNipG07jeiXfYPW4_Js5Q", + "chain_root": "sha256:f68aa2dbeac17e81c6fc8a96138128731465ac20d7a296ee7f17b43983ce6e51", + "event_type": "https://atrib.dev/v1/types/tool_call", + "context_id": "c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9", + "timestamp": 1767225660000, + "tool_name": "search", + "signature": "EB103fMYnKH3khtwpKR572zQtKuv4DRo28jpBgjFYFpKJYjWvG_uHKVXDUU3eBqz1p4zcPB3R2jFKBAE6c9kAw", + "delegation_cert_hash": "sha256:48435d55996b9451da2a2ad19d81490babb9d5f586fd0ab6e8e25a021ccfd3f6" + }, + "certificates": [ + { + "cert_type": "atrib/delegation-cert/v1", + "context_id": "c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9c9", + "not_after": 1767229200000, + "not_before": 1767225600000, + "principal_key": "iojj3XQJ8ZX9UtstPLpdcspnCb8dlBIb83SIAbQPb1w", + "run_pubkey": "gTl3Dqh9F19Wo1Rmw0x-zMuNipG07jeiXfYPW4_Js5Q", + "scope": { + "tool_names": [ + "search", + "read_email" + ], + "cost_policy": { + "model_tiers": [ + "economy", + "standard" + ], + "max_tokens": 500000 + } + }, + "signature": "ZXqClVv_uBTB-XNUC_BL_BcqgL9yzPC4Y3TwZZuVTnnvH28sezixTNSUVf521KcPAJy9K5zSMwdLVYXt1M87Cg" + } + ], + "usage_vectors": [ + { + "name": "within-grant", + "usage": { + "model_tier": "standard", + "tokens_spent": 120000 + }, + "expected": { + "in_scope": true, + "mismatches": [] + } + }, + { + "name": "tier-and-budget-exceeded", + "usage": { + "model_tier": "premium", + "tokens_spent": 500001 + }, + "expected": { + "in_scope": false, + "mismatches": [ + "cost_policy.model_tiers", + "cost_policy.max_tokens" + ] + } + } + ] + }, + "expected": { + "record_signature_valid": true, + "record_hash": "sha256:877a2bee6535d6c398283f89f6c907ef1f1ead92a326a1f3fec8fd75c7b9bc42", + "signal_not_block": true, + "delegation": { + "depth": 1, + "principal_key": "iojj3XQJ8ZX9UtstPLpdcspnCb8dlBIb83SIAbQPb1w", + "cert_hash": "sha256:48435d55996b9451da2a2ad19d81490babb9d5f586fd0ab6e8e25a021ccfd3f6", + "cert_valid": true, + "in_window": true, + "context_bound": true, + "cert_bound": true, + "scope_check": { + "in_scope": true, + "attenuation_ok": null, + "mismatches": [] + }, + "revoked": false, + "errors": [] + } + } +} diff --git a/spec/conformance/delegation-certificates/manifest.json b/spec/conformance/delegation-certificates/manifest.json index 28f16e2b..abba61b4 100644 --- a/spec/conformance/delegation-certificates/manifest.json +++ b/spec/conformance/delegation-certificates/manifest.json @@ -33,6 +33,10 @@ "file": "cases/walk-scope-mismatch.json", "name": "walk-scope-mismatch" }, + { + "file": "cases/walk-scope-cost-policy.json", + "name": "walk-scope-cost-policy" + }, { "file": "cases/walk-wrong-principal-signature.json", "name": "walk-wrong-principal-signature" @@ -81,5 +85,5 @@ "other_producer_pubkey": "7UkoxijRwsbq6QM4kFmVYSlZJzpcY_k2NsFGFKyHN9E", "rogue_pubkey": "ypOsFwUYcHHWe4PH_w7-gQjo7EUwV113JoeTM9vavnw" }, - "note": "Six case families: certificate canonical form + signing (4), verifier walk (5), depth-0 byte-identity against spec/conformance/1.4/signing-vectors.json (1), delegation_cert_hash genesis lex-slotting (1), run-key revocation extending spec/conformance/1.9/ (2), D067 multi-producer cert_bound: null posture (1). The principal seed (0x01 fill) deliberately matches the §1.4 corpus signer, so the depth-0 case is literally a principal signing directly. Edge cases pin malformed keys, multiple principal candidates, and the one-hop depth limit." + "note": "Six case families: certificate canonical form + signing (4), verifier walk (6), depth-0 byte-identity against spec/conformance/1.4/signing-vectors.json (1), delegation_cert_hash genesis lex-slotting (1), run-key revocation extending spec/conformance/1.9/ (2), D067 multi-producer cert_bound: null posture (1). The principal seed (0x01 fill) deliberately matches the §1.4 corpus signer, so the depth-0 case is literally a principal signing directly. Edge cases pin malformed keys, multiple principal candidates, and the one-hop depth limit." }