Skip to content

Commit b22ab8d

Browse files
docs(adr): ADR-0126 wall 3 holds for the access class; ADR-0131 D5 records the single-posture re-slug rule (#24446) (#25384)
Part of #24446 This is the ADR text that ruling 6259332195 on #24443 (items 1–2, 4–5) owes now that U1 and U2 have landed, written under claim 6357802112. One Tier H docs PR also carries the ADR-0131 D5 line that #25286 owes (#15212 ledger 6352526831). It contains notes only: no decision text changes and no rename spelling is added to the spec. #24446 remains open for the seat to close. Clause-②: no — expected: ADR notes only; no published surface moves. ## What changed **`docs/adr/0126-packaged-metadata-customization-model.md`**: one dated note (2026-10-23) inside §6 wall 3, which now holds in practice for the access class: - **The boot report**: U1, #25311, `54df640b38`. It lists orphans, suspected re-enablements, sharing-rule collisions, broken references and uncovered field denies, and writes and refuses nothing. - **The marketplace refusal**: U2, #25355, `448374d3f8`. It refuses on the delta only, with 409 `ACCESS_UPGRADE_CONFIRMATION_REQUIRED` before the first write. A stopped check answers 503. - **The confirm token**: `findingSet`, sent as `confirmAccessUpgrade`. It confirms exactly the findings it names, and a bare `true` confirms nothing. The confirmation is recorded in the install's own entry in the marketplace install ledger, not in the activation ledger. The CLI flag is `--confirm-access-upgrade`. - **Who is told**: - under `single`, the administrator: the Default Organization's `admin_full_access` holders and `OS_PLATFORM_OWNER_EMAIL` for the report, and the installing administrator for the refusal; - under a wall, the operator: one `error` line in the operator's log (seat ruling 6353787407, option A), and only the operator reaches the refusal, because ADR-0131 D12 answers a tenant administrator 403 first. - **The two accepted limits under §9**: a re-enablement is only suspected, and the field-deny check reads "grants the same object". - **The two doors still open**: `POST /api/v1/packages` with `overwrite` (#25382), and the catalog-name collision before the first write (#25366). - **The other classes follow after v18** (ruling item 4). **`docs/adr/0131-total-organization-ownership-no-null-organization-id.md`**: one dated note beside D5's 2026-10-22 note (#25286, `09e07c259a`; landed record 6353078154 on #25271): - Under `single`, a slug changed to `default` is admitted only on the current Default Organization. Every other context is refused 403 `PERMISSION_DENIED`. - **The removed move**: moving the Default Organization among several organizations. To move the anchor, move the grant, not the slug. - **The stranding**: after a re-slug away under several organizations, no organization can take `default` until the store holds one organization or declares a wall. The census already reports this shape as `SINGLE_POSTURE_MANY_ORGANIZATIONS`. ## Proof (head `6cddca9`) - `node scripts/pm/dispatch-gates.mjs --commands`: 20 commands derived, all 20 exit 0, with each exit captured before any pipe. - `check:doc-formula-expressions` first exited 3 (prerequisite: formula and lint not built). After the build under `os-verify-lock` it exits 0. - `--ran`: "20 derived famil(ies) accounted for — 20 run, 0 NOT-MEASURED (a DERIVED zero …)". - `node scripts/pm/check-governed-merges.mjs --branch claude/issue-24446-adr-0126-wall-3-note`: GOVERNED, landing tier H, `docs/adr/**` ×2, 64 changed lines, as expected. - **Changeset**: none. `docs/adr/**` publishes nothing and the claim allows no other file, so the PR carries `skip-changeset`. ## 维护者速读(草稿) - **改了什么**:两份 ADR 各加一条带日期的注记,不改任何决定文本。ADR-0126 墙 3 注明:访问类(共享规则、权限集、岗位、能力)升级保护已由 U1 启动报告与 U2 市场重装拒绝落地。ADR-0131 D5 注明:`single` 下只有当前默认组织能把 slug 改成 `default`。 - **为什么改**:裁决 6259332195 第 5 条要求落地阶段在 ADR-0126 写注记;#25286 留下的 D5 文本欠账记在 #15212 台账上。不写的话,读 ADR 的人分不清哪些升级入口已受保护、哪些还开着(#25382、#25366)。 - **风险与代价(含回滚)**:纯文档,不影响运行时;回滚就是 revert 这一个提交。 - **席位意见**: - **你要做的**:审读两条注记,批准并合并。 --- _Generated by [Claude Code](https://claude.ai/code/session_01HbVJUgUf5HuWXPQvAo4sFg)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent a6351bc commit b22ab8d

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

‎docs/adr/0126-packaged-metadata-customization-model.md‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,49 @@ read at each runtime's own consult point; an empty ledger changes nothing anywhe
276276
they never write the ledger — the ledger records the customer's **choices**, and no upgrade
277277
un-makes a choice. The converse holds too: disabling never blocks or edits the base's upgrade
278278
stream. The two never meet: the base's version history and the ledger share no columns.
279+
280+
> **Note (2026-10-23, ruling of record `6259332195` on #24443, items 1–2, 4–5) — wall 3 is true
281+
> in practice for the access class.** Two stages of #24446 make this wall hold for sharing rules,
282+
> permission sets, positions and capabilities. They add no spec key and no rename spelling.
283+
>
284+
> - **The boot report** (U1, #25311, `54df640b38`). At `kernel:bootstrapped`, plugin-security
285+
> compares the customer's access state with the registry the boot holds. That state is the
286+
> activation-ledger rows, the environment-authored permission sets and positions (clones
287+
> included) and the `sys_sharing_rule` rows. The report lists orphans, suspected re-enablements,
288+
> sharing-rule collisions, broken references and field denies a customer set lacks. It writes and
289+
> refuses nothing.
290+
> - **The marketplace refusal** (U2, #25355, `448374d3f8`). On a re-install over an installed
291+
> package, before its first write, `POST /api/v1/marketplace/install-local` runs the same
292+
> comparison through the `security.accessUpgradeCheck` service. It runs over the registry and
293+
> over a projection with the incoming manifest. Only the delta refuses: 409
294+
> `ACCESS_UPGRADE_CONFIRMATION_REQUIRED`, naming each finding, and nothing is written. A finding
295+
> that predates the upgrade does not block it; the boot report keeps raising it. A check that
296+
> cannot read its sources answers 503 and writes nothing.
297+
> - **The confirm token.** The refusal carries a `findingSet` token. The same call with
298+
> `confirmAccessUpgrade` set to that token installs. The install's own entry in the marketplace
299+
> install ledger, not the activation ledger, records the confirmation (`findingSet`,
300+
> `confirmedBy`, `confirmedAt`). The token confirms exactly the findings it names: if they
301+
> change, the call is refused again with the fresh set, and a bare `true` confirms nothing. The
302+
> CLI sends it as `os package install … --confirm-access-upgrade`.
303+
> - **Who is told.** Under `single`, the administrator. The boot report notifies the Default
304+
> Organization's `admin_full_access` holders and any `OS_PLATFORM_OWNER_EMAIL` address, once per
305+
> finding set. The refusal answers the administrator who installs (`manage_metadata`). Under a
306+
> wall (`group`, `isolated`), the operator. The boot report is one `error` line in the operator's
307+
> log and writes into no organization (seat ruling `6353787407`, option A). Only the operator
308+
> reaches the refusal: ADR-0131 D12 answers a tenant administrator 403 first, with no finding.
309+
> A tenant administrator is never the fallback.
310+
> - **Two accepted limits**, both following from §9 (a clone records no base and no link) and from
311+
> the bare `(metadata_type, name)` ledger key. A re-enablement is only *suspected*: nothing
312+
> records which name replaced which, so a package with several unswitched names yields several
313+
> candidates. The field-deny check reads "grants the same object", not "copied from that set",
314+
> so it can name an unrelated customer set and is a review item.
315+
> - **Two doors are still open**, each with its card. `POST /api/v1/packages` with `overwrite`
316+
> replaces an installed package without this check (#25382). A cloud-fetched marketplace
317+
> install of a package shipping a catalog name an environment-authored item holds writes its
318+
> install entry, and the next cold boot refuses; it is to be refused before the first write, not
319+
> confirmed (#25366).
320+
> - **The other classes follow after v18** (ruling item 4), as does the retirement of
321+
> `package-upgrade.zod.ts`. Until then this wall holds in practice for the access class only.
279322
4. **The paper protocol is not the model.** The three-layer
280323
`metadata-customization.zod.ts` surface (#12057 — exported, documented, zero consumers) is
281324
**superseded by this ADR as a matter of record**: nothing may build against it, and its

‎docs/adr/0131-total-organization-ownership-no-null-organization-id.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,27 @@ The `single` anchor's own disposition is #11979's (Choice 4B), which this record
455455
> outside `os serve` over a database built before 18.x, it let the deployment's only platform
456456
> administrator be banned. That anchor is retired.
457457
458+
> **Note (2026-10-22, #25286, `09e07c259a`; landed record `6353078154` on #25271) — under `single`,
459+
> no slug change makes another organization the Default Organization.** Under `single`, a
460+
> `sys_organization` slug changed to `default` is admitted only on the organization that is already
461+
> the Default Organization. Every other such write is refused 403 `PERMISSION_DENIED` by the
462+
> last-admin guard's `sys_organization` `beforeUpdate` hook, for every context: better-auth's
463+
> `organization/update`, seeds and plugins alike, `isSystem` included. A read the guard cannot
464+
> complete refuses too. Before this hook, such a re-slug made that organization the Default
465+
> Organization, and every holder of a grant it owned a platform administrator. Walled postures are
466+
> unchanged, because no grant anchors there.
467+
>
468+
> - **Removed, with no in-place replacement:** moving the Default Organization among several
469+
> organizations. Its second write, another organization re-slugged to `default`, is exactly the
470+
> refused capture. To move the anchor, move the grant, not the slug: a grant re-owned by the
471+
> Default Organization anchors.
472+
> - **The stranding it leaves.** Re-slugging the Default Organization away while other
473+
> organizations exist is still admitted when an administrator stays standing (ADR-0135 D5.2,
474+
> unchanged). After it, no organization is the Default Organization and none can take `default`,
475+
> the previous holder included, until the store holds one organization again or declares a wall.
476+
> The tenancy census already reports that shape at error level
477+
> (`SINGLE_POSTURE_MANY_ORGANIZATIONS`), with those two ways out.
478+
458479
### D6 — Managed definitions are sealed; customization is environment-level state beside them, never an edit and never per organization
459480

460481
**Two install modes.** The package **declares** the modes it permits (`installModes`, default

0 commit comments

Comments
 (0)