portage-cli 0.13.0 → 0.13.1

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: 7c58c0040e6d1f58b5cf9dbbe64c91273c198a4cfc8119477e1fcbbf04c4c4dd
4
- data.tar.gz: ba48da2d241487885a286eb8044e493f6727b7e7d4b96f3b9dc7d748b1644f67
3
+ metadata.gz: b9dba6d24f2ebadd03022d53d85684d8cbe8e3110b609f1becadd4d5212ec47b
4
+ data.tar.gz: ed6cfcbb1516e7addd017c25a52343f95c71b72bde869aa7e1acd06a44eb3a48
5
5
  SHA512:
6
- metadata.gz: 388d5ea84ba8ba1279862b57b07cf36db2164cec50752638ae3eb2d924fd81c56a569ea2d107994fc56f3d1989ab0d5b567fed3e0a12e9b3e7c65a291b95e198
7
- data.tar.gz: fd18e9aa2c798935143742e961936c359ff68722a3f8bad2f20489322456df3467f042ef9c653a4f72f167fc4a765a08bd81680e01ec6c2a0b26fb0ce934b477
6
+ metadata.gz: 54563a08ca407b2847502f2a95290598147f4e78aa47f346ce0f20d637d416afb6e5c7119ba4e19e233fe64286a83cb5b47fc1351d63a7aabac81f5065060893
7
+ data.tar.gz: 396ee2c09e74e5708fd5ade1555c412bf5b342979549b04932809760e0744b303050c0dc92b4a4e97d3e4c8f0090a92f7921cc29b020e277370e2566cdf8ca07
data/CHANGELOG.md CHANGED
@@ -6,6 +6,14 @@ pre-1.0, so APIs may still shift between minor versions.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.13.1] - 2026-10-07
10
+
11
+ - **Fix: `portage orders reconcile` can settle a hand-off on a BigCommerce, Magento, Wix or Shopify-adapter store.** Those adapters kept checkout status in the process that created the checkout, so reconcile (a new process) only ever saw `incomplete` or not-found. With their new releases, an adapter checkout that became a paid order reads as `completed` with its `order`, and reconcile settles it and snapshots the order as it does for native UCP stores. No CLI code changed; a new spec covers the adapter loopback path end to end. WooCommerce has no such signal and still stays pending (design-log §55).
12
+
13
+ - **Fix: `portage orders reconcile` (and `buy --wait`) can read a native UCP checkout again.** `HandoffReconciler` called `get_checkout` and `get_order` without `meta.ucp-agent.profile`, so every real UCP store answered `MissingAgentProfileError`. Reconcile read that as "not found", so a pending hand-off never settled while the checkout lived and would have settled `failed`/`unknown` at expiry even if the shopper had paid. It now sends the same agent profile `buy` does (found in the handoff-reconcile Phase 0 live check, design-log §55). A store error's note is now the server's own message (`ServerError#summary`), not Shopify's whole several-kilobyte UCP envelope.
14
+
15
+ - **Fix: a store that escalates its checkout can be approved and reconciled under `require_approval`.** On a dry run, including the dry run the default `require_approval: any` makes of an unapproved `buy --yes`, a `requires_escalation` checkout was reported as `requires_escalation`, so no quote was saved, there was nothing to `portage approve`, and the report pointed at the checkout link with no pending hand-off recorded. A dry run now reports it as `dry_run` with a new `handoff_expected: true` key and a note that a real run hands it off, so the gated run returns `needs_approval` and the approved run's hand-off is recorded for reconcile (design-log §55). A plain `--yes` run outside the gate still escalates as before.
16
+
9
17
  ## [0.13.0] - 2026-10-05
10
18
 
11
19
  - **Fix: `payment_methods` adapter fallback no longer silently fails.** `PaymentMethods#adapter_session` referenced `Buy::PermissiveAuthenticator`, which doesn't exist (the class is `Portage::Cli::PermissiveAuthenticator`); the resulting `NameError` was swallowed by the method's `rescue StandardError`, so enrolling against your own adapter-backed store always reported no session. It now requires and uses the right constant. Patch-level bug fix.
@@ -1075,8 +1075,13 @@ module Portage
1075
1075
  return quote_changed_report(source, products, checkout, warnings) if quote_exceeded?(checkout)
1076
1076
 
1077
1077
  escalation = decide_escalation(checkout, warnings)
1078
+ # A dry run never hands off, so an escalating checkout is still just
1079
+ # a priced dry run: reporting it as `requires_escalation` saved no
1080
+ # quote, and the CLI's approval gate had nothing to approve
1081
+ # (design-log §55). The real run after approval escalates and hands
1082
+ # off, which is where the pending record gets written.
1083
+ return dry_run_report(source, products, checkout, warnings, escalates: escalation[:escalate]) if @dry_run
1078
1084
  return escalated_report(source, products, checkout, warnings, escalation) if escalation[:escalate]
1079
- return dry_run_report(source, products, checkout, warnings) if @dry_run
1080
1085
  return webmcp_handoff_checkout_report(source, products, checkout, warnings) if force_handoff
1081
1086
  return confirmation_needed_report(source, products, checkout, warnings) unless confirmed?
1082
1087
 
@@ -1582,9 +1587,13 @@ module Portage
1582
1587
  # `warnings` here are #reconcile_checkout's mismatches, which a real
1583
1588
  # run of the same checkout stops on — so the report says so up front,
1584
1589
  # before anyone approves a quote for it.
1585
- def dry_run_report(source, products, checkout, warnings = [])
1590
+ def dry_run_report(source, products, checkout, warnings = [], escalates: false)
1586
1591
  message = "Dry run — checkout created but not completed."
1587
1592
  extra = {}
1593
+ if escalates
1594
+ message += " The store needs the shopper to finish this checkout, so a real run hands it off."
1595
+ extra[:handoff_expected] = true
1596
+ end
1588
1597
  if warnings.any?
1589
1598
  message += " It doesn't match the request (#{warnings.join(' ')}), so a real purchase would stop " \
1590
1599
  "with checkout_mismatch."
@@ -4,6 +4,7 @@ require "portage/ucp"
4
4
  require "portage/ucp/client"
5
5
  require "portage/ucp/journal"
6
6
  require_relative "user_agent"
7
+ require_relative "agent_profile_url"
7
8
  require_relative "adapter_session"
8
9
  require_relative "handoff_spend_mode"
9
10
  require_relative "reconcile_notifier"
@@ -92,7 +93,7 @@ module Portage
92
93
 
93
94
  def reconcile(record)
94
95
  session = reconnect(record["store_url"])
95
- checkout = session.get_checkout(checkout_id: record["checkout_id"])
96
+ checkout = session.get_checkout(checkout_id: record["checkout_id"], meta: agent_meta)
96
97
  settle_from_checkout(record, checkout)
97
98
  rescue StandardError => e
98
99
  # A reconnect failure (no automated path back into the store) and a
@@ -102,7 +103,9 @@ module Portage
102
103
  # buys nothing here: either way, this run learned nothing new about
103
104
  # whether the shopper paid, and the record's own `expires_at` is
104
105
  # still the only thing allowed to turn that into a settle.
105
- settle_not_found(record, note: e.message)
106
+ # ServerError#summary: Shopify answers a gone checkout with its whole
107
+ # UCP envelope as the message; the note only needs its messages[].
108
+ settle_not_found(record, note: e.respond_to?(:summary) ? e.summary : e.message)
106
109
  end
107
110
 
108
111
  def settle_from_checkout(record, checkout)
@@ -176,7 +179,7 @@ module Portage
176
179
  return nil unless order && order["id"]
177
180
 
178
181
  session = reconnect(record["store_url"])
179
- order_wire = session.get_order(order_id: order["id"])
182
+ order_wire = session.get_order(order_id: order["id"], meta: agent_meta)
180
183
  return order["id"] unless order_wire
181
184
 
182
185
  @order_ledger.record(idempotency_key: record["idempotency_key"], order: WireOrder.new(order_wire))
@@ -233,6 +236,11 @@ module Portage
233
236
  raise(ReconnectError, "no automated path back into #{store_url}")
234
237
  end
235
238
 
239
+ # Same `meta.ucp-agent.profile` Buy#agent_meta sends: a real UCP
240
+ # server rejects any call without it, so leaving it off made every
241
+ # native reconcile fail as "not found" (design-log §55).
242
+ def agent_meta = { agent_profile: AgentProfileUrl.resolve }
243
+
236
244
  def native_session(uri)
237
245
  Portage::Ucp::Client.discover(uri.to_s, headers: UserAgent.headers)
238
246
  rescue Portage::Ucp::Client::DiscoveryError
@@ -1,5 +1,5 @@
1
1
  module Portage
2
2
  module Cli
3
- VERSION = "0.13.0".freeze
3
+ VERSION = "0.13.1".freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: portage-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Whitbread
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-05 00:00:00.000000000 Z
11
+ date: 2026-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: irb