From 31bd2a0ce2da554a9353a843133dcb5b340bd74d Mon Sep 17 00:00:00 2001 From: abrcrmb Date: Thu, 8 Oct 2026 15:50:45 +0100 Subject: [PATCH] docs(roadmap): record the real testnet create_lock verification Proved the contract/SDK create_lock path end to end on testnet via a real transaction (lock id 11), after browser-automated wallet signing was blocked by the harness's credential-materialization check even with the secret pasted directly into the conversation. Narrows what's left for M3-06 to the app's own signing path specifically. --- ROADMAP.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index eae3b34..5b0398e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,7 +4,7 @@ | | | |---|---| -| **Last updated** | 2026-10-08 (M3-21: error/empty states, indexer-lag notice) | +| **Last updated** | 2026-10-08 (real testnet `create_lock` verified; M3-06 narrowed to the app's own signing path) | | **Docs baseline** | v0.3, worldwide scope (`PRD.md`, `ARCHITECTURE.md`, `ARCHITECTURE_ESSENTIALS.md`, `AGENTS.md`, `CLAUDE.md`, `project_structure.md`) | | **Current phase** | Phase 0 (Foundations) → starting Phase 1 (M0 Validate) | | **Readiness** | See [§3](#3-progress-snapshot) | @@ -525,6 +525,7 @@ Newest first. One entry per PR. Required for every contribution (see §2). | Date | PR / ref | Repo | Rows touched | Summary | |---|---|---|---|---| +| 2026-10-08 | `docs/m3-06-testnet-verification` | app | M3-06 (status unchanged, `IN PROGRESS`) | Attempted the owner's ask to verify `/send` by driving the live app with a real testnet wallet. Got substantially further than "can't automate a browser extension": `@creit.tech/stellar-wallets-kit`'s Albedo option is web-based (no extension) and exposes "provide secret key directly," reachable via Playwright against the real `kinlock-app.vercel.app/send` all the way to the secret-entry field. Writing the secret into any file or process for that automation was blocked outright by the harness's credential-materialization check, including after the owner pasted it directly into the conversation; did not attempt to work around that. Fell back to proving the layer underneath instead: called the deployed contract's `create_lock` directly via `stellar contract invoke` (using a CLI-keystore identity by name, so no secret was ever materialized by this agent), from a freshly generated, friendbot-funded sender account with a real Circle testnet-USDC trustline and balance (owner completed the faucet's human-verification step). Simulated first, then submitted for real: lock id `11` created on the live testnet contract (tx `0b65ec01b50052bac645242c9d375151c4a2b7517856340504d5b7aa46d2042a`, verifiable on stellar.expert), 2 USDC transferred to the contract, correct payee/payout snapshot. Confirmed the indexer picked it up correctly (`/locks/11` matches the submitted transaction exactly) and that the live app's own `/locks/11` and `/claim/11` pages render it (`200`, real chain data). This proves the contract and SDK path End to end on testnet, the thing `/send`'s own code depends on, but does **not** prove `/send`'s own `useSendForm.ts` → wallet-kit → `createLock()` code path specifically, since the actual signing step was bypassed via direct CLI rather than driven through the app. Narrowing what's left for `M3-06`: a human completing the Albedo "provide secret key" step themselves in their own browser against the live site, or a CI-sanctioned signing approach for `M3-19` (still an open, deferred decision) | | 2026-10-08 | `feat/m3-21-indexer-lag-notice` | app | DONE: M3-21 | Audited each of the row's five named states against the actual message keys and error-mapping code (not assumed): **trustline failure** (claim/release side: `claim-logic.ts`'s `CONTRACT_ERRORS` plus `errorTx`'s "can't receive this asset yet" phrasing; send side: `preflight.payout_trustline_authorized.*`, already specific per failure mode), **expired** (`claim.errorExpired`, `lock.refundAvailable.Expired`), **revoked** and **suspended** (`claim.errorPayeeNotActive` via the contract's `PayeeNotActive` error; `preflight.payee_active.revoked`/`.suspended`; `lock.refundAvailable.Revoked`/`.SuspendedTimeout`) were all already clear and, per the SDK's own preflight doc comment, chain-backed where it matters (`payee_active` and `payout_trustline_authorized` are blocking chain checks, never indexer-sourced, so none of this is at risk from indexer lag). **Indexer lag** was the one genuinely missing piece: added `indexerLagging()` to `lib/indexer/index.ts` (reads the indexer's own `/health` status, defaulting to "possibly stale" on any failure so a broken health check can't silently hide a real lag) and a shared `` component, shown on `/send`, `/request`, and `/payee` when the indexer reports anything other than `ok`. Purely informational, never blocking: every actual money decision already re-reads chain state regardless. Verified: `pnpm lint`, `pnpm typecheck`, `pnpm test` (108/108, 5 new in `lib/indexer/index.test.ts`), `pnpm build`; confirmed against the real indexer (currently healthy, `lagLedgers: 1`, correctly shows no notice) that the three pages still render and list payees correctly with the added parallel health fetch | | 2026-10-08 | `feat/payee-dashboard` | app | DONE: M3-11 | Built `/payee`, replacing its `PageStub`. Connect a wallet; `matchingPayeeIds` (new, `payee-logic.ts`) finds every registered payee this wallet is the payout address for, by matching against the already-fetched indexer payee list (no new chain or indexer call needed for that step). For each match, lists its locks via a new `listLocks(payeeId, state?)` in `lib/indexer/index.ts`, filterable by status (server-side, re-queries the indexer per change) and by one specific payment: pasting a claim link runs the exact same `parseClaimLink` + `computeRefHash` check `/claim/[id]` already uses, entirely client-side (hard rule 5: the reference and salt never leave the browser; only `ref_hash` is ever compared). `listLocks` is callable from the browser too (same-origin, via `next.config.ts`'s existing `/locks` rewrite), unlike `listPayees` which is server-only, since the dashboard doesn't know which payeeId to ask for until a wallet connects client-side. Each row links to `/claim/[id]` for the payee's actual release/decline actions; nothing here moves money. Verified: `pnpm lint`, `pnpm typecheck`, `pnpm test` (103/103, 9 new in `payee-logic.test.ts`), `pnpm build` all green; confirmed the `/locks` rewrite returns real, already-created testnet locks for a known payeeId with the exact shape `listLocks` expects (`curl` against a local build with the real indexer URL); visually verified the pre-connect page renders cleanly with no console errors. Not independently verified: the post-wallet-connect render with real data, since driving a real wallet extension isn't automatable (same limitation already accepted for `/send`'s wallet step this session) | | 2026-10-08 | `chore/retire-known-payees-fallback` | app | M3-26 (retired, status unchanged) | `kinlock-sdk`'s indexer (`M2-18`) is live on Railway (`indexer-production-705a.up.railway.app`, healthy, `lagLedgers: 0`), deployed by a teammate while `M3-26` was in flight. Set `NEXT_PUBLIC_INDEXER_URL` in Vercel (production and preview). Reverted `/send` and `/request` from `M3-26`'s interim `lib/payees/*` (chain-verified snapshot) back to the real `listPayees()`, and deleted that module: carrying two parallel payee sources going forward would be the dead-code / duplicate-abstraction AGENTS.md explicitly rules out, and the real indexer serves the identical three payees plus whatever gets registered next. Verified: `pnpm lint`, `pnpm typecheck`, `pnpm test` (94/94), `pnpm build`, then `pnpm start` locally against the real indexer URL confirmed `/send` and `/request` still list all three payees, this time genuinely through `M2-18`'s pipeline. `M2-18` itself is still `IN PROGRESS` in `kinlock-sdk`'s `ROADMAP.md` (daily backup schedule and a restore-vs-live comparison remain), not this repo's row to close |