Repository navigation
Conversation
…cli 1.7.0 Checked against the released icp-cli 1.7.0 binary (read-only and no-`--yes` runs only, no orders) and CyclePay's backend. - Compatibility is icp-cli >= 1.7.0, with the npm, Homebrew and shell-installer upgrade commands. - The flow is quote (no `--yes`, creates nothing), the user's yes, then `--yes --no-open --no-wait --json`, then poll `order_status`. The old flow blocked for up to an hour, so an agent whose tool call returns only at exit never showed the user the link. - Re-running the same command returns the same order only while it is unpaid and under an hour old, for the same amount; after payment or an hour it makes a second payable order. - `-e ic` works inside a project. - `estimated_cycles` is already net of the 200M ledger fee. - The real end states: line 2 only when the order settles, no line 2 on a timeout or stop, exit 0 after line 1 with `--no-wait`; read `detail` (DAILY_CAP, OVER_CAP, ORDER_EXPIRED, REFUND_HELD) and give the buyer contact. - The money rules: the amount on Stripe's page is editable, paying a link twice is charged and then refunded, and card payments are capped at USD 1,000 per UTC day per identity and per card. - New Pitfall 11: check the Gateway line and the buy.stripe.com host; never set `--gateway` or ICP_CYCLES_GATEWAY_CANISTER_ID unasked. - Evals: fixed the `-e ic`, output-shape and end-state expectations, and added cases for re-running, a shell that times out, refusing to pay on the user's behalf, and a quote naming another gateway.
…iew findings From the independent review of e231f73. - Pitfall 7 no longer matches on detail codes, which no CyclePay release shows on a CLI order. The agent relays `detail` as it is, waits for the next UTC day when it names a daily limit, and makes a new order only when the user asks. A payment refused on arrival may show as a refund state, or as AwaitingPayment with or without a detail. - Step 4 keeps `detail` and `(null)` in its output and gets a stop rule: stop at an end state; if the order is still unpaid after about 10 minutes, ask the user and back off; stop on their word or at expires_at. - Pitfall 9: over USD 200 takes several orders, one at a time, each paid before the next; past USD 1,000 in a UTC day, wait; never use another identity or card to get round a cap. - Lows: - `unknown` is dropped (it never appears in --json); - Pitfall 7's opening and the "Use --yes to proceed" note are fixed; - the user runs `icp identity new` themselves (it prints the seed); - the buy.stripe.com check does not identify CyclePay, the Gateway line does; - the fallback's queries pass --identity anonymous; - the delivered example matches an observed delivery; - the four duplicated passages the review marked are trimmed. - Evals: - the core flow requires the quote first; - Final statuses uses a plain-words detail; - the gateway case no longer names a canister that cannot quote; - a new case covers a USD 1,500 purchase and the daily cap.
…s as a plain list From the re-review of 0ad9a1f (R1, R2 and the nits). - Pitfall 7, refunding/refunded: - `refunded` is set only sometimes; - the same link may still be payable until expires_at (detail then says so), and step 4 has stopped: tell the user, and poll again only if they pay it again; - after a daily-limit refusal, no new order and no second payment of the link before 00:00 UTC; such a payment is refused again and counts toward the per-card limit on automatic refunds; - a new order on the user's word leaves the first link payable too: pay only the new one. This holds for today's backend as well, where a refused payment leaves the order AwaitingPayment and the detail is empty. - Step 4 points to that rule. Pitfall 6 notes that a refused payment also ends reuse. - Pitfall 9 lists the caps as a plain list, at the time of writing (USD 1,000 per identity, USD 1,000 per card, USD 5,000 for all CyclePay buyers together, as in the live card config), and its long line is re-wrapped. - Eval 4 quotes the settled detail wording, so it tests the payable link and the one-link rule.
… for the refund limit From the delta check of 4cce26e (D1, D2). - Pitfall 6: a re-run makes a second order "once a payment of the order has arrived (on newer CyclePay releases, even one it refused)". On today's mainnet a refused payment leaves the order reusable within the hour. - Pitfall 7: the refund bullet and the refund_owed bullet both say "several" automatic refunds per card; the per-card limit is configurable and not on mainnet yet. The bullet drops the clause refund_owed already covers.
Skill Validation ReportValidating skill: /home/runner/work/icskills/icskills/skills/buy-cyclesStructure
Frontmatter
Markdown
Tokens
Content Analysis
Contamination Analysis
Result: passed Project Checks |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
buy-cycleslanded (#416) before icp-cli 1.7.0 shippedicp cycles buy. This PR checks it against the released binary and CyclePay's backend. Several things an agent follows literally are wrong, or unsafe:icp cycles buy … --yes --jsonin the foreground waits up to an hour, so a tool call that returns only when the command exits (or times out) never shows the user the link. The flow is now:--yes, which creates nothing and exits 1; that "Use --yes to proceed" is not a cue to add--yes);--yes --no-open --no-wait --json(one line, exit 0);order_statusthat keepsdetailand(null), with a stop rule: stop at an end state; if the order is still unpaid after about 10 minutes, ask the user, then back off; stop on their word or atexpires_at.create_orderhands back the caller's open order for the same amount only while it is unpaid and under an hour old (ORDER_REUSE_SECS). Once a payment of the order has arrived (on newer CyclePay releases, even one it refused), or after an hour (as on a re-run once the first run's hour-long wait gives up), the same command makes a second payable order (Pitfall 6).--yes. With--cycles, they approve the dollar Price it quotes (Pitfall 3).-e icworks. Inside a project,icp cycles buy … -e icquotes on mainnet (Pitfall 4).estimated_cyclesis already net of the 200M delivery fee (Pitfall 8, Verify).--no-wait, exit is 0 after line 1.processing,payment_pendingand statuses newer than the CLI never end the wait.detailas it is and makes a new order only when the user asks. The skill never matches on codes, which no CyclePay release shows on a CLI order.refundedis set only sometimes.expires_at, anddetailthen says so. Since step 4 has stopped by then, the agent tells the user, and polls again only if they pay it again.AwaitingPaymentwith or without adetail, depending on CyclePay's release. The text holds in all three cases.--yes --jsonthe gateway is never printed. The quote'sGateway: 2bwus-iiaaa-aaaac-bee3a-cai (CyclePay)line is the check against a strayICP_CYCLES_GATEWAY_CANISTER_ID. Thebuy.stripe.comhost only catches a non-Stripe page. Never set the variable or--gatewayunasked.--identity anonymous.--gateway.icp identity newthemselves (it prints the seed phrase).The structure, the pitfall numbering (1 to 10, plus 11) and the fallback are kept. The description is unchanged, so the trigger evals were not re-run. The body is 4,898 tokens by skill-validator 1.5.1 (3,129 on
main).Open question for @raymondk
Should step 3 pin
--gateway 2bwus-iiaaa-aaaac-bee3a-cai? The flag wins overICP_CYCLES_GATEWAY_CANISTER_ID(buy.rs139 to 142). A stray variable could then not move the order between the quote and the create. That matters because--yes --jsonprints no gateway at all. This PR leaves the steps unpinned and relies on checking the Gateway line in step 2. If you prefer the pin, the security rule becomes "never point--gatewayor the variable at another canister unless the user names it". A CLI-side fix would also work: printgatewayin the--jsonline 1.CyclePay-side changes this anticipates
CyclePay is changing what
order_statusshows for a payment it refuses on arrival, in dfinity/cyclepay. The work is in progress, not yet released. Today such an order staysAwaitingPaymentwith nodetail. With the change, the order shows a refund state with a plain-wordsdetail, and the same link stays payable until it expires. The skill is written to be right before and after the change: Pitfall 7's refund bullet and itsAwaitingPaymentparagraph.How it was checked
icp-cli-aarch64-apple-darwin.tar.xz, sha256f256a947…bd4a, equal to the release digest), with a throwaway identity in an isolatedICP_HOME. Everycycles buyrun had no--yesand stdin from/dev/null, so no order was created. The runs covered:--versionandcycles buy --help;--amountand with--cycles;-e icin a project, the default environment, and no project with no network;failed to read the card terms);order_statuson an unknown id ((null)) through the new poll filter;cycles balance, and thegrep/sedextractions.cyclepay/purchase.rs,commands/cycles/buy.rs,tests/cycles_tests.rs):--no-waitexiting 0;detailcodes, because no CyclePay release shows them on a CLI order.skill-validator check skills/buy-cycles --skip links(v1.5.1, the version CI pins) exits 0: passed, 0 warnings. This is the check CI runs on a PR, on the changed skill only.npm run validateexits 2, the same as onmain. Over all 32 skills, skill-validator prints "32 skills validated: all passed" with 20 warnings, the same 20 as onmain; none are in buy-cycles. Its exit status is 2 because of those warnings, so the&& node scripts/check-project.jsafter it never runs.node scripts/check-project.js buy-cycles, run on its own, exits 0: passed, 0 warnings. Over all skills it also exits 0.npm run buildexits 0, anddist/.well-known/skills/buy-cycles/SKILL.mdequals the source.git diff --checkis clean.Eval results
All 12 output evals, with baseline (
node scripts/evaluate-skills.js buy-cycles --eval N). With the skill: 37/37. Without it: 11/37. Each case's result is from its latest run, and nothing a case covers changed after that run:Evaluation results
Not in this PR
icp-cli(line 214) still recommends--yes --no-open --jsonfor the purchase. It should say quote first and add--no-wait.cycles-management(lines 276 to 277) should name icp-cli 1.7.0.Those are one-line changes, in a separate PR per the one-skill-per-PR rule.