Repository navigation
Route every money and time display through the shared components, and enforce it #40
Description
Task
AmountDisplay, AssetLabel, RateNotice and DateTimeDisplay already exist. Find every place a page formats an amount, an asset or a timestamp without them, replace those call sites, and add a test that fails when someone hand-rolls formatting again.
Context
- Components:
components/money/AssetLabel.tsx:8,components/money/AmountDisplay.tsx:10,components/money/RateNotice.tsx:4,components/time/DateTimeDisplay.tsx:5. Formatting helpers:lib/i18n/format.ts:9,20,27. M3-15isIN PROGRESSbecause the components exist but universal use is not proven — that is the remaining work.- Hard rules this enforces: always show asset code and issuer, never code alone (§8.3); dates in UTC and local (§8.3); amounts are decimal strings, never
number(§8.2). lib/i18n/inline-strings.test.ts:22already scansapp/andcomponents/for hard-coded UI strings and passes — the same guard style works for formatting calls.- USDC is 7 decimals;
messages/en.jsonholds the copy; the inline-string guard is the precedent to copy.
Before you start: mandatory context review
Kinlock moves other people's money, so the docs are binding constraints, not background reading.
They record decisions this task must not silently reopen. Read them in this order and tick each box:
-
docs/ARCHITECTURE_ESSENTIALS.md— start here, it is short (AGENTS.md§1) -
docs/ARCHITECTURE.md— only the sections named in Context above -
docs/PRD.md— if this task touches user-facing behaviour or scope -
AGENTS.md§3 (hard rules), §4 (deferred list), §8 for this repo, §12 (roadmap rule) -
ROADMAP.md— the row(s) listed under Related roadmap rows, including their Depends on
In your first PR comment, name the hard-rule numbers or doc sections that constrain this task. That
line is how we know the review happened.
Then:
- If code and docs disagree, or two docs disagree: stop and say so on this issue. Do not pick one
silently, and do not "fix" either side without approval (AGENTS.md§1). - If the task looks like it needs anything from the deferred list (§4), a change to a hard rule (§3),
or a new dependency (§2): raise it here first. Those need a human; deciding them is not your job. - If a row you are told to finish is
BLOCKEDor depends on a row that is notDONE, say so here
instead of starting.
Why we need this
- Two formatters for the same number is how a sender sees 1,000.00 USDC on one page and 1,000 on the next and starts wondering which is real.
- The issuer is what distinguishes USDC from a look-alike token. Displaying a code alone is a genuine scam surface, not a style preference.
- A guard test turns three rules that live in prose into something CI enforces.
Scope
In scope
- Call-site replacements in
app/(marketing)/,app/payee/,app/request/,app/verify/,app/_components/,components/ lib/i18n/format.ts— display-only helpers, if a gap needs closing- A new guard test, e.g.
lib/i18n/format-usage.test.ts components/ui/if a small shared piece is missing
Do not touch (security-sensitive or generated; changing these needs a human first — AGENTS.md §2, docs/project_structure.md §8)
app/claim/**andlib/claim-links/**— the claim-link fragment (#r=…&s=…) must never reach a server, log or analytics tool (hard rule 5)next.config.ts,proxy.ts/middleware.ts, CSP headerslib/wallet/**and any direct contract call site — money-moving calls go through the SDK and the wallet adapter onlyapp/send/**,app/locks/**,app/payee/**action logic — release, refund and decline paths stay with maintainerspackage.json,pnpm-lock.yaml(no new dependencies without approval),.github/workflows/**app/send/**,app/locks/**,app/claim/**,app/r/**— these are the money-action pages; if they need changes, list them on the issue and a maintainer will land them- changing rounding, precision or any value semantics — presentation only
Suggested implementation
- Read
ARCHITECTURE_ESSENTIALS.md,AGENTS.md§8.2 (amount types) and §8.3 (asset and date display). - Inventory first: grep for
Intl.NumberFormat,Intl.DateTimeFormat,toLocaleString,toFixed, and raw{amount}interpolations outsidelib/i18n/format.ts. Put the list in the PR before you start changing things. - Replace one route at a time, committing each so a review can stop at a route if a replacement is wrong.
- The guard test: scan
app/andcomponents/for those patterns, allowlistlib/i18n/format.tsandcomponents/money/*,components/time/*, and fail otherwise. Copy the structure oflib/i18n/inline-strings.test.ts:22. - Where an amount is shown without its issuer, add
AssetLabel. Where a timestamp appears without both UTC and local, useDateTimeDisplay. AGENTS.md§8.3 says an indicative local-currency equivalent is display-only with a rate-risk note (RateNotice), and never guaranteed — do not build or change rate logic.
A different approach is usually welcome — post it here before building and we will answer. This is a
suggestion, not a spec.
Edge cases and traps
- Zero, the smallest USDC unit (
0.0000001), and a large multi-tranche total: three different formatting traps in one component. - 0-, 2- and 3-decimal
local_currencydisplay, plus a right-to-leftdisplay_namethat must not break the layout (§8.3). - A long issuer address next to a short asset code is where “show code and issuer” gets hard — check the narrow case.
- Allowlist only
lib/i18n/format.ts,components/money/*andcomponents/time/*. A wider allowlist turns the guard into decoration. - Presentation only. If a conversion changes a rendered value, stop — that is
M3-15's money-action half, which is not yours to touch.
Done means
- Every in-scope page renders money through
AmountDisplay/AssetLabeland times throughDateTimeDisplay - No asset appears by code alone in any in-scope view
- The guard test exists, fails on a hand-rolled formatter you add locally, and passes in CI
-
pnpm lint,typecheck,test,buildpass;lib/i18n/inline-strings.test.ts:22still passes - You list the out-of-scope money-action routes that still need conversion in the PR, so
M3-15can be closed honestly
Tests
One new guard test plus any unit case for a helper you add. If you touch lib/i18n/format.ts, extend messages.test.ts-style coverage for the edge cases (zero, 2- and 3-decimal currencies, RTL names) without changing its output for existing cases.
Documentation
Update the README component note if the convention changes. No ADR unless you change the formatting rule itself.
Verify before you open the PR
pnpm install
pnpm lint
pnpm typecheck
pnpm test
pnpm build
Run every command above and paste the real output in the PR. We do not accept "should pass" —
AGENTS.md §6: "You actually ran the commands."
Opening the pull request
- Assignment is required before you start. Comment on this issue and wait to be assigned. Do not
open a PR against an issue someone already holds — if it has been quiet, ask us to release it first. Closes #40in the PR description, so the issue and the PR link automatically.- Branch
feat/…,fix/…,docs/…,test/…orchore/…. Commit subject example:
refactor(app): render money and time only through the shared components - Leave
M3-15IN PROGRESSunless the money-action routes are also converted (they are not yours to touch); add a row for the remainder. - Follow
.github/PULL_REQUEST_TEMPLATE.md(AGENTS.md§7). In "Hard rules / invariants touched",
give numbers, not prose. - Small and focused: one logical change. Do not mix a refactor into it.
- Do not add the
security-sensitivelabel — that is the maintainer's flag for contract-logic PRs. - Do not reformat or touch files outside Scope. Unrelated diff noise costs review time and can
break the roadmap merge. - Never commit to
main; open a PR. Never force-push. - Evidence for the PR: Attach before/after screenshots of one converted route showing the smallest and the largest amount it can render.
How we review
Two maintainers: one reviews correctness against the hard rules and the roadmap row's Done when, the
other reviews diff scope and whether the tests were actually run. Order is hard-rule correctness → tests
really executed → diff stays inside Scope → style last. First response on a PR: 2 business days.
What fails review fastest: a change outside Scope, a deleted or weakened assertion, a claim with no
command output behind it, and a ROADMAP.md with no Changelog entry (AGENTS.md §12).
For this issue, look first at the inventory list you post before changing anything, and the guard test's allowlist — that is where a false pass hides.
Tier and effort
tier/medium · roughly 1–2 days. Wave complexity tag: Medium = 150 points — the published mapping is Trivial 100 · Medium 150 · High 200
(Drips' maintainer guide); the cycle's
own values are confirmed on drips.network/wave/stellar before
the Wave opens. Kinlock's tier rules and what each tier means for review:
docs/wave-issue-format.md.
Related roadmap rows
M3-15 — AssetLabel, AmountDisplay, UTC+local time components (IN PROGRESS)
M3-15's Done when is "Used everywhere money or time is shown" — the components exist, so this issue is the "everywhere" half plus the guard that keeps it true.
Questions
Comment on this issue — a maintainer answers within 2 business days. Say "I'll take this" so we can flag it on
the Wave board and stop two people doing the same work. If you get half way and stall, post what you
have as a draft PR rather than disappearing; a blocked draft is more useful to us than silence.
Activity
jdjioe5-cpu commentedon Oct 17, 2026
I'll take this! Will inventory every money/time display call site, route in-scope pages through AmountDisplay, AssetLabel, and DateTimeDisplay, build the format-usage guard test (lib/i18n/format-usage.test.ts) modeled after inline-strings, preserve all security/money-action boundaries out of scope, and verify full CI suite.
jdjioe5-cpu commentedon Oct 17, 2026
PR is ready for review: #48 - Pre-change inventory recorded; in-scope call-sites in RequestForm, ReceiptResult, and SavedClaimLinks routed through AmountDisplay, AssetLabel, and DateTimeDisplay; AmountDisplay supports optional assetCode/issuer for fallback safety; strict format-usage guard test in lib/i18n/format-usage.test.ts passing; money-action routes listed out-of-scope with M3-15 kept IN PROGRESS; full CI test suite green (209/209 tests passed, production build passed).