@integraledger/lcp-placement-visa-tap 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,97 @@
1
1
  # @integraledger/lcp-placement-visa-tap
2
2
 
3
+ ## 0.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 822190a: Give the terms URL the write path the published set never had, and certify the composition that broke
8
+ without it (integra-protocol#8).
9
+
10
+ A third party assembling a seller from published parts emitted a 402 the published buyer refuses: every
11
+ published reader demanded `legalContextUrl` and no published writer placed it, and the schema
12
+ `placement-x402` inlined onto the wire (`required: ["type","value"]`, closed) contradicted the authority
13
+ document integraledger.com serves (`required: ["type","value","legalContextUrl"]`, closed) — two
14
+ definitions of one `info`, no document valid against both, each package self-consistent. Three structural
15
+ gaps let it ship: the manifest's `termsUrlField` was singular and read-only (declared, hygiene-checked,
16
+ never written — and x402's wire carries the URL in two slots, so one path could not even name the shape),
17
+ nothing compared the inlined schema to the authority document, and the corpus certified `place` and
18
+ `extract` separately but never fed one to the other.
19
+
20
+ `binding-core` — the placement seam now moves an ADVERTISEMENT, not a bare reference. `place` takes
21
+ `{ ref, termsUrl? }` and writes the URL at every slot the manifest's new `termsUrlFields` (plural,
22
+ replacing `termsUrlField`) declares; it REFUSES an integrity-bearing advertisement with no URL where slots
23
+ are declared (a hash no counterparty can resolve is unverifiable by construction), a URL where no slot
24
+ exists (silent dropping is fail-open), and a non-https URL on either side of the seam. `extract` returns
25
+ `{ ref, termsUrl }` with absence as a typed value — `no-field-declared` is a fact about the protocol,
26
+ `declared-fields-empty` a fact about the document, and the gate decides what an absence means — while two
27
+ slots that disagree, or a malformed value in either, refuse. The object-path writer learned to descend
28
+ into an EXISTING array element (never minting one, never extending a list, refusing an index segment it
29
+ would have to create), which is what lets x402's `accepts[0].extra` mirrors land.
30
+
31
+ `placement-x402` — the inlined wire schema now IS the authority document minus `$id` and `$defs`
32
+ (Bazaar forbids both on the wire), drift-gated in `lcp-conformance` where the two packages meet.
33
+ `termsUrlFields` declares both slots the wire carries; the bare-hash alias is written (`extra` stopped
34
+ being wholly scheme-private when x402 §6.1 reserved names inside it, and LCP v1.38 §C.4's own Tier A
35
+ illustration carries the pair there); the `url` carrier admission is withdrawn (`carrierTypes` is
36
+ `sha256` alone — the schema on the wire is `const: "sha256"`, and no shipped reader ever accepted a url
37
+ in this slot). The `place` override shrinks to composition: the kit performs the whole placement and the
38
+ override adds only the `{info, schema}` wrapper.
39
+
40
+ `placement-mpp` / `placement-acp` — the singular member becomes the one-entry `termsUrlFields`; the kit
41
+ now writes the slot their buyer parsers always demanded and refuses first at the seller.
42
+
43
+ `lcp-conformance` — the corpus grows 812 → 844: a `roundtrip` op (place then extract in one case, the
44
+ composition certification whose absence let two separately-conformant halves ship jointly broken),
45
+ advertisement-rule refusals for every manifest, and the authority↔wire drift gate. Extract expectations
46
+ across every placement area become the extracted advertisement.
47
+
48
+ `lcp-verify` — `referencePlacementStep` reads the advertisement (`extracted.ref.value`) and deliberately
49
+ ignores `termsUrl`: where the terms live is the gate's fetch concern, not a fact the record can
50
+ contradict.
51
+
52
+ `lcp-discovery` — the x402 authority document restates the atrHash pattern inline in both definitions
53
+ (no `$defs` indirection the wire copy would have to rewrite) and moves the two-definitions rationale into
54
+ `$defs.receipt`, so the challenge-time root is byte-derivable for the wire.
55
+
56
+ ### Patch Changes
57
+
58
+ - Updated dependencies [b2ffecc]
59
+ - Updated dependencies [822190a]
60
+ - @integraledger/lcp-binding-core@0.11.0
61
+
62
+ ## 0.10.1
63
+
64
+ **0.10.0 was staged and withdrawn before approval; this is that release, re-cut.** The conformance corpus
65
+ was re-sealed after 0.10.0 was staged — its root moved `32fa90a6…` → `28bbf4ef…` when the vector tree was
66
+ brought inside the prose gates — so the staged `lcp-conformance` tarball carried a seal that no longer
67
+ matched the repository. The seal is what proves corpus authenticity to an independent implementer, and a
68
+ published version cannot be replaced, so the whole set was rejected and re-cut rather than shipping one
69
+ package that disagreed with its own source. No version 0.10.0 exists on the registry.
70
+
71
+ ### Minor Changes
72
+
73
+ - 3f2d2e3: **Breaking, two wire identities and one exported name.**
74
+
75
+ `LCP_CAPABILITY_NAME` is now `com.integraledger.legal_context` (was `com.integraledger.legal-context`), and
76
+ `LCP_TERMS_HASH_SUFFIX` is now `lcp_terms_hash` (was `lcp-terms-hash`). Both go on a counterparty's wire, and
77
+ both were hyphenated where the host they are written into spells its own vocabulary with underscores
78
+ throughout — UCP (`dev.ucp.shopping.checkout`, `com.example.policy.price_match`) and Verifiable Intent (all
79
+ eight registered constraint types). Our own UCP `policies[]` carrier already used underscores, so one
80
+ deployment identity was spelled two ways. No host forces either spelling, which is why the house had to rule
81
+ it: **follow the vocabulary you are writing into.** `LEGAL_CONTEXT_WELL_KNOWN_PATH` keeps its hyphen for the
82
+ same reason — RFC 8615 well-known names are hyphenated.
83
+
84
+ `VISA_TAP_PLACEMENT_TIER_A` is renamed `VISA_TAP_PLACEMENT`, matching every sibling placement. The tier is a
85
+ manifest field because it can move; an identifier carrying the answer could only be corrected by a breaking
86
+ rename, which is the hazard `placement-mastercard-vi` states as `tier: "B"` IS A LABEL, NOT A GATE.
87
+
88
+ The conformance corpus is re-sealed: root `32fa90a62eb83930…`, 812/812 across 44 areas, unchanged in size.
89
+ Twenty-two cases pinned the old spellings and were updated; the retired spelling survives deliberately in
90
+ `binding-core`'s kit fixtures, where it is sample input to container-validation cases cut against v1.37
91
+ §C.3's `extensions` shape and asserts nothing about this deployment's identity.
92
+
93
+ `minor` rather than `major` because these packages are pre-1.0, where minor is the breaking increment.
94
+
3
95
  ## 0.9.0
4
96
 
5
97
  First public release.
package/README.md CHANGED
@@ -24,11 +24,11 @@ npm install @integraledger/lcp-placement-visa-tap
24
24
  ## Use
25
25
 
26
26
  ```ts
27
- import { VISA_TAP_PLACEMENT_TIER_A, visaTapPlacement } from "@integraledger/lcp-placement-visa-tap";
27
+ import { VISA_TAP_PLACEMENT, visaTapPlacement } from "@integraledger/lcp-placement-visa-tap";
28
28
 
29
29
  declare const request: { headers: Record<string, string> }; // the TAP request, as received
30
30
 
31
- const placed = visaTapPlacement.place({ type: "sha256", value: "0x…" }, { headers: {} });
31
+ const placed = visaTapPlacement.place({ ref: { type: "sha256", value: "0x…" } }, { headers: {} });
32
32
  const ref = visaTapPlacement.extract(request);
33
33
  ```
34
34
 
@@ -62,7 +62,10 @@ claims were put to the spec, and all three hold:
62
62
  silently replaceable while *looking* bound to a reader, which is strictly worse than an honestly
63
63
  advertised header.
64
64
 
65
- **Drift against LCP v1.37 §C.6: none material.** The appendix is an illustration; the host spec is binding
65
+ **Drift against LCP §C.6: none material, and v1.38 closed the one wording gap.** v1.37 §C.6 did not name
66
+ TAP's RFC 9421 signature parameters; v1.38 does — `created`, `expires`, `keyid`, `alg`, `nonce`, `tag`,
67
+ noting `keyid` rather than `kid` and what `tag` distinguishes — which is the distinction this package draws
68
+ below. The appendix is an illustration; the host spec is binding
66
69
  — the host's live specification is what binds. §C.6's description of the covered components, the body-object quartet and the nonce-match rule all
67
70
  survive the live read. Two notes for the record: the covered-component limitation is stated for the agent
68
71
  recognition signature in the specification and repeated for the reference implementation, so it is the
@@ -95,9 +98,9 @@ HTTP-Layer Advisory, which is exactly what an uncovered header is.
95
98
 
96
99
  ## The surface is deliberately two exports
97
100
 
98
- `visaTapPlacement` is `makePlacement(VISA_TAP_PLACEMENT_TIER_A)` — `makePlacement` comes from
101
+ `visaTapPlacement` is `makePlacement(VISA_TAP_PLACEMENT)` — `makePlacement` comes from
99
102
  [`@integraledger/lcp-binding-core`](../binding-core#readme) — and nothing else — the manifest *is* the
100
- adapter (S7). Both members are total: a refusal is a returned value, never a thrown exception.
103
+ adapter. Both members are total: a refusal is a returned value, never a thrown exception.
101
104
 
102
105
  **There is no helper here that builds an unsigned sibling body object, and a test pins the export set so
103
106
  there never is.** That helper is the single worst thing this package could ship: a body sibling carrying the
@@ -137,7 +140,7 @@ reach and reads with an honest `not-attempted` at settlement-enumeration.
137
140
  ## Provenance
138
141
 
139
142
  Cut against the Visa TAP specification at Visa Developer and RFC 9421, gate discharged **2026-07-30**, and
140
- reconciled against LCP v1.37 §C.6 the same day.
143
+ reconciled against LCP v1.37 §C.6 the same day, and re-read against **v1.38 §C.6** on 2026-08-12.
141
144
 
142
145
  ---
143
146
 
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { VISA_TAP_PLACEMENT_TIER_A } from "./manifest.js";
1
+ export { VISA_TAP_PLACEMENT } from "./manifest.js";
2
2
  export { visaTapPlacement } from "./placement.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { VISA_TAP_PLACEMENT_TIER_A } from "./manifest.js";
1
+ export { VISA_TAP_PLACEMENT } from "./manifest.js";
2
2
  export { visaTapPlacement } from "./placement.js";
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -47,6 +47,14 @@ import type { PlacementManifest } from "@integraledger/lcp-binding-core";
47
47
  * The container is `header-map`, the only one in the placement set: RFC 9110 field names compare
48
48
  * case-insensitively, so the kit folds case on read and reuses an existing key's casing on write. Neither is
49
49
  * a heuristic — it is what reading and writing an HTTP field map correctly means.
50
+ *
51
+ * **THE TIER IS NOT IN THE NAME, AND THAT IS DELIBERATE.** This constant was `VISA_TAP_PLACEMENT_TIER_A`.
52
+ * The tier is a manifest FIELD precisely because it can move: Tier A here rests on a specific reading —
53
+ * that TAP's `Signature-Input` covers exactly `"@authority" "@path"`, so a custom header is uncovered and a
54
+ * stock verifier tolerates it. If TAP widens that coverage the field below changes, and an identifier
55
+ * carrying the old answer could only be corrected by a breaking rename. It is the same hazard
56
+ * `placement-mastercard-vi` states in capitals — `tier: "B"` IS A LABEL, NOT A GATE — one level up, at the
57
+ * API. Every sibling placement exports `<PROTOCOL>_PLACEMENT`; this one now does too.
50
58
  */
51
- export declare const VISA_TAP_PLACEMENT_TIER_A: PlacementManifest;
59
+ export declare const VISA_TAP_PLACEMENT: PlacementManifest;
52
60
  //# sourceMappingURL=manifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,eAAO,MAAM,yBAAyB,EAAE,iBAUvC,CAAC"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,eAAO,MAAM,kBAAkB,EAAE,iBAUhC,CAAC"}
package/dist/manifest.js CHANGED
@@ -46,8 +46,16 @@
46
46
  * The container is `header-map`, the only one in the placement set: RFC 9110 field names compare
47
47
  * case-insensitively, so the kit folds case on read and reuses an existing key's casing on write. Neither is
48
48
  * a heuristic — it is what reading and writing an HTTP field map correctly means.
49
+ *
50
+ * **THE TIER IS NOT IN THE NAME, AND THAT IS DELIBERATE.** This constant was `VISA_TAP_PLACEMENT_TIER_A`.
51
+ * The tier is a manifest FIELD precisely because it can move: Tier A here rests on a specific reading —
52
+ * that TAP's `Signature-Input` covers exactly `"@authority" "@path"`, so a custom header is uncovered and a
53
+ * stock verifier tolerates it. If TAP widens that coverage the field below changes, and an identifier
54
+ * carrying the old answer could only be corrected by a breaking rename. It is the same hazard
55
+ * `placement-mastercard-vi` states in capitals — `tier: "B"` IS A LABEL, NOT A GATE — one level up, at the
56
+ * API. Every sibling placement exports `<PROTOCOL>_PLACEMENT`; this one now does too.
49
57
  */
50
- export const VISA_TAP_PLACEMENT_TIER_A = {
58
+ export const VISA_TAP_PLACEMENT = {
51
59
  protocol: "visa-tap",
52
60
  pattern: "http-advisory",
53
61
  tier: "A",
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAsB;IAC1D,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,eAAe;IACxB,IAAI,EAAE,GAAG;IACT,QAAQ,EAAE,YAAY;IACtB,SAAS,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE;IACjC,KAAK,EAAE,oBAAoB;IAC3B,YAAY,EAAE,CAAC,QAAQ,CAAC;IACxB,OAAO,EACL,wJAAwJ;CAC3J,CAAC"}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,eAAe;IACxB,IAAI,EAAE,GAAG;IACT,QAAQ,EAAE,YAAY;IACtB,SAAS,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE;IACjC,KAAK,EAAE,oBAAoB;IAC3B,YAAY,EAAE,CAAC,QAAQ,CAAC;IACxB,OAAO,EACL,wJAAwJ;CAC3J,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { type ReferencePlacementAdapter } from "@integraledger/lcp-binding-core";
2
2
  /**
3
- * The Visa TAP reference placement. The manifest IS the adapter (S7) there is nothing else here, and that
3
+ * The Visa TAP reference placement. The manifest IS the adapter — `binding-core`'s kit needs nothing else
4
+ * here, and that
4
5
  * is a claim about the protocol, not brevity for its own sake.
5
6
  *
6
7
  * `header-map` supplies every mechanic this placement needs: the RFC 9110 case fold on read, the
@@ -1 +1 @@
1
- {"version":3,"file":"placement.d.ts","sourceRoot":"","sources":["../src/placement.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gBAAgB,EAAE,yBAE9B,CAAC"}
1
+ {"version":3,"file":"placement.d.ts","sourceRoot":"","sources":["../src/placement.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gBAAgB,EAAE,yBACI,CAAC"}
package/dist/placement.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { makePlacement, } from "@integraledger/lcp-binding-core";
2
- import { VISA_TAP_PLACEMENT_TIER_A } from "./manifest.js";
2
+ import { VISA_TAP_PLACEMENT } from "./manifest.js";
3
3
  /**
4
- * The Visa TAP reference placement. The manifest IS the adapter (S7) there is nothing else here, and that
4
+ * The Visa TAP reference placement. The manifest IS the adapter — `binding-core`'s kit needs nothing else
5
+ * here, and that
5
6
  * is a claim about the protocol, not brevity for its own sake.
6
7
  *
7
8
  * `header-map` supplies every mechanic this placement needs: the RFC 9110 case fold on read, the
@@ -17,5 +18,5 @@ import { VISA_TAP_PLACEMENT_TIER_A } from "./manifest.js";
17
18
  * would make the header any more bound than it is. The export set is pinned by a test for the related
18
19
  * reason — no helper here may build an unsigned sibling body object.
19
20
  */
20
- export const visaTapPlacement = makePlacement(VISA_TAP_PLACEMENT_TIER_A);
21
+ export const visaTapPlacement = makePlacement(VISA_TAP_PLACEMENT);
21
22
  //# sourceMappingURL=placement.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"placement.js","sourceRoot":"","sources":["../src/placement.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,GAEd,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE1D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA8B,aAAa,CACtE,yBAAyB,CAC1B,CAAC"}
1
+ {"version":3,"file":"placement.js","sourceRoot":"","sources":["../src/placement.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,GAEd,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,aAAa,CAAC,kBAAkB,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integraledger/lcp-placement-visa-tap",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Places an LCP reference into a Visa TAP request. The header sits outside TAP's signature.",
5
5
  "keywords": [
6
6
  "lcp",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "homepage": "https://github.com/IntegraLedger/integra-protocol/tree/main/packages/placement-visa-tap#readme",
43
43
  "dependencies": {
44
- "@integraledger/lcp-binding-core": "0.9.0"
44
+ "@integraledger/lcp-binding-core": "0.11.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@cfworker/json-schema": "4.1.1",
package/src/index.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { VISA_TAP_PLACEMENT_TIER_A } from "./manifest.js";
1
+ export { VISA_TAP_PLACEMENT } from "./manifest.js";
2
2
  export { visaTapPlacement } from "./placement.js";
package/src/manifest.ts CHANGED
@@ -48,8 +48,16 @@ import type { PlacementManifest } from "@integraledger/lcp-binding-core";
48
48
  * The container is `header-map`, the only one in the placement set: RFC 9110 field names compare
49
49
  * case-insensitively, so the kit folds case on read and reuses an existing key's casing on write. Neither is
50
50
  * a heuristic — it is what reading and writing an HTTP field map correctly means.
51
+ *
52
+ * **THE TIER IS NOT IN THE NAME, AND THAT IS DELIBERATE.** This constant was `VISA_TAP_PLACEMENT_TIER_A`.
53
+ * The tier is a manifest FIELD precisely because it can move: Tier A here rests on a specific reading —
54
+ * that TAP's `Signature-Input` covers exactly `"@authority" "@path"`, so a custom header is uncovered and a
55
+ * stock verifier tolerates it. If TAP widens that coverage the field below changes, and an identifier
56
+ * carrying the old answer could only be corrected by a breaking rename. It is the same hazard
57
+ * `placement-mastercard-vi` states in capitals — `tier: "B"` IS A LABEL, NOT A GATE — one level up, at the
58
+ * API. Every sibling placement exports `<PROTOCOL>_PLACEMENT`; this one now does too.
51
59
  */
52
- export const VISA_TAP_PLACEMENT_TIER_A: PlacementManifest = {
60
+ export const VISA_TAP_PLACEMENT: PlacementManifest = {
53
61
  protocol: "visa-tap",
54
62
  pattern: "http-advisory",
55
63
  tier: "A",
package/src/placement.ts CHANGED
@@ -2,10 +2,11 @@ import {
2
2
  makePlacement,
3
3
  type ReferencePlacementAdapter,
4
4
  } from "@integraledger/lcp-binding-core";
5
- import { VISA_TAP_PLACEMENT_TIER_A } from "./manifest.js";
5
+ import { VISA_TAP_PLACEMENT } from "./manifest.js";
6
6
 
7
7
  /**
8
- * The Visa TAP reference placement. The manifest IS the adapter (S7) there is nothing else here, and that
8
+ * The Visa TAP reference placement. The manifest IS the adapter — `binding-core`'s kit needs nothing else
9
+ * here, and that
9
10
  * is a claim about the protocol, not brevity for its own sake.
10
11
  *
11
12
  * `header-map` supplies every mechanic this placement needs: the RFC 9110 case fold on read, the
@@ -21,6 +22,5 @@ import { VISA_TAP_PLACEMENT_TIER_A } from "./manifest.js";
21
22
  * would make the header any more bound than it is. The export set is pinned by a test for the related
22
23
  * reason — no helper here may build an unsigned sibling body object.
23
24
  */
24
- export const visaTapPlacement: ReferencePlacementAdapter = makePlacement(
25
- VISA_TAP_PLACEMENT_TIER_A,
26
- );
25
+ export const visaTapPlacement: ReferencePlacementAdapter =
26
+ makePlacement(VISA_TAP_PLACEMENT);