3: A RETIRED SUBCOMMAND OF `spt endpoint access` REFUSES BY NAME RATHER THAN FALLING THROUGH ITS OWN OPTIONAL POSITIONAL AND ANSWERING PLAUSIBLY. `access` takes an optional endpoint id positionally, so once `list` and `rules` stopped being subcommands (retired when the roster views replaced them) clap handed those tokens to the POSITIONAL, and the view reported `no access entities ruled for 'list'` at exit 0 -- a well-formed, confident report about an entity nobody has ever created. Found by flynn in the v0.49.0 field verify (releases#67). THE DEFECT IS NOT A MISSING ERROR MESSAGE, IT IS AN ANSWER: the sentence is TRUE of any name nobody has ruled, so it is indistinguishable from the real result for a real endpoint, and an operator or agent still carrying the old form reads it as a report and stops looking. The same failure shape as a stale carried-forward instruction -- the surface looks diligent and is wrong. MEASURED BEFORE THE FIX, and the measurement widened the filing: `rules` falls through IDENTICALLY, so the population is the retired-token FAMILY and not the one token the issue named. The node tier is NOT affected and that was checked rather than assumed: `spt node access` declares no positional at all, so clap refuses an extra argument natively. THE REFUSAL FAILS OPEN ONTO A REAL ENDPOINT. This guard is added to a path that previously accepted EVERY positional, so it is a narrowing, and a narrowing has an unruled arm: an endpoint genuinely named `list` must stay viewable. The known-target check therefore runs FIRST and wins -- a fix that made a real target unreachable would be a new defect wearing the old one's repair. Known means the access store rules it or a local perch carries the id; the store is already loaded at that point, so the check costs no read. The token match is case-folded, because an operator who typed the old verb in any casing typed the old verb. Gate: impl -- the retired-token table with its replacement guidance, the case-folded lookup, the refusal composer naming token then retirement then the working form, and the guard placed AFTER the known-target check inside the view; unit -- a decision table whose expectations are WRITTEN OUT AS LITERALS: each retired token refuses with a nonzero exit and names its replacement, an ordinary unruled endpoint id still gets the honest roster sentence (the silence the fix must not swallow), a real endpoint that happens to BE named `list` is viewed rather than refused, the casing variants refuse alike, and every token in the table is a token the current grammar does NOT accept as a subcommand -- walked against the real parser, so a token that comes BACK as a verb cannot sit here refusing itself. 11: DISCOVER IS ON BY DEFAULT: a `closed` posture at ANY mode tier -- endpoint mode, node mode, or the join-time-captured subnet mode -- no longer implies a DISCOVER deny, and the ONLY thing that closes DISCOVER is a deny that NAMES it: an access rule row, or a `per_surface` mode entry (engine-room-set at the node tier). Operator-ruled 2026-08-17 (releases#180/#181): being findable is what makes a knock -- the ask to be admitted -- possible at all, so a node that closed its posture to say 'do not talk to me' had also silently said 'and you may not ask', which was never the choice being made; the messaging-first majority pays for a discovery decision they never took. THE DEFAULT LIVES IN THE SURFACE VOCABULARY, NOT BESIDE THE CHAIN: the `surface::TABLE` row grows a `default_on` discriminant (single-source, the shape `attributable` already established) and exactly ONE method reads it -- `Modes::for_surface_governing`, which for a default-on surface resolves `per_surface` ALONE (exact key then case-fold, the releases#66 precedence rule VERBATIM) and declines the `self.all` blanket fallback. `Modes::for_surface` is untouched, so every reader that asks 'what posture is set here' keeps its present meaning; only the readers that DECIDE change. ALL THREE MODE TIERS AND `effective_posture` ROUTE THROUGH IT: the posture reading is deliberately the chain's own tiers in the chain's own order, so leaving it on the blanket would put two accounts of one fact out of agreement -- the exact failure that method exists to prevent -- and the `may_grant_node_subject` consequence is CONSISTENT rather than a hole, since post-change a node-subject DISCOVER allow admits nothing the default already does not. `MatchedTier::SurfaceDefaultOpen` is minted: a pass by surface default and a pass by chain bottom are different facts, needed by the ER/DISCOVER conjunction notice and the trust warning's pass-reason, and cheap only at mint. WHAT DOES NOT MOVE: ADR-0053's degrade (a store that cannot be read still refuses DISCOVER outright -- the default is a posture carve-out, never a licence to advertise policy we cannot read), REQ-ER-NOT-ADVERTISED's pre-chain engine-room carve-out (posture beats whitelist), and the redemption path, which is DISCOVER-free by design and node-tier-routed. Gate: doc -- the CONTEXT.md mode-tier entry stating the carve-out as ratified vocabulary, naming the one off-switch and the three things that do not move; impl -- the table discriminant, its single table-driven reader, the governing resolver, the three tiers, `effective_posture`, and the new tier; unit -- a blanket-closed node admits DISCOVER and STILL refuses MSG in one cell, an explicit DISCOVER-naming mode closes it at each of the three tiers SEPARATELY, a DISCOVER-naming deny ROW still denies, the case-folded key is honoured ON THE NEW PATH with exact-match precedence intact, the chain and `effective_posture` agree in both directions, the default-open pass is distinguishable from the implicit-open bottom, the reader is proven a function OF THE TABLE against a fixture the shipped table cannot express, and a degraded store refuses DISCOVER beside a readable blanket-closed one that admits it. 15: AN ACCEPTED RULE THAT CANNOT CURRENTLY TAKE EFFECT SAYS SO: an endpoint-scoped access mutation naming the ENGINE ROOM is accepted and, when its `DISCOVER` half cannot presently disclose anything, the accept site prints a LOUD CONJUNCTION NOTICE naming the engine room's advertisement whitelist as the other lever and its current posture. Disclosure of the engine room is an AND across two records: `DiscoverGate` checks the ER advertisement filter BEFORE the access chain (REQ-ER-NOT-ADVERTISED, ratified and unchanged by this requirement), so a rule accepted against an empty whitelist discloses NOTHING while reading as policy in force. Field case: ENLYZEAM's sole `DISCOVER` allow was ER-scoped `any-of SPT_DEV` with an empty whitelist, and every SPT_DEV peer saw only its own rows (hertz RCA 2026-08-05). THE RULED SHAPE (doyle, releases#163) IS ACCEPT-AND-TELL, and both halves of it are load-bearing: do NOT refuse the mutation -- the rule IS the conjunction's legitimate chain half and refusing it would make the only expressible half unexpressible -- and do NOT route it into the ER whitelist, because that record is the human/engine-room seat's authority and an AGENT-INVOCABLE VERB MUST NOT MUTATE IT. Kin doctrine: refused-rather-than-quietly-ignored (#5); the defect is DECLARE-SITE VS ENFORCE-SITE SILENCE, and acceptance is not the defect. WHICH HALF IS MISSING IS NAMED, not merely that one is: the whitelist names nobody; the whitelist does not name the machine this rule admits (checkable EXACTLY for a node subject, because the whitelist is node-keyed -- an advertisement lands on a peer NODE and every endpoint there reads it -- and deliberately NOT guessed for a subnet wildcard, which this site cannot enumerate into node keys, NOR for a sender-endpoint subject, which names no node at all: resolving that id to a machine would be a guess wearing a lookup, since the directory answers where this node last SAW that endpoint and not where the grant will be exercised from, so a notice built on it would name the wrong machine with full confidence. Both fall-throughs are the same non-guess doctrine and both are PINNED BY A CELL, because an unstated deliberate silence is indistinguishable from an unhandled arm to the next reader (doyle, #146 review); or the whitelist would disclose and the posture withholds ahead of it. GAPS ARE REPORTED WHITELIST-FIRST THOUGH THE GATE CHECKS POSTURE FIRST: posture is a state that changes the moment a controller attaches, the whitelist is the durable lever the operator must actually edit, and naming a transient blocker while a permanent one sits underneath sends them to fix the wrong thing. ONLY AN ALLOW IS CLASSIFIED -- a deny's `DISCOVER` half takes effect the moment it lands, so a notice there would teach that a refusal is conditional when it is not -- and a REMOVAL never reaches the notice by an explicit op guard rather than by the decision it carries, since a removal restates the tuple with the WIDENING polarity the gate wanted and not the rule's own. The record is read only when the target IS the engine room, so an ordinary rule mutation gains no file read. Gate: doc -- the CONTEXT.md entry stating the conjunction, the accept-and-tell shape, the whitelist-first order and what must not happen; impl -- the pure classifier over (target-is-ER, rule, whitelist, posture), the notice composer, and the accept-site call after the acceptance line; unit -- each gap arm classified apart, an ALLOW that the whitelist DOES name plus an online posture yields NO notice (the silence is earned, not merely unimplemented), a deny and a removal are silent, a non-ER target is silent, and the notice text names the whitelist as the other lever and the posture as its current state. 16: [--] REQ-ACL-FAIL-CLOSED required: [doc, impl, unit] stages: -doc +impl +unit -int 17: A corrupt or unreadable access store degrades CLOSED, loudly (ADR-0053 — an ADR-level flip of ADR-0009's deliberate fail-open ruling). ADR-0009 justified fail-open explicitly: the whitelist was 'a same-subnet convenience boundary layered inside subnet membership, not the system's outer wall', because every member node was the one user's own machine. The SHARED SUBNET ruling breaks that premise — member nodes now belong to different human operators and the access layer gates agents — and a boundary that evaporates when its store file corrupts is not a boundary an operator can reason about. So a store that CANNOT BE READ refuses unlisted-subject traffic on every surface rather than admitting it, and says so on the refusing node naming the store path (silence plus refusal reads as network failure and burns diagnosis time). TWO THINGS SURVIVE THE DEGRADE: the stateful-firewall reply exemption (correlation state is a separate file keyed on the endpoint's own recent outbound — a degraded node keeps its own conversations alive) and the same-node loopback Allow. THE SPLIT THAT MAKES THIS SHIPPABLE: an ABSENT store file is NOT a degrade. It is the unconfigured baseline — every fleet node today has no access.json, and reading absence as closed would refuse all unsolicited cross-node traffic fleet-wide on upgrade. The two cases split on std::io::ErrorKind::NotFound and nothing else: never-created reads as an empty store whose chain bottoms out open; any other io error or a parse failure is a degrade. Absent-is-open keeps an unconfigured fleet reachable, unreadable-is-closed makes the boundary real. Consequence accepted per ADR-0053: a personal fleet with zero configured rules now blocks unsolicited cross-node traffic if its store file CORRUPTS — the failure is loud, local, and repairable, and the alternative (silent admission on a shared subnet) is the worse lie. Existing fail-open tests are repinned to the new contract as part of this diff (a product contract change, not a test fixup). Gate: doc — ADR-0053 and the CONTEXT.md control-surface-modes entry carrying the degrade rule; impl — AccessStore::load_checked/load_checked_from returning AccessDegraded (path + cause) with the NotFound split, and the daemon gate's refuse-plus-diagnostic arm ordered AFTER the reply exemption; unit — the degrade split (absent is baseline, corrupt and unreadable both degrade, diagnostic names the path) and the gate-level proof that the same call is allowed before corruption, refused on every surface during it, still allowed as a reply throughout, and allowed again once the store is repaired without a restart. 19: A `FORK` GRANT WHOSE SUBJECT CANNOT RESOLVE THE ENDPOINT SAYS SO AT WRITE TIME -- a stated consequence, never a refusal: the rule lands exactly as typed and the operator is told that as written the subject cannot resolve the endpoint it may now fork, so the attempt fails later as an unresolvable subject. Forking a remote endpoint takes TWO surfaces and only one is obvious: `FORK` authorizes the operation, `DISCOVER` is what lets the grantee resolve the row at all. Field origin (releases#76, from the releases#29 cross-node rung): node B granted node A `FORK` alone, A could never fork, and granting the pair fixed it -- diagnosed empirically, not by reading. The operator-facing defect was never that the pair is required; it is that the requirement was INVISIBLE, since the write succeeds and reads back as typed. THE CONDITION IS THE CHAIN'S OWN `DISCOVER` VERDICT FOR THE SUBJECT, NEVER RULE-ROW PRESENCE, and the re-derivation matters because #76's own filed premise stopped being true inside this milestone: the filing reasoned that per-surface isolation closes every unlisted surface so a FORK-only grant DENIES DISCOVER, but read against the code a FORK rule simply does not COVER DISCOVER (`AccessRule::covers`), so DISCOVER fell THROUGH to the mode tiers and it was the blanket-closed posture there that refused it -- exactly what REQ-ACL-DISCOVER-DEFAULT-ON removed. A row-scan condition would therefore now be wrong in BOTH directions: silent on the explicit deny that still bites, loud on the blanket close that no longer does. ONE VERDICT, TWO CONSUMERS: the same predicate answers this write-time consequence and REQ-KNOCK-PRESCRIBES-FORK-PAIR, asked of one function rather than described twice -- this file already records what a second copy of an approval classifier cost. SUBJECT SHAPES ARE ANSWERED HONESTLY OR NOT AT ALL: a NODE subject is exact; a SUBNET WILDCARD is one rule with a verdict per machine, so the refused members are NAMED (with the count this node can SEE stated beside them, because a roster is this node's view and not the subnet's truth); a SENDER-ENDPOINT subject is deliberately NOT answered, since `FORK` carries no proven sender and such a rule matches no traffic at all -- its real defect is a different sentence owned by the attributability surface, and adding 'it could not resolve either' would rank a second problem above the first. A blanket rule (empty surface list) covers `DISCOVER` by definition and is never a gap; a DENY is never a gap. Gate: doc -- the CONTEXT.md entry stating the pair, the verdict-not-rows condition and the re-derivation that makes it necessary; impl -- the shared verdict function over a caller-held roster/subnet snapshot, the gap classifier, the consequence line, and the accept-site call; unit -- a FORK-only allow to a DISCOVER-denied node states the consequence, the same grant to a node that CAN resolve is silent (the earned silence), a FORK+DISCOVER grant is silent, a blanket grant is silent, a deny is silent, a subnet subject names the refused members apart from the seen count, and a sender-endpoint subject is silent by ruling rather than by accident. 25: WHERE AN INVOCATION CAME FROM IS DATA THE CHAIN REASONS ABOUT, NOT A SHORT-CIRCUIT BEFORE IT. Until releases#209 the chain's step 1 was `if local, return Allow(SameNode)`, so locally-authored traffic never reached `AccessStore::decide` at all. That was invisible while local traffic was ungated, and it is exactly what would have foreclosed the operator's intra-node governance surface (releases#211) — which under the ruled shape is NOT a new tier between the per-endpoint entries and the node tier, but SELF-REFERENTIAL Node subjects at the EXISTING tiers (v1's slot-3.5 reading withdrawn in full, operator-rephrased 2026-08-22). A tier the local path short-circuits past is not a tier. So `AccessRequest` carries an `Origin` SET AT EXACTLY ONE CONSTRUCTION SITE — the chain's own — and never re-derived per call site, because a discriminant with two derivations is one that will eventually disagree with itself. THE LOCAL VARIANT CARRIES THE ORIGIN ENDPOINT ID, not merely the fact of locality: same-node governance discriminates by SENDER at the EXISTING tier-1 `SenderEndpoint` subject, so the id has to reach `decide` as part of the origin rather than be re-derived at each call site. Dropping it at construction would cost a second plumbing pass through this very seam — and releases#215 measures precisely what re-derivation costs when it is left to call sites: three feeders hand this one parameter three different value shapes, and a tier-1 rule silently cannot match on one of them. It is carried from the first commit even though nothing consumes it yet, and it is carried in the VARIANT rather than beside it so that constructing a local origin without deciding what to put there is not expressible. Its provenance differs from the wire's and the type says so: a remote sender endpoint is DAEMON-STAMPED and session-proven, while a local one is SESSION-DETECTED in the authoring process — never the caller-supplied `from`, which an explicit `--from` owns and which would admit a forged subject (KNOWN-HAZARDS 7.5, the same reasoning that keeps `from` out of tier 1). The same-node allow is DEMOTED to the chain BOTTOM, where it always semantically belonged: nothing named this traffic, so it is the same-node operation it always was. THE FIX MUST WIDEN NOTHING, and two tiers would have widened it silently if left alone. (1) THE MODE TIERS ABSTAIN FOR A LOCAL ORIGIN. A mode is a blanket posture about who may reach this node FROM OUTSIDE it; `closed` has never in this codebase's history been asked about traffic authored on this machine, because such traffic never reached the function. Letting the modes answer now would convert every `closed` posture in the field into 'and also stop the agents on this box talking to each other' — the hole-punch failure arriving through a door nobody opened deliberately. (2) A LOCAL ORIGIN CARRIES NO SUBNETS. This node is a member of its own subnets, so handing them in would let a subnet-wildcard DENY — written to keep some peer group out — begin refusing locally-authored traffic the moment that traffic reached the chain. Only rules that NAME the local origin explicitly speak about it; everything else falls to the same-node bottom, so today's local behaviour is preserved BY CONSTRUCTION rather than by inspection. A STORE DEGRADE MUST NOT CLOSE THE NODE IN ON ITSELF: ADR-0053's fail-closed refusal protects the node from the network, and applied to local traffic it would stop the agents here talking to each other every time `access.json` was mid-write, so a local origin takes the chain bottom on a degrade. The engine room is unaffected by that arm — its lock is step 0, above all of this, and its own posture read is independently fail-closed. A NODE WITH NO IDENTITY reads as local: nothing can be proven remote either, since a wire arrival carries a handshake-proven hex by construction (REQ-HAZARD-WAN-ORIGIN-AUTH), so an empty origin can only have come from a local authoring path — and that is also the fail-open direction for the same-node hole-punch. THESE SEMANTICS ARE NOT DECIDED HERE. This requirement owes #211 a reachable slot and a carried subject, nothing more: the ORIGIN AXIS and the local sender it carries. NO NEW TIER AND NO MODE TWIN ARE OWED — the v1 shape (a slot 3.5 with a mode twin 6.5, whose twin would refuse local traffic rather than abstain) is WITHDRAWN IN FULL, operator-rephrased 2026-08-22: #211's intra-node governance is SELF-REFERENTIAL Node subjects at the EXISTING tiers, rules-only, with modes-only-abstain staying exactly as this requirement built it. The ER lock stays at step 0 regardless, because same-node governance is a rules surface and never a replacement for the ratified engine-room semantics. Gate: impl — the `Origin` axis on `AccessRequest` set at one site and carrying the local sender id, the mode-tier abstention, the empty local subnets, the demoted same-node bottom, and the local degrade arm (the v1 slot markers are NOT part of this requirement's evidence and are removed with the withdrawal above — a marker describing an unbuilt tier certifies a design, not an implementation); unit — the hole-punch negatives with a CONCRETE adversary rule in the store (a subnet-scoped DENY present, and a closed node mode present, and a local send still admitted under each), plus a remote send under the same store still refused, which is what proves the abstention did not simply disable the tiers rather than scope them. Kin REQ-ACL-SUBJECT-CHAIN (the tier order this inserts into), REQ-ER-INBOUND-LOCK-ALL-PATHS (the lane that needed the axis), REQ-SEC-1. 27: EVERY LOCAL FEEDER OF THE ADMISSION GATE HANDS IT A PROVEN ENDPOINT ID OR NOTHING — ONE MEANING, ONE SPELLING (releases#215): `gate::admit_local_delivery`'s `sender` is a SUBJECT (`Subject::SenderEndpoint` matches it by equality), and its three local feeders disagreed on what they put there — `spt send` passed the session-proven bare id, `spt ring` passed `resolve_from(--from)` (where an EXPLICIT `--from` BEATS session detection), and `notify` passed `NotifRow.from_id`, the COMPOSITE `{issuer}@{node_hex}`. A tier-1 rule an operator wrote for endpoint `ling`, that the CLI accepted and the view renders, therefore covered two of three local delivery verbs and COULD NEVER MATCH ON NOTIFY: it failed in the SILENT direction. `access_check_with_sender`'s own doc already forbade exactly this (NEVER pass `WanMessage.from` here -- an explicit `--from` beats session detection in `resolve_from`, so it would admit a forged subject), as does REQ-ACL-SUBJECT-CHAIN's ratified title — the code contradicted three landed documents. NOT FILED AS A SECURITY CLAIM, deliberately: REQ-ER-INBOUND-LOCK-ALL-PATHS already publishes same-user local code as outside what any spt gate holds, so a forged-subject framing would overclaim against a boundary we published ourselves. THE REMEDY IS ONE DERIVATION SITE, NOT THREE CORRECTIONS: `session_proven_sender` is threaded IN from the CLI (`ring::ring` gains a `sender_proven` parameter in `cmd_send`'s shape) rather than re-derived inside each verb, and `cmd_notify`'s INLINE RE-SPELLING of `resolve_from`'s body — the drift mechanism that let two sites diverge with neither looking wrong on its face — is replaced by a call to it. NOTIFY PASSES `None` AND TIER 1 ABSTAINS (doyle ruling 6): the chain falls to the node tier, byte-for-byte the documented behaviour for a sender who proved nothing. Splitting the composite at `@` is REFUSED as a fix — deriving a subject from a DISPLAY string is the same defect one layer down, and `from_id` legitimately carries the composite FOR RENDERING, which a fix blaming it would break. THE SMALLER PROMISE IS RECORDED, NOT SWALLOWED: a tier-1 rule about `ling` still does not MATCH on notify — it abstains BY DESIGN — and carrying a proven-sender field on the row so it could match is tracked as a separate EVAL item, never as delivered here. THE AUTHORING-TIME REFUSAL GAINS THE OTHER HALF OF THE SAME CONFUSION (doyle ruling 5): `build_access_rule` already refuses a sender-endpoint rule on a surface carrying no proven sender (`ACCESS_DEAD_SUBJECT`, REQ-ACL-SURFACE-ATTRIBUTABILITY) — it now also refuses a COMPOSITE typed into the bare-id slot (`--endpoint ling@`), which can never equal a proven bare id, in that arm's existing vocabulary. A rule that can never match must refuse at authoring time whichever half of the confusion produced it. SIDE EFFECT PINNED, NOT LEFT INCIDENTAL (doyle ruling 9): `admit_local_delivery` records the stateful-firewall reply window with `note_outbound(sender, …)` — the SAME string — so a caller-chosen id could open a reply window it never earned; one argument feeds both, so normalizing the feeders closes it BY CONSTRUCTION and a unit asserts that rather than leaving it to be re-derived. That closure is reported on the ticket carrying its own limit verbatim: its consumers were read, never measured. Gate: impl — the threaded `sender_proven` parameter + the notify `None` + the `cmd_notify` call to `resolve_from` + the composite-subject refusal; unit — the three feeders asserted TOGETHER (send and ring hand a proven bare id or None; ring's gate subject is UNCHANGED by an explicit `--from`, the arm that fails on the pre-fix spelling; notify hands None so tier 1 abstains rather than judging a display string), the composite-into-bare-slot refusal named and distinct from `ACCESS_DEAD_SUBJECT`'s surface arm, and the `note_outbound` window opening only for a proven subject. 33: ADR-0053'S FAIL-CLOSED HAS A WRITE SIDE, AND IT IS ENFORCED AT THE SAVE SEAM RATHER THAN AT THE MUTATION CALL SITES. `load_checked` split NotFound from corrupt and the gate refused a husk correctly -- but the store is READ-MODIFY-WRITE at every path that edits it, and `AccessStore::load` is `load_checked_from(..).unwrap_or_default()`, so a degrade was swallowed into an EMPTY document. The consequence inverted the guarantee (releases#57): the gate refused unsolicited traffic, and then the first `spt endpoint access` command an operator ran to investigate the refusal saved that empty document over the damaged file -- permanent rule loss, and a posture that flipped from degraded-closed to open-with-one-rule. The boundary held exactly until someone touched the CLI, and the command most likely to be run is the one an operator reaches for BECAUSE the gate is refusing. THE SWALLOWED DEGRADE IS THEREFORE CARRIED ON THE DOCUMENT (`StoreOrigin::Degraded`, `#[serde(skip)]`, never part of the record) AND `save_to` REFUSES IT. This placement is the requirement, not an implementation detail: converting the nine production mutation call sites to `load_checked` closes the population that exists today and leaves the next read-modify-write path free to re-open the class -- the same shape of defect as REQ-SEC-REPLY-EXEMPTION-SEND-LEG's per-caller write, which shipped inert because a rule satisfied by remembering to is satisfied until someone forgets. Every write of this document funnels through `save_to`, so a refusal there binds call sites that do not exist yet, and an audit is not what holds it. The call sites ARE converted as well, for the diagnostic: a refusal at the load names the store before anything is attempted, which reads better than one raised by a save the operator already believes succeeded. FRESH IS NOT DEGRADED: a document constructed in memory -- the minted baseline, a test fixture -- stands in for nothing on disk and stays writable, so mint-on-first-absence (the split that keeps an unconfigured fleet reachable) is untouched. The refusal names the store path per ADR-0053, names the cause, and names the way out, which is real rather than rhetorical: a store the operator MOVES ASIDE is re-minted empty by the next command, so starting from empty stays available as a deliberate, loud choice -- which is precisely what silently rewriting it from empty was not. Gate: impl -- `StoreOrigin` on `AccessStore` set by the loaders (`load_from` marks the swallowed degrade, `load_checked_from` marks a parsed store `Loaded`), the `save_to` refusal in the single voice of `husk_write_refusal`, and the nine production mutation sites loading checked (cli.rs `endpoint access` allow/revoke/open + subnet-mint capture + endpoint purge, api/engineroom.rs `enforce`/node-mode/node-surface-mode, daemon pairhost.rs join-time capture); unit -- a decision table over the three origins whose expectations are WRITTEN OUT rather than derived from the code under test, asserting on-disk bytes and not merely the returned Result: a husk-derived document refuses the write and the damaged file survives BYTE-FOR-BYTE, a loaded document's read-modify-write preserves the rules it did not touch, a fresh document writes, and the refusal names the store path. 43: THE PRECISE `access allow` IS A RULE VERB; THE POSITIONAL ONE IS A RULE+POSTURE VERB -- and the precise arm therefore says so at write time rather than leaving the operator to discover it. releases#196 asked whether the flag spelling should also close an endpoint's posture on its first rule (the v1 semantic `restrict_if_unset` carries, which the positional arm re-applies explicitly). RULED HORN B (doyle 2026-08-21, on a census read at b88fab2a): default-open-until-explicit-close IS the shipped T6 semantic, the mutation seam stays POSTURE-BLIND, and what was missing was never the close -- it was any signal that a rule written onto an unrestricted endpoint changes no verdict. TWO STRUCTURAL FACTS DECIDE THIS RATHER THAN A PREFERENCE. (1) A CLOSE CANNOT LIVE AT THE SEAM: `tuple_mutation` is shared by the endpoint precise arms AND the engine room's node-wide seat including its per-endpoint `--for` form, and below it `apply_mutation` is also the seam for knock-answer, redeem and the knock store -- so a close in `apply_mutation` would fire on every approved knock and every redeem, and a close in `tuple_mutation` would have the engine room closing another endpoint's posture as a side effect of writing one rule for it, while being meaningless for `MutationScope::Node` (`restrict_if_unset` is per-endpoint by construction). A posture close is a CLI-ARM opinion either way; it is not, and cannot be, a property of the seam. (2) THE CODEBASE ALREADY RULES THE INERTNESS IN BINDING GATE CODE: `may_grant_node_subject` derives the unset grant-nodes policy from `effective_posture` with the premise stated in its own comment -- open implies permitted, because an allow entry on an open surface punches no hole. Closing the posture inside the very command that policy gates would falsify that premise one statement later, so horn A would have owed a re-ruling of the grant-nodes policy and was never the one-line change it reads as. WHAT THE NOTICE IS. On an operator-driven precise allow, and BEFORE the write, the chain is ASKED -- `decide()` per covered surface, per admitted origin class, at every concrete origin there is to ask about -- and if it already allowed that subject everywhere, the acceptance is followed by a sentence saying the rule stands and changes no verdict today, naming the lever that would make it bite. NEVER A REFUSAL: pre-positioning a rule before a later close is legitimate and common, and a verb that refused it would be wrong more often than the silence it replaced. ASKED, NEVER RE-DERIVED: the condition is the chain's own verdict and not a scan of rule rows, the doctrine `node_still_sees_endpoint` and `fork_without_discover` already carry -- a row scan would miss a per-surface close, count a row naming a different subject, and become a second definition of a verdict the store owns. AN ABSENCE IS NEVER A VERDICT: a subnet subject with no roster members, and a sender-endpoint subject that resolves to no machine this node knows, have NO origin to put the question to, so they get a NAMED ABSENCE saying the question was not answered -- never the word inert, which would render a missing measurement as a finding. A subnet subject with members is inert only if EVERY member reads inert. THE FIRING POPULATION IS DECLARED AND PINNED, not left to placement: operator-driven precise ALLOW upserts on an endpoint scope, and nothing else -- the engine room's `--for` seat is excluded by the seat flag the caller already passes, removals and denies are excluded by op and decision, and knock-answer and redeem never reach this body at all. NO NOTICE ON DENY THIS LANE (doyle, ruled): deny is the fail-closed direction, an inert deny pre-positions defense at zero cost, and the narrowed-deny shape is un-censused. POSTURE-NEUTRALITY IS PART OF THE CONTRACT AND IS PINNED AS SUCH: the precise arm leaves `modes` exactly as it found them, which was true at b88fab2a and untested in either direction -- a ruling no test can falsify is a ruling that survives its own reversal. Gate: impl -- the population predicate, the chain-asked probe over resolved origins and its named-absence arm, the notice line naming the lever, and the call site before the write inside the precise arm; unit -- a decision table whose expectations are WRITTEN OUT AS LITERALS rather than derived from the predicate under test: an allow onto an unrestricted endpoint reads inert, the SAME allow shadowing a node-scope deny reads NOT inert (the hole-punch negative control -- the case the notice must stay silent on), a rule whose surfaces are only partly refused reads not inert, an empty subnet reads NAMED ABSENCE and never inert, a subnet whose members split reads not inert, and the population predicate admits the operator's precise allow while refusing the daemon seat, the removal and the deny; int -- the real binary against a real store: a precise allow leaves the endpoint's posture EMPTY (the literal partition, not a value read back from the code under test) and prints the notice, the positional spelling beside it still closes the posture and prints NO notice, and the NEGATIVE POPULATION PIN -- a rule written through the knock-approval path prints no notice at all. 47: Access rule mutation and revocation are TUPLE-SHAPED, never id-shaped: a removal restates the subject, surfaces and origin qualifier rather than naming a generated rule id, which makes it idempotent and script-safe and removes rule-id bookkeeping from the model entirely (tuples ARE the identity). Every entry records its PROVENANCE — knock-approve, code-redeem, or manual — and the access drill-down view prints the exact removal command beside each rule together with that provenance, so revocation is copy-paste from the view that showed it. Entries born of knocks or codes have no special lifecycle after birth. Authority is threefold: the owner endpoint's agent (narrowing mutations always free, widening node-subject mutations in either polarity gated by the grant-nodes policy); same-node users always (node-sovereign, the emergency lever against a bad agent grant — there is no remote rule mutation of any kind, the remote-human path is to rc into the node); and the engine-room, which may edit node-tier entries, modes AND endpoint-scope entries across the node as the node's admin command center for access. That engine-room rule-EDITING authority supersedes the earlier 'engine-room owns modes only' clause; knock-ANSWER routing is unchanged — endpoint-target knocks still land with, and are answered by, the target endpoint. Every mutation path loads the store through the FAIL-CLOSED reader: a degraded store refuses the mutation loudly, leaves the store bytes byte-identical, and — where the mutation was a knock answer — leaves the knock PENDING rather than consuming it, because a refused write must never burn a single-use answer. Gate: doc — the CONTEXT.md rule-mutation entry and the CLI reference; impl — the tuple-matching removal primitive, the provenance field, the view's per-rule removal command, and the fail-closed load on every mutation path; unit — a tuple removal is idempotent and removes only the matching tuple, provenance survives a round-trip and renders in the view, the printed removal command actually removes the rule it was printed beside, a narrowing mutation needs no acknowledgment while a widening one does, and a mutation against a degraded store refuses loudly with the file bytes unchanged and any answering knock still pending. 49: One FIRST-MATCH-WINS subject chain decides every access question, with an implicit-open bottom that preserves current fleet behavior. Order: per-endpoint sender-endpoint rule -> per-endpoint node rule -> per-endpoint subnet-wildcard rule -> node-scope node rule -> node-scope subnet-wildcard rule -> endpoint mode for the surface -> node mode for the surface -> join-time-captured subnet mode for the surface -> (nothing matched) OPEN. The stateful-firewall reply exemption and the same-node loopback Allow PRECEDE the chain and are not tiers — a degraded or locked-down node keeps its own conversations alive. Modes are the per-surface default posture for unlisted subjects (open/closed), settable at endpoint and node scope; the captured-subnet-mode table ships as a SCHEMA STUB in W1 (W2's join capture populates it — absent means the chain bottoms out open). Store schema v2 carries subject-kind entries at both scopes, migrates every v1 AccessEntry.nodes row into an equivalent (endpoint mode closed + one all-surface node Allow per listed node, so a listed node keeps access and an unlisted one loses it exactly as before), and DROPS the inert users field with the no-user-identity ruling (a shared subnet's trust unit is the node as human-proxy; 'user' is deliberately not a security identity). TIER 1 IS SCHEMA-REAL BUT INERT IN W1 (doyle ruling 2026-07-29, on todlando's pre-build finding): the design-of-record premise 'sender identity is daemon-stamped and nodes are trusted' is FALSE of its first clause today. The only wire record carrying a sender endpoint is WanMessage.from, which KNOWN-HAZARDS 7.5 (REQ-HAZARD-WAN-ORIGIN-AUTH) binds verbatim as reply-routing metadata 'never an authorization subject' — and it is caller-supplied (spt send --from; cli.rs resolve_from lets the explicit arg win over session detection), so keying a rule on it would admit a FORGED subject, the exact failure 7.5 exists to prevent. Precedent already ruled in-tree: REQ-MSG-5's classify_local_origin faces the same question and answers it with the session-proven perch id, noting '--from alone never confers an agent identity'. Independently, five of the six gate families (attach, xfer, rest, shell-link, digest) carry no sender endpoint on the wire in any form. So the kind persists, matches, and round-trips, but every W1 call site supplies None. Lighting it up needs an authenticated daemon-stamped sender field — a NEW additive field, never a repurposing of from — which is its own wave and its own REQ. Gate: doc — the CONTEXT.md endpoint-access-whitelist entry, whose false daemon-stamped premise this change corrects in the same diff, plus the control-surface-modes entry carrying the chain; impl — spt_store::access schema v2 (Subject/AccessRule/Modes/EndpointAcl/NodeAcl/CapturedSubnetModes), AccessStore::decide, the v1 migration, and the daemon gate threading origin subnet membership in; unit — the chain proven tier by tier (each tier decides only when every tier above abstains, bottom is open), the v1 verbs' polarity (allow restricts, revoke narrows without widening, open is the only widening), v1->v2 migration semantics, v2 round-trip of every construct, and TWO inertness guards — a store-level one proving the kind matches when fed and abstains when not, and a gate-level one proving no call site feeds it (that guard goes red on purpose if a later change threads a sender identity into the gate). 70: [OK] REQ-ADAPTER-ADD-SURFACE-ERRORS required: [impl, unit] stages: -doc +impl +unit -int 71: Bug #1: adapter add runs the install-as-first-update via conduct (cli.rs:6963) which on a non-zero exit prints only the exit code and DISCARDS the subprocess stdout/stderr, so the real error is invisible (the failure itself does propagate). Fix: include out.stderr/stdout in the ADAPTER_INSTALL_FAIL message (mirror run_update_post_step). Operator ruling: ALSO run the [update.post] composite step at install-time (today it runs only on explicit adapter update), so an install both surfaces detail and completes the delegated post-step. See docs/NEXT-MILESTONE-BUG-TRIAGE.md #1. 73: F-5 (REMOTE-TRUTH triage §F-5 + doyle rulings 2026-07-05): BOTH adapter acquisition verbs (spt adapter add + spt adapter update) REFUSE when the installed spt-core is BELOW the adapter's declared [adapter].min_spt_core_version floor — with an F-1 operator refusal naming the installed core, the floor, and the next action (update spt-core first). ROOT: the floor was PARSED + required (manifest.rs) but never compared to the running core — dead enforcement; and the [update].version_check knob that gated it was DOC'D-BUT-DEAD (never read by any production path — a contract lie). RULINGS: RETIRE version_check (drop the manifest field + schema + docs + the cfg(test) literals; a pre-existing manifest still setting it deserializes fine — serde ignores the unknown key, no deny_unknown_fields, so retiring is back-compatible); SEMVER-compare NOT string-compare (the 0.9.0 < 0.25.0 lexical trap); enforce on BOTH verbs; nothing installs / registry untouched on refuse (binds both verbs, no residuals). FIX: (1) a pure spt-runtime version_meets_floor(core, floor) -> bool (numeric per-component: split '.', u64, missing→0, non-numeric→0, first-diff decides, equal-when-zero-padded ⇒ satisfied) — mirrors the CLI version_is_newer parse (same numeric model, different question: freshness=strictly-newer vs floor=at-least). (2) ADD: the gate lives INSIDE registry::register (the choke point) via a register_with_core(core_version) seam register() delegates to with env!(CARGO_PKG_VERSION) — the floor check runs right after the manifest parse, BEFORE any registry write, returning the typed RegistryError::CoreFloor{adapter,core,floor} (Display = the ONE F-1 refusal both verbs surface); nothing recorded on refuse. (3) UPDATE: a PRE-SWAP peek (staged_floor_ok) extracts the staged .spt to a THROWAWAY temp, parses its manifest floor, and refuses BEFORE apply_release_crc_swap mutates the live pointer-mode home — so a refusal (or an unverifiable floor: FAIL-CLOSED) leaves the live install BYTE-UNTOUCHED; register@8932 stays as the defense-in-depth backstop for every other entry path. THE COMPARISON BASIS IS THE CALLER'S, NOT THIS FUNCTION'S (releases#159): the bare verb judges against the running CLI, and a COMPOSITE update judges against the core that run will activate — see REQ-ADAPTER-FLOOR-VS-STAGED-CORE. The FAIL-CLOSED discipline in this clause is about an unverifiable STAGED MANIFEST and is unrelated to that input. doyle bind: the register-only gate would let the crc-swap replace the live files with a floor-violating version while the record refuses (record and reality disagree — the exact contract-lie shape this milestone kills), so the pre-swap peek is the only correct answer. Red-first: perri negative repro on ADD (fresh home + synthetic low core + high-floor manifest → CoreFloor refuse, registry untouched) + the UPDATE pre-swap refuse (live home byte-untouched) + a floor-met positive control (0.25.0-on-0.25.0 installs); + version_meets_floor table incl. the 0.9<0.25 trap. === CRLF check === CRLF: 47 bare LF: 1