diff --git a/ROADMAP.md b/ROADMAP.md index 8e45f12..0b2a524 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,7 +4,7 @@ | | | |---|---| -| **Last updated** | 2026-10-08 (Wave issue template adopted; rows M1-38..M1-41, M2-20, M2-21, M3-27 added; W-05 left TODO — org row, resolved by .github#26) | +| **Last updated** | 2026-10-08 (W-05 resolved; 17 Wave issues seeded with seven new rows; issue format proofed against Drips' maintainer guide; the three pre-format issues triaged — app #16 re-emitted, app #15 and registry #7 closed as superseded; repo-side Changelog lines unioned back into canonical by `scripts/roadmap-merge`; that merge's ADR-link normalization anchored so quoted prose cannot be rewritten) | | **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) | @@ -137,9 +137,9 @@ Readiness % = `DONE ÷ (all rows − DEFERRED − DROPPED)`. Conditional rows (P | 7 Hardening | 26 | 0 | 0 | 26 | 0 | 0 | 0 | 0% | | 8 Launch | 8 | 0 | 0 | 8 | 0 | 0 | 0 | 0% | | 9 Conditional ramp | 9 | 0 | 0 | 0 | 0 | 9 | 0 | n/a | -| 10 Wave + community | 10 | 4 | 1 | 5 | 0 | 0 | 0 | 40% | +| 10 Wave + community | 10 | 5 | 1 | 4 | 0 | 0 | 0 | 50% | | 11 Deferred parking lot | 16 | 0 | 0 | 0 | 0 | 16 | 0 | n/a | -| **All** | **211** | **74** | **21** | **91** | **0** | **25** | **0** | **40%** | +| **All** | **211** | **75** | **21** | **90** | **0** | **25** | **0** | **40%** | --- @@ -495,7 +495,7 @@ Resolve each with an ADR and link it here. Move resolved rows to the bottom with | W-02 | Seed issues: tests and docs in contracts (security-aware), registry tooling, app UI, SDK, indexer fixtures | all | P1 | DONE | M1-01 | Backlog exists; security-sensitive issues excluded from open contribution | | W-03 | Org and repo `CONTRIBUTING.md` including the roadmap rule | org | P0 | DONE | F-09 | Rule stated prominently | | W-04 | Contributor quick-start tested on a clean machine for each repo | all | P1 | TODO | M3-01 | New contributor runs tests in under 15 minutes | -| W-05 | Issue sizing guide aligned with current program guidelines | org | P1 | TODO | M0-10 | Guide published | +| W-05 | Issue sizing guide aligned with current program guidelines | org | P1 | DONE | M0-10 | `docs/wave-issue-format.md` published: one issue format, three tiers mapped to Wave points, exclusions, triage SLAs | | W-06 | PR review SLAs and triage routine | org | P1 | TODO | F-12 | Documented; rota exists | | W-07 | Maintainer rota for `security-sensitive` PRs | org | P1 | TODO | F-12 | Named reviewers per week | | W-08 | Track application limits and KYC requirements per cycle | org | P2 | TODO | W-01 | Calendar and owner set | @@ -535,7 +535,16 @@ Newest first. One entry per PR. Required for every contribution (see §2). | Date | PR / ref | Repo | Rows touched | Summary | |---|---|---|---|---| +| 2026-10-08 | `chore/roadmap-copy-back` (#39) | sdk | no row changes | Copy-back run: `ROADMAP.md` replaced with the merged canonical (`.github` main as of #31) in repo-root link form, and §3 recomputed with `scripts/roadmap-progress`. This is the step `roadmap-merge`'s header requires after every merge and that was skipped between 2026-10-06 and 2026-10-08, which is how the seven copies reached 92 divergent statuses. Verified in this checkout: the file equals canonical byte for byte apart from this entry and the ADR link prefix, `W-05` reads `DONE`, the seven Wave rows are present, duplicate row-ID scan is 0. No row, status, priority, dependency or **Done when** was authored in this repo. | +| 2026-10-08 | `fix/roadmap-merge-link-anchor` | org | no row changes | Found while preparing the copy-back. `roadmap-merge`'s read normalization rewrote the docs-rooted ADR link form into the root-relative one on every copy so the two depths compare equal — correct for links, wrong for prose: the union entry above quotes both forms, so reading that line collapsed its own quote and emitted a Changelog entry contradicting itself, in a copy that no longer matched canonical line for line. Fixed by anchoring the replace on the numeric ADR filename that always follows the slash, so only real links are rewritten, and by re-wording the union entry to name the two forms as `docs/adr/` and `adr/` rather than spelling them as link text: **an entry that describes this rewrite must not itself be a string the rewrite can act on.** Verified: the anchored read leaves a quoted form byte-identical where the blanket replace corrupted it; a full `roadmap-merge` over all five copies against `b12d290` exits 0 with **0 lines** differing from canonical, which also confirms the union is complete; the one real link (`ADR-0026`) still normalizes both ways. No row, status, priority, dependency or **Done when** in this diff. | +| 2026-10-08 | `chore/roadmap-changelog-union` | org | no row changes | Second `scripts/roadmap-merge` run now that the Wave-format PRs have merged (`.github` #26 and #27, contracts #31, sdk #38, app #43, registry #18). Three-way against `b12d290`, the revision where all five copies were last identical. The merge's **entire** output is four lines: the repo-side `docs/wave-issue-format` Changelog rows that each repo wrote into its own copy and canonical never saw. No row ID, status, priority, dependency or **Done when** moved — the seven new rows went in byte-identically, and `W-05`'s `TODO` → `DONE` stayed on the `org` row in canonical as §12 requires, so the repo copies' deliberately-untouched `TODO` was not merged back over it. §3 verified unchanged by `roadmap-progress`: 211 rows, 75 `DONE`, 40%. **Not in this PR:** the copy-back. Each repo's `ROADMAP.md` must be rewritten from the merged canonical, with the ADR links rewritten for the repo-root location, so all five copies are byte-identical again — that invariant is what makes `--ancestor` meaningful next time, and it needs four repo PRs that can only be cut after this lands. | +| 2026-10-08 | `docs/legacy-issue-triage` | org | no row changes | Worked the three issues left over from the 2026-10-07 seeding, in the proved format, without touching any label. `kinlock-app` #16 (`M3-20`, P0 copy and wording review) re-emitted through the same renderer as the rest of the backlog and judged fresh: `medium` = 150 points, stated in the body. Its evidence is now real rather than asserted — the file holds 293 strings (`lib/i18n/inline-strings.test.ts` proves that is the whole surface), the rule-7 guard blocks exactly two literals (`lib/i18n/messages.test.ts:26-30`) so any synonym of overclaim passes CI, and `pages.request.referenceHint` (`messages/en.json:85`) and `pages.send.referenceHint` (`:112`) make **different privacy promises about the same field**, which is routed to a maintainer as `AGENTS.md` §3 rule 5 territory instead of a contributor's guess. `kinlock-app` #15 closed as superseded: the `M3-17` umbrella is now #38 (contrast, three named call sites with measured ratios) and #39 (skip link, focus, keyboard), with the unowned remainder spelled out in the closing comment. `kinlock-registry` #7 closed as superseded: both things it asked for already exist (`ATTESTER_CHECKLIST.md`, `scripts/check-attester-readiness.ts`), and the row's remaining "runs end to end on testnet" half needs a named attester and a signed testnet transaction, so it cannot be a Wave issue at all — `M1-26` stays `IN PROGRESS`, #17 is the claimable half. Also repaired six citations of a non-existent "§3.5" across published issues (now "hard rule 5"; `ARCHITECTURE.md` §3 is the tech stack) and added the three triage rules these cases imply to `docs/wave-issue-format.md`: confirm the deliverable is not already in `main`, re-emit or close-with-a-pointer any pre-format issue, and make every rule reference resolve. The format audit covers 18 issues now, gaps: none. | +| 2026-10-08 | `docs/issue-format-proof` | org | no row changes | Proved the issue format and all 17 seeded Wave issues against Drips' maintainer guide (*Creating Meaningful Issues*). Two sections added to the format — **Edge cases and traps** (principle 4: direction without micromanagement) and **How we review** (principle 5: say how the work will be reviewed) — and **Opening the pull request** now requires assignment before starting, `Closes #`, a commit-subject example and the evidence the PR must carry. **Tier and effort** states the Wave point value (Trivial 100 · Medium 150 · High 200) on every issue, with the cycle's numbers to be re-confirmed on `drips.network/wave/stellar`; the `tier/*` GitHub labels stay Kinlock's bookkeeping, no new labels introduced. `contracts` #27's effort was restated and its tier/points seam disclosed rather than papered over. | +| 2026-10-08 | `docs/wave-issue-format` | org | Added: M1-38, M1-39, M1-40, M1-41, M2-20, M2-21, M3-27. Resolved: W-05 | Published `docs/wave-issue-format.md` (one issue format, three tiers mapped to Wave points, exclusion list, triage SLAs) and rewrote `ISSUE_TEMPLATE/wave-task.md` to match. Seeded the Wave backlog: 17 tiered issues across the four repos (contracts #27–#30, sdk #34–#37, app #37–#42, registry #15–#17), each mapped to a roadmap row and confined to the `project_structure.md` §8 safe categories. The seven new rows were inserted byte-identically into all five ROADMAP.md copies so the merge sees no divergence. | +| 2026-10-08 | `docs/wave-issue-format` (org side: `.github` #26) | contracts | Added `M1-38`, `M1-39` (this repo); `M1-40`, `M1-41`, `M2-20`, `M2-21`, `M3-27` mirrored for copy parity; `W-05` left `TODO` (org row) | Adopted the single org issue format and seeded the contracts half of the Wave backlog: #27 → `M1-38`, #28 → `M1-15`, #29 → `M1-39`, #30 → `F-18`/`F-19`. The seven rows are byte-identical across all five `ROADMAP.md` copies so `scripts/roadmap-merge` sees no divergence. | +| 2026-10-08 | `docs/wave-issue-format` (org side: `.github` #26) | registry | Added `M1-40`, `M1-41` (this repo); `M1-38`, `M1-39`, `M2-20`, `M2-21`, `M3-27` mirrored for copy parity; `W-05` left `TODO` (org row) | Adopted the single org issue format and seeded the registry half of the Wave backlog: #15 → `M1-40`, #16 → `M1-41`, #17 → `M1-26`. Seven rows byte-identical across all five `ROADMAP.md` copies. | | 2026-10-08 | `docs/wave-issue-format` (org side: `.github` #26) | sdk | Added `M2-20`, `M2-21` (this repo); `M1-38`, `M1-39`, `M1-40`, `M1-41`, `M3-27` mirrored for copy parity; `W-05` left `TODO` (org row) | Adopted the single org issue format and seeded the SDK/indexer half of the Wave backlog: #34 → `M2-14`, #35 → `M2-20`, #36 → `M2-21`, #37 → `W-04`. Seven rows byte-identical across all five `ROADMAP.md` copies. | +| 2026-10-08 | `docs/wave-issue-format` (org side: `.github` #26) | app | Added `M3-27` (this repo); `M1-38`, `M1-39`, `M1-40`, `M1-41`, `M2-20`, `M2-21` mirrored for copy parity; `W-05` left `TODO` (org row) | Adopted the single org issue format and seeded the app half of the Wave backlog: #37 → `M3-27`, #38 and #39 → `M3-17`, #40 → `M3-15`, #41 → `M3-18`, #42 → `M3-12`. Seven rows byte-identical across all five `ROADMAP.md` copies. | | 2026-10-08 | `chore/roadmap-reconcile` | org | canonical: 43 statuses corrected, rows M1-37 / M3-25 / M3-26 added; each repo copy: 36-52 statuses corrected, rows W-09 / W-10 added | First run of `scripts/roadmap-merge` (F-15) since the copies diverged at c488967 (2026-10-06). Three-way merge against that revision as ancestor: rows owned by a repo taken from that repo's copy, `org` / `all` rows from whichever copy changed them relative to the ancestor. One conflict adjudicated by hand: **F-11** - the canonical copy's reword and `DONE` (bea228e, ADR-0026) supersedes every repo copy's stale `IN PROGRESS`. **DEC-03** differed only in its ADR link prefix (`adr/` under `docs/`, `docs/adr/` at a repo root); `roadmap-merge` now normalizes that on read, so a path difference is never reported as a conflict. Root cause: each copy only ever advanced its own repo's rows, so every copy was stale for every other repo's rows too - 92 divergent statuses across the seven copies. Also corrects PR #25's changelog row, which was dated 2026-10-09 although the merge ran 2026-10-08 UTC. Progress table regenerated with `scripts/roadmap-progress`: **74 of 204 rows DONE, 41% readiness** | | 2026-10-08 | `feat/m3-24-country-agnostic-guard` | app | DONE: M3-24 | `M3-24` is a cross-cutting row ("all"); this PR plus a matching one in `kinlock-sdk` (PR #32 there) together complete it, so both mark it `DONE`. Added `lib/country-agnostic.test.ts`: fails if `nigeria`, `naira`, `ngn`, or `lagos` (case-insensitive, whole-word) appears anywhere under `app/`, `components/`, `lib/`, or `messages/`, excluding `*.test.ts`/`*.test.tsx`. Grounded in `AGENTS.md`'s own quick-reference wording ("Don't assume Nigeria or naira in code, tests, fixtures, or copy"), not a general country/currency-word ban, since this app's fixtures and UI deliberately exercise several real markets side by side (`messages/en.json` is included, since "copy" is explicitly in scope per that same rule). `docs/PRD.md`/`docs/ARCHITECTURE.md` are out of scope, since they legitimately narrate the pre-v0.3 "abroad to Nigeria" history. Runs via the existing `pnpm test` step already in CI, mirroring `lib/i18n/inline-strings.test.ts`'s existing pattern for a different check: no CI workflow changes needed. Verified the guard can actually fail: temporarily added a real violation to `lib/constants.ts`, confirmed it was caught with the exact term and file named, reverted (`git diff` empty), confirmed a clean pass. `pnpm lint`, `pnpm typecheck`, `pnpm test` (166/166), `pnpm build` all green | | 2026-10-08 | `feat/m3-14-fragment-leak-test` | app | DONE: M3-14 | The security headers, strict CSP, and no-third-party-scripts work was already built (`proxy.ts`, `lib/security/csp.ts`); the row's actual "Done when" (an automated test confirming the claim-link fragment never appears in any network request) didn't exist, and `lib/security/csp.test.ts` only unit-tests the CSP string builder, not real runtime behavior. Added `e2e/claim-fragment-privacy.spec.ts`: loads `/claim/11` (a real, already-existing testnet lock) with a real-shaped fragment, captures every outbound request for the page's lifetime (URL, body, headers), and fails if the reference or salt appears anywhere. Configured `playwright.config.ts` with a `webServer` (`pnpm build && pnpm start`, since `/claim/*`'s CSP differs between dev and production) and a `baseURL`, neither of which existed before. Verified the test can actually fail, not just trivially pass: temporarily injected a real leak into `useClaim.ts` (a stray `fetch` echoing the reference and salt), confirmed the test caught it with a clear diagnostic, then reverted (`git diff` confirms byte-identical to before) and confirmed a clean pass on the real code. Also caught and fixed two test-design bugs along the way: an invalid (non-`generateSalt()`-shaped) test salt silently made `parseClaimLink` throw before any of the code under test ran, and `waitForLoadState("networkidle")` never resolves on this page (it polls), so switched to a fixed wait. Verified: `pnpm lint`, `pnpm typecheck`, `pnpm test` (108/108), `pnpm build`, and the E2E test itself, run manually multiple times. **Not done here**: wiring this into CI. `.github/workflows/e2e.yml` is marked `DRAFT (ask-first: CI)` and currently just echoes a placeholder; actually running `pnpm e2e` there needs a human to approve editing the workflow file, per `AGENTS.md`'s CI/CD ask-first rule. No new secrets needed (all required env vars are the existing public `NEXT_PUBLIC_*` testnet config) |