Skip to content

feat(ap2): official-toolchain fixtures + interop + kid-binding fix (AK-1 tail)#106

Merged
RBKunnela merged 2 commits into
mainfrom
ns/ak1-tail-fixtures-interop
Jun 13, 2026
Merged

feat(ap2): official-toolchain fixtures + interop + kid-binding fix (AK-1 tail)#106
RBKunnela merged 2 commits into
mainfrom
ns/ak1-tail-fixtures-interop

Conversation

@RBKunnela

Copy link
Copy Markdown
Owner

AK-1 tail — AP2 official-toolchain fixtures + interop + kid-binding fix

Status

What this delivers

  • Official-toolchain fixtures — reference-MINTED with provenance (not hand-rolled); interop verified against the AP2 reference toolchain.
  • kid-binding fix — proven NOT to open key-confusion: 5 negative tests assert a mismatched/spoofed kid is rejected (no key-confusion path).
  • Interop coverage for the AP2 envelope.

Known gaps / skips (documented)

  • AC6: single documented skip (intentional, recorded in test).
  • Python parity: documented gap (parity work tracked separately).
  • If the pre-existing tests/scripts/verify-policy-wasm.test.ts (OPA WASM) surfaces red, it is out-of-scope and non-blocking.

Independence

Independent of B1 wire-contract PRs — touches different files, no ordering dependency.

Resolves part of #103.

RBKunnela and others added 2 commits June 13, 2026 20:12
…T1/AC1/AC6/T9)

Closes the AK-1 DoD tail: the VC verify pipeline was merged but backed only by
synthetic fixtures, so the approved AP2 deck phrase was not yet earned. This
slice produces official-toolchain interop evidence.

T1/AC1 — official reference fixtures:
- The google-agentic-commerce/AP2 reference repo (pinned e1ea56d, the same
  commit our spec-pin already targets) ships NO pre-signed sample mandate VCs,
  only models/schemas/tests that mint at runtime. So the honest "official
  sample" is one minted with the reference SD-JWT issuer.
- tests/fixtures/ap2/generate.py drives ap2.sdk.sdjwt.sd_jwt.create (ES256,
  RFC 9901) over the genuine ap2.models IntentMandate/CartMandate bodies with a
  deterministic issuer key, and vendors intent/cart VC envelopes + the issuer
  DID document + meta + PROVENANCE.md (source URL, commit SHA, retrieval date).

AC1 interop finding (real, fixed): the reference issuer emits a BARE JOSE kid
(e.g. `issuer-key-1`), but the verifier required a DID-fragment-qualified VM id.
Different namespaces — every reference mandate failed AP2_KEY_RESOLUTION_FAILED.
Fixed: kid now also matches the VM-id fragment / `<did>#<kid>`, WITHOUT weakening
the cross-issuer-confusion guard (candidate keys still come only from the
issuer's own DID document). Regression tests cover bare-kid match + non-match.

AC6 — interop CI: .github/workflows/ap2-interop.yml (workflow_dispatch + weekly
schedule, ubuntu-latest, SHA-pinned actions). interop-checked-in always verifies
the committed reference-minted fixtures; interop-fresh regenerates them from the
reference SDK and round-trips — guarded by a single documented skip when the
upstream toolchain is unavailable (the sole permitted skip in this epic).

T7 — Python parity gap: documented (not half-implemented) in the Python README
with an explicit "do not represent Python as verifying" guardrail + follow-up
TODO, per T7's allowance.

T9 — honesty docs: ap2-vc.ts spec-pin notes the vendored fixtures; the main
README AP2 honesty note now states official-fixture-backed verification (TS-side)
and what it does NOT cover (Python verify, mandate issuing).

Gates: eslint src 0 errors, tsc --noEmit clean, full suite 555 pass / 0 skip,
coverage on src/ap2-vc.ts 87.8% stmts / 86.2% branch (≥80/70), open-core
boundary clean. Fixtures regenerate deterministically (issuer key byte-stable).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eanup)

The generator imports the external google-agentic-commerce/AP2 reference SDK,
present only in the AC6 interop CI job / a local reference env. File-level pyright
directive suppresses the expected unresolved-import + unused warnings (IDE noise
only; not on build/test/lint path). Non-blocking CONCERNS from @qa gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@RBKunnela, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 18 minutes and 29 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d292c498-2e80-4cef-997a-6493d88a7180

📥 Commits

Reviewing files that changed from the base of the PR and between 97d3f3e and 8c345b5.

📒 Files selected for processing (12)
  • .github/workflows/ap2-interop.yml
  • README.md
  • packages/python/README.md
  • src/ap2-vc.ts
  • tests/ap2-interop.test.ts
  • tests/ap2-vc.test.ts
  • tests/fixtures/ap2/PROVENANCE.md
  • tests/fixtures/ap2/cart-mandate.vc.json
  • tests/fixtures/ap2/generate.py
  • tests/fixtures/ap2/intent-mandate.vc.json
  • tests/fixtures/ap2/issuer-did-document.json
  • tests/fixtures/ap2/meta.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ns/ak1-tail-fixtures-interop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown

FriendlyAI review unavailable — upstream error

The review service returned an error, so no verdict was produced. This is a neutral result, not a block. If this PR needs to merge while review is unavailable, a maintainer can apply the friendlyai-bypass-ack-by-maintainer label as a documented soft override.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request integrates official-toolchain interop fixtures and tests for AP2 mandate verification, documenting the Python SDK's parity gap and updating the TypeScript verifier to resolve bare kid namespaces emitted by the reference issuer. Feedback on the changes suggests simplifying the fragmentOf helper function in src/ap2-vc.ts and replacing the legacy JSON.parse(JSON.stringify(...)) deep cloning pattern in the tests with the native structuredClone() API.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/ap2-vc.ts
Comment on lines +1167 to +1170
const fragmentOf = (id: string): string => {
const hash = id.indexOf('#');
return hash >= 0 ? id.slice(hash + 1) : id;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The fragmentOf helper function can be simplified by leveraging the behavior of String.prototype.slice() when passed 0 (which happens when indexOf('#') returns -1). This avoids the need for an explicit ternary check and local variable assignment, making the code more concise and readable.

      const fragmentOf = (id: string): string => id.slice(id.indexOf('#') + 1);

Comment thread tests/ap2-interop.test.ts

describe('[INTEROP] AC2 - tampered real Cart Mandate fails closed', () => {
function tamper(mutate: (vc: Ap2MandateVc) => void): Ap2MandateVc {
const clone = JSON.parse(JSON.stringify(cartVc)) as Ap2MandateVc;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Instead of using the legacy JSON.parse(JSON.stringify(...)) pattern for deep cloning, you can use the native, standard structuredClone() API. It is cleaner, more performant, and preserves the type of cartVc without requiring an explicit type assertion.

Suggested change
const clone = JSON.parse(JSON.stringify(cartVc)) as Ap2MandateVc;
const clone = structuredClone(cartVc);

@RBKunnela RBKunnela merged commit c949e33 into main Jun 13, 2026
12 checks passed
@RBKunnela RBKunnela deleted the ns/ak1-tail-fixtures-interop branch June 13, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant