Repository navigation
feat(web): the hero shows the flow, and the figures stop reading as a toy - #22
Merged
Merged
Conversation
The hero tile said "My servers in the payment path / 0", which asks the reader to already know the claim before the number means anything. It is three parties now — the agent, the allowance, the seller — with what each one holds, so the flow reads left to right and the outcome is the caption under the middle one. The outcome names which rule stopped each payment rather than totalling them, and a clause with nothing in it is left out. The run itself was too small to prove its own point. With 0.05 deposited and a 0.025 cap, a reader cannot tell a refusal from the funds running out; the allowance now holds 5 USDC and the cap stops the agent well short of it. Eight payments fit inside the window and a ninth does not, so the agent asks ten times and takes 0.08 — enough of a run that the cap is visibly a rule and not an accident. That needed a bigger swap, and a floor under it so a thin book fails on the swap row rather than two steps later on the deploy. Two duplicates of the step list went with it. `app/run.tsx` carried its own copy of the titles and still said "Swap 10 XLM" after the run swapped 100; it reads the recording's rows now. `baked.json` is the other copy, and a test pins it to what the generator emits, because a stale recording is wrong on the first screen and silent about it. The live run takes about two minutes, not about a minute. Said so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The form opened at a 1 USDC deposit and a 0.25 cap, which is small enough that the rule reads as a formality. It offers 5 now, and 20 to cover four of those windows. Both are clamped to what the wallet holds, because a cap the balance cannot reach is a rule that can never bind. A wallet holding no USDC is offered the plain numbers rather than zeros: a form full of zeros reads as broken, and the deploy is what says in words that there is nothing to deposit. The suggestion is a function on the wallet rather than a literal in the panel, so it can be tested. It is: String(1e-7) is '1e-7', which the amount parser refuses, so every suggestion is fixed to seven decimals and the test hands each one to the parser the form will hand it to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The link preview arrives as a black box and a lavender one, and the page it opened was black on white with the lavender nowhere in sight. The allowance cell carries the tint now, so the pairing a reader clicked through survives the click. It is the allowance's colour elsewhere too, which is why this one and not the seller's: a refused row is tinted because the allowance is what refused it, and an inactive allowance's pill is the same tint. The refused rows keep their 4px rule and their chip, so nothing reads as refused for being lavender. Two cleanups in the file while I was in it. The comment above `.run + .stats` belonged to `.cell.seller`, which went away when the seller cell went back to left-aligned; the rule itself was dead — no `.stats` follows a `.run` on any page — so both are gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eight payments meant every visitor who pressed the button settled nine through the public facilitator and waited two minutes for it. Back to two payments and a third refused: four asks, 0.02 to the seller, a 0.025 cap, and 66 seconds measured in the browser. The 5 USDC deposit stays. It is what makes the refusal legible — against a 0.05 balance a reader cannot tell the rule from the money running out — and it costs nothing extra to deposit once. The cap now sits at half a percent of the balance, which is the same point made harder. Kept from the larger run: the row list the page derives from the recording rather than repeating, the test that pins the recording to the generator, and the swap that funds a 5 USDC deposit with a floor under it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
--lavender-tint was #e9e5f7, which is a hue with no lightness behind it: 1.016 against the page's cream ground, so on the stat cell it registered as a rendering artifact rather than a colour. #ded7f4 separates from both grounds — 1.318 against the seller's white, 1.142 against the cream — while the 12.5px caption keeps 5.2 to 1, well clear of the line C's #d2c9ef was flirting with. The token, so everything that means "the allowance" moves together: the stat cell, a refused row in the run, and an inactive allowance's pill. A refused row now separates from a settled one at 1.318 rather than 1.172, which is the beat the demo exists for. The OG card's hardcoded copy moves with it, or the page and the link that opened it drift apart again. Every surface the token sits behind, re-checked: the pill's label 6.3, the OG box's plum 8.6, ink on a refused row 13.8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hero's figures were read out of the recording, which tied two things that want different sizes: the run happens in a stranger's browser while they wait, so it has to be small and fast, and a minute's worth of payments is too few to show what a cap is for. The hero states its own numbers now — ten asks, eight through, one off the list, one over the cap — and the run below states the run's, with a hash on every row. The seller's caption said "what actually moved", which claimed a provenance the cell no longer has. It says what the rules allow instead, which is true of an illustration and of the contract. The link preview's description was the headline again. The card already renders that in type nobody can miss, so the one line under it now says what the thing is: on-chain spending limits for AI agents that pay for API calls. Two things fixed on the way. `const allowance = …deploy…hash` had been dead since the tile was rewritten. And the divider moved to 1.7fr: the allowance caption had five pixels behind it at every desktop width, which is inside the jitter of a font that has not loaded yet. It has 21 now, the seller 16. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"all the rules allow" reads as though the seller is allowed the rules. The other two cells say what that party did — the agent asked, the allowance paid and refused — so this one says it was paid, which mirrors "asked 10 times" and closes the loop between the two numbers: asked ten, paid eight. The divider goes back to 1.45fr. That ratio was set by eye and I moved it on my own initiative to buy the allowance caption room; the caption was the thing to change, not the column. At 1.45fr the seller has 34px behind its line and the allowance the 5px it has always had. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
The hero tile read
My servers in the payment path / 0, which asks a reader to already hold the claim before the number means anything. It states the flow instead: what the agent holds, what the allowance holds, what reached the seller.What is here
lib/demo/params.ts500_000nto50_000_000n, 0.05 to 5 USDClib/demo/live.tsdestMin1 to 10 USDCapp/run.tsxtest/run.test.tslib/dashboard/wallet.tssuggestDefaults, the new-allowance form's opening figuresapp/globals.css--lavender-tint#e9e5f7to#ded7f4app/opengraph-image.tsxapp/layout.tsxFindings
A cap against a near-empty balance proves nothing. With 0.05 deposited and a 0.025 cap, the screen gives a reader no way to tell a refusal by the rule from the funds running out, and that distinction is the product. The allowance holds 5 USDC now and the cap stops the agent at 0.5% of it, so one explanation remains. The seller's price is unchanged at $0.01, because a one-cent API call is the reason x402 exists.
The step list had 3 copies.
app/run.tsxcarried its own array of titles for the pending skeleton, and it went stale the moment the generator changed: it renderedSwap 10 XLM for USDCbeside an amount from a 100 XLM swap, on the first screen. It reads the recording's rows now.baked.jsonis the second copy and drifts the same way whenever a step changes without a re-bake, so a test pins its ids and titles to what the generator emits.The hero illustrates and the run reports. The run happens in a stranger's browser while they wait, so it stays at 4 asks and 66 seconds; a minute of payments is too few to show what a cap is for. The hero states its own figures. That is why the seller's caption no longer says "what actually moved", which claimed a provenance the cell does not have.
String(1e-7)is'1e-7', which the amount parser refuses. A wallet holding a single stroop would have produced a default the form could not parse, so every suggestion is fixed to 7 decimals and the test hands each one to the parser the form will hand it to.The tint had no lightness behind it.
#e9e5f7measures 1.016 against the page's cream ground, so hue carried it alone and it read as a rendering artifact. The token moved rather than the one cell, so a refused row and an inactive pill deepen with it, and the card's hardcoded copy moved to match.The create form
The panel opened at a 1 USDC deposit against a 0.25 cap, small enough that the rule reads as a formality. It offers 5 now, with a deposit of 20 covering 4 of those windows.
Both clamp to what the wallet holds, because a cap the balance cannot reach can never bind. A wallet holding no USDC is offered the plain figures rather than zeros: zeros read as a broken form, and the deploy already states in words that there is nothing to deposit.
Verification
36 tests pass,
tsc --noEmitclean, 4 of 4 CI jobs green.A live run pressed in the browser: 8 rows, both refusals in place, 66 seconds locally and 78 on production.
Contrast measured on every surface the token sits behind, against the old value and the new:
Caption wrapping measured at 375, 768 and 1440 by text height against line height. The earlier check counted client rects, which returns 1 for a block element however many lines it takes and could not see a wrap at all:
The 5px behind the allowance caption is constant from 1100px up, where the layout caps, and predates this branch: the caption it replaced is the same length.
Dead code removed:
const allowance = …deploy…hash, unused since the tile was rewritten, and.run + .stats, which no page matches.Scope
Web only. The contract, the SDK and the seller are untouched. Unaudited, and built for testnet.