Skip to content

fix(app-settings): treat first-run 404s as empty, enable G17, and correct the appKey claim - #30

Merged
Adron merged 2 commits into
devfrom
fix/app-settings-first-run-404
Sep 7, 2026
Merged

fix(app-settings): treat first-run 404s as empty, enable G17, and correct the appKey claim#30
Adron merged 2 commits into
devfrom
fix/app-settings-first-run-404

Conversation

@Adron

@Adron Adron commented Sep 7, 2026

Copy link
Copy Markdown
Member

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 appKey with 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/devices200 {"devices":[]} for a key invented on the spot
  • OPTIONS /api/user/app-settings/{key}204, allow: DELETE, GET, HEAD, OPTIONS, PUT

The segment is a free-form namespace. appSettingsKey is set to interlinedlist-macos and G17 is on. The key must stay stable — changing it orphans stored settings.

2 · The defect behind it: 404 is the first-run state

  • an app key with nothing stored yet → 404
  • bootstrap?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.md still 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 — reply offers only email, follow offers 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 **
  • App target test** TEST SUCCEEDED **, 659/659
  • swift test — Kit 365, Domain 656, Persistence 135; 0 failures
  • Decision-0003 guard → 0 real import InterlinedKit in App/Features|Navigation|MenuCommands

Caveat 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

Adron and others added 2 commits September 6, 2026 22:27
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
@Adron Adron changed the title fix(app-settings): treat first-run 404s as empty, and enable G17 fix(app-settings): treat first-run 404s as empty, enable G17, and correct the appKey claim Sep 7, 2026
@Adron
Adron merged commit c49f496 into dev Sep 7, 2026
8 checks passed
@Adron
Adron deleted the fix/app-settings-first-run-404 branch September 7, 2026 06:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant