Repository navigation
test(runtime): pin the /data door's 409 RESOURCE_CONFLICT for a sharing_rule row created or renamed under a declared rule's name - #25289
Merged
objectstack-fleet[bot] merged 3 commits intoOct 22, 2026
Conversation
…ng_rule row created or renamed under a declared rule's name Claude-Session: https://claude.ai/code/session_016g7nYP9wdNx1GDa5XDr7Uy Co-authored-by: Claude <noreply@anthropic.com>
…ed-name pin Claude-Session: https://claude.ai/code/session_016g7nYP9wdNx1GDa5XDr7Uy Co-authored-by: Claude <noreply@anthropic.com>
…so a unique-constraint 409 cannot stand in Claude-Session: https://claude.ai/code/session_016g7nYP9wdNx1GDa5XDr7Uy Co-authored-by: Claude <noreply@anthropic.com>
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
Contributor
Author
席位核对 · ACCEPT · 2026-10-22T20:53Z
Generated by Claude Code |
objectstack-fleet
Bot
deleted the
claude/issue-25253-declared-rule-409-http
branch
October 22, 2026 21:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Sales · project thread
Fixes #25253
Clause-②: no
Metadata-protocol: none
What
Adds one end-to-end suite,
packages/runtime/src/sharing-rule-declared-name.data-door.pin.test.ts. It boots the compositionbootStackmirrors (ObjectQL, sqlite-wasm in memory, Hono, the app, platform objects, auth, security, sharing, REST, dispatcher). The application declares two sharing rules through its ownsharingRules. A signed-in administrator then drives the generic/datadoor over HTTP:POST /api/v1/data/sys_sharing_ruleunder a declared rule's name answers409withcode: RESOURCE_CONFLICT, and no row lands.PATCH /api/v1/data/sys_sharing_rule/:idrenaming an authored row onto a declared rule's name answers409withcode: RESOURCE_CONFLICT, and the row keeps its name.POSTunder a name no declaration holds answers 2xx and the row lands. So the 409 comes from the guard and not from the rig.declaredRuleNamed) reads both declared rules, and neither has a row.This is test-only. No production file changed and there is no behaviour change. Today the door does answer 409
RESOURCE_CONFLICTon both paths, socontent/docs/permissions/sharing-rules.mdx(regime C) is accurate.Wire shape, measured rather than assumed. The
/datadoor answers its flat envelope:{ error: "SENTENCE", code: "RESOURCE_CONFLICT", object: "sys_sharing_rule" }.codesits at the top level (mapDataError,@objectstack/types), and the pin reads it there. It does not use a fallback that would also accept a nestederror.code. The first run asserted the nested spelling and went red on exactly that.Why each refusal has its own declared rule.
sys_sharing_rule's unique constraint on the name also answers409, asUNIQUE_VIOLATION. My first draft used one declared name for both cases. Under ablation the create landed, and the rename then answered409 UNIQUE_VIOLATION, so a status-only pin would have read that 409 as the guard's. Now each case targets its own declared rule and stays independent under ablation.codeis asserted besidestatus.Lane: runtime, not plugin-sharing (deviation from the claim's file surface)
The claim's file surface was
packages/plugins/plugin-sharing/src/**test files, based on the assumption thatbusiness-unit-parent-delete-restrict.test.tsis an HTTP rig. I measured it, and it is not. It drivesengine.deleteon a real ObjectQL + SqlDriver, and/api/v1/data/...appears only in a comment. plugin-sharing has no@objectstack/rest,@objectstack/plugin-hono-serveror@objectstack/runtimedependency.@objectstack/runtimedepends on plugin-sharing, so adding runtime as a dev dependency would create a workspace cycle. An HTTP pin in plugin-sharing would therefore need new dependencies and a hand-built door. The card names "plugin-sharing or runtime" as the lane, and runtime already carries every piece: its full-composition pins (unresolved-object-name.actions-door.pin.test.ts,hook-handler-package-scope.pin.test.ts) use the same boot. So the suite lives there. It adds no new testkit or helper file.Evidence (head a949814)
pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/sharing-rule-declared-name.data-door.pin.test.tsgave Test Files 1 passed (1), Tests 4 passed (4).pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2gave Test Files 436 passed (436), Tests 6889 passed | 19 skipped. The lock reported VERDICT command-exit 0.pnpm --filter @objectstack/runtime typecheckexited 0. The pin is in thetsconfig.test.jsonprogram (--listFilesOnlycount 1) and carries no ledger entry, so it compiles with zero errors.@objectstack/plugin-sharingthroughdist/, so each leg rebuilt it.node scripts/ablation-replace.mjsreplaced thebindDeclaredRuleNameGuardthrow (if (declared) throw new DeclaredSharingRuleNameConflictError(...)) with a marker assignment. On disk: anchor x1 to x0, blob 26f18e1e7c30 to d5199123b3f6.pnpm --filter @objectstack/plugin-sharing buildran, thenablation-dist-preflight.mjs @objectstack/plugin-sharing __ABLATION_25253reported the marker present in 2 built files.expected 201 to be 409and the rename case readexpected 200 to be 409. The precondition and the control stayed green, as predicted.git diff HEADwas empty. I rebuilt, and--absentreported the marker absent from all 6 built files, with the tree clean against HEAD. The pin went back to 4 passed (4).Changeset
None. This PR carries
skip-changesetbecause it publishes nothing.packages/runtime'sfiles[]isdist,README.mdandCHANGELOG.md. After a runtime build, the pin's own fixture namepin_amber_projects_declaredhas 0 hits there, and the positive controlcreateDispatcherPluginhas 4 hits indist/.Gates
node scripts/pm/dispatch-gates.mjs --commandsderived 53 families for this diff. I ran each one under the verify lock and recorded its exit code.--ranreports: 53 accounted for, 52 run (all exit 0), 1 NOT MEASURED. The NOT MEASURED one ispnpm check:dual-build-cjs-loads, which exited 3 (PREREQUISITE NOT MET) because it reads every package'sdist/and this checkout built only runtime's closure. This diff changes no built output. CI runs it.Acceptance notes
Duration 15.6–16.8 s, of whichimportis about 14 s andtestsabout 2 s. That is over the 10 s file line, and the cost is the full-composition boot, the same one the sibling runtime pins pay. I declared no budget line: none of the three ruled classes fits. This note is for the shard-timings refresh, which reports and never reds.POST /sharing/rules) answers this refusal in the nested envelope (error.code), while the/datadoor answers it flat (code). This is not a defect for this card. It is the known two-dialect split, recorded here so nobody asserts the wrong spelling on the wrong door.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_016g7nYP9wdNx1GDa5XDr7Uy