Repository navigation
feat: add the demo UI, built into the binary - #7
Merged
Merged
Conversation
added 19 commits
November 3, 2026 22:02
A demo that makes its own vendor calls should record every one without opening a port that could clash with LocalStack's. The client hands each request straight to the proxy and gets the answer back, so the inspector sees the same events as with a listener.
The lender measures the last 30 days from the moment the seeded history ends, not from the wall clock, because seeding moves the app emulators' clocks ahead. Keeping that moment in the facts makes the windows the same on every run.
The lender connects to the merchant's three accounts and reads sales, payouts, the bank account and receivables. Every call goes through a wire proxy so it is recorded, and the windows are measured from the moment the seeded history ends. The Stripe key is exported so the seeder and the lender use the same one.
The advance is the last 30 days of card sales rounded down to $500, for a flat 8% fee. Every number the offer rests on names the vendor call it came from, so a presenter can show where it comes from. Plaid's balance is shown but not used, since the app emulator's fixture is too thin to price against.
An application moves through apply, connect, review and offer, and each step is refused out of order. A step that fails saves nothing and can be tried again. State lives in the store, so the command line and the server see the same applications.
The API tests need the same three-vendor fake the CLI tests use. Moving it to one package keeps a single copy, with its strictness about the Stripe key.
The UI and the inspector need the flow over HTTP: status, reset, apply, connect, review, offer and the recorded vendor calls. A reset waits for in-flight calls and they wait for it, so nothing reads half a world. The server mounts the API under /api/ beside the UI.
Until the UI exists, the API is how the flow is shown. List the routes and the one offer the seeded merchant gets, so a presenter can run it from a terminal and know what to expect.
The inspector shows recorded calls on a projector, and the full account number was readable in the request that registers a bank account and in Plaid's auth answer. Mask account_number fields, including bracketed form names, and Plaid's account field beside its routing. Routing numbers and Stripe account ids stay visible.
Accepting an approved offer registers the bank account the merchant verified with Plaid as the Stripe payout destination, transfers the advance to the merchant's Stripe account, and pays it out. Each step is saved as it finishes and the Stripe calls carry idempotency keys, so a funding that stopped halfway resumes without moving money twice. Only the last four digits of the account number are kept.
The UI's accept click calls both. A declined offer is a conflict, the same as a step taken out of order.
Say what funding does in Stripe, that it can be run again after a failure without paying twice, and what the Stripe app emulator does not show: a platform balance and the payout's destination. Xero settlement is not built yet.
The demo UIs use wondertwin-web's toolchain: pnpm 10.33 with its seven-day release age, trust and no-exotic-subdeps policy copied unchanged, Node 22, and the same Biome rules. No exclusion is added to the trust policy.
UIs written against shadcn's variable names and the cva variant pattern need the brand to arrive in that shape, as wondertwin-web does with its design package. Mint is the one action color. The style guide has no error color, so one is added and labelled as an addition.
The UI shows a presenter what to do about an app emulator that is not ready, and the Go code already writes that sentence. Sending it in the status JSON keeps one source for it, instead of the UI guessing at the same words.
A route inside the app, such as /inspector, has no file, so loading it directly must still give index.html. A missing file with an extension stays a 404 rather than the app. Hashed assets are cached for good and the page that names them never is. With no build yet, the placeholder is served.
Two clicks run the demo: apply, then accept and fund. The page shows each app emulator and who serves it, each step as it finishes, the offer with the vendor call behind every number, and how the advance reached the merchant. It is React, TanStack Router and Query and Tailwind 4 on the LocalStack brand package, with no network needed at run time. make web builds it into the Go binary.
Each demo's UI is built with pnpm and embedded in its Go binary, so CI lints, type-checks, tests and builds the UI, builds the binary, and checks that it serves the app, a client-side route, a hashed script and a real 404. The local gate runs the web checks where pnpm install has been run.
Say what the two clicks show, how to build the UI into the binary with make web, and the rules the web workspace follows, including preferring to remove a dependency over excepting it from the supply-chain policy.
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.
Stacked on #6, which is stacked on #5 and #4. Merge in order: #4, #5, #6, then this. The base branch is
feat/accept-and-fund, and GitHub retargets this once that branch is merged and deleted.Motivation
The demo needs a UI a presenter can run in two clicks. This adds it, on
wondertwin-web's toolchain and LocalStack's brand, and builds it into the Go binary so the demo is still one file that needs no network.Changes
chore(web): set up the workspace like wondertwin-web: pnpm 10.33, Node 22, the same Biome rules, and the supply-chain policy copied unchanged. No exclusion is added to the trust policy.feat(web): add a shadcn theme and variants to the brand package: the shadcn variable names set from the LocalStack tokens, andcvabutton and badge variants, aswondertwin-webdoes with its design package. Mint is the one action color. The style guide has no error color, so one is added and labelled as an addition.feat(shared): include the hint in each app emulator's status: the UI shows what to do about an app emulator that is not ready, and the Go code already writes that sentence, so it now travels in the status JSON.feat(acme): serve the UI as a single-page app: a client-side route such as/inspectorloadsindex.htmlwhen opened directly, a missing file with an extension stays a 404, hashed assets are cached for good, and the page that names them never is.feat(web): add the ACME Commerce Lenders UI: React 18, Vite, TanStack Router and Query, Tailwind 4 and Vitest. Click 1 applies, connects, reads and prices an offer. Click 2 accepts and funds it. Each step shows as it finishes, the offer lists the vendor call behind every number, and a failed step has a Retry that carries on from that step. A Reset button puts everything back.ci: build the UI and check the binary serves it: awebjob lints, type-checks, tests and builds the UI, builds the binary, and checks it serves the app, a client-side route, a hashed script and a real 404.docs: document the UI and how to build itDependency decisions
@base-ui/reactis not used. It pulled inreselect@5.1.1, which lost its provenance attestation and trips the trust policy. A plain<button>does the same job here, so the dependency is gone and the bundle is smaller.tailwind-mergeis 3.x, which is what Tailwind 4 pairs with. 2.6.1 also lost its attestation.wondertwin-web's, which pins versions that pass the policy, and pnpm pruned it to what this app uses.Tests
On the final tip:
The UI tests cover the readiness display and its hints, an unseeded world, the first click running apply, connect, review and offer in order, the second click running accept and fund and showing the money's path, a declined offer that offers no Accept, a failed step that retries from that step without applying twice, Reset, and a failed Reset. The Go tests cover the single-page-app handler.
Run for real in Chrome, with the binary serving the built UI against Plaid, Xero and Stripe built from
WonderTwin-AI/wondertwinorigin/main(9a92fbdf):The CI job's embedding check was also run locally exactly as written, and passes.
Not tested: running with the network off, and a browser other than Chrome.
Related
DOC-581