The reference architecture for agent-ready commerce has one central rule:
Every external surface should be a projection of the same commerce deci...
For further actions, you may consider blocking this person and/or reporting abuse
The framing of every surface as a projection of one decision spine is the part that's going to stick w/ me, and the eligibility vs authority split even more, people collapse those two into "authz" constantly and then act surprised when a perfectly authenticated actor walks an invalid action straight through (same energy in the payment-artifact-is-evidence-not-permission point, that reframe alone kills a whole category of bugs). Where i'd push is the line you almost throw away, that the domain decision path has to be the easiest path to use. imo that's the actual ballgame and it's more of an org problem than an architecture one. Every anti-pattern you list, adapter sneaking in a local in_stock && price<150 check, generated text quietly becoming truth, feed going optimistic, is just what happens under a deadline when the shortcut is one line and the spine is a call through three layers. The invariants are right but nothing structural stops a dev bypassing them at 5pm on a friday, so the only teeth i've seen actually hold is the cross-surface contract test you gesture at, i.e. run the same Travel Backpack scenario through the feed, the tool adapter, checkout and admin and fail CI when they disagree on even one blocker code. w/o that the drift isn't a risk, it's a certainty on a delay.
fwiw this maps almost 1:1 onto grounding an LLM, which is where i spend most of my time. "Source data is not automatically truth, it becomes a referenced fact" and "generated claims are derived, not authoritative" is the same discipline as not letting a model assert anything it can't cite back to a verified source. Different domain, identical spine.
Thank you very much for the thoughtful review and I'm glad the architecture resonated! I'm taking your points into consideration and I'm going to refine both the article and the reference implementation. The architecture already assumes one decision spine, with eligibility, authority, checkout state, payment evidence, policy grounding, and generated claims kept separate. What I'd make more explicit is that the domain decision path also has to be the easiest path for adapters to use. Shared route and public type ownership have already been consolidated across API/client/protocol surfaces, command dispatch has been simplified, and the next source step is a canonical decision envelope that carries eligibility, authority, blocker codes, evidence references, checkout state, payment authority, generated-claim status, and next safe actions in one shape.
One clarification, the Travel Backpack scenario is meant as a small semantic drift check across feed, tool adapter, checkout, and admin, not a big contract-test framework. It simply confirms that the same blocker codes and causal meaning survive across surfaces.
totally fair, and the small-drift-check framing is actually the stronger version, i was overloading it. my point wasn't really about size though, it was about where it's wired. a small semantic drift check that runs as a required check on merge is worth more than a full contract-test framework that's optional, because the thing that kills you isn't the check being too thin, it's it being skippable at 5pm on a friday. so keep it small, just make it non-optional. same blocker codes surviving across feed/adapter/checkout/admin is exactly the right assertion, and if that one check red-blocks the merge, the drift you're worried about can't land quietly.
the canonical decision envelope is the part i'd be most excited to see, especially carrying generated-claim status as a first-class field. that's the same move as making an LLM cite back to a verified source before it's allowed to assert: generated is derived until something grounds it. once that status rides in the envelope instead of living in each adapter's head, the "generated text quietly becoming truth" failure basically can't express itself. different domain, identical spine, like i said.
Updated. Implemented the small non-optional drift check, one canonical envelope, and thin projections.
The implementation now centers on one canonical decision envelope carrying eligibility, actor authority, checkout state, payment authority, generated-claim status, evidence references, and next safe actions. Each surface projects from that envelope instead of recreating meaning locally.
Generated-claim status is first-class in the envelope, so generated text remains derived unless it is grounded, scoped, reviewed, and allowed for that use.
love that you shipped it, and putting eligibility, authority, checkout, payment and generated-claim status in the one envelope is exactly the move. now that it's real, the next thing that can still slip is scope on the grounding.
"grounded, scoped, reviewed, allowed" is a status, but grounded is never global, it's grounded FOR something. the failure that can still express itself is a claim that got grounded and allowed for one surface getting projected into another where the scope doesn't hold, and because the envelope says allowed, the second surface trusts it. it's the same as a citation licensing the exact claim it grounds and not every paraphrase downstream of it. so i'd have the envelope carry what the allowance is scoped TO, and let the projections check allowed-for-this-use, not just allowed. thin projection means pure read, and the one thing it must never do is widen its own scope.
the other one is decay. grounding is only as true as the evidence it pinned to, so if the reference isn't pinned to a version or a hash, the source can move and "grounded" quietly goes stale with no status ever changing. pin the grounding to the evidence version and the check can catch a claim still riding as grounded against evidence that shifted under it.
and both of those are things your small non-optional check can assert, not just the blocker codes surviving: no projection re-derives status locally, and no grounded claim outlives the evidence version it was grounded against. keep it small, keep it non-optional, just let it guard the envelope's meaning and not only its shape.
Really appreciate you catching that! I'll make the grounded/allowed status scoped to a requested use and pinned to source evidence.
The current baseline has hashes, source versions, and use-specific policy helpers, but the shared envelope does not force the "allowed for this use against this evidence pin" decision.
I'm adding that as a compact domain primitive, so the envelope carries
allowedForandevidencePin, and projections can only use a grounded claim after calling the domain helper for the exact requested use/surface/scope. The generated policy-claim projection should use that helper, so it does not widen scope locally or keep trusting a claim if the pinned evidence changes.Came for the architecture, stayed for the thread — you two independently rebuilt something I've been calling a typed provenance vector from the commerce side, and the convergence is the interesting part.
Your decision spine is a provenance chain: every projection is only as trustworthy as the facts it pins to, and evidencePin is exactly a freshness axis — "grounded" quietly goes stale when the source moves unless the claim is pinned to a version. The move Vinicius pushed you toward — grounded text isn't a field you read, it's a right you exercise through a gate that checks use/surface/scope/evidence and hands back something tamper-evident — is capability-based security applied to generated claims. Same thing I landed on for agent side effects: make the unsafe path unrepresentable, so "forgot to call the helper" can't be written, not just shouldn't be.
Two things from the provenance-chain angle that might transfer:
1/ refused-here vs never-grounded is the start of a taint distinction, and it wants to propagate. If a claim refused for surface A feeds a derived claim that then gets projected to surface B, the derivation can launder the refusal — B sees a fresh-looking claim and never learns its input was refused upstream. The scope check has to taint downstream, not just gate at the leaf, or the exact failure you closed re-enters one level up.
2/
allowedForscoped to a use is really one axis of several. Freshness (evidencePin) and scope (allowedFor) degrade independently — a claim can be perfectly fresh but out-of-scope, or in-scope but stale. Collapsing them into one allowed/refused bit works until a consumer cares about one axis and not the other. At that point the envelope wants to carry the axes separately and let each projection apply its own floor.Genuinely one of the best comment threads I've read here — the "different domain, same spine" line is doing a lot of work, and it's correct.
Hi Sergei! Agreed. Your framing helped me make the model more precise: not just "a claim is grounded" but "a claim is projectable only with its provenance state carried through".
I've now applied that in the implementation. Derived claims now carry upstream projection state, so a refused input cannot become clean just because it was reworded or composed into another claim. I also kept the projection reasons separated (source, freshness, scope, surface, use, payload, and inherited refusal) so each surface can apply its own floor instead of reading one flattened allowed/refused bit.
That connects directly with Vinicius capability-gate point. Thanks for pushing on this.