fleetd #424: make architect-slot identity checks read fleet.architects live #428
Closed
agent
wants to merge 0 commits from
worker/424-architect-slot-hot-038b41-7 into main
pull from: worker/424-architect-slot-hot-038b41-7
merge into: fleet:main
fleet:main
fleet:worker/fleetd-612-unita-87807e-1
fleet:worker/612-b3-mcpwirings-da2b58-3
fleet:worker/612-b2-cb185-176d3a-2
fleet:worker/612-b1-completion-457459-1
fleet:worker/612-agaps-73a926-2
fleet:worker/608-sleeps-3a64ff-3
fleet:worker/621-b4520b-1
fleet:worker/618-b83894-2
fleet:worker/fleetd-615-e05481-5
fleet:worker/lead-autocompact-5f1ab2-3
fleet:worker/fleetd-613-f85deb-3
fleet:worker/fleetd-608-flaky-nudge-test-d0c2d1-3
fleet:worker/lead-context-gauge-ad404f-1
fleet:worker/gauge-wiring-9158c1-4
fleet:worker/redeploy-slowstart-ead0e5-5
fleet:worker/charter-bytes-13668c-6
fleet:worker/rollover-outcome-291483-2
fleet:worker/589-f64303-2
fleet:worker/593-1a8025-5
fleet:worker/589-fcd2aa-1
fleet:worker/568-9fdaa2-3
fleet:worker/571-attempted-outcome-5739f7-2
fleet:worker/581-completionresolver-cas-sites-0542b7-6
fleet:worker/562-loop-health-wiring-test-99611c-5
fleet:worker/562-surface-loop-health-7df5cc-4
fleet:worker/575-waiter-cleanup-sites-62ad80-1
fleet:worker/572-answer-lock-release-46a9ae-5
fleet:worker/567-probe-channel-leak-a38fc5-6
fleet:worker/551-record-before-send-7cbf56-1
fleet:worker/561-listener-fanout-survives-a-throw-61d538-2
fleet:worker/555-redeploy-main-flow-seam-65c2f5-2
fleet:worker/556-injector-owns-registration-e027a5-1
fleet:worker/552-post-restart-mktemp-abort-bc2672-4
fleet:worker/553-onstatus-completion-leak-0da881-2
fleet:worker/550-shasum-linux-196132-1
fleet:worker/538-loop-dies-on-error-4a5eeb-6
fleet:worker/426-health-coverage-ef1fd4-4
fleet:worker/504-failed-reported-clean-3cfd66-3
fleet:worker/537-capturedlog-close-e4c437-2
fleet:worker/459-broken-link-targets-cadc17-5
fleet:worker/535-appender-leak-fe74c1-1
fleet:worker/512-part2-shutdown-detection-434701-9
fleet:worker/529-logger-level-sweep-2a5533-8
fleet:worker/528-drain-gate-call-site-5de83d-7
fleet:charter/forge-mcp-vs-token
fleet:worker/521-swap-guard-unpinned-28e931-5
fleet:worker/519-probe-test-harness-d25ab8-4
fleet:worker/525-logger-level-leak-1b4eb0-6
fleet:worker/518-fleetmcp-resolver-wiring-8ef96c-1
fleet:worker/512-drain-complete-line-7edd71-3
fleet:worker/517-abort-branch-and-jar-id-41b641-2
fleet:worker/500-9e52c9-3
fleet:worker/509-4912f4-2
fleet:worker/511-9a4b23-1
fleet:worker/493-479f45-2
fleet:worker/505-03f8b2-1
fleet:worker/492-followup-detect-unclear
fleet:worker/501-a31fa0-7
fleet:worker/498-451d1c-5
fleet:worker/494-1015ce-2
fleet:worker/492-209647-1
fleet:worker/489-001902-2
fleet:worker/480-relative-handover-path-906323-1
fleet:worker/480-b-handover-skill-45bf1f-5
fleet:worker/474-followup-source-pin-f54a55-17
fleet:worker/474-charter-check-on-reload-f54a55-17
fleet:worker/466-quarantine-repeatcount-report
fleet:worker/393-opencode-skill-seeding-71854b-13
fleet:worker/469-canonical-tool-names-2a472a-16
fleet:worker/466-quarantine-escalation-5ae9c1-15
fleet:worker/446-hot-exhausted-pattern-0af580-6
fleet:worker/464-charter-tool-name-guard-a85635-12
fleet:worker/463-listfleet-default-fails-open-f1c76c-11
fleet:worker/458-invariant-5-by-purpose-862f9a-10
fleet:worker/439-coordinator-row-gate-bc032a-8
fleet:worker/449-herdr-protocol-576015-4
fleet:worker/450-abstract-spawn-599e1c-5
fleet:worker/437-ack-refuses-177d91-1
fleet:worker/444-placement-window-feb56a-2
fleet:worker/440-helddurable-derived-d462d7-13
fleet:worker/425-rework-placement-resolve-c58ba1-9
fleet:worker/421-lead-peek-held-msgs-cdbad2-10
fleet:worker/435-fixed-policy-cap-fe11de-12
fleet:worker/422-gate-state-observability-9e79d6-11
fleet:worker/431-memberregistry-live-readers-cdbad2-10
fleet:worker/422-model-gate-spawn-c29f48-6
fleet:worker/425-default-profile-live-f55534-8
fleet:worker/415-coverage-wording-2cbf9c-5
fleet:worker/416-3ad1da-1
fleet:worker/418-588283-3
fleet:worker/deterministic-stamp-race-409-3cb7b6-10
fleet:worker/armed-reads-live-config-404-ed931f-9
fleet:worker/reply-peer-refusal-391-5a34bd-7
fleet:worker/models-allowlist-aa9e9b-3
fleet:worker/ttl-stamp-race-399-f1122f-8
fleet:worker/scrub-receipt-400-316b3e-5
fleet:worker/exhaustion-detection-395-105105-6
fleet:worker/scrub-abort-394-316b3e-5
fleet:fix/scrub-uid-abort
fleet:worker/task-scrub-517574-2
fleet:worker/t386-clock-bd5b78-4
fleet:worker/t384-scrub-813790-5
fleet:worker/t381-cc-748314-2
fleet:worker/t373-336973-2
fleet:worker/t365-3920c5-3
fleet:worker/t358-6e989b-1
fleet:worker/t355-8b321c-1
fleet:worker/fleetd-369-hermetic-git-tests-e8b19a-3
fleet:worker/fleetd-368-stale-lead-binding-f5682e-2
fleet:worker/fleetd-360-deploy-units-0d3793-1
fleet:worker/359-dead-lead-tabs-f1253b-4
fleet:worker/362-worktree-skills-c03e51-3
fleet:worker/361-coord-visibility-655144-1
fleet:362-plugin-visibility-and-drift
fleet:worker/errscan-bed2ca-2
fleet:worker/amqp-log-identity-bed2ca-2
fleet:worker/withdefaults-guard-561704
fleet:worker/sleepguard-82076d-1
fleet:worker/fd334-9ee1b6-5
fleet:worker/fd348-f1ab27-4
fleet:worker/fd335-a71c35-1
fleet:worker/fd342-174a17-2
fleet:worker/fd345-490d0f-3
fleet:worker/fleetd-337-5ec7d4-21
fleet:worker/fleetd-341-af5a6b-24
fleet:worker/fleetd-339-5ca0a2-23
fleet:worker/fleetd-338-83a4a1-22
fleet:worker/fleetd-333-281f46-18
fleet:worker/fleetd-329-11bdbb-16
fleet:worker/fleetd-330-2770fb-17
fleet:worker/fix-326-50506e-15
fleet:worker/fix-324-3e9bbf-14
fleet:worker/fix-323-b8287d-13
fleet:worker/fix-316b-bd0860-11
fleet:worker/fix-318-76ca36-9
fleet:worker/fix-317-486aec-8
fleet:worker/fix-315-ce47c5-6
fleet:worker/fix-307-275890-6
fleet:worker/fix-308-b4f664-7
fleet:worker/fix-309-ec3939-8
fleet:worker/fix-310-7a3974-9
fleet:worker/fix-302-52ad0e-9
fleet:worker/fix-298-ce1acb-8
fleet:worker/fix-297-66bd11-7
fleet:worker/fix-296-104622-6
fleet:worker/fix-293-bare-closetab-eb22b5-3
fleet:worker/fix-280-gone-ask-lapse-bca98e-2
fleet:worker/fix-290-reapidle-guard-coverage-9b0dd1-1
fleet:worker/fix-285-trust-seed-8f3565-10
fleet:worker/fix-284-backend-error-seat-85912c-11
fleet:worker/fix-282-chained-ask-e6d0bb-8
fleet:worker/fix-283-teardown-leaks-f40dfa-9
fleet:worker/fix-281-pin-handler-actions-4921ac-7
fleet:worker/audit-rendezvous-lifecycle-d072ae-2
fleet:worker/audit-health-placement-1a2476-6
fleet:worker/audit-teardown-exits-e207a5-3
fleet:worker/audit-launcher-asymmetry-27e370-4
fleet:worker/audit-rest-authz-6ca53c-5
fleet:worker/investigate-275-abandon-asking-fdef52-8
fleet:worker/fix-274-worktree-leak-b0095d-7
fleet:worker/fix-273-exhausted-pattern-9665b5-6
fleet:worker/fleetd-267-model-check-bd8068-1
fleet:worker/fleetd-131-archunit-18b834-7
fleet:worker/fleetd-266-sshagent-rename-a014ff-6
fleet:worker/fleetd-184-uid-claim-8e1f31-4
fleet:worker/fleetd-184-warn-b381ee-10
fleet:worker/fleetd-184-docs-be1d12-9
fleet:worker/fleetd-257-9bf010-7
fleet:worker/fleetd-103-23a113-6
fleet:worker/fleetd-247-342356-5
fleet:worker/fleetd-116-04dea8-4
fleet:worker/fleetd-252-a830e0-3
fleet:worker/fleetd-111-7e8673-9
fleet:worker/fleetd-155c-f8ef4b-8
fleet:worker/fleetd-176-b928ca-3
fleet:worker/fleetd-249-7a7878-2
fleet:worker/cb248-composition-root-b-9acdf7-15
fleet:worker/cb148-envrc-default-fa6c82-12
fleet:worker/cb201-unit5-wiring-6c12e6-8
fleet:worker/cb241-fallback-echo-1175e9-11
fleet:worker/cb149-trust-dialog-2392a5-9
fleet:worker/cb134-148-overlay-visible-c9b986-10
fleet:worker/cb234-session-id-keyed-04e1fc-1
fleet:worker/cb201-unit3-nudge-abdf5c-6
fleet:worker/cb201-unit2-policy-c1102c-5
fleet:worker/cb201-unit4-outcome-a13bfa-7
fleet:worker/cb201-unit1-classifier-91b9b1-4
fleet:worker/cb201-227-refine-831980-3
fleet:worker/cb175-model-readback-0f085f-1
fleet:worker/cb222-charter-tmpdir-17f013-1
fleet:worker/cb226-architect-slot-race-cd3aa8-3
fleet:worker/cb224-worktree-root-group-024523-2
fleet:worker/cb-123-role-demotion-c600f7-2
fleet:worker/cb-219-opencode-roots-1f677e-1
fleet:worker/cb214-claude-session-id-b9eab4-4
fleet:worker/cb213-zdotdir-wrong-process-dd6de4-3
fleet:worker/cb211-exhaustion-classification-9546e0-2
fleet:worker/cb137-ambiguous-task-4df3d8-4
fleet:worker/cb209-agentsessionid-4dfdb6-2
fleet:worker/cb185-hostenvnames-2692b5-3
fleet:worker/cb206-opencode-sqlite-128718-2
fleet:worker/cb185-worktree-group-fc0c99-1
fleet:worker/cb-137-ask-ticket-e7760c-2
fleet:worker/cb-172-broker-uri-d36ae4-4
fleet:worker/cb-175-model-readback-76ead6-3
fleet:worker/cb-161-pane-ancestry-293510-1
fleet:worker/cb-164-rebase-885863-8
fleet:worker/cb-164-empty-scrape-false-success-1a80af-3
fleet:fix/cb-197-ticket-ttl-from-completion
fleet:worker/cb-189-remote-url-coverage-4692f3-1
fleet:worker/cb-185-blockers-027756-4
fleet:worker/cb-192-gap-log-11b631-2
fleet:worker/cb-633-fix-5f4396-3
fleet:worker/cb185-router-d6436d-3
fleet:worker/cb185-router-routing-gaps-9e9d33-3
fleet:worker/cb185-paneids-992586-2
fleet:worker/cb-633-allow-list-union-ed374b-1
fleet:worker/cb-157-credential-in-remote-url-496e44-2
fleet:worker/cb-641-health-herdr-evidence-8f1f54-6
fleet:worker/cb-640-health-msg-evidence-99c9cd-1
fleet:worker/cb-642-fleets-status-skill-bbbc40-5
fleet:cb-634-ide-mcp
fleet:worker/lead-comms-wiring-c014b9-7
fleet:worker/lead-mailbox-c19577-6
fleet:worker/autocompact-window-82bc2f-5
fleet:worker/cb-634-probe-18056f-4
fleet:worker/cb635-broker-urienv
fleet:worker/cb-632-config-retry-8e0efa-7
fleet:lead/cb-622e-claude-md
fleet:lead/cb-622-followup
fleet:worker/cb-622a-165dff-1
fleet:lead/cb-622d-opencode-mount
fleet:worker/cb-622b-717c67-2
fleet:worker/cb-622c-ab7759-3
fleet:worker/cb-617b2-20ca4b-3
fleet:worker/cb-617a-5c2f4a-1
fleet:worker/cb596-4e49ef-3
fleet:worker/cb586-10500c-1
fleet:worker/cb-606-b9343a-25
fleet:worker/cb604-1445f8-24
fleet:worker/cb582-477374-21
fleet:worker/cb584-8c2281-22
fleet:worker/cb600-e6b9a9-20
fleet:worker/cb602-ce257f-19
fleet:worker/cb601-b42837-18
fleet:worker/cb598-6c7ba7-17
fleet:worker/cb599-740fe4-16
fleet:worker/cb597-282224-15
fleet:worker/cb590fix-185e9a-10
fleet:worker/cb528-recovery-race
fleet:worker/cb594-96bead-8
fleet:worker/cb590-916766-2
fleet:worker/cb527-997d99-3
fleet:worker/cb592-env-leak-3cbf9c-1
fleet:worker/cb588-async-ticket-nudge-3218f7-5
fleet:worker/cb578b-9dcb13-6
fleet:worker/cb581-d24826-5
fleet:worker/m2-u5-ef8c42-15
fleet:worker/cb578a-516499-2
fleet:worker/cb576-01a04b-17
fleet:worker/cb579-lead-tab-acba06-20
fleet:worker/cb580-terminal-health-ed6058-21
fleet:worker/cb577-f36fdc-18
fleet:worker/cb573b-3db06f-16
fleet:worker/cb568c-f36fdc-18
fleet:worker/cb568-drop-cause-c3ac1c
fleet:worker/cb575-cancelled-notification-c3ac1c
fleet:worker/m4-sol-a2cbec-3
fleet:worker/cb574-async-ask-c3ac1c
fleet:worker/cb573-health-model-8ca857-14
fleet:worker/cb572-unknown-target-7f2e35-13
fleet:worker/u4-700706-9
fleet:worker/u3-b9fcb6-6
fleet:worker/u2-ef5b68-4
fleet:worker/u1-469dce-1-clean
fleet:worker/u1-469dce-1
fleet:worker/cb-564-health-events-70cf7e-2
fleet:worker/cb-565-recycle-drops-role-98e58f-3
fleet:worker/cb-563-missing-reply-df2866-1
fleet:worker/cb-562-readiness-gate-silent-6c23c9-3
fleet:worker/cb-560-architect-presence-da8155-1
fleet:worker/cb-561-architect-silent-off-a71cab-2
fleet:worker/cb-548-bind-architect-slot-fe1b8c-1
fleet:worker/parity-overlay-settings-5fb711-1
fleet:secrets-central-store
fleet:cb-559-hot-key-correction
fleet:cb-557-fleet-role-pools
fleet:worker/cb-553-maxload-explicit-spawn-305ee3-6
fleet:worker/cb-551-idle-lead-heartbeat-f1633c-1
fleet:worker/cb-544-drain-preserves-worktree-925fad-3
fleet:worker/cb-552-docs-sync-1cb9cf-4
fleet:worker/cb-548-rendezvous-guard-rebased
fleet:worker/cb-548-rendezvous-guard-116b53-10
fleet:worker/cb-548-authz-v2-586df6-8
fleet:worker/cb-548-authz-264363-5
fleet:salvage/cb-528b-codex-home
fleet:salvage/cb-528a-codex-launcher
fleet:CB-518-primary-flow
fleet:feature/peer-launcher-spi
fleet:cb-103-injector
No Reviewers
Labels
Clear labels
blocked
needs-live-proof
ready-to-delegate
silent-default
Cannot start until something else lands. The body says what.
Merged and green, but never shown working on the running daemon. Not the same as done.
Scope, files and acceptance criteria are written. A worker can be briefed from the body alone.
A feature that compiles, passes tests, and ships turned off. Nine recurrences and counting.
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: fleet/fleetd#428
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "worker/424-architect-slot-hot-038b41-7"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
fleetd #424 — revoking an architect slot does not revoke it
The defect
MemberRegistryflattenedfleet.architectsinto a frozen map at construction and neverre-read config.
reserve()/requireSlotFor()(the two spawn-side checks) matched against thatfrozen snapshot forever, so removing an architect slot from config never actually refused a later
spawn asking for it — an architect is materially more privileged than a worker, and this left a
"revoked" privilege open with no warning.
ConfigRefalso told the operator anarchitects-onlyedit was "already applied" through the wrong consumer (
CompositePeerLauncher, which only reads itfor placement, not identity).
The fix — binding semantic
Implemented the lead's ruling: config governs what may be bound next; it never retroactively
unbinds a live session.
MemberRegistry.live(Supplier<FleetConfig.Fleet>)— a new static factory — re-flattensfleet.architects/developers/reviewerson everyslots()/slotsFor()call.reserve()andrequireSlotFor()both read throughslotsFor, so both are now live with no restart. The oldsingle-arg
MemberRegistry(FleetConfig.Fleet)constructor is kept (frozen) for tests and for afixed/code-built config.
unbound, nothing is killed.
CallerResolver.resolvecallsroleForSlot/nameForSloton every request from a bound pane to confirm it is still an architectslot — every successful
bindnow caches the slot'sEntryinto a newboundEntriesmap.roleForSlot/nameForSlot/profileForSlot/isSlotfall back to that cache when the slot is nolonger live in config.
unbindclears the cached entry in the same critical section it clears thebinding, so a slot that's both unbound and gone from config stops being "known".
Fleetd.javanow wiresMemberRegistry.live(() -> config.get().fleet())instead of the frozennew MemberRegistry(cfg.fleet()).ConfigRef: corrected thefleet.leaderssplit-key message and the class doc's Hot bullet —architectsis hot for two independent consumers now (CompositePeerLauncherfor placement,MemberRegistryfor identity), not only the one the message used to name. Anarchitects-onlyedit still reports nothing beyond "config reloaded" — that's honest now that the key really is
fully hot for both consumers, so no change was needed to that reporting branch itself, only to the
wording of the
fleet:split message.Tests (
MemberRegistryLiveTest, new)Real
ConfigRef.reload()against a@TempDirfile, assertingOutcome.applied(), never twofrozen registries compared in memory:
requireSlotForrefuses a removed slot / allows an added slot (2 tests, both directions).reserverefuses a removed slot / allows an added slot (2 tests, both directions, testedseparately from
requireSlotFor).snapshot()unchanged,AND
roleForSlot/nameForSlotkeep answering ARCHITECT/"designer" — whilerequireSlotFor/reserverefuse the same profile for a new spawn.Mutation-proofing (each applied, run, confirmed failing, then reverted)
requireSlotForto a construction-time snapshot only →requireSlotForRefusesAProfileWhoseSlotWasRemovedByReloadfailed ("ExpectedIllegalArgumentException to be thrown, but nothing was thrown") and its added-slot mirror failed
too (unexpected
IllegalArgumentException).reservethe same way →reserveRefusesAProfileWhoseSlotWasRemovedByReloadfailed the same way, andreserveAllowsAProfileWhoseSlotWasAddedByReloaderrored with "no free architect slot for profile'sonnet'".
boundEntriesfallback so a removed slot's identity isslots()-only →anArchitectAlreadyBoundToASlotSurvivesTheSlotsRemovalByReloadfailed onroleForSlot("architect:designer"):expected: <ARCHITECT> but was: <null>.All three restored before the final build below.
Build
mvn clean install(fromfleetd/, no root pom): Tests run: 1510, Failures: 0, Errors: 0,Skipped: 0 — BUILD SUCCESS.
Out of scope (per ticket)
fleet.developers/fleet.reviewersinsideMemberRegistrystay dead data — untouched.defaultProfile) —CompositePeerLauncher.defaultProfile,FleetMcp's"default"field,SessionManager.acquireWithWorktree— untouched.ConfigRefcoverage-test exclusion was added.Caveat for review
entryFor()(backingroleForSlot/nameForSlot/profileForSlot) reads live config first andfalls back to the cached bound entry only when the slot is no longer live. If a live config entry
changes shape for the same qualified key while a session is bound under the old identity (as
opposed to being removed outright), the live value wins — that case is not addressed here; the
ticket's scope was specifically survival-through-removal.
Lead review — not merging yet, and the change I want is my own ticket's fault
Verified on
ce74e16myself:mvn -f <abs>/fleetd/pom.xml clean install→Tests run: 1510, Failures: 0, Errors: 0, Skipped: 0,BUILD SUCCESS, 0 compile errors.What is right, and I am keeping it:
MemberRegistry.live(() -> config.get().fleet()),slots()re-reading through the supplier on every call, the private-constructor-not-an-overload reasoning (aFleetand aSupplier<Fleet>overload really are ambiguous for a literalnull), all four reload-direction tests, and theConfigRefjavadoc and reload-message corrections. That last part matters as much as the code: the old message told the operatorarchitectswas "already applied" when it was not.What has to change:
boundEntries.The PR keeps a live architect's full identity after its slot is removed from config — role and name both survive via the cache. So removing a slot and reloading does not revoke anything from the running session; it only stops future binds.
This ticket is titled "Revoking an architect slot does not revoke it". Shipping that leaves the headline defect half-closed.
This is my wording, not the implementer's. The ticket says "a rebuild must not drop or invalidate an existing architect's binding" and asks for "a test that an already-bound architect survives the rebuild". The implementer did exactly that. I had conflated two different things:
terminalToSlot, which must survive orunbindbreaks and slots leak;Only the first needs to survive.
Ruling: the binding survives, the privilege does not. I checked the alternative before ruling rather than assuming it was clean: with no cache,
roleForSlotreturns null andCallerResolver.resolveatCallerResolver.java:220falls through toPrincipal.worker(c.terminal(), c.pid()). An immediate, clean demotion, andunbindstill works because it never consultsisSlot.Why this direction. If an operator believes removal revokes and it does not, a privilege stays open silently — the exact defect class of this ticket. If an operator believes removal only affects new spawns and it actually revokes, the session is demoted and its next privileged call fails loudly. The charter says to fail toward the recoverable error, and a demoted member can still end its turn, because
fleet_replyis allowed to any member as itself. I have asked the implementer to confirm that last point inAuthzand to stop and ask if it is not true, since it would change the ruling.Briefed: delete
boundEntriesand theentryForcache fallback, revertisSlottoslots().containsKey(...), rewrite the class doc's binding rule, and check whether anything else cross-referencesslots()againstterminalToSlot(snapshot()in particular). Tests: the demotion asserted throughCallerResolver.resolve, not throughroleForSlot— the seam is not the caller — plus two tests for what must not change (the binding still occupies the slot, andunbindstill succeeds). Mutation proof per direction, including the mirror: demotion must not fire for a slot that is still in config.Merged by hand as
7667727onmain. The forge could not do it: this branch and #429 both rewrote the same "Hot" bullet inConfigRef.java, so the merge conflicted on content.Verified on the merged tree:
Tests run: 1535, Failures: 0, Errors: 0, Skipped: 0, 0 compile errors, BUILD SUCCESS.Two stale claims fixed during the merge. The second commit
7f672f0reversed the behaviour but never touchedConfigRef, whose whole job is to tell the operator what a reload does. It still said:Both were the pre-fix statement — that is, the bug this ticket exists to fix, written down as documentation. They now say removal revokes ARCHITECT on the bound pane's next request, and only the slot occupancy survives.
Mutation of three halves your own proof did not cover. Your three mutations plus the mirror all killed exactly the right test — that part is clean. So I mutated the sibling reads this PR also made live. Each pointed at a frozen snapshot taken at construction; each printed its method name and line, and the live-reader count went 5 -> 4:
profileForSlotfrozennameForSlotfrozenisSlotfrozenAll three passed, so none of them is pinned.
profileForSlotis the one that matters: the spawn lifecycle reads it to choose the architect's backend, so if it were ever frozen again, a reload that repoints a slot to another profile would spawn on the old one and every test would stay green.isSlotgatesbind/reserve, and it can lie without a single test noticing.Not a merge blocker — the ticket's own fix is well pinned. Filed as a follow-up.
Pull request closed