Skip to content

AP2 mandate-chain developer surface — spec + runnable prototypes (#92) - #95

Merged
dzuluaga merged 15 commits into
mainfrom
009-ap2-mandate-chain-dx
Jul 25, 2026
Merged

AP2 mandate-chain developer surface — spec + runnable prototypes (#92)#95
dzuluaga merged 15 commits into
mainfrom
009-ap2-mandate-chain-dx

Conversation

@dzuluaga

@dzuluaga dzuluaga commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

In plain terms

This PR adds two things: a written design for how a developer adds a consent check to their app — proving a credential (like "I'm over 21") before something consequential happens — and two small demo programs you can run on your own machine to watch that design work. It does not change the real library or anything users see; the actual implementation is a later PR.

What you're approving

A design document plus two runnable demos. No library code changes, nothing ships to users, nothing to undo. The demos are examples you run locally to check the design feels right.

How to test

npm run build --workspaces
node examples/grants-proto/server.mjs   # open http://localhost:4020 — no phone needed
node examples/orders-proto/server.mjs   # open http://localhost:4010 — real checkout (phone for the age step)
  • The "buy while the user is away" demo (:4020) — set up a $100 budget, then: click Spend $20 wine a few times (watch it count down) → try a $50 item (refused: over the $30 per-purchase limit) → spend past $100 (refused: over budget) → Retry the same purchase (safe — returns the original, no double charge) → Revoke (every further purchase refused).
  • The "user is here" checkout demo (:4010) — start a checkout, prove age on your phone and pay, and watch the order complete carrying a signed receipt.

For reviewers — the detail

Feature 009-ap2-mandate-chain-dx. The design is the output of a 9-round DX review; the round-by-round journey is in the spec.

The surface:

  • Configure once (new CredentAgent({ origin, catalog }) + mount), one ordered list of required credentials (policy).
  • One result shape everywhere — a call returns ok (with the signed records), needs-proof (with a link to send the user), or refused (with a reason code). A trust label rides on every one.
  • credentagent.gate(handler) — wrap any single agent tool so it refuses until proven (this ships in Gate a single tool in one call — credentagent.gate() + the gate-my-tool skill (#17) #94, not here).
  • orders — the checkout flow where you host a page (orders.create / orders.retrieve).
  • grants — the buy-while-away flow. A grant is a pre-approval with limits — like a prepaid card ("$100 at this store, $30 per purchase"). grants.create / grant.spend / grant.revoke.
  • The three AP2 records (Intent / Cart / Payment mandate — the signed "who agreed to what") are returned together as a mandateBundle.

Honesty: every result is labelled presence-only-demo — it proves the user showed and bound a credential, not that a real issuer vouched for it, and no real money moves.

In this PR: specs/009-ap2-mandate-chain-dx/spec.md (the design) + examples/orders-proto/ (wired to the real checkout ceremony) + examples/grants-proto/ (over the real delegated-spend engine).

Scope: design + demos only. Turning the demo orders/grants code into the real library API is a follow-up PR (it edits client.ts, now unblocked since #84/#91/#94 settled their pieces).

Open questions for you:

  1. The grants names — grants.create/retrieve, grant.spend/revoke, and the refusal codes per-spend-exceeded | budget-exceeded | revoked. Good?
  2. Should the checkout result carry the payment rail's actual signed payment record, rather than one rebuilt from the settlement? (The last "looks real vs is real" gap.)
  3. Setting up a grant is sealed on the server today; the version where the user's phone signs it is HNP increment 3 — wallet server (credentagent-wallet, Kotlin/JVM) #71. Is that ordering right?

Refs #92 · #17 (credentagent.gate()) · #12 (delegated spend) · #14 (real signing).

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
credentagent-demo Ready Ready Preview, Comment Jul 25, 2026 5:08pm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d212a82cf0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread examples/orders-proto/orders.mjs Outdated
Comment thread examples/grants-proto/grants.mjs
dzuluaga added a commit that referenced this pull request Jul 22, 2026
1. Remove examples/orders-proto/orders.mjs — dead code (the real-ceremony server.mjs
   never imports it). It was the superseded first-iteration facade and carried both a
   caller-controlled-$__orderId bind bug (a verified order could run the handler with
   arbitrary new args) and the abandoned orders.gate naming.
2. grants _bundle(): build proper CartMandateLine { id, quantity, unitPrice, lineTotal }
   from the catalog price, so a recipient can reconcile the signed cart mandate to the
   priced purchase (was { sku, qty }, missing the required fields).

Refs #92

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
dzuluaga added 12 commits July 21, 2026 19:17
SpecKit feature 008. Two entry surfaces (HP live-ceremony, HNP delegated), one
shared MandateBundle { intentMandate?, cartMandate, paymentMandate }; presence
is a flag on the Payment Mandate. Additive, dev-signed presence-only (VII).

Refs #92 #12 #17

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
The AI DX council (4 cold-reader personas + a 4-lens Stripe-grade council per
round, 9 rounds) validated the architecture and directed the spine to be frozen.
Final surface: one client, one catalog price source, one policy array, one typed
{ok|pending|code} door, Money-as-type, trustLevel on every branch; two symmetric
resources (orders.create/retrieve, grants.create/retrieve) + requireInTool, mapped
to Stripe Checkout/PaymentIntent/SetupIntent. Applies the round-9 naming pass
(credential+code, orders.create, opaque Money, approveUrl, delegated authorization
stamped into the serialized mandate). Spec carries the round-by-round journey.

Refs #92 #17 #12 #14

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
… production

FR-009: the 'human finished proving' transition is delivered by a webhook
(credentagent.on('order.settled')) + return-URL, mirroring Stripe success_url +
checkout.session.completed; retrieve(id) is a single read, awaitProof() an inline
option; a poll loop is never the documented path; orders.gate needs no signal.
FR-010: the Intent Mandate is produced in the grants authorize ceremony (not
orders — a human-present order signs the Cart directly); dev-sealed today,
wallet-key-signed on the roadmap (#14/#39/#71).

Refs #92 #17 #14

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
Consolidate the orders.* prototype onto the 008 feature branch so the spec and
the runnable demo that proves it live together (was on a stray proto/orders-surface
branch). Real gate (createStorefront + mount), orders.create/retrieve over it, the
order.settled webhook, two-pane lifecycle UI. node examples/orders-proto/server.mjs → :4010.

Refs #92

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
The orders door's ok branch now carries a real signed ap2.CartMandate
(issueCartMandate over the order lines, .serialize() → base64url) + a paymentMandate
assembled from the real settlement record (presenceMode human_present, amount-bound
to the cart), intentMandate null (HP order, FR-010), trustLevel on every object. A
test-only /api/_test/settle hook lets the ok branch be verified without a phone.
Smoke-tested: cart+payment mandates present, serialize() works, amounts bind.

Refs #92

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
…urface

The v10 grants surface over the REAL DelegatedGate engine: grants.create →
authorize (the Intent Mandate is produced, dev-sealed today) → grant.spend loop
→ grant.revoke. Facade demonstrates the v10 design over the real engine: usd()
Money, the { ok | code } door, idempotent replay ({ok:true, replayed:true}, no
double-charge), split reason codes (over-cap→per-spend-exceeded, over-total→
budget-exceeded), mandateBundle (intentMandate PRESENT, presenceMode
human_not_present). Two-pane UI on :4020. Smoke-tested: per-spend + budget caps,
replay, revoke all correct over the real bounds/ledger/revocation.

Refs #92 #12

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
Both goal increments built, smoke-tested green, committed: (A) real mandateBundle
on the orders door; (B) runnable grants.* prototype over the real DelegatedGate.
Brief covers what's real vs stubbed, how to run each (:4010/:4020/:3007), restart
commands, and four open questions for the maintainer.

Refs #92

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
Maintainer decision: the page-less tool-gate is credentagent.gate(handler, opts)
— top-level and action-agnostic — not orders.gate. Gating a non-commerce tool (a
records release, a deploy) shouldn't live under a commerce resource; identity
leads, payment is one application. #17 shipped credentagent.gate(); the spec now
matches it. orders/grants remain the checkout/delegated lifecycles.

Refs #92 #17

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
Ever's #91 (delegated-verifier seam) is approved and keeps SpecKit feature 008;
renumber ours to 009 to end the collision. Renames the spec dir, the branch, and
.specify/feature.json + the feature-branch/slug references. FR-008 (a requirement
number) and the historical commit quotes are unrelated and untouched.

Refs #92

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
Its open questions are carried into the PR description; the file was an overnight
handoff note, not a main-appropriate artifact.

Refs #92

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
1. Remove examples/orders-proto/orders.mjs — dead code (the real-ceremony server.mjs
   never imports it). It was the superseded first-iteration facade and carried both a
   caller-controlled-$__orderId bind bug (a verified order could run the handler with
   arbitrary new args) and the abandoned orders.gate naming.
2. grants _bundle(): build proper CartMandateLine { id, quantity, unitPrice, lineTotal }
   from the catalog price, so a recipient can reconcile the signed cart mandate to the
   priced purchase (was { sku, qty }, missing the required fields).

Refs #92

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
Add a spec note + a top-of-file banner on each demo saying these are validation
facades, not the shipping API — they graduate into the real credentagent.orders.*
/ grants.* library API in #97 (rewired, not deleted). Keeps #95 honest about what
the prototype code is for.

Refs #92 #97

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
@dzuluaga

Copy link
Copy Markdown
Contributor Author

@TheBlackBit — requesting your review, scoped to what matters for your track.

Please review the design (specs/009-ap2-mandate-chain-dx/spec.md), especially:

  • the grants surfacegrants.create / grant.spend / grant.revoke, the refusal codes (per-spend-exceeded | budget-exceeded | revoked), and the create → authorize → spend → revoke lifecycle;
  • how the AP2 mandates are exposed — the mandateBundle (Intent / Cart / Payment mandate).

This is the contract the HNP graduation (#97) builds to: grants (human-not-present) sits on your DelegatedGate + intent rail, so if you'd want the surface or the mandate model shaped differently, now is the cheap time to say so — before the real build starts.

Skip the prototype code. The examples/*-proto/ files are throwaway validation demos (there's a ⚠ banner on each + a note in the spec) — skim them, don't line-review them. The design in spec.md is the thing to react to.

Thanks 🙏

@TheBlackBit TheBlackBit left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PTAL, thank you🙏🏾

Comment thread specs/009-ap2-mandate-chain-dx/spec.md Outdated
Comment thread specs/009-ap2-mandate-chain-dx/spec.md Outdated
Comment thread specs/009-ap2-mandate-chain-dx/spec.md
Comment thread specs/009-ap2-mandate-chain-dx/spec.md
Comment thread specs/009-ap2-mandate-chain-dx/spec.md
dzuluaga added 2 commits July 25, 2026 11:17
Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
…efusalCode, denied state, Money reconciliation

Addresses the #95 review (TheBlackBit + Codex):
- Age gate: headline example now uses .when() so 21+ applies ONLY to age-restricted carts,
  not every order (the example is the DX test).
- Intent bounds gain an optional `allow` constraint (SKUs/categories/attributes) — a human
  who's away bounds WHAT the agent buys, not just how much; enforced fail-closed each spend
  (new code `not-allowed`). (FR-010)
- RefusalCode is documented as a TYPED union, not string (autocomplete + exhaustiveness).
- Grant lifecycle: all four states documented incl. `denied` (never-authorized, terminal)
  vs `revoked` (authorized-then-cancelled), plus the revoke-wins-fail-closed race. (FR-007)
- Prototypes note refreshed: orders graduated in #98; the caller-controlled-id wrapper was
  removed (Codex P1); grants graduates under #104.
- Honest reconciliation: #98 shipped plain dollar numbers, not the opaque Money type —
  flagged as an open decision for #104 rather than pretending Money shipped.

Codex P1s already fixed on-branch: orders-proto wrapper deleted; grants-proto CartMandate
lines now carry { id, quantity, unitPrice, lineTotal }.

Refs #95 #92 · epic #97

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
@dzuluaga

Copy link
Copy Markdown
Contributor Author

Review addressed — ready for re-review

Thanks @TheBlackBit + Codex. All 7 threads resolved; branch is up to date with main, CI green.

Ever's points:

  • Age looked global → headline example now uses .when() — 21+ applies only to age-restricted carts.
  • Grant bounded how much, not what → added optional allow (SKUs/categories) sealed into the Intent Mandate (FR-010), enforced fail-closed each spend (new not-allowed code). Your exact proposal.
  • Refusal code should be typed → now documented as a typed RefusalCode union (autocomplete + exhaustiveness).
  • 4 states, only 3 explained → FR-007 now documents denied (never-authorized, terminal) vs revoked, plus the revoke-wins-fail-closed race.
  • authorization inside the mandate? → confirmed: FR-006 + HR-002 + SC-004 (a bypass test asserts it through serialization).

Codex P1s (already fixed on-branch): the caller-controlled-id orders-proto wrapper was removed (orders graduated to the real API in #98); grants-proto cart lines now carry { id, quantity, unitPrice, lineTotal }.

One thing I surfaced, not hidden: #98 shipped plain dollar numbers, not the spec's opaque Money type (money.ts was removed under 'dollars everywhere'). I added an honest reconciliation note (FR-005) flagging Money-vs-numbers as an open decision to settle in #104, rather than pretend Money shipped.

@dzuluaga
dzuluaga requested a review from TheBlackBit July 25, 2026 16:23
dzuluaga added a commit that referenced this pull request Jul 25, 2026
…test-d.ts (#111 Codex)

Codex (P2): the compile-time assertion in orders.test.ts never ran through tsc —
tsconfig.test.json type-checks ONLY src/types.test-d.ts, and Vitest transpiles tests
without type-checking, so reverting OrderDoor.code to string left the build green.

Fix: put the real guard in types.test-d.ts (the file tsc -p tsconfig.test.json checks) —
a @ts-expect-error on a non-member code that goes UNUSED (TS2578 → build fails) if code
regresses to string. Verified: reverting OrderDoor.code to string now breaks the build.
orders.test.ts keeps only the runtime value assertion (no misleading untyped guard).

Build/lint green; gate 345, storefront 86.

Refs #95 #111

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
dzuluaga added a commit that referenced this pull request Jul 25, 2026
Closes Ever's #95 typed-code point in shipped orders code. OrderDoor.code → OrderDoorCode union; load-bearing compile guard in types.test-d.ts. Additive, type-only.
Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
@dzuluaga

Copy link
Copy Markdown
Contributor Author

Merging now — all 7 threads addressed and resolved, branch updated onto main, CI green. Thanks @TheBlackBit for the review; your allow point (bound what the agent buys, not just how much) went into the design (FR-010) and is the kind of constraint the grants build (#104) will enforce. The Money-vs-plain-numbers reconciliation is flagged in FR-005 as an open decision for #104.

@dzuluaga
dzuluaga merged commit 4a22abc into main Jul 25, 2026
6 checks passed
@dzuluaga
dzuluaga deleted the 009-ap2-mandate-chain-dx branch July 25, 2026 17:09
dzuluaga added a commit that referenced this pull request Jul 25, 2026
… while you're away (visible in the hub)

The first NEW capability the original demo never had, shipped end-to-end and CLICKABLE:

Library (grants.ts, over the real DelegatedGate engine):
- grants.create({ merchant, budget, perSpend, allow? }) → pending grant; the human approves
  once (the _authorize/_deny seam — today the demo page, the wallet ceremony later calls the
  SAME seam); grants.retrieve(id) rehydrates; grant.spend()/revoke().
- allow bounds: WHAT may be bought (SKUs/categories), enforced server-side fail-closed →
  'not-allowed' (#95 review, Ever's point — now in shipped code, not just the spec).
- Typed GrantDoorCode door mapping the engine refusals (per-spend-exceeded, budget-exceeded,
  wrong-merchant, step-up, revoked…). Age is NON-delegable: wine refuses step-up/needs-human.
- Lifecycle per spec FR-007: pending → authorized/denied(terminal) → revoked; idempotent replay.
- CatalogEntry gains optional category (additive); client gains { catalog } option.

Hub (Section 3): pre-approve '$100 · $30/purchase · Beverages' → YOU approve/deny → play the
agent: coffee ✓ ($82 left), headphones ✗ not-allowed, espresso ✗ per-spend, wine ✗ step-up,
6th coffee ✗ budget-exceeded, revoke → ✗ revoked — every outcome lands in the live feed.

Money decision (spec FR-005 reconciliation): grants use plain dollar numbers, consistent with
the shipped orders surface — recorded for #104.

Tests: 9 (grants.test.ts); the two NEW controls (allow bounds, status gate) proven RED when
removed. Full suite green: gate 354, storefront 86; lint clean. Verified end-to-end headless
AND in the browser (feed screenshot).

Refs #104 · epic #97 · spec 009

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
dzuluaga added a commit that referenced this pull request Jul 25, 2026
…API; facade deleted

Answering the duplication audit on #112:
- examples/grants-proto/grants.mjs (the #95 hand-rolled GrantsProto facade + usd shim +
  REASON_MAP) DELETED — it duplicated what grants.ts now ships. Per the spec's graduation
  promise, server.mjs is REWIRED onto the real credentagent.grants API (same two-pane UI;
  verified over HTTP: create+authorize → wine ✓ $20 → case ✗ per-spend-exceeded →
  replay ✓ → revoke → ✗ revoked).
- examples/hnp-draws: marked as the lower-level DelegatedGate seams reference, superseded
  for everyday use by grants + the demo hub.
- examples/README: HNP section points at the graduated demo.

Audited, NOT duplicated: orders-proto already consumes the real API (no facade); Ever's #103
is the external verify/settle rail — zero grants surface (only client.ts/index.ts merge-order
overlap). #44 remains recommended for closure (superseded by #41 + this).

Merged main in (brings #95 protos + #111). Gate 359 + storefront 86 green; lint clean.

Refs #104 · addresses the #112 duplication audit

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
dzuluaga added a commit that referenced this pull request Jul 25, 2026
…kable (#104) (#112)

* examples(demo-hub): one page to SEE it work — drive the checkout, watch the signed webhook verify live

Closes the reviewer's confidence gap: a stakeholder who can't read diffs can now open one page
and validate the product by USING it. Built on the real library (orders.serve + webhooks):
- a checkout an agent drives (ungated = click-to-complete, no wallet; wine = the real 21+ gate),
- a live feed that shows each order.settled webhook arrive and get SIGNATURE-VERIFIED in real time
  (the invisible cross-service notification, made visible).

Plus GUARANTEES.md — plain-English 'what each piece guarantees + the one command that proves it',
every command a test that fails if the protection is removed. Verified the promised output matches.

  node examples/demo-hub/hub.mjs   # → http://localhost:4000

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>

* feat(gate,#104): credentagent.grants — approve once, the agent spends while you're away (visible in the hub)

The first NEW capability the original demo never had, shipped end-to-end and CLICKABLE:

Library (grants.ts, over the real DelegatedGate engine):
- grants.create({ merchant, budget, perSpend, allow? }) → pending grant; the human approves
  once (the _authorize/_deny seam — today the demo page, the wallet ceremony later calls the
  SAME seam); grants.retrieve(id) rehydrates; grant.spend()/revoke().
- allow bounds: WHAT may be bought (SKUs/categories), enforced server-side fail-closed →
  'not-allowed' (#95 review, Ever's point — now in shipped code, not just the spec).
- Typed GrantDoorCode door mapping the engine refusals (per-spend-exceeded, budget-exceeded,
  wrong-merchant, step-up, revoked…). Age is NON-delegable: wine refuses step-up/needs-human.
- Lifecycle per spec FR-007: pending → authorized/denied(terminal) → revoked; idempotent replay.
- CatalogEntry gains optional category (additive); client gains { catalog } option.

Hub (Section 3): pre-approve '$100 · $30/purchase · Beverages' → YOU approve/deny → play the
agent: coffee ✓ ($82 left), headphones ✗ not-allowed, espresso ✗ per-spend, wine ✗ step-up,
6th coffee ✗ budget-exceeded, revoke → ✗ revoked — every outcome lands in the live feed.

Money decision (spec FR-005 reconciliation): grants use plain dollar numbers, consistent with
the shipped orders surface — recorded for #104.

Tests: 9 (grants.test.ts); the two NEW controls (allow bounds, status gate) proven RED when
removed. Full suite green: gate 354, storefront 86; lint clean. Verified end-to-end headless
AND in the browser (feed screenshot).

Refs #104 · epic #97 · spec 009

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>

* fix(gate,#104): address PR #112 review — sealed bounds immutable, approveUrl real, honest doors

All five Codex findings on #112:
- P1 sealed bounds: create() now deep-freezes a structuredClone snapshot — neither mutating
  grant.allow nor the caller's original options object can widen what the human approved.
  Proven RED when the freeze is removed.
- P1 approveUrl: grants.serve(app) (new grants-serve.ts) serves the approve/deny page at
  /credentagent/grants/:id + the POST actions — the documented create-and-send-the-link flow
  works end-to-end (verified over HTTP: page 200 → public approve → authorized → spend ok).
  The page is the demo stand-in for the wallet ceremony; #71 replaces it via the same seams.
- P2 refusal replay: an idempotency key now replays the ORIGINAL outcome, refusal included —
  a refused key can't be repurposed with a cheaper item. Proven RED when reverted.
- P2 multi-item: items must be exactly one → typed 'invalid-request' (key NOT consumed);
  nothing is silently dropped.
- P2 honest mapping: every engine RefusalCode mapped deliberately (invalid-amount surfaces
  as itself; unpermitted-presentment → step-up; not-yet-valid → expired); the unreachable
  integrity class coarsens to the catch-all 'refused' — never misreported as 'revoked'.

Lifecycle-status refusals stay deliberately uncached (pending → authorized transitions must
let a retry proceed) — documented inline.

Tests: 14 (was 9); gate 359 + storefront 86 green; lint clean.

Refs #104 · addresses #112 review

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>

* chore(#104): de-duplicate — grants-proto now runs on the REAL grants API; facade deleted

Answering the duplication audit on #112:
- examples/grants-proto/grants.mjs (the #95 hand-rolled GrantsProto facade + usd shim +
  REASON_MAP) DELETED — it duplicated what grants.ts now ships. Per the spec's graduation
  promise, server.mjs is REWIRED onto the real credentagent.grants API (same two-pane UI;
  verified over HTTP: create+authorize → wine ✓ $20 → case ✗ per-spend-exceeded →
  replay ✓ → revoke → ✗ revoked).
- examples/hnp-draws: marked as the lower-level DelegatedGate seams reference, superseded
  for everyday use by grants + the demo hub.
- examples/README: HNP section points at the graduated demo.

Audited, NOT duplicated: orders-proto already consumes the real API (no facade); Ever's #103
is the external verify/settle rail — zero grants surface (only client.ts/index.ts merge-order
overlap). #44 remains recommended for closure (superseded by #41 + this).

Merged main in (brings #95 protos + #111). Gate 359 + storefront 86 green; lint clean.

Refs #104 · addresses the #112 duplication audit

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>

---------

Signed-off-by: Diego Zuluaga <dfzuluaga@gmail.com>
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.

2 participants