From 7f5f924896d92cd915342a3f16153845ee5029cb Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 09:55:35 +0000 Subject: [PATCH 1/2] fix(spec): stop verbatim-quoting a rotted proof-registry reason in the sharing_rule ledger note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `_note` of `packages/spec/liveness/sharing_rule.json` quoted the `declarative-rbac-seeding` entry's `blockedReason` VERBATIM. PR #18797 (`ac720a9865`) rewrote that reason, so the quoted string stopped existing in the very file the note sends a reader to. The substance was never wrong — the seeding does falsify the entry's original premise — so this replaces the quotation rather than the judgement: cite the registry and the stable `declarative-rbac-seeding` id, state the substance in the note's own words, and quote nothing. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude --- packages/spec/liveness/sharing_rule.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/spec/liveness/sharing_rule.json b/packages/spec/liveness/sharing_rule.json index 3586ad84bce..02be7286e47 100644 --- a/packages/spec/liveness/sharing_rule.json +++ b/packages/spec/liveness/sharing_rule.json @@ -1,6 +1,6 @@ { "type": "sharing_rule", - "_note": "SharingRuleSchema (packages/spec/src/security/sharing.zod.ts) — `SharingRuleSchema = CriteriaSharingRuleSchema`, the one authorable rule form. Seeded 2026-09-17 (#18582): the SECOND of the three PENDING_GOVERNANCE debts #18133 declared when PR #18581 widened the governance denominator from the registered kinds to `authorableTypes()`; `connector` and `analytics_cube` are still owed on that card. NOT a registered metadata KIND — it is bound in `UNREGISTERED_KIND_SCHEMAS` (#6245) and reaches this walk through `getMetadataTypeSchema`'s unregistered-kind fallback, so the ledger governs it while `listMetadataTypeSchemaTypes()` still does not enumerate it. THE SHAPE FACT THAT DECIDES EVERY ROW BELOW: the authoring shape is not the enforced shape. ADR-0057 D6 makes the RUNTIME row canonical (`sys_sharing_rule`: `object_name` + `criteria_json` + `recipient_type`/`recipient_id` + `access_level`) and `bootstrapDeclaredSharingRules` TRANSLATES each authored key into it at boot — nothing re-parses `SharingRuleSchema` at enforcement time. So every consumer cited below reads a COLUMN that a producer had to populate, and every row therefore carries a `producer` (#4837) naming the threading site: a consumer citation alone would be the `seed.env` shape, where the mechanism was right and nobody supplied the input. PREVIEW READ POINTS ENUMERATED (the #7131 mechanical rule, objectui @dda8f381): `registerBuiltinPreviews()` in packages/app-shell/src/views/metadata-admin/previews/index.ts registers twenty types and `sharing_rule` is NOT one of them — this type has no registered metadata-admin preview. Recorded rather than skipped, because \"the type has no registered preview\" is the sentence a later sweep needs. What objectui DOES consume is the whole SHAPE: `clientValidation.ts`'s `AUTHOR_SHAPE_ONLY_TYPES` gates the metadata-admin CREATE door on `SharingRuleSchema` itself (the EDIT door is deliberately not gated — a served body carries the `_diagnostics` read decoration this `.strict()` schema rejects), so an authored rule that fails this schema is refused before it is written. DECOY, do not cite it as a consumer: objectui's own `SharingRuleConfig` (objectui packages/types/src/permissions.ts) is a DIFFERENT shape — `{ type: 'role'|'user'|'group'|'public', entity, actions, filter }` — re-exported twice and read by nothing. It matches this type by name only. RUNTIME PROOF THAT ALREADY EXISTS, unbound: packages/qa/dogfood/test/showcase-declarative-rbac-seeding.dogfood.test.ts authors `sharingRules[]` on the showcase stack and asserts the seeded row's `object_name`, `recipient_type`, `recipient_id` and translated `criteria_json` — i.e. it exercises `name`/`object`/`sharedWith.type`/`sharedWith.value`/`condition` end to end. It is registered in ../scripts/liveness/proof-registry.mts as `declarative-rbac-seeding` with `bound: false`, whose `blockedReason` reads \"not on a per-type authorable property\" — a premise this seeding falsifies. ⛔ No `proof` is claimed on any row here: binding a high-risk class is a separate ADR-0054 §3 act, one class at a time, and it is filed rather than slipped in.", + "_note": "SharingRuleSchema (packages/spec/src/security/sharing.zod.ts) — `SharingRuleSchema = CriteriaSharingRuleSchema`, the one authorable rule form. Seeded 2026-09-17 (#18582): the SECOND of the three PENDING_GOVERNANCE debts #18133 declared when PR #18581 widened the governance denominator from the registered kinds to `authorableTypes()`; `connector` and `analytics_cube` are still owed on that card. NOT a registered metadata KIND — it is bound in `UNREGISTERED_KIND_SCHEMAS` (#6245) and reaches this walk through `getMetadataTypeSchema`'s unregistered-kind fallback, so the ledger governs it while `listMetadataTypeSchemaTypes()` still does not enumerate it. THE SHAPE FACT THAT DECIDES EVERY ROW BELOW: the authoring shape is not the enforced shape. ADR-0057 D6 makes the RUNTIME row canonical (`sys_sharing_rule`: `object_name` + `criteria_json` + `recipient_type`/`recipient_id` + `access_level`) and `bootstrapDeclaredSharingRules` TRANSLATES each authored key into it at boot — nothing re-parses `SharingRuleSchema` at enforcement time. So every consumer cited below reads a COLUMN that a producer had to populate, and every row therefore carries a `producer` (#4837) naming the threading site: a consumer citation alone would be the `seed.env` shape, where the mechanism was right and nobody supplied the input. PREVIEW READ POINTS ENUMERATED (the #7131 mechanical rule, objectui @dda8f381): `registerBuiltinPreviews()` in packages/app-shell/src/views/metadata-admin/previews/index.ts registers twenty types and `sharing_rule` is NOT one of them — this type has no registered metadata-admin preview. Recorded rather than skipped, because \"the type has no registered preview\" is the sentence a later sweep needs. What objectui DOES consume is the whole SHAPE: `clientValidation.ts`'s `AUTHOR_SHAPE_ONLY_TYPES` gates the metadata-admin CREATE door on `SharingRuleSchema` itself (the EDIT door is deliberately not gated — a served body carries the `_diagnostics` read decoration this `.strict()` schema rejects), so an authored rule that fails this schema is refused before it is written. DECOY, do not cite it as a consumer: objectui's own `SharingRuleConfig` (objectui packages/types/src/permissions.ts) is a DIFFERENT shape — `{ type: 'role'|'user'|'group'|'public', entity, actions, filter }` — re-exported twice and read by nothing. It matches this type by name only. RUNTIME PROOF THAT ALREADY EXISTS, unbound: packages/qa/dogfood/test/showcase-declarative-rbac-seeding.dogfood.test.ts authors `sharingRules[]` on the showcase stack and asserts the seeded row's `object_name`, `recipient_type`, `recipient_id` and translated `criteria_json` — i.e. it exercises `name`/`object`/`sharedWith.type`/`sharedWith.value`/`condition` end to end. It is registered in ../scripts/liveness/proof-registry.mts as `declarative-rbac-seeding` with `bound: false`; grep that id there for the entry’s current reason — ⛔ it is deliberately NOT quoted here. This note used to quote that reason VERBATIM, #18797 rewrote it, and the quoted string stopped existing in the very file the note sends you to: the id is the durable anchor, the prose is not. The substance is unchanged and now sits on the entry itself — this seeding falsifies the entry’s original premise, that it acted only on the stack-level collections and reached no authorable key of a governed type (#18587 having supplied the per-type coordinate that premise said was missing) — so the registry records the class as a REAL ADR-0054 §3 binding candidate, held back by the adoption act alone. ⛔ No `proof` is claimed on any row here: binding a high-risk class is a separate ADR-0054 §3 act, one class at a time, and it is filed rather than slipped in.", "props": { "name": { "status": "live", From 897ddd27240e23fa4308437f2f68a1492682c653 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 10:00:30 +0000 Subject: [PATCH 2/2] chore(changeset): record the sharing_rule ledger note fix as a spec patch `packages/spec`'s `files[]` ships `liveness`, so the edited ledger is published content: `npm pack --dry-run` reads 275 published entries with `liveness/sharing_rule.json` among them (positive control: 39 `liveness/*.json` ship; negative control: 0 entries under `scripts/`, so the proof registry this note cites does NOT ship). Published bytes move, which is exactly the condition `skip-changeset` does not cover. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude --- .../18801-sharing-rule-note-quotation-rot.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .changeset/18801-sharing-rule-note-quotation-rot.md diff --git a/.changeset/18801-sharing-rule-note-quotation-rot.md b/.changeset/18801-sharing-rule-note-quotation-rot.md new file mode 100644 index 00000000000..c529f5c0264 --- /dev/null +++ b/.changeset/18801-sharing-rule-note-quotation-rot.md @@ -0,0 +1,14 @@ +--- +"@objectstack/spec": patch +--- + +`liveness/sharing_rule.json` — the file `_note` stops quoting the `declarative-rbac-seeding` proof-registry entry VERBATIM, so the pointer it hands a reader survives the next rewrite of that entry's prose (#18801). + +The ledgers ship inside this package, so this is a pointer a consumer can actually follow. The note said the entry's `blockedReason` "reads" a specific sentence and quoted it. PR #18797 (`ac720a9865`) rewrote that reason — correctly, because #18587 had made its premise false — and the quoted sentence stopped existing in the very file the note sends a reader to. Measured repo-wide with a fold-proof predicate (whitespace folds and TypeScript `' + '` concatenation seams dissolved before matching, because the registry splits every reason across source literals mid-phrase): the quoted string read **0** on `main`, while the entry id `declarative-rbac-seeding` read **18** in the same run. + +- **The judgement was never wrong; the quotation was.** The seeding does falsify the entry's original premise, and the rewritten reason on the entry now records exactly that — as a real ADR-0054 §3 binding candidate held back by the adoption act. The note still asserts it, in its own words. +- **What replaces the quote is an id, not a better sentence.** `declarative-rbac-seeding` is the entry's key: exactly **1** of the registry's **42** `id:` declarations spells it, and it reads 6 occurrences across 5 lines of `scripts/liveness/proof-registry.mts` — so a reader who greps it lands on the entry rather than on nothing. Quoting prose that changes is what rotted; an id does not rot on someone else's schedule. ⚠️ Measured, not assumed: nothing *asserts* those ids unique — the one other declaration of this id in the tree is `packages/qa/dogfood/test/authz-conformance.matrix.ts`, which names the same proof on purpose. +- **The old premise is paraphrased, deliberately not re-quoted.** A paraphrase of a premise that has already been retired cannot rot: the text it describes is frozen in history and nothing will rewrite it again. +- **The two sibling ledgers already wrote it this way.** `liveness/api.json` and `liveness/qa.json` cite `proof-registry.mts` by name and claim, and quote none of its prose. + +No verdict moved. Every `status`, `verifiedAt`, `evidence`, `producer` and per-row `note` in the file is byte-identical to `main`; the only changed field is `_note`, and `check:liveness` reports `sharing_rule 17 classified (live 16, planned 1)` before and after.