Repository navigation
fix: verify membership writes, adapt the toolbar to its window, and give panes their own state - #12
Merged
Merged
Conversation
…ive panes their own state The remaining audit findings, plus the toolbar complaint that started this. Membership writes are now proved, not assumed --------------------------------------------- Every configuration write reads back, because Apple acknowledges an out-of-spec profile with a 2xx and silently drops it. Membership writes had no such check: "attached" was reported on the response code alone, and a member inlined into a blueprint create was reported attached purely because the create returned 2xx. ApplyBlueprints now re-reads the relationships it wrote and confirms the tenant holds what each write claimed. The cost is per RELATIONSHIP, not per member — attaching twenty configs to one blueprint is ONE extra GET. A verification whose cost scaled with the number of writes would discourage its own use on an API that rate-limits this hard. A read that FAILS downgrades the row to NOT VERIFIED rather than failing it: a GET that errored says nothing about the POST, the same posture the configuration read-back takes. get blueprint was the last caller swallowing relationship errors ---------------------------------------------------------------- `rels[rel], _ = ...` rendered a 429 or a 403 as an empty member list, in the table AND in the --json abgui's inspector consumes — an affirmatively false answer from the screen you open to see what a blueprint carries. ListPackages is permission-gated and legitimately 403s, so this fired on a real condition. `status config`, `status device` and `apply -f` were fixed for this already. The toolbar adapts instead of always shouting --------------------------------------------- Labelling every control was the first answer to "hovering says no text", and it traded one problem for another: the toolbar outgrew the window and clipped its last item at ~1090px. Width is finite, so it is spent where a word resolves an ambiguity — `Verify Configs` and `Apply…` wear near-identical checkmarks while one reads local files and the other writes the tenant, and `New`/`Delete` are generic glyphs on tenant writes. Refresh, Workspace, Edit and Membership keep a legible glyph plus a tooltip. Every control has help text either way, which is what the original complaint was actually about. Panes own their load state -------------------------- One `isLoading`/`loadError` shared by every screen meant one screen's fetch was another screen's spinner and another screen's error: a slow Devices fetch spun Configurations, and an OS Releases failure — raisable from inside a device detail SHEET — rendered on whatever list sat behind it. A generation counter existed purely to stop overlapping reads clobbering each other's message. Each pane now answers `loading(_:)`/`failure(_:)` for itself. The shared `isLoading` is gone entirely: the plan has `isPlanning`, the seed has `isSeeding`. The dashboard's pass reads back the failure of the load it just ran instead of whatever last touched a shared slot. Main-actor file I/O ------------------- ArchiveFileView read the profile with a synchronous String(contentsOf:) INSIDE the view body — up to Apple's 1 MiB cap, re-executed on every body evaluation. RunLogsView scanned the directory, probed fifty file tails and read the selected transcript (capped at 5 MiB) synchronously from `.task`. Both now read off the main actor into state, and the log reader drops a result whose selection changed while it was loading. Go: build, vet, gofmt, full suite green, 3 new tests covering an unapplied membership write, the one-read-per-relationship budget, and a failed re-read being "not verified" rather than failed. Swift: macOS CI is the gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
The remaining audit findings, plus the toolbar complaint that started this session.
Membership writes are now proved, not assumed
Every configuration write reads back, because Apple acknowledges an out-of-spec profile with a 2xx and silently drops it. Membership writes had no such check — "attached" was reported on the response code alone, and a member inlined into a blueprint create was reported attached purely because the create returned 2xx.
ApplyBlueprintsnow re-reads the relationships it wrote and confirms the tenant holds what each write claimed.The cost is per relationship, not per member: attaching twenty configs to one blueprint is one extra GET. A verification whose cost scaled with the number of writes would discourage its own use on an API that rate-limits this hard — there's a test pinning that.
A read that fails downgrades the row to
NOT VERIFIEDrather than failing it: a GET that errored says nothing about the POST, the same posture the configuration read-back takes.get blueprintwas the last caller swallowing relationship errorsrels[rel], _ = ...rendered a 429 or 403 as an empty member list — in the table and in the--jsonabgui's inspector consumes. An affirmatively false answer from the screen you open to see what a blueprint carries.ListPackagesis permission-gated and legitimately 403s, so this fired on a real condition.The toolbar adapts instead of always shouting
Labelling every control was the first answer to "hovering says no text", and it traded one problem for another — the toolbar outgrew the window and clipped its last item at ~1090px.
Width is now spent where a word resolves an ambiguity:
Verify Configs,Apply…— near-identical checkmarks, one reads local files, the other writes the tenantRefresh,Workspace,Edit,MembershipNew,Delete— generic glyphs on tenant writesEvery control has help text either way, which is what the original complaint was actually about.
Panes own their load state
One
isLoading/loadErrorshared by every screen meant one screen's fetch was another screen's spinner and another screen's error: a slow Devices fetch spun Configurations, and an OS Releases failure — raisable from inside a device detail sheet — rendered on whatever list sat behind it. A generation counter existed purely to stop overlapping reads clobbering each other's message.Each pane now answers
loading(_:)/failure(_:)for itself. The sharedisLoadingis gone entirely — the plan hasisPlanning, the seed hasisSeeding. The dashboard reads back the failure of the load it just ran rather than whatever last touched a shared slot.Main-actor file I/O
ArchiveFileViewread the profile with a synchronousString(contentsOf:)inside the view body — up to Apple's 1 MiB cap, re-executed on every body evaluation.RunLogsViewscanned the directory, probed fifty file tails and read the selected transcript synchronously from.task. Both now read off the main actor into state, and the log reader drops a result whose selection changed while it was loading.Verification
Go: build, vet, gofmt, full suite green. 3 new tests — an unapplied membership write must not read as done, the one-read-per-relationship budget, and a failed re-read being "not verified" rather than failed. Swift: macOS CI is the gate.
🤖 Generated with Claude Code