portage-cli 0.6.0 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4acb63ece20e24b7d43048315145a96e15ded1c4e5b0cf83c3f045603a033af6
4
- data.tar.gz: 536057c80badcd44ee1be90679fd7f9233adb3d5a1c07b5b11a12201fbae5701
3
+ metadata.gz: 5c2f71cd7ac7290b314bc824678fc8a9fbc31b6e43cdea4c4e78077440c555c9
4
+ data.tar.gz: f63510ff1b5d5d945e6e9753b0cf1a521d8da6b084d562c230c4d6b5a98ad6ad
5
5
  SHA512:
6
- metadata.gz: 54bc26bf74fe96c2a2154062ab41619e10bf046da1cc06f067fc1f0f7eaec9c29ff6ab461e9cf2907ede4084805b8718f65557e5d4c3c79c486650eea10c1075
7
- data.tar.gz: deb45f093ce769ebae837bad5af628c8970aac76b6741ea8b083393b45038451a455ece2d603671c3d2884e3974858941ff6c29d96affd7bd3bbcc89f6a689d9
6
+ metadata.gz: 802fb90a17c93986ff7f9101e09e845af467e11607985ddd62cefcd4704f25c5f05292f871c168adeebf386d92620471d6ea6bd8273a74fabb11a3c765324f7d
7
+ data.tar.gz: '021008e8975eb3002986f0e9f27d5d776b1da067c3fbc6083909abbecde99a616680d3569c609fae0d87ae402e4002577c8cca6bd62ca4811d95c580d8ba8d10'
data/CHANGELOG.md CHANGED
@@ -4,6 +4,194 @@ All notable changes to this project are documented here. Format loosely follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.0.0/); this project is
5
5
  pre-1.0, so APIs may still shift between minor versions.
6
6
 
7
+ ## [0.7.0] - 2026-09-23
8
+
9
+ - **The decision layer is wired in.** `buy`/`find` make their judgment
10
+ calls through the same rules `portage-ucp-decision` wraps. Ranking,
11
+ escalation and the policy check live in `portage-ucp` core
12
+ (`Support::OfferRanking`, `Support::Escalation`, `PolicyGuard`), so they
13
+ run the same with or without that gem. `portage-ucp-decision` stays an
14
+ optional plugin that only the confidence gate needs. Every checkout
15
+ report carries the verdicts under `decisions:` (`escalation`, `policy`,
16
+ and `confidence` when enabled). Requires `portage-ucp ~> 0.9`.
17
+ - `find` ranks offers buyable first, then cheapest, then unpriced. The
18
+ order is unchanged.
19
+ - When a checkout both requires escalation and mismatches the request
20
+ under `PORTAGE_ABORT_ON_CHECKOUT_MISMATCH`, `buy` now reports the
21
+ store's escalation, not the mismatch. Both hand off the checkout.
22
+ - `buy --yes` now checks your spend policy before completing, on remote
23
+ native-UCP stores too. Before, only the own-store flow's in-process
24
+ `Dispatcher` enforced it, so a remote store never saw your caps,
25
+ allowlist or token scopes. A blocked purchase hands off the checkout.
26
+ - New opt-in confidence gate: `--decision-backend jev|laya` /
27
+ `PORTAGE_DECISION_BACKEND`, with a threshold from `--min-confidence` /
28
+ `PORTAGE_MIN_CONFIDENCE` (default `0.8`). Needs `portage-ucp-decision`.
29
+ A low score, a backend that can't answer, or a missing gem holds the
30
+ purchase and hands off the checkout.
31
+ - Every verdict carries a `reason`: a string, or null when the gate
32
+ passed, the same in-process as in `--json`. `confidence.reason` is
33
+ `below_threshold`, `backend_error` or `not_installed`, and
34
+ `confidence.error` holds the detail behind the last two.
35
+ - **`buy` checked out sold-out items when in-stock matches were right
36
+ there.** It took the top search hit and its first variant unconditionally,
37
+ so a sold-out top hit dead-ended on the store's "Sold out" refusal
38
+ (allbirds.com and billabong.com, 2026-09-23). It now takes the first hit
39
+ with stock, and that product's first in-stock variant. When nothing
40
+ reports stock it still falls back to the top hit, and `--product-id`
41
+ still buys exactly that product.
42
+ - `doctor` accepts `TYPESAFE_API_KEY` in place of `JEV_API_KEY`, matching
43
+ `portage-ucp-decision`'s Jev backend.
44
+ - **An agent couldn't tell a held purchase from a bought one without reading
45
+ prose.** `decisions:` only covers the three gates, so a missing payment
46
+ token, a permission-denied store, a dry run or a missing `--yes` all
47
+ showed `escalate: false, allowed: true`. Every `buy` report now carries
48
+ `outcome` (`purchased`, `no_payment_token`, `policy_blocked`, ... — the
49
+ full list is in the README), and the text output leads with it as
50
+ `[outcome]`. Checkout reports also carry `items`, what the checkout
51
+ actually holds, alongside `products`, the search results.
52
+ - **History recorded the wrong things.**
53
+ - A purchase entry listed all ten search results, not what was bought,
54
+ and had no way to tell a completed purchase from a dry run or a held
55
+ one. Entries now carry `outcome`, `items`, `total`/`currency`,
56
+ `checkout_id`, `source`, and the `checkout_url` for anything not
57
+ completed.
58
+ - A `buy` whose search matched nothing was logged as a purchase. It's now
59
+ a search at that store, as are browse-only and dead-end buys, which
60
+ weren't logged anywhere.
61
+ - The search behind a `buy` with no URL wasn't logged at all.
62
+ - **Checkout hand-off webhooks.** The body now includes the report's
63
+ `message`, the `store` and the shopper's `query`, so a Slack/Zapier relay
64
+ can post it without a lookup. A plain-text 2xx (Slack's `ok`) was
65
+ reported as a failed delivery, because the reply was parsed as JSON; any
66
+ 2xx now counts. The POST times out after 5s instead of stalling the buy
67
+ for up to two minutes.
68
+ - **The confidence gate could crash a buy after the checkout existed.** Only
69
+ `Decision::Error` was rescued, so a backend's timeout, parse error or
70
+ missing binary escaped as a backtrace with no report, hand-off or history
71
+ entry. Any failure now holds the purchase like a low score does.
72
+ - **The spend policy failed open on a checkout with no total.** PolicyGuard
73
+ skips both caps without an amount, so `buy --yes` completed past a
74
+ configured cap while reporting `allowed: true`. It's now denied as
75
+ `total_unknown` whenever a cap is set.
76
+ - `doctor` checks the confidence backend you selected
77
+ (`PORTAGE_DECISION_BACKEND`): the gem missing, an unknown name, a missing
78
+ `JEV_API_KEY` or Laya bridge, or a bad `PORTAGE_MIN_CONFIDENCE`. It no
79
+ longer asks for `JEV_API_KEY` when no backend is selected.
80
+ - The no-buyer-context warning names every variable it reads and says to
81
+ set at least `PORTAGE_SHIP_COUNTRY`.
82
+ - **Settings resolve one way everywhere.** The auto-open toggle, the
83
+ notify webhook, `PORTAGE_ABORT_ON_CHECKOUT_MISMATCH` and the confidence
84
+ gate's backend/threshold each parsed their own env var and repeated the
85
+ "flag, then env var, then `config.json`" order. They now share
86
+ `Portage::Cli::Setting`. Two edge cases change:
87
+ - A blank flag or env var (`PORTAGE_AUTO_OPEN_CHECKOUT=`,
88
+ `--notify-webhook ""`) is unset and falls through to the next level.
89
+ Before, an empty auto-open env var turned auto-open off, and an empty
90
+ `--notify-webhook` turned the webhook off.
91
+ - A `config.json` `"auto_open_checkout"` string is read like the env var,
92
+ so `"no"` means off. Before, any non-empty value meant on.
93
+ - Report totals and the policy check's amount come from core's
94
+ `Support::Totals.amount` instead of their own lookups.
95
+ - **Remote purchases didn't count toward the rolling cap or velocity
96
+ limit.** Both count the transaction log (`~/.portage/transactions.json`),
97
+ and only the own-store `Dispatcher` wrote to it, so `buy --yes` against a
98
+ remote native-UCP store never added to either. `buy` now records a remote
99
+ completion there under the merchant host, with its amount, currency and
100
+ token ref: reserved before the store is asked, settled `complete` only
101
+ when it comes back purchased, `failed` otherwise. The own-store loopback
102
+ path is still left to `Dispatcher`, so nothing is counted twice. If the
103
+ log can't be written after the purchase, the report says so in
104
+ `warnings` rather than losing the purchase to an exception.
105
+ - **A garbage `PORTAGE_MIN_CONFIDENCE` broke every `buy`**, even with no
106
+ decision backend selected, because the threshold was parsed up front
107
+ either way. The env var is now read, and validated, only once a backend
108
+ is selected. An explicit `--min-confidence` is still always validated.
109
+ - **`buy --json` could refuse a flag without any JSON.** An out-of-range
110
+ threshold went to stderr as a bare line, and a flag OptionParser couldn't
111
+ read (`--min-confidence high`, an unknown flag) escaped as a backtrace.
112
+ Under `--json` both now print a report with `outcome: "invalid_option"`
113
+ and exit 1; without it, both are one line on stderr. The threshold is
114
+ also checked before the search when `buy` has no URL, not after it.
115
+
116
+ ## [0.6.4] - 2026-09-22
117
+
118
+ - **Every dead-end hand-off pointed the shopper at the store's refund
119
+ policy.** `#checkout_url_of` took the first entry in the checkout's
120
+ `links` with a url, on the reasoning that not every backend types its
121
+ link entries. Live UCP stores put nothing *but* policy links there:
122
+ five third-party Shopify stores checked 2026-09-22 returned
123
+ `refund_policy`, `privacy_policy`, `terms_of_service`, `shipping_policy`
124
+ and `contact_information`, and never a checkout link — the checkout is
125
+ always at `continue_url`. So `requires_escalation`, permission-denied and
126
+ no-payment-token reports all handed over a policy page, `--auto-open`
127
+ opened it and `--notify-webhook` posted it. Now reads `continue_url`
128
+ first, and the `links` fallback skips policy/contact entries rather than
129
+ handing over a wrong URL.
130
+ - `adapter_flow` rescued `LoadError` and `StandardError` identically,
131
+ returning `nil` either way — correct once the adapter gem genuinely isn't
132
+ installed, wrong once it's live and its own call actually failed. A live
133
+ adapter's `StandardError` (e.g. "no payment_method configured on this
134
+ Adapter") now comes back as its own report instead of the generic "no
135
+ automated path" dead end, distinguishable from "no adapter for this
136
+ platform" by `source`.
137
+
138
+ ## [0.6.3] - 2026-09-22
139
+
140
+ - A store refusing a cart or checkout call on its own terms — out of stock,
141
+ a line it won't take, an expired cart — is reported as a normal outcome
142
+ carrying the store's own sentence and its `continue_url`, instead of
143
+ escaping `Buy#call` as an unhandled `Client::ServerError`. It printed a
144
+ Ruby backtrace whose "message" was the store's entire several-kilobyte
145
+ `ucp` error envelope; confirmed live 2026-09-22 against a genuinely
146
+ sold-out variant. Same posture `requires_escalation` and
147
+ `PaymentPermissionError` already had.
148
+ - Requires `portage-ucp-client ~> 0.6` (was `~> 0.5`), which is what
149
+ `Buy#complete`'s `rescue Client::PaymentPermissionError` has actually
150
+ needed since 0.6.2 — that constant landed in client 0.6.0, and a `rescue`
151
+ naming a missing constant raises `NameError` over the top of whatever
152
+ error it was meant to catch.
153
+
154
+ ## [0.6.2] - 2026-09-22
155
+
156
+ - `Buy#complete` treats a `Client::PaymentPermissionError` from
157
+ `complete_checkout` (this agent not yet granted checkout-completion on the
158
+ store) as a normal outcome rather than a failure — same posture as
159
+ `requires_escalation`: the report carries the checkout's `continue_url`
160
+ so the shopper can finish on the merchant's own checkout page.
161
+ - Every dead-end `buy` outcome that hands a shopper a `checkout_url`
162
+ (`requires_escalation`, permission denied, no `--payment-token`) can now
163
+ auto-open that link in the shopper's browser and/or POST it to a webhook,
164
+ instead of leaving it as inert text/JSON. Both off by default; opt in with
165
+ `--auto-open`/`--notify-webhook <url>`, `PORTAGE_AUTO_OPEN_CHECKOUT`/
166
+ `PORTAGE_NOTIFY_WEBHOOK_URL`, or `~/.portage/config.json`
167
+ (`auto_open_checkout`/`notify_webhook_url`), in that precedence order.
168
+ Never fires on `--dry-run`. Best-effort throughout: a failed open or POST
169
+ never fails the buy, and surfaces instead as `handoff: {opened:,
170
+ notified:, notify_error:}` on the report. New `CheckoutHandoff`, `Notifier`,
171
+ and `Config` classes; `portage-ucp` core is untouched.
172
+
173
+ ## [0.6.1] - 2026-09-22
174
+
175
+ - `portage generate agent-profile` emits the capability identifiers a UCP
176
+ server actually resolves an agent's tool registry from. It was emitting
177
+ `dev.ucp.shopping.catalog` — reusing `Portage::Ucp::Capabilities::CATALOG
178
+ .name`, which is correct for a business's own manifest, where one Capability
179
+ owns all three catalog actions, and wrong here: the registry is per action
180
+ (`dev.ucp.shopping.catalog.search`, `dev.ucp.shopping.catalog.lookup`). A
181
+ profile declaring the coarse name resolved to zero catalog tools, and stores
182
+ reported that as `-32602 Tool not found: search_catalog` seconds after
183
+ `tools/list` advertised it. Versions are now spec revisions (`2026-08-25`)
184
+ rather than `"1"`, and `ucp.services` declares the shopping service instead
185
+ of being left `{}`. The checked-in
186
+ `agent-profile/agent-profile.json` is regenerated, existing signing keys
187
+ kept. See `docs/ucp-tool-gating-investigation.md`.
188
+ - New `Portage::Cli::BuyerContext` builds the UCP `context` object from
189
+ `PORTAGE_SHIP_COUNTRY`/`PORTAGE_SHIP_REGION`/`PORTAGE_SHIP_POSTAL_CODE`,
190
+ `PORTAGE_CURRENCY` and `PORTAGE_LANGUAGE`. `buy` and `find` send it on every
191
+ catalog and checkout call — without it a real store builds an empty cart and
192
+ calls it sold out. Partial by design, unlike `ShippingProfile`, which stays
193
+ all-or-nothing because a half-filled address can't be submitted.
194
+
7
195
  ## [0.6.0] - 2026-09-17
8
196
 
9
197
  - Fixed `buy`/`find` crashing with a raw `Faraday::UnprocessableContentError`
data/README.md CHANGED
@@ -68,7 +68,8 @@ gem install portage-cli
68
68
 
69
69
  ```bash
70
70
  portage buy <url> --query "..." [--qty N] [--payment-token TOKEN] [--product-id ID]
71
- [--yes] [--dry-run] [--json]
71
+ [--yes] [--dry-run] [--decision-backend jev|laya]
72
+ [--min-confidence N] [--json]
72
73
  portage buy --query "..." [--store URL] [--max-price N] [--limit N] ...
73
74
  portage find --query "..." [--max-price N] [--limit N] [--json]
74
75
  portage compare <url> --product-id ID [--id VALUE ...] [--results N]
@@ -105,11 +106,20 @@ portage policy set [--per-transaction-cap N --currency CUR]
105
106
  - `--limit` — how many candidate stores to probe, capped at 12.
106
107
  - `--yes` — skip the confirmation prompt before completing checkout.
107
108
  - `--dry-run` — resolve and price the order without completing checkout.
109
+ - `--decision-backend` — opt into the confidence gate (see "Decisions" below):
110
+ `jev` or `laya`. Defaults to `PORTAGE_DECISION_BACKEND`; unset means off.
111
+ - `--min-confidence` — the gate's threshold, `0.0`–`1.0`. Defaults to
112
+ `PORTAGE_MIN_CONFIDENCE`, then `0.8`. The flag is always checked; the env
113
+ var is read (and checked) only while a backend is selected, so a stale
114
+ value can't block a buy that doesn't use the gate.
108
115
  - `--json` — machine-readable report instead of the human-readable summary.
109
116
 
110
117
  Exits `0` when a checkout completed (or a dry-run/browse/search resolved
111
118
  successfully), `1` otherwise — including the "no native manifest, no adapter
112
- credentials" dead-end case, so it's scriptable in CI.
119
+ credentials" dead-end case, so it's scriptable in CI. A flag that can't be
120
+ used (an unreadable value, an out-of-range threshold) stops the buy before
121
+ anything runs: on stderr normally, or as a JSON report with `outcome:
122
+ "invalid_option"` under `--json`.
113
123
 
114
124
  ### Compare
115
125
 
@@ -162,10 +172,15 @@ abandoned carts on someone else's site; left out of scope for now.
162
172
 
163
173
  ### History
164
174
 
165
- Every `find` (and `buy`, once it reaches a search) and every `buy` that
166
- reaches checkout is logged locally to `~/.portage/history.json` — most recent
167
- 200 entries each, purchases and searches kept separately. Browse-only `buy`
168
- reports (no checkout reached) aren't logged as purchases.
175
+ Every `portage buy` that creates a checkout is logged locally to
176
+ `~/.portage/history.json` as a purchase, whatever came of it. Each entry
177
+ carries the report's `outcome` (`purchased`, `dry_run`, `policy_blocked`,
178
+ ...), what the checkout held (`items`), its `total`, and, when it wasn't
179
+ completed, the `checkout_url` to finish it. "What did I already buy" is
180
+ the entries whose `outcome` is `purchased`. A `buy` that never reached a
181
+ checkout (no match, browse-only, a dead end) is logged as a search at that
182
+ store instead, as is every `find`, `compare`, and the search behind a
183
+ `buy` with no URL. The most recent 200 entries of each are kept.
169
184
 
170
185
  ```bash
171
186
  portage history # both lists, most recent last
@@ -255,6 +270,102 @@ opt-in guardrail, not a default-deny one. Per-token scopes (merchant/amount
255
270
  limits bound to one enrolled card) are set via `portage payment enroll
256
271
  --scope-*` above, not here.
257
272
 
273
+ ### Decisions
274
+
275
+ `portage buy` and `portage find` make their judgment calls
276
+ (`docs/plans/system-one-decision-layer.md`) through rules that live in
277
+ `portage-ucp` core: `Support::OfferRanking`, `Support::Escalation` and
278
+ `PolicyGuard`. `portage-ucp-decision` wraps the same modules as typed
279
+ verdicts, so ranking, escalation and the policy check answer the same way
280
+ whether or not it's installed. It's an optional plugin, not a dependency:
281
+
282
+ ```bash
283
+ gem install portage-ucp-decision # only needed for the confidence gate
284
+ ```
285
+
286
+ The confidence gate is the one feature that needs the gem, because the
287
+ model backends live there.
288
+
289
+ Every `portage buy` report carries an `outcome`, so a script or agent loop
290
+ can branch on data rather than on the message text. The text output leads
291
+ with the same value, as `[outcome]`.
292
+
293
+ | `outcome` | Meaning | `checkout_url`? |
294
+ | --- | --- | --- |
295
+ | `purchased` | Completed. | no |
296
+ | `needs_confirmation` | Checkout ready; rerun with `--yes`. | no |
297
+ | `dry_run` | Checkout created, `--dry-run` stopped it. | no |
298
+ | `requires_escalation` | The store wants the shopper to finish. | yes |
299
+ | `checkout_mismatch` | Checkout differs from the request (`PORTAGE_ABORT_ON_CHECKOUT_MISMATCH`). | yes |
300
+ | `no_payment_token` | No `--payment-token` and no default payment method. | yes |
301
+ | `policy_blocked` | Your spend policy denied it; `decisions.policy.reason` says why. | yes |
302
+ | `low_confidence` | The confidence gate held it. | yes |
303
+ | `permission_denied` | The store doesn't let this agent complete checkout. | yes |
304
+ | `store_refused` | The store refused a cart/checkout call (e.g. sold out). | when the store gave one |
305
+ | `no_match` | Nothing in the store's results matched. | no |
306
+ | `browse_only` | The store has a catalog but no UCP checkout. | when an adapter offers a link |
307
+ | `agent_profile_missing`, `request_rejected`, `unsupported_wire_shape` | Native UCP setup problems; the message names the fix. | no |
308
+ | `adapter_error`, `adapter_misconfigured` | Your own-store adapter failed; the message quotes it. | no |
309
+ | `dead_end` | No UCP and no adapter for this store. | no |
310
+ | `invalid_option` | A flag was refused before the buy started (`--json` only); `message` says which. | no |
311
+
312
+ Checkout reports also carry `items` (what the checkout holds, as opposed to
313
+ `products`, the search results) and the verdicts under `decisions:`:
314
+
315
+ ```json
316
+ "decisions": {
317
+ "escalation": { "escalate": false, "reason": null },
318
+ "policy": { "allowed": true, "reason": null },
319
+ "confidence": { "proceed": false, "reason": "below_threshold", "confidence": 0.41,
320
+ "threshold": 0.8, "backend": "jev", "error": null }
321
+ }
322
+ ```
323
+
324
+ Every verdict has a `reason`: `null` when the gate let the purchase through,
325
+ otherwise a string naming why it stopped it.
326
+
327
+ - **escalation** — `Support::Escalation`. A `requires_escalation` checkout
328
+ always escalates (`reason: "requires_escalation"`). A checkout that doesn't
329
+ match the request escalates (`reason: "mismatch"`) only under
330
+ `PORTAGE_ABORT_ON_CHECKOUT_MISMATCH`; otherwise it's reported as `warnings`.
331
+ - **policy** — `PolicyGuard`, run on your policy file (see "Policy" above)
332
+ before any `--yes` completion. `reason` is the guard's own
333
+ (`per_transaction_cap_exceeded`, `rolling_spend_cap_exceeded`,
334
+ `velocity_exceeded`, `merchant_not_allowlisted`, `token_scope_merchant`,
335
+ `token_scope_amount`, `currency_mismatch`, or `total_unknown`, below).
336
+ It applies to remote native-UCP stores too. Before this check, only the
337
+ own-store adapter flow's in-process `Dispatcher` enforced the policy.
338
+ Rolling caps and velocity limits count the completed purchases at that
339
+ merchant in `~/.portage/transactions.json`. `Dispatcher` records
340
+ own-store purchases there, and `portage buy` records remote ones: reserved
341
+ before the store is asked to complete, then settled as `complete` only
342
+ when the store answers that it's purchased.
343
+ - **confidence** — `ConfidenceGate`, off unless `--decision-backend` or
344
+ `PORTAGE_DECISION_BACKEND` names a backend. Right before a `--yes`
345
+ completion it asks the backend whether the checkout matches the request
346
+ and is safe to complete unattended. It sends the query, merchant,
347
+ quantity, line items, totals and warnings, never the payment token. The
348
+ gate fails closed, so three things hold the purchase: a score below the
349
+ threshold (`reason: "below_threshold"`), a backend that can't answer
350
+ (`"backend_error"`), and naming a backend without `portage-ucp-decision`
351
+ installed (`"not_installed"`). For the last two, `error` says what went
352
+ wrong. `jev` needs `JEV_API_KEY`; `laya` needs `LAYA_BRIDGE_SCRIPT` (see
353
+ `portage-ucp-decision`'s README). `portage doctor` flags whichever of
354
+ these is missing for the selected backend.
355
+ - **policy** also denies a checkout with no `total` line as
356
+ `total_unknown` whenever a spend cap is set, rather than skipping the cap.
357
+
358
+ A blocked or held purchase hands the checkout off the same way an escalation
359
+ does (`checkout_url`, plus `--auto-open`/`--notify-webhook` if set). The
360
+ shopper can finish it themselves. The webhook body is JSON: `event`
361
+ (`checkout_handoff`), `reason` (the report's `outcome`), `message` (the
362
+ report's own sentence), `store`, `query`, `checkout_url`, `checkout_id`,
363
+ `source`, `totals` and `warnings`. Any 2xx counts as delivered; the POST
364
+ gives up after 5 seconds and reports `notify_error` instead.
365
+
366
+ `portage find`'s offer order comes from `Support::OfferRanking`: buyable
367
+ first, then cheapest, then unpriced.
368
+
258
369
  ### Shipping address (own-store checkouts only)
259
370
 
260
371
  When buying against your own store (`portage buy`'s step 2 adapter-credentials