fix(app-settings): treat first-run 404s as empty, enable G17, and correct the appKey claim - #30
Merged
Merged
Conversation
A read-only live probe settled the G17 "register an appKey" prerequisite and
exposed a real defect behind it.
The appKey needs no registration. The gap definition said to "pick and
register an `appKey` with the backend owner", but the live API treats the
segment as a free-form namespace: `GET /api/user/app-settings/<invented-key>/devices`
answers `200 {"devices":[]}` for a key the server has never seen, and
`OPTIONS` on the parent reports `allow: DELETE, GET, HEAD, OPTIONS, PUT`.
`appSettingsKey` is therefore set to `interlinedlist-macos` and the feature
is on. The key's only job is to keep this app's settings separate from other
companion apps on the account, so it must stay stable — changing it orphans
whatever was stored under the old one.
The defect: 404 is the ordinary first-run state, not a failure. An app key
with nothing stored yet 404s, and `bootstrap` answers `404 {"source":"none"}`
for a device the server has not seen. Both reads threw, so a fresh install
would have shown an error in the Devices pane and on every settings read
instead of empty settings — on exactly the first launch the feature exists
to serve. The three read paths now map 404 to an empty bag / an empty
snapshot flagged `isNewDevice`, which is the signal the caller registers
off. Every other status still propagates: a 401 or a 500 is a real failure
and must reach the UI.
This also retires a test that asserted the opposite (that a 404 must throw
as an unknown-key rejection); it now covers 403, the meaningful auth failure.
Live probing additionally confirmed G18/G19/G20 shapes match the DTOs
exactly, so G20's autocomplete hedge is downgraded from "unpinned" to
"verified wrapped, tolerant anyway". G18's per-event channels genuinely vary
(`reply` offers only email; `follow` offers email+push but not inApp), which
validates rendering only the channels the server sends — a fixed three-switch
pane would show dead controls on six of the eight events.
Domain 646 -> 650 tests. Full gate green: build, App 660, Kit 342,
Persistence 135, Decision-0003 guard 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpH4PQZhdYiCfsk5PQmwzq
The G17 entry told every future reader to "pick and register an `appKey`
with the backend owner before building". That instruction is wrong, and it
cost real time: it was reported to the owner as a hard blocker before anyone
checked it. A read-only probe disproved it in about two minutes.
No registration mechanism exists. The segment is a free-form namespace —
`GET /api/user/app-settings/<invented-key>/devices` answers
`200 {"devices":[]}` for a key the server has never seen, and `OPTIONS` on
the parent reports `allow: DELETE, GET, HEAD, OPTIONS, PUT`. The correction
is recorded inline rather than by deleting the sentence, so the next reader
sees both the original claim and why it does not hold.
Also records what the same probe established: 404 is the ordinary first-run
state on these routes, the G18/G19/G20 response shapes are now live-verified
rather than correct-by-construction, and G17's populated payloads remain
unverified because nothing is stored under the key yet.
G17-G20 marked shipped (PR #25 + #30).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpH4PQZhdYiCfsk5PQmwzq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #25. The fix commit was pushed to that branch a few minutes after it merged, so it never reached
dev— this PR carries it over, rebased, plus the doc correction it implies.1 · The appKey needs no registration
The G17 gap definition said to "pick and register an
appKeywith the backend owner before building", and I reported that to the owner as a hard blocker. A read-only probe disproved it in about two minutes:GET /api/user/app-settings/zzz-not-a-real-app-xyz/devices→200 {"devices":[]}for a key invented on the spotOPTIONS /api/user/app-settings/{key}→204,allow: DELETE, GET, HEAD, OPTIONS, PUTThe segment is a free-form namespace.
appSettingsKeyis set tointerlinedlist-macosand G17 is on. The key must stay stable — changing it orphans stored settings.2 · The defect behind it: 404 is the first-run state
404bootstrap?deviceId=<uuid>for an unseen device →404 {"source":"none"}Both reads threw. A fresh install would have shown an error in the Devices pane and on every settings read — on exactly the first launch the feature exists to serve. The three read paths now map 404 to an empty bag, or an empty snapshot flagged
isNewDevice(the signal the caller registers off). Every other status still propagates: a 401 or a 500 is a real failure and must reach the UI.This retires a test that asserted the opposite — that 404 must throw as an unknown-key rejection. It now covers 403, the meaningful auth failure.
3 · Doc correction
work-consolidation.mdstill instructed every future reader to register an appKey. That wrong sentence had already cost time once, so the correction is recorded inline next to the original claim rather than by deleting it — the next reader sees both what was believed and why it does not hold. G17–G20 are marked shipped.Also confirmed by the probe
G18/G19/G20 shapes match the DTOs exactly, so they are no longer correct-by-construction:
/api/user/sessions→{sessions:[{id, deviceLabel, createdAt, lastUsedAt, isCurrent}]}/api/tags/trending→{tags:[{tag, count, lastUsedAt}]}/api/tags/autocomplete→{tags:[…]}(wrapped; decoder stays tolerant)/api/user/notification-preferences→{events:[{key, label, description, channels{…}}]}G18's per-event channels genuinely vary —
replyoffers only email,followoffers email+push but not inApp — validating render-only-what-the-server-sends. A fixed three-switch pane would show dead controls on six of eight events.Still unverified: G17's populated payload shapes. Nothing is stored under the key yet, so only the empty and 404 paths are exercised; the tolerant decoders and their "tighten after a live probe" comments stay.
Verification
xcodebuild build→** BUILD SUCCEEDED **test→** TEST SUCCEEDED **, 659/659swift test— Kit 365, Domain 656, Persistence 135; 0 failuresimport InterlinedKitinApp/Features|Navigation|MenuCommandsCaveat on the record: the first App run on this branch reported 2 failures; four subsequent runs were clean at 659/659. I did not capture the failing run's log and cannot name the two tests. Treat it as an unexplained flake, not a clean bill of health — several Claude sessions build against the same DerivedData on this machine, which is a plausible cause.
🤖 Generated with Claude Code