Repository navigation
plugin-sharing, runtime: sharing_rule under regime C, so every same-name row is refused and declared rules are switched through the activation ledger (ruling A on #24378) - #24428
Conversation
…owed declaration Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
… and the immediate reconcile Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
…wing Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
…n the system-context page Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 46 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 31 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 8d5d7088abd42e67ade8d105d2ec69d4b11ed2aa && git checkout 8d5d7088abd42e67ade8d105d2ec69d4b11ed2aa
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin fa26308bb0332152a359335be27ba1a7f18574de 768fc0b2c1a03dba28e9dc01267aab5fab49f2f3 && git checkout -B drift-repro fa26308bb0332152a359335be27ba1a7f18574de && git merge --no-ff 768fc0b2c1a03dba28e9dc01267aab5fab49f2f3
node scripts/docs-audit/affected-docs.mjs --json fa26308bb0332152a359335be27ba1a7f18574de
|
Contract reviewServed-tier: Head verified against Check-runs on the head (their conclusions are the gate verdicts): 40 runs. 34 ① Derived judgmentsEvery accept-set and public-surface change the diff makes, read in the cited code and judged:
(a) The guard.
(b) The reconcile. The copy's creation reconciles the declaration's grants in the same request (pinned: after (c) Scopes. Under a wall the overlay is the organization's own: pinned ( (d) Pins. 13 in ② Semver level
③ Boundary flagsThe report's four
The two
Further flags, none blocking:
Implemented-by: This record was rendered by an isolated at-tier reviewer subagent of the adopting seat ( VERDICT: PASS |
|
Ruling-pointer · the landing stays held; rework under A · #24378 re-ruled A at 6258883445 (batch #368 item 1, the maintainer's 「24378 同意 A」; supersedes B′) · director seat (summon #43 continuation 5, This PR implements ruling B′ (overlay kept; guard on New and rename; immediate reconcile). Ruling A retires the overlay: keep the guard and widen it to every |
…the ledger switch for declared sharing rules Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
… ledger switch and its reconcile, the declaration-over-row report Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
…_rule on the activation route's ledger note Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
…activation switch Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
…ion reads them Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
…er-row report Claude-Session: https://claude.ai/code/session_01MaH9HpfmNnJGzgWQPqNmhP Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Head verified against Check-runs on the head (their conclusions are the gate verdicts): 41 runs, every one ① Derived judgmentsEvery accept-set, ledger and public-surface change the diff makes, read in the cited code and judged against ruling A items 1, 2, 5, 6, the claim amendment and the D6 note: (a) The widened guard — every same-name row refused, new or renamed; the round-1
(b) The ledger switch —
(c) Loud at load — a declaration over an authored row.
(d) The immediate reconcile — dropped, nothing stale.
(e) Pins and ablations.
② Semver level
③ Boundary flagsThe round-2 report's four
Stale PR body and title — every line now false, for the seat's replacement from the dev's owed
Further flags, none blocking:
Implemented-by: This record was rendered by an isolated at-tier reviewer subagent of the adopting seat ( VERDICT: PASS |
Part of #24421'; check-partof-closing-keyword with this text as PR_BODY exits 0, and check-closing-keyword-parity exits 0.
Part of #24421
Clause-②: no (narrowing)
This PR carries items 2 and 5 of ruling A on #24378 (comment 6258883445, maintainer 「24378 同意 A」), which supersedes ruling B′. ADR-0131 D6 now charts
sharing_ruleunder regime C: a declared sharing rule is read-only and clonable, and it is switched through the activation ledger. The scope is the claim amendment 6258985416. The ownership-ceremony step (each customised copy becomes a clone plus the base switched off) is a later stage, which carries the card's closing line.What changed
The guard: every same-name row is refused with 409
RESOURCE_CONFLICT, naming the declared rule. It applies at both write doors.SharingRuleService.defineRulecoversPOST /api/v1/sharing/rules, the SDK'sshares.rules.saveand any service caller. A rule created under a declared name is refused whether or not that rule is switched off. The first round'sid-addressed projection edit is removed, so there is no customised-copy door. A stored row that already holds a declared name stays editable as itself: that is the upsert path, not a new row.bindDeclaredRuleNameGuard, abeforeInsert/beforeUpdatehook onsys_sharing_rule, covers the/datadoor. It refuses an insert of a declared name, and an update that renames a row onto one (judged againstctx.previous). System writes pass.DeclaredSharingRuleNameConflictErrorcarriescodeRESOURCE_CONFLICTandstatus409, which isHttpStatusErrorCodeMap[409]. It is catalogued, so nothing is minted. Its message tells the caller to create their rule under another name and to switch the declared rule off through the activation door.The ledger switch.
sys_metadata_activationnow recordsmetadata_typesharing_rule(SHARING_RULE_ACTIVATION_TYPE).declaredRulesreads the ledger once per call, asreadPositionActivedoes: no ledger object means no read, and a failed read switches nothing off. A declared rule switched off is served in no scope.bindSharingRuleActivationReconcilehooksafterInsert/afterUpdate/afterDeleteon the ledger and callsreconcileDeclaredRuleActivation(name)in the same request. Switched off, the rule's name-keyed grants are purged across every scope. Switched on, each projection's one-rule pass re-derives them. When scopes are unknown, nothing is reconciled. A failure is thrown to the door, which answers 5xx. The ledger row is not rolled back: a code-registered hook opens no abort unit, so the grants converge at the next boot sweep or the next flip (contract review 2, ③).POST /api/v1/security/_activation/:type/:name(packages/runtime/src/domains/catalog-activation.ts), withsharing_ruleadmitted. It enforces the same two tiers:manage_metadata, then the ADR-0126 §5 posture rule, which makes the switch the platform operator's under a wall. The name is resolved through thesharingRulesservice (declaredRuleNamed), and the row records the registry item's_packageId(newDeclaredSharingRule.packageId). An undeclared name answers 404. With no service bound, or a failed read, the door answers 503.A declaration over a stored row. When a package declares a rule whose name a stored row already holds (a package upgrade, or a pre-C customised copy), the row is never treated as an overlay. It answers as its own rule. The declaration is withheld in that scope, and the conflict is logged at
erroronce per name and scope per process, with the remedy (rename the row, and switch the declared rule off if it must not grant).Dropped: the first round's immediate reconcile on
sys_sharing_rulewrites, and its delete path.sharing-plugin.ts's rule trigger is back tomain's shape, and theidpluck insharing-routes.tsis reverted.Tests
pnpm --filter @objectstack/plugin-sharing test: 48 files / 1156 tests.typecheck: exit 0.@objectstack/runtime: the 7 activation-door test files, 145 tests.typecheck: exit 0.@objectstack/rest: the sharing-rule test files, 19 tests.typecheck: exit 0.sharing-rule.test.ts, block "regime C", 17 pins:/datadoor refuses an insert and a rename onto the name.catalog-activation-door.test.ts, 6 pins forsharing_rule: switched undersinglewith the package recorded; operator-only undergroup/isolated; 404 for an undeclared name; 503 with no service; 503 on a failed read; a row with no package.sharing-rule-declared-name-conflict.test.ts:POST /sharing/rulesanswers 409 nested.Ablations (
scripts/ablation-replace.mjs; each leg restored,blob == HEAD,git diff HEADempty; the tests importsrc/relatively):defineRulethrowdeclaredRules/datahook throwdeclaredRuleNamedlookupGates were run locally at
768fc0b2c: 149 commands, made up of the 101 familiesdispatch-gates --commandsderived plus the artifact-roster block. 145 exited 0.--ranreports 101 derived, 101 run, 0 NOT-MEASURED. The other four need PR or event context:check-closing-target-claim,check-single-claim-paths,check-partof-closing-keyword(exit 0 withPR_BODYset) andcheck-governed-queue-guard.Acceptance notes
not-required (no-migration-prescription), as the contract review on the first round decided from ADR-0087 D8 and ADR-0131 D13.check:adr-0087-registrationaccepts it.runtime/src/domains/{catalog-activation,activation-gate}.ts: the activation door.runtime/src/domains/security.ts,dispatcher-plugin.ts,route-ledger.ts: notes now namesharing_rule.content/docs/permissions/system-context.mdxrow 40: the guard'sisSystemanchor.sys-metadata-activation.object.tsis unchanged. Itsmetadata_typedescription reads "('flow', 'permission', …)" and needed no edit.content/docs/permissions/sharing-rules.mdx, which does not describe regime C yet.Generated by Claude Code