Repository navigation
/attester: read-only payout-account checks (replace the stub) #42
Description
Task
app/attester/page.tsx is a stub. Build the attester-facing read-only tool: given a Stellar account and a USDC issuer, report whether the payout account exists, holds enough XLM for the reserve, and has an authorised USDC trustline — the same answers check-attester-readiness.ts gives on the command line, in a browser.
Context
app/attester/page.tsx:5renders<PageStub titleKey="pages.attester.title" />— nothing else.- Equivalent logic exists as a script:
kinlock-registry/scripts/check-attester-readiness.ts, which checks account existence, XLM float and an authorised trustline; it deliberately does not automate the test release (:6-9). app/payee/page.tsx:10and itsusePayeeDashboard.ts:10,48show this repo's shape for a data-fetching page: hooks plus shared components, SDK/adapter for anything chain-facing.- Registry data drives everything country-ish:
AGENTS.md§3.11 — no hard-coded country, currency or anchor; read from registry data. messages/en.json(293 keys) +lib/i18n/messages.ts:15, enforced bylib/i18n/inline-strings.test.ts:22.
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
- An attester verifying a school's payout account today has to run a Node script. That is the difference between "we can onboard payees in the pilot" and "only the maintainers can".
- Hard rule: a lock can only pay out to a payout address that can actually receive USDC. A missing or unauthorised trustline means the school cannot claim, and
AGENTS.md§8.2 requires preflight to see it. A read-only check page is the cheapest way to surface it early. M3-12is the last unclaimed piece of the attester flow before the pilot.
Scope
In scope
app/attester/**(page, form, results view — new sub-components allowed)lib/read-only account-check helper, ideally mirroringcheck-attester-readiness.ts's three checksmessages/en.jsonkeys- Tests for the checks and the view
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/**- anything that signs, submits a transaction, or moves a trustline — this page is read-only. A "test release" button is out of scope and needs a decision (
M1-26) kinlock-registrydata files — read them, never write
Suggested implementation
- Read
ARCHITECTURE_ESSENTIALS.md,AGENTS.md§3 (rules 3, 5, 11) and §8.3–8.4, pluskinlock-registry/scripts/check-attester-readiness.tsso your results match it field for field. - Use Horizon's public read endpoints through the existing SDK/
libpatterns; validate input with the SDK's StrKey utilities (AGENTS.md§8.2), and never accept or ask for a secret key, seed phrase or signing — if the UI would tempt a user to paste one, redesign it. - Three explicit results: account exists / does not; XLM float vs the reserve requirement (state where the reserve number comes from; if you cannot verify it, show the float and say the threshold needs checking rather than inventing it); USDC trustline absent / unauthorised / authorised — show the issuer, not just "USDC" (§8.3).
- Say what the attester should do about each failure in plain language, sourced from
ATTESTER_CHECKLIST.md. - No country, currency or anchor hard-coded (§3.11): the USDC issuer comes from registry or config, not a literal in your component.
- Log nothing personal, and never log an address alongside payee details (hard rule 5).
- Accessibility from the start: labels, focus order,
role="status"on results, contrast — copycomponents/indexer/LagNotice.tsx:8.
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
- Trustline absent and trustline present-but-unauthorised are different answers with different fixes. Collapsing them into “not ready” is the main failure mode of this page.
- The XLM reserve number: state where it comes from. If you cannot verify it from an official source, show the float and say the threshold needs checking — do not invent a figure (§10).
- An invalid StrKey input must be refused by validation before any network call, with a plain message.
- Never a field that invites a secret key or seed phrase, and never log an address beside payee details (hard rule 5).
- The USDC issuer comes from registry or config. A literal issuer or country in a component breaks §3.11.
Done means
-
/attesteris a working page, no stub, keyboard- and screen-reader-navigable - The three checks return the same verdicts as
check-attester-readiness.tsfor the same inputs — show at least two side-by-side examples in the PR - No signing, no transaction submission, no secret input anywhere in the flow
- Asset is always shown as code and issuer; no country or currency literal in the component
- All copy in
messages/en.json; tests cover each verdict branch;pnpm lint/typecheck/test/buildpass -
M3-12flipped only if the parity criterion is met; otherwiseIN PROGRESSwith the remainder as a new row
Tests
Unit tests for the check logic against fixture Horizon responses (no live network in tests), and one case proving an unauthorised trustline is reported distinctly from a missing one. A DOM testing library would be a new dev dependency — ask on the issue before installing.
Documentation
Add a section to this repo's README describing the page, and note it in ATTESTER_CHECKLIST.md (canonical Kinlock-Org/.github/docs) so the two stay in step. If you introduce an architectural choice (where checks run, which endpoint), open an ADR draft and ask.
Verify before you open the PR
pnpm install
pnpm lint
pnpm typecheck
pnpm test
pnpm build
pnpm dev # then open /attester and check three accounts against pnpm --dir ../kinlock-registry check-attester-readiness
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 #42in the PR description, so the issue and the PR link automatically.- Branch
feat/…,fix/…,docs/…,test/…orchore/…. Commit subject example:
feat(app): /attester read-only payout-account checks M3-12→DONEonly with the parity evidence; Changelog entry;Last updated. Do not touchM1-26— it is registry-owned.- 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 screenshots of each trustline verdict (absent, unauthorised, authorised) and the side-by-side parity output against
check-attester-readiness.ts.
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 parity evidence against the script for the same inputs, and that nothing in the flow signs, submits or asks for a secret.
Tier and effort
tier/high · roughly 3–5 days. Highest-value issue in this repo's backlog, and the one most likely to need a maintainer conversation — start it early.. Wave complexity tag: High = 200 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-12 — /attester tooling: read-only account checks (TODO)
M1-26 — ATTESTER_CHECKLIST.md and the readiness script (IN PROGRESS)
M3-12 depends on M1-26, which is IN PROGRESS. The three checks this page needs already exist in kinlock-registry/scripts/check-attester-readiness.ts, so you can build against them; the checklist's test-release half is unautomated on purpose, so do not try to surface it — confirm the parity target on the issue before you finish.
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!
Reviewing constraints: ARCHITECTURE_ESSENTIALS.md, AGENTS.md §3 (Hard Rules 3, 5, 11), §8.3 (app rules: no inline strings, asset code + issuer, plain language, accessibility), §8.4 (registry data parity), and docs/project_structure.md §8.
Related roadmap row: M3-12. Starting implementation of
/attesterread-only payout-account checks, matchingcheck-attester-readiness.tsverdicts, with comprehensive unit tests and zero transaction/signing capabilities.jdjioe5-cpu commentedon Oct 17, 2026
Delivered in PR #50 !
Summary of Delivery:
/attester): Fully replaces the placeholderPageStubwith an interactive, accessible tool (app/attester/page.tsx,AttesterChecker.tsx,useAttesterChecker.ts,lib/attester/readiness.ts,lib/attester/strkey.ts).check-attester-readiness.ts:account.exists);minRequired = 2XLM);absent), unapproved (unauthorised), limit reached (no_room), andauthorisedwith room, with actionable remediation guidance for each case.messages/en.json;role="status"accessibility; and 100% clean Next.js 16.3.8 production build.README.md, advancedM3-12toDONE, and recorded Changelog entry.