Whole-branch review — Fable 5.1 (dispatched as claude-fable-5-1), 2026-09-23
Merge cb534c3 reviewed against both parents (AGENTS.md §1.11): origin/main...origin/merge/native-testflight and origin/native/testflight..origin/merge/native-testflight. Scratch worktree; nothing edited.
VERDICT: APPROVE-WITH-NITS
Nothing main had is reverted
- main gained three commits since native forked at aab25c0: c8eea5e (
make releasestamped linux-arm64 binary), the release-target merge, and 29247af (AGENTS.md worktree rule). Diffing the merge against the native side shows exactly those two files changing (AGENTS.md +38, Makefile +17/-2) and nothing else — i.e. the merge carried main's later work and only that. - Diffing the merge against main shows no deletion of main content anywhere: the only
-lines are the credits-excludelist andcatalog.Collectmap (both extended, not replaced) andhomeRouterFixture.localrefactored intolocalAswithlocalkept. AGENTS.md is byte-identical to main. Conflict-free claim holds; there were no resolutions to judge.
The /api/native/* routes are authenticated and authorised
- All three (
GET /api/native/session,POST /api/native/search,POST /api/native/notes) are registered inside ther.Groupthat appliesa.requireSession, which fails closed (a.auth == nil-> 500), runs rastrillo'sRequireSession, thenidentity+Admit(owners list) and 404s otherwise. The whole router belowhomeMiddlewareis wrapped incsrf.Protect(origin)(home.go:72), so the JSON POSTs get the same-origin check the web forms get. nativeSearchpasses the session's own address as thesuite.SubjecttoStore.Search, the same call the web results page makes (handlers.go:628), so §4.1's per-person, per-query custody ruling is asked the same way. It strips<mark>from snippets and adds no new corpus read path.nativeNotegoes throughStore.Ingestlike the web paste path.TestNativeSearchUsesCurrentPermissionproves the state: anonymous -> non-200; native-exchanged cookie -> 200 with both docs; custody NOT_VISIBLE/UNKNOWN -> sibling doc withheld; owner removed viaSetOwners-> refused; second member searches as themselves. I read the test rather than trusting the discussion; it asserts on the custody asker struct, not on copy.- The ceremony routes (
/native/request|exchange|start|authorize) sit outside the session group by design; inside the package/native/authorizeis wrapped in therequirepassed in (memoria'srequireSession), andexchangere-runs that samerequireon a fresh request carrying only the approved cookie before minting, so a removed owner cannot complete an exchange (pinned by the package's own test, mutation-checked upstream 2026-09-12).Resumeruns abovehomeMiddleware, but it only acts onGET /with a valid 32-byte pending cookie and an existing session, and only redirects.wireNativeopens a secondsessions.Sessionsover the same table with the same Origin, so cookie names agree with auth's; the exchanged cookie is accepted byRequireSession(test above shows 200). TestNoUndeclaredRouteslists all eight; the gate keys on method+path. No new log lines; no query or note text reaches a log or error string. Spec §"Native Apple clients" added, not amended over a settled decision.
Gates
gofmt -l clean, go vet ./... clean, full go test ./... green on the stacked head (one TestWiringStartsTheCountingClock TempDir-cleanup failure appeared once, then passed three times in isolation, on a full re-run, and on origin/main — a cleanup race, not this branch). amadan ci status passed at cb534c3. I did not re-run the Swift builds or the review binary; the discussion's account of them names a real artefact (PNG written), which is the "verify it ran" check AGENTS.md asks for.
Findings (none blocking)
- [nit]
native/README.md:18vsnative/apple/Package.swift:6/Package.resolved— pin drift, and the README is the wrong one. In the local rastrillo/native checkout33ffe99(2026-09-12) is an ancestor of2295259(2026-09-13, "Refresh saved accounts before per-window account changes"), five commits apart. Package.swift pins the newer client that the last native commit ("Restore recent accounts") needs; the README should say2295259for Swift and33ffe99for Go. One-line doc fix, follow-up is fine. - [nit]
go.mod:27—github.com/google/uuidis still// indirectwhilenative.goimports it directly.make cihas no tidy check, so nothing catches it;go mod tidywill move it on the next touch. Harmless today. - [nit] Import grouping in
native.goandhandlers.goputsgithub.com/google/uuidinside the stdlib block andnativeauthat the head ofhandlers.go's block. gofmt accepts it; the rest of the package separates stdlib from the others. Cosmetic. - [note, upstream design, no change] Browser-approval phishing shape. A signed-in person lured to
GET /native/start?id=<attacker's pending id>is redirected to the approval page and, if they click the button, binds their session to the attacker's request. The page shows a code the app must match and needs an explicit click, which is the accepted mitigation for this flow in rastrillo/native (reviewed there 2026-09-12). Recording it so the decision is visible from this repo; not a memoria defect. - [note] B10 (server-minted key per request) is real and is fixed on #14, stacked on this branch — reviewed there.
Live defects found: 0. Mutations: none needed on the merge itself; the fixture tests were read against the code rather than re-mutated.