Three reconciliation screens later, the over/short display became Delta
/register-closeCaptured at a 375px viewport. Long pages scroll inside the frame.
The screenshots stay in Japanese. Every round is a real screen for a real Japanese industry, so an English re-render would no longer be the screen the agent actually produced.
Write-up
Three reconciliation screens later, the over/short display became Delta
Build log series: I hand a real screen to an AI that has never seen Gunjo (
@gunjo/ui) and let it build. Retail screen three: a register close / daily cash-up (a denomination-by-denomination cash count, reconciliation against the expected balance, over/short, a breakdown by payment method, and the carry-over float).
Same policy as the last two rounds: three to five screens per industry, spread across the most different jobs I can find. After #42 (selling) and #43 (counting), this round closes: at the end of the day, count the drawer, reconcile it against the expected balance, and close the register. Neither a sales screen nor an inventory screen, but a cash reconciliation screen.
Result: 4/5
tsc and build passed, the console stayed clear, 375px held up (a handheld or tablet next to the register), the denomination count recalculated the counted cash instantly, over/short rendered with sign, arrow, word, color, and announcement, anything past plus-or-minus ¥1,000 revealed a reason Select and blocked the close, a completed close went read-only, the page had a single h1, and the payment-method breakdown matched total sales.
The components held on a third mirror-opposite screen
Retail #42, the register (selling): cart, change, yen-or-percent discounts
Retail #43, the stocktake (counting): book stock, counted stock, variance
Retail #44, the close (closing): denomination count, expected balance, over/short
Three different jobs, and the cross-cutting components fit every one. The agent's notes:
-
EditableDataTable(issue #199): used it, fits well. A denomination-by-quantity count grid with computed amounts.renderFooterCellgave the "counted cash total" row for free,rowLabelplus the automaticctx.ariaLabellabeled every quantity cell, and the table-to-card switch meant zero horizontal scroll at 375px. "The single most effective component on this screen."-
RevealSection(issue #213): used it, exactly right. Only when over/short exceeds plus-or-minus ¥1,000 does the reason Select appear throughrole="region"with an announcement, and the close stays blocked until a reason is chosen.-
NumberInput: used it for quantities and the float.minandmax, a draft string you can clear and retype, anddisabledin the read-only state.-
CurrencyInput/formatCurrency {signed}(issue #180): used them, the twelfth consecutive round. Signed yen on over/short and paid-ins and paid-outs (+¥930,-¥1,280). "JPY and ja-JP defaults with zero setup, genuinely good."
The main event: three sightings of over/short, so I built the component
The agent named the over/short display as the biggest gap:
The missing higher-level component is a signed difference, an over/short display. Every reconciliation screen (cash over/short, inventory variance, budget variance) needs a signed amount, a state word, an icon decided by the sign, a meaningful tone, and an announcement of all of it.
Statisticdoes not format signed yen for you and cannot carry a state word. So I hand-rolled aVarianceDisplay. A<Variance value={-930} ... />would be the single most effective addition.
That made three sightings: profit-and-loss (#33), stocktake variance (#43), and the register over/short here. Each time, an agent with no prior knowledge rewrote the same thing by hand (a signed number, a direction arrow, a sign-driven tone, an announced label) because Statistic is a card that does not fit in a cell.
Following the rule of three (three separate screens, then build it), I shipped Delta (issue #221, PR #223):
// Profit and loss: default tones (positive = success, negative = destructive)
<Delta value={12500} format={yen} labels={{ positive: "Increase", negative: "Decrease" }} />
// Cash over/short: positive is not necessarily good, so override the tones
<Delta value={-930} format={yen}
tones={{ positive: "warning", negative: "destructive", zero: "success" }}
labels={{ positive: "Over", negative: "Short", zero: "Balanced" }} showLabel />
- The sign of
valuedecides the arrow (up, down, or flat), the tone, and the label. It is a minimal inline<span>component, and unlikeStatisticit fits inside a table cell. formatdefaults to signed, digit-grouped ja-JP. PassformatCurrencyand you get yen.tonescan be overridden per sign: profit keeps the green-when-positive default, while cash reconciliation flips an overage to the warning tone. "Positive means good" is not universal, and that is the crux of reconciliation.labelsare always announced (sr-only, or visible withshowLabel). The meaning never rides on color or the arrow alone.- Pure display, so it is server-safe, with no client boundary.
Logged, not built (not yet at three)
- 🟠 A payment-method breakdown, a summary-table component: the label, amount, and share rows for cash, credit, e-money, QR, and gift certificates, plus a total row, went into a bare
<table>by hand. This is issue #178 territory (DescriptionList, a summary list). Review screens keep reconfirming it, and it is approaching the threshold (the transfer confirmation in #32, the invoice screen in #37, and here). - 🟠 A cash reconciliation, ledger-summary component: opening float plus cash sales, plus or minus paid-ins and paid-outs, equals expected against counted. That stack of signed label-value rows with an emphasized total was also hand-rolled (
ReconRow). Same issue #178 neighborhood. - 🟠 Denomination-count helpers:
EditableDataTableis a good base, but the denomination rows, the computed amount column (denomination times quantity), and the counted-total footer get rebuilt every time. POS-specific, first sighting. - 🟡
EditableDataTablerenders both the desktop table and the mobile cards into the DOM (hidden withmd:), so nine denominations mean eighteen quantity inputs exist. Worth knowing for scripts, tests, and form submission. - 🟡 The JSDoc never says
ctx.ariaLabelalready combinesrowLabelwith the header, so the agent prefixed it twice (now fixed).
What I took away: "three to five different screens" is a component-building machine
The cross-cutting components (money, grid, reveal) work even when the job is inverted. On top of that, when the same missing component recurs on three screens, it is not screen glue; it is the signature of an absent component. This round, the signed-difference display crossed that line. Running an industry three to five screens deep with the jobs spread out does two things at once: it gives the industry verdict its depth, and it fires the rule of three so the design system gains exactly one component. Next, issue #178 (the summary list) is approaching the same threshold.
Next up: build log #45
- One more retail screen (returns, register open, or a third scan-input sighting for issue #220), or a new industry. I will also watch whether the next clean agent discovers and uses
Delta(issue #221).
The run
Gunjo was still in early alpha at the time; issues are welcome.
This series is co-created with AI (Claude and Codex). A human designs the experiments, makes the calls, and fact-checks before publishing; the AI does the hands-on work and drafting.
Components from @gunjo/ui
The components this screen's source imports directly.
The code the agent actually wrote
Click a file to expand its source.