Who Owns a Partition's Access Shape

A partition's _Policy node and its children's _Access deny assignments have exactly ONE owner. Where two components both wrote them, neither converged, and the loser was the platform.

Two components write that shape today:

Writer Lives in Reads Runs
PackageInstaller.EnsureDeclaredAccess this repository, src/MeshWeaver.PluginCatalog/ the package manifest (price, publicSegments) — and, when re-asserting from a stored manifest, preInstalled off the partition's root node whenever that root is a plugin root once per install, and on the boot repair pass
PluginGate.SeedGating MeshWeaver.Plugins, Store/Licensing/Source/ — in-mesh source the root node's PluginContent on every plugin-root activation, and on every subtree change

For a pre-installed partition the two agree: the installer publishes it fully public, and the gating reconcile's pre-installed arm retracts child denies. Nothing fights.

For a free, non-pre-installed package that declares no publicSegments, they used to disagree outright — and both were emphatic about it in their own comments:

the installer: "a free package that a catalog hands out must be readable by everyone, signed in or not."

the gating reconcile: "the cover + declared public segments are the ONLY public surface … there is no open-content tier: a product without a price simply has no self-service way in yet. (The old rule left non-purchasable plugins' content readable — guides, dashboards, even imported personal LinkedIn data were world-visible via the cover grants.)"

What the disagreement cost

The installer's legacy heal treated the pairing policy-withholding-public-read + Public/Anonymous child denies as damage left by a pre-#902 installer, retired the denies and reopened the policy. That pairing is precisely what the gating reconcile writes, on purpose, every pass. So each pass of each component undid the other:

Measured on CD run 34190841613 (2026-09-08), package Chess: the idempotence re-install retired 26 deny assignments, the reconcile immediately rewrote them, and the heal's own Chess/_Policy write starved behind the contention for 20 s and faulted — MeshNode Unknown at 'Chess/_Policy': TimeoutException. That failed the package-install idempotence gate, so Plugins: bake + seal went red and Register the publication with memex never ran: a complete image set was promoted and verified, and no installation could adopt it, because a promoted set that is not sealed is held.

The run 65 minutes earlier, on the same commit, passed while doing the same thing at a smaller amplitude — 9 denies retired instead of 26. Identical content, different count. A steady state that differs run to run on unchanged inputs is not a steady state; it is a race, and the count is just how far one writer got before the other looked.

Chess/_Policy has a longer history of write storms — measured at version 210,801 on 2026-08-25 and Chess/_Access/Public_Access at 11,899 on 2026-08-09 — but those are not this defect and should not be cited as it: PluginGate's own comments attribute them to the reconcile writing off blind subtree snapshots, which the targeted-read VerifiedWrite change then fixed. They belong here only as evidence that these particular nodes are a contended surface with more than one way to loop, which is the reason to give them exactly one owner.

The rule

A partition that is not pre-installed belongs to the gating reconcile. The installer does not heal it.

The installer still creates a fully-public _Policy where none exists — a create cannot loop — and still heals the legacy shape on pre-installed partitions, which is the case the #902 incident was actually about ("its 8 pre-installed partitions carried 136 legacy denies, while the partitions installed after #902 were correct"). What it no longer does is tear down denies that a live component is writing on purpose.

This does not settle whether a free plugin should be world-readable. It settles where that question is answered — in one place, so that the answer can be changed by changing one rule rather than by winning a race. If the gating model is wrong, it is wrong in one component.

One fact, one source: "pre-installed" is read off the ROOT

The rule above only holds if both components agree on which partitions are pre-installed, and for three days they did not. The gating reconcile reads preInstalled off the root node's PluginContent; the installer read it off whatever manifest its caller held — and the boot repair pass holds the install record's stored copy, stamped at the last install. When a package stops being pre-installed, the root changes (a sync, an install) and a record stamped earlier does not.

Measured on the public instance, 2026-09-21 → 2026-09-23, package Hosting (pre-installed → enterprise, MeshWeaver.Plugins#1959): Plugins/Hosting v110 carried preInstalled: true until the re-install at 2026-09-23 17:52Z (v111, no flag), while the Hosting root carried no flag. Hosting/_Policy reached version 238, alternating between the gate's shape (redirectOnDenied only) and the installer's legacy heal (publicRead: true over the gate's own redirectOnDenied — the exact output of PublicReadPolicy(partition, existing)); the child denies the gate wrote were retired by the heal, and the gate reported, truthfully, the gating shape is NOT STAYING for Hosting/<child>/_Access/… (MeshWeaver#5297, #5578). The flips stopped with the re-install that refreshed the record: no Hosting/_Policy version after 17:46Z.

So a caller holding a STORED manifest — the boot repair pass (the install record) and a held default install (the catalog listing) — calls PackageInstaller.ReassertDeclaredAccess, which reconciles the manifest with the partition's live root first (LiveDeclaration): a root whose content is a PluginContent (bare or namespaced $type) decides preInstalled (an absent flag is false — the serializer omits a default bool, which is how a gated root is stored); any other root, or none, leaves the manifest to decide. An INSTALL keeps calling EnsureDeclaredAccess on the manifest it carries: a delta re-asserts access before the root it is installing lands, so reading the store there would decide on the previous declaration. A "NOT STAYING" line from the gate is therefore a statement worth believing: before chasing a stale read, look for a second writer in the node's version history.

The survey mistake, which generalises

The heal's guard was not the pairing alone. It was the pairing plus a claim:

"Current code cannot produce that (the scoped branch requires declared.Count > 0); only a pre-#902 installer could."

That claim was reached by reading this repository's code, and it is true of this repository's code. The component that produces the shape is in-mesh source: a .cs node stored in a mesh package, compiled at runtime in the portal. No dotnet build here compiles it, no core test executes it, and grep --include='*.cs' over this repository cannot see it. The survey was complete for the compiler's view of one repository and empty of the thing it needed to find.

When a heal, a migration or a guard is justified by "no current code produces this", the survey has to cover the MESH — every node repository's Source/*.cs, every NodeType configuration lambda, every layout area — not one repository's compiler view. AGENTS.md states this for deletions of public surface; it applies identically to any premise of the form nothing writes this any more.