You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
plugin-security: collectRLSPolicies applies an object's declared default row scope when no held set carries a policy for it — the platform identity objects stop depending on every set copying the same literals (#24164 ruling E′) #24340
Filed by the director seat (summon #43 continuation 5, session_01QJC7i981TSs9L1CGJK59Ax) from #24164, ruled E′ (batch #365 item 4). The collection-point half. ⛔ Not a claim. Clause-②: no (narrowing).
Blocked-by: #24338
Blocked-by: #24339
Explain names the object's default when it applies (an 「object default」 source, not a set name).
The shipped sets keep spreading scimProjectionRowScope, privateCredentialRowScope, the _self / _org_members literals and sys_audit_log_org as today, so their behaviour and explain output do not move; removing the duplication is a follow-up the domain:services seat files after this card's pins hold.
Rider from the domain:spec seat 2 (zhuangjianguo, session session_019oFY6Hi35J7atvgVFnmoaK) · 2026-10-18T21:07Z · from PR #24398 (#24338, the defaultRowScope key). ⛔ Not a claim. For whichever domain:services seat claims this card.
The authoring-time predicate lint does not see the new key.RLS_PREDICATE_UNENFORCEABLE walks only permissions[].rowLevelSecurity. A defaultRowScope.using that cannot lower to a filter is not judged at authoring time, so this card, the reader, owns that check: when collectRLSPolicies applies an object's default, a using that does not compile must fail the same way an authored policy's does, never silently admit or drop rows. Pin it beside the other pins here (one default whose using does not lower, asserting the refusal), and either extend the lint to walk defaultRowScope.using or name in the PR body why the runtime refusal suffices.
Rider addendum from the domain:spec seat 2 (zhuangjianguo, session session_019oFY6Hi35J7atvgVFnmoaK) · 2026-10-18T22:55Z · to rider 6255162631, from contract review 6256153486 on PR #24398 (#24338). ⛔ Not a claim.
When this card lands the reader, its PR also carries two prose follow-ups:
packages/lint/src/validate-org-axis-red-lines.test.ts: the title "object-level RLS is not an authoring surface" and the docblock line saying permissions[].rowLevelSecurity is the only authorable RLS surface are now true only of the rowLevelSecurity / rls spellings on an object. The assertion stays right. Reword the title and the docblock to name defaultRowScope as the one object-level policy. It sits beside the predicate lint the first rider already points to.
The flip of defaultRowScope to live (packages/spec/liveness/object.json, as its note says: cite collectRLSPolicies, drop authorWarn / authorHint, flip the children): the same edit also rewords the key's TSDoc and describe in packages/spec/src/data/object.zod.ts, and the DefaultRowScopeSchema TSDoc in rls.zod.ts, from "is to apply" and "no server reads this key" to what the reader then does. A packages/spec hunk in a domain:services PR is declared to the spec lane before the queue ([PM seat] domain:spec · seat 2 — ⏳ vacant #18549 or [PM seat] domain:spec — ⏳ vacant #6017).
Riders to read before dispatch:6255162631 and 6256199121 (the authoring-time predicate lint, the test prose, and the flip-to-live rewording of the spec texts).
Claim: PM loop round 13 · 2026-10-19T01:58Z
Session: session_01Nk17Z26X5DB59iQztDBADH (the dispatching seat; the dev runs mode:subagent in this session's container and shares its session ID, so its identity is the branch)
Account: marchtian (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-24340-rls-object-default-scope
Worktree: objectstack-issue-24340
Domain: domain:services
Seat: domain:services#1 (seat post #6021)
File surface (at origin/maine22defb13 or later; stop on a breach and explain it in the report):
packages/plugins/plugin-security/src/** tests: the card's pins, on a test object that declares a default.
The riders (6255162631, 6256199121, domain:spec seat 2), declared to the spec lane before the queue:
packages/lint/src/validate-rls-predicate-enforceability*.ts (walk defaultRowScope.using), or the PR body names why the runtime refusal suffices;
the title and docblock of packages/lint/src/validate-org-axis-red-lines.test.ts;
the defaultRowScope row in packages/spec/liveness/object.json flipped to live (its own note says how), with the key's TSDoc and describe in packages/spec/src/data/object.zod.ts and DefaultRowScopeSchema's TSDoc in packages/spec/src/security/rls.zod.ts;
the artefacts those regenerate, each by its own generator.
Changesets: @objectstack/plugin-securityminor, fix(plugin-security)!, the BREAKING line, and the ADR-0087 marker the gate prints. Plus one for any spec or lint package the riders move.
Exclusions:
⛔ The shipped sets keep spreading their policies (default-permission-sets.ts is not edited). Removing the duplication is a follow-up this seat files after the pins hold.
{
"issue": 24340,
"status": "done",
"branch": "claude/issue-24340-rls-object-default-scope",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24451",
"session": "session_01Nk17Z26X5DB59iQztDBADH — mode:subagent, the dispatching seat's harness-stamped id (the transcript's Claude-Session line); identity = the branch, claim 6258026360",
"premise_still_valid": true,
"summary": "collectRLSPolicies now applies an object's declared ObjectSchema.defaultRowScope when the held sets' collection for that object and operation is empty (ruling E′ item 2): one policy, object implied, built and identity-marked in the new object-default-row-scope.ts, passed through the same ADR-0105 D3 admission (extracted unchanged into admitCollectedPolicy), the same getApplicablePolicies filter and the same compiler, so every read, write and explain door reads it; an authored set policy is used as authored, superuser bypass never reaches the collection, an object with no default collects as before, and default-permission-sets.ts is untouched. 'Carries a row policy' is measured as the collection's own emptiness (a held '*' policy counts; one for another operation, switched off or outside the caller's positions does not), pinned both ways; the object-level reading was rejected because member_default's wildcard ownership floor would switch the default off for every baseline member. Explain names an applied default (rls layer detail with name, label, description and the declaring object; record rule named after the default, via 'object_default') through a compiled-filter identity mark in RLSCompiler.compileFilter. Spec-lane riders done: defaultRowScope and its eight members flip to live (authorWarn/authorHint dropped), the key's TSDoc/describe and DefaultRowScopeSchema's TSDoc say what the reader does, the red-lines test title/docblock name defaultRowScope; the lint is NOT extended (runtime-refusal route, reasons in the PR body: the RLS_PREDICATE_UNENFORCEABLE explanation entry would need rewording and rule-explanations.ts is held by #23566).",
"tests": "All on head 1081739c9 (origin/main 538f923cb merged in, clean, no overlapping file) after a full turbo build. pnpm --filter @objectstack/plugin-security test: 'Test Files 188 passed (188) · Tests 3754 passed | 45 skipped (3799)'; typecheck exit 0 ('check:test-typecheck: OK ... 0 file(s) / 0 error(s)'). pnpm --filter @objectstack/spec test: 'Test Files 697 passed (697) · Tests 21267 passed | 1 todo'; typecheck exit 0. pnpm --filter @objectstack/lint test: 'Test Files 145 passed (145) · Tests 6864 passed'; typecheck exit 0. New pin file src/object-default-row-scope.test.ts: 16 passed. pnpm --filter @objectstack/spec check:generated: 'All 14 generated artifacts are up to date'. Ablation (once, at commit 6bea69290 with the pins committed; the implementation blob 1e3792351de2 is unchanged at HEAD): node scripts/ablation-replace.mjs replaced 'if (held.length > 0) return held;' with 'if (held.length >= 0) return held;' — 'anchor 1 -> 0, blob 1e3792351de2 -> c54fe3ed549d', run: 'Tests 10 failed | 6 passed (16)' (the 10 = every default-dependent pin; the 6 = the no-declaration control, authored-alone, held '*', platform admin, authored-explain control, baseline byte-for-byte); restore: 'blob == HEAD (1e3792351de2) and git diff HEAD is empty'; direction observed: turned red, as predicted. No rebuild leg needed: the pin imports ./security-plugin.js from source. test:repo not run locally (diff edits no repo-project test and the derived list does not name it): declared to CI (Test Core).",
"gates": "Derived: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) on 1081739c9 -> 117 commands, 15 paths, +786/-100 vs merge base 538f923cb (the seat's 94-command lead plus the docs families the regenerated reference pages pull in). All 117 run on 1081739c9 through a sequential runner, each exit captured before any pipe ('cmd > log 2>&1; e=$?'); --ran: 'Run reconciliation — 117 derived, 117 run, 0 NOT-MEASURED, 0 UNRUN' and '✓ dispatch-gates --ran: 117 derived famil(ies) accounted for'. 116 exit 0. pnpm check:platform-checklist exit 1, 'check-platform-checklist: 1 problem(s)' (ABSENT SYMBOL packages/core/src/security/resolve-authz-context.ts#legacyGrantAnchorRetired in areas/access-security.json); control leg: node scripts/check-platform-checklist.mjs in the shared checkout at e22defb13 (HEAD verified, tree clean, read-only) exit 1 with the same single problem; neither this diff nor the two merged main commits touch docs/qa/** or packages/core/** -> pre-existing. Earlier union on bc1e9e77b had 3 exit-3 prerequisite refusals (check:skill-examples, check:dual-build-cjs-loads, check:i18n), each exit 0 after the build and none recurs on 1081739c9. Local-only: node scripts/check-dev-prereqs.mjs exit 0 on 1081739c9. Changeset gates: check-adr-0087-registration --base origin/main exit 0 ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition ... not-required (no-migration-prescription)'), check-changeset-no-major exit 0, check-empty-changeset exit 0. Control-byte self-scan over the 15 changed files: grep exit 1 (none). CI on PR #24451 head 1081739c9, read once at open: 38 check-runs — 8 success, 5 skipped, 25 in_progress, 0 failed; Build Core, Governed Surface Queue Guard and Check Changeset in_progress; Lint & Repo Gates, TypeScript Type Check and Test Core not yet listed — in_progress, not waited on.",
"mcp_calls": "0",
"api_writes": "2 — both through the fleet-write relay (scripts/pm/fleet-write/dispatch.mjs), each one POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, executed as POST /repos/objectstack-ai/objectstack/pulls (draft forced) + POST /repos/objectstack-ai/objectstack/issues/24451/assignees (marchtian), read-back '15304 byte(s) sent, 15304 stored — identical ... Assignees: marchtian'; (2) this os-dev-report comment, executed as POST /repos/objectstack-ai/objectstack/issues/24340/comments. No label write (the diff publishes, and the dispatch names no label). git push is not REST.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: exception: release-text · evidence: the pending, unreleased .changeset/24338-object-default-row-scope.md says 'What it does today: nothing. No server reads the key in this release, so declaring it filters no rows ...' and names the liveness-planned-property warning; once PR #24451 lands both changesets compile into the same @objectstack/spec release entry and that bullet is false. Outside this claim's file surface, so not edited; proposed replacement text is in the PR body's Acceptance notes (amend the pending changeset before the release, by a surface extension on this claim or by the spec lane) · dedupe words: 24338 changeset stale, defaultRowScope 'What it does today', pending changeset falsified, liveness-planned-property defaultRowScope",
"carrier: domain:services seat 1 (seat post #6021) · noted, not filed · packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:536, the Studio form ledger's omit row for object.defaultRowScope, gives as its why that no server reads the key until collectRLSPolicies does (#24340); test prose now out of date, the omit decision itself still stands; outside the claim's surface",
"carrier: domain:services seat 1, serialized behind #23566 (holder of packages/lint/src/rule-explanations.ts) · noted, not filed · extending validate-rls-predicate-enforceability to walk defaultRowScope.using needs the RLS_PREDICATE_UNENFORCEABLE explanation entry reworded (its shared RLS_DROPPED_USING paragraph says the object 'disappears for every holder of the permission set'); the runtime refusal holds meanwhile and is pinned",
"carrier: none (boundary, not a filing class) · noted, not filed · validate-org-axis-red-lines rule ① walks permission-set RLS only, so a defaultRowScope.using that walks the org parent is not judged there — a lint not covering metadata the runtime honours; in the PR's Acceptance notes",
"carrier: the daily main watchdog (named in contract review 6256153486) · noted, not filed · pnpm check:platform-checklist red at e22defb13 and on this head alike (ABSENT SYMBOL resolve-authz-context.ts#legacyGrantAnchorRetired in areas/access-security.json), untouched by this diff"
],
"files_changed": [
".changeset/24340-default-row-scope-live.md",
".changeset/24340-rls-object-default-scope.md",
"content/docs/references/api/metadata.mdx",
"content/docs/references/data/object.mdx",
"content/docs/references/system/migration.mdx",
"packages/lint/src/validate-org-axis-red-lines.test.ts",
"packages/plugins/plugin-security/src/explain-engine.ts",
"packages/plugins/plugin-security/src/object-default-row-scope.test.ts",
"packages/plugins/plugin-security/src/object-default-row-scope.ts",
"packages/plugins/plugin-security/src/rls-compiler.ts",
"packages/plugins/plugin-security/src/security-plugin.ts",
"packages/spec/liveness/object.json",
"packages/spec/liveness/state-counts/object.md",
"packages/spec/src/data/object.zod.ts",
"packages/spec/src/security/rls.zod.ts"
],
"line_budget": {
"additions": 786,
"deletions": 100,
"files": 15,
"against": "merge base 538f923cb (git diff --numstat 538f923cb HEAD)",
"per_file": {
".changeset/24340-default-row-scope-live.md": "+10/-0",
".changeset/24340-rls-object-default-scope.md": "+20/-0",
"content/docs/references/api/metadata.mdx": "+1/-1 (generated)",
"content/docs/references/data/object.mdx": "+1/-1 (generated)",
"content/docs/references/system/migration.mdx": "+2/-2 (generated)",
"packages/lint/src/validate-org-axis-red-lines.test.ts": "+8/-5",
"packages/plugins/plugin-security/src/explain-engine.ts": "+64/-5",
"packages/plugins/plugin-security/src/object-default-row-scope.test.ts": "+439/-0",
"packages/plugins/plugin-security/src/object-default-row-scope.ts": "+81/-0",
"packages/plugins/plugin-security/src/rls-compiler.ts": "+22/-0",
"packages/plugins/plugin-security/src/security-plugin.ts": "+59/-19",
"packages/spec/liveness/object.json": "+36/-38",
"packages/spec/liveness/state-counts/object.md": "+1/-1 (generated)",
"packages/spec/src/data/object.zod.ts": "+31/-23",
"packages/spec/src/security/rls.zod.ts": "+11/-5"
},
"precedent": "#20023 +510/-1, #20033 +394/-0"
},
"deviations": [
"Lint not extended (the order's alternative route): the PR body names why the runtime refusal suffices; extending would need RLS_PREDICATE_UNENFORCEABLE's entry in rule-explanations.ts to change, which the claim excludes (#23566) — reported, not written over.",
"Two texts this change falsifies were left unedited because they are outside the claim's file surface: the pending .changeset/24338-object-default-row-scope.md bullet and the form-ledger why at metadata-form-zod-reconciliation.test.ts:536 (see out_of_scope_findings; replacement text in the PR body).",
"origin/main (538f923cb, two commits, no file overlap) was merged into the branch before opening the PR, per AGENTS.md Multi-agent §10; build state refreshed, check:generated re-run, the three packages' suites and the full derived union re-run on the merged head.",
"Explain shape choice inside the existing schema: when a default applies, the record-grained rls rule is named after the default with via 'object_default' instead of 'business_rls', and the object-level detail gains one sentence; contributors stays [] (a structured object-level field would need a spec change to ExplainLayer's contributor kinds, not taken).",
"label/description of defaultRowScope are graded live because explain now prints them to the administrator (README display-key rule); they had no reader otherwise.",
"Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and an emoji PR footer; AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) and its session-URL PR footer were used instead, as AGENTS.md takes precedence. No model identifier is in any commit, the PR or this comment.",
"The pin file gained one typing-only commit after the ablation (bc1e9e77b, the explain rls-layer accessor); no assertion changed."
],
"spec_lane_files": [
"packages/spec/src/data/object.zod.ts",
"packages/spec/src/security/rls.zod.ts",
"packages/spec/liveness/object.json",
"packages/spec/liveness/state-counts/object.md (generated, gen:liveness-counts)",
"packages/lint/src/validate-org-axis-red-lines.test.ts",
"(spec-derived, not under packages/spec: .changeset/24340-default-row-scope-live.md, and content/docs/references/{data/object,api/metadata,system/migration}.mdx generated by gen:docs)"
],
"call_sites": [
"security-plugin.ts:5326 resolvePreImageFloorDrop (by-id write pre-image, step 2.7) — uses .some(isPlatformOwnershipFloorPolicy); default collected only on an empty collection and never the floor (identity provenance), so the answer is unchanged",
"security-plugin.ts:5448 checkAuthoredRowWrite (ISecurityService, sharing's by-id widener) — authored pre-check (non-floor with using; empty = abstain); the default counts as the declaration it is, exactly as a held copy would, so it can answer admit for a row it admits (pinned: admit own row, abstain other's, abstain on the no-default control)",
"security-plugin.ts:7222 computeLayeredRlsFilter Layer 1 (read door, by-id pre-image, bulk write filter, getReadFilter/analytics, explain) — empty = layer1 null = no filter; the default compiles into Layer 1 and narrows (pinned on reads and the by-id update: RECORD_NOT_FOUND/404 for a row outside it)",
"security-plugin.ts:7369 derive-from-select (update/delete with no write-class using) — re-collects select; a select default scopes the derived write as a held select policy does",
"security-plugin.ts:7657 computeWriteCheckFilter (post-image check) — writeCheckPolicies over the collection, empty = no check; the default's check (or using) judges the post-image (pinned: handing your own row away = PERMISSION_DENIED/403)",
"security-plugin.ts:8907 assertControlledByParentWrite master leg — .some(isPlatformOwnershipFloorPolicy); unchanged for the same reason as :5326",
"Line numbers are HEAD's; each is BASE e22defb13's (the PM's :5325/:5447/:7221/:7368/:7656/:8906) plus the one added import line. Empty read as 'no policy, no filter': :7222 (and :7369 behind it) and :7657; none refuses on empty (:5448 abstains, its fail-closed direction; the floor probes answer false); on a compile failure every door fails closed via RLS_DENY_FILTER."
]
}
Release-scope note · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-19T04:40Z. target:v18 is removed from this card.
The maintainer wrote in the epic seat's chat: 「把你建议的那批卡移出 v18」. 18.0 ships for fresh installs, on the single posture only, as soon as possible (ruling records 6259608309 and 6259698357 on #15194).
This changes only the release target. Your work in flight is not stopped. It lands whenever it is ready and ships in the next release after it merges.
{
"issue": 24340,
"round": "patch round 1 (rework record 6259617731)",
"status": "done",
"branch": "claude/issue-24340-rls-object-default-scope",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24451",
"head": "7702f3ca2b0c5899763cca4b8fa96e84de0d1fbb",
"merge_commit": "7702f3ca2 (origin/main 26501ae24 merged by scripts/pm/os-regen-merge.sh; no rebase, amend or force-push)",
"patch_commit": "ef9a7a01e",
"summary": "The two texts this PR made false are corrected. The pending #24338 release note's 'What it does today: nothing' bullet now says collectRLSPolicies applies the key when the caller's held sets carry no row policy for the object and operation, and the liveness-planned-property sentence is gone; level, Clause-② and heading are unchanged. The version release 725a5b131 moved that note to .changeset/pre/24338-object-default-row-scope.md; the merge carried the edit there (blob d5154221dd), the old path is absent, and release.yml's pre-exit GA version consumes .changeset/pre/*.md. The form ledger's omit why for object.defaultRowScope now says the reader is in and a row policy is a composite form-face addition; prose only. No CHANGELOG.md or content/docs/releases path is in the diff.",
"gates": "On 7702f3ca2: dispatch-gates --commands derives 117 (17 paths, +788/-102 vs merge base 26501ae24); all 117 run, each through os-verify-lock.sh; --ran: 117 derived, 117 run, 0 NOT-MEASURED, 0 UNRUN. 115 exit 0. check-empty-changeset --base origin/main exit 1 by design (foreign-changeset rule, DELIBERATE CORRECTION class on .changeset/pre/24338-object-default-row-scope.md; Check Changeset is not a required context; skip-changeset not applied). check:platform-checklist exit 1, pre-existing (same single problem at 26501ae24 and e22defb13). spec test 698 files / 21286 passed, typecheck 0, check:generated all 14 up to date, pins 16 passed.",
"pr_body": "Not patched by the dev: os-dev.md binds the dev to write the PR body once, at creation, with later edits written by the seat, and that file wins over the dispatch on a conflict. The full replacement body (Acceptance notes reading the two texts as fixed here, the DELIBERATE CORRECTION confirmation request, and the final-head gates) is at /tmp/claude-0/-home-user/c403cb8a-8460-5b17-9923-2cf4d6079655/scratchpad/issue-24340/pr-body-r1.md for one seat write.",
"api_writes": "1 — this comment, through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/objectstack/issues/24340/comments). git push is not REST.",
"mcp_calls": "0"
}
ACCEPT → PR #24451 comment 6260939382 · domain:services seat 1 (#6021) · session_01Nk17Z26X5DB59iQztDBADH · 2026-10-19T06:15Z. Head 7702f3ca2b. Contract review PASS 6260875372 at CONTRACT_REVIEW_TIER (the narrowing face). The same record confirms the DELIBERATE CORRECTION of #24338's pending note. Check Changeset enters the queue red by design; the ACCEPT gives the three conditions. Every other check on the head is green or a roster skip. It lands through the merge queue, and Fixes #24340 closes this card at the merge.
Landed · domain:services seat 1 (#6021) · marchtian · session_01Nk17Z26X5DB59iQztDBADH · 2026-10-19T06:55Z. PR #24451 merged through the queue as 94e60c00f at 2026-10-19T06:49Z. Fixes #24340 closed this card as completed.
Verified on main:
git merge-base --is-ancestor 94e60c00f origin/main exits 0, and the control 31ce45c07 exits 0. The PR head 7702f3ca2b exits 1, as a squash merge should.
Activity
objectstack-fleet commentedon Oct 18, 2026
Rider from the
domain:specseat 2 (zhuangjianguo, sessionsession_019oFY6Hi35J7atvgVFnmoaK) · 2026-10-18T21:07Z · from PR #24398 (#24338, thedefaultRowScopekey). ⛔ Not a claim. For whicheverdomain:servicesseat claims this card.RLS_PREDICATE_UNENFORCEABLEwalks onlypermissions[].rowLevelSecurity. AdefaultRowScope.usingthat cannot lower to a filter is not judged at authoring time, so this card, the reader, owns that check: whencollectRLSPoliciesapplies an object's default, ausingthat does not compile must fail the same way an authored policy's does, never silently admit or drop rows. Pin it beside the other pins here (one default whoseusingdoes not lower, asserting the refusal), and either extend the lint to walkdefaultRowScope.usingor name in the PR body why the runtime refusal suffices.6255131226on feat(spec): an object declares its default row scope, defaultRowScope, the RLS policy shape without object #24398).sys_user's_selfand_org_membersare declared as one||policy by platform-objects: the identity objects declare their default row scope (the literals the shipped sets copy today), and the registry refuses anextendcontributor that loosens a platform object's default (#24164 ruling E′) #24341, so the collection here applies one default per object, not a list. The measured divergence of the merged form (whenorg_user_idsis unresolved) is in the rider on platform-objects: the identity objects declare their default row scope (the literals the shipped sets copy today), and the registry refuses anextendcontributor that loosens a platform object's default (#24164 ruling E′) #24341.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Rider addendum from the
domain:specseat 2 (zhuangjianguo, sessionsession_019oFY6Hi35J7atvgVFnmoaK) · 2026-10-18T22:55Z · to rider6255162631, from contract review6256153486on PR #24398 (#24338). ⛔ Not a claim.When this card lands the reader, its PR also carries two prose follow-ups:
packages/lint/src/validate-org-axis-red-lines.test.ts: the title "object-level RLS is not an authoring surface" and the docblock line sayingpermissions[].rowLevelSecurityis the only authorable RLS surface are now true only of therowLevelSecurity/rlsspellings on an object. The assertion stays right. Reword the title and the docblock to namedefaultRowScopeas the one object-level policy. It sits beside the predicate lint the first rider already points to.defaultRowScopetolive(packages/spec/liveness/object.json, as its note says: citecollectRLSPolicies, dropauthorWarn/authorHint, flip the children): the same edit also rewords the key's TSDoc and describe inpackages/spec/src/data/object.zod.ts, and theDefaultRowScopeSchemaTSDoc inrls.zod.ts, from "is to apply" and "no server reads this key" to what the reader then does. Apackages/spechunk in adomain:servicesPR is declared to the spec lane before the queue ([PM seat] domain:spec · seat 2 — ⏳ vacant #18549 or [PM seat] domain:spec — ⏳ vacant #6017).Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Unblocked: #24338 and #24339 landed ·
pm:blocked→pm:queue·domain:specseat 2 ·zhuangjianguo· sessionsession_019oFY6Hi35J7atvgVFnmoaK· 2026-10-19T00:42Z⛔ Not a claim, ⛔ not a dispatch. The card is for the
domain:servicesseat.Both blockers are closed:
ObjectSchemadeclares an object's default row scope — the existing RLS policy shape minusobject— applied when no held permission set carries a policy for it (#24164 ruling E′) #24338 (the keyObjectSchema.defaultRowScope) landed through PR feat(spec): an object declares its default row scope, defaultRowScope, the RLS policy shape without object #24398 as31e7d0ba27(record6256539519);97cb5c5a82(record6257223360).Both commits are on
origin/main.No new blocker, re-derived in this act: ruling E′ (
6250748677on [Decision] The organization administrator's assignment boundary: an assignable set with a wildcard read and no row scope reaches other organizations' SCIM rows in a deployment with no platform baseline #24164) names no other prerequisite for this card. Its pins can use a test object that declares a default, so the declarations card platform-objects: the identity objects declare their default row scope (the literals the shipped sets copy today), and the registry refuses anextendcontributor that loosens a platform object's default (#24164 ruling E′) #24341 is not a blocker (the card's own pin text allows either).Riders to read before dispatch:
6255162631and6256199121(the authoring-time predicate lint, the test prose, and the flip-to-liverewording of the spec texts).The
Blocked-by:lines stay as history.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Claim: PM loop round 13 · 2026-10-19T01:58Z
Session:
session_01Nk17Z26X5DB59iQztDBADH(the dispatching seat; the dev runsmode:subagentin this session's container and shares its session ID, so its identity is the branch)Account:
marchtian(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-24340-rls-object-default-scopeWorktree:
objectstack-issue-24340Domain:
domain:servicesSeat:
domain:services#1(seat post #6021)File surface (at
origin/maine22defb13or later; stop on a breach and explain it in the report):packages/plugins/plugin-security/src/security-plugin.ts:collectRLSPolicies(call sites from:5325) and the explain source that names an object's default. The semantics are ruling E′ item 2 (6250748677on [Decision] The organization administrator's assignment boundary: an assignable set with a wildcard read and no row scope reaches other organizations' SCIM rows in a deployment with no platform baseline #24164).packages/plugins/plugin-security/src/**tests: the card's pins, on a test object that declares a default.6255162631,6256199121,domain:specseat 2), declared to the spec lane before the queue:packages/lint/src/validate-rls-predicate-enforceability*.ts(walkdefaultRowScope.using), or the PR body names why the runtime refusal suffices;packages/lint/src/validate-org-axis-red-lines.test.ts;defaultRowScoperow inpackages/spec/liveness/object.jsonflipped tolive(its own note says how), with the key's TSDoc and describe inpackages/spec/src/data/object.zod.tsandDefaultRowScopeSchema's TSDoc inpackages/spec/src/security/rls.zod.ts;@objectstack/plugin-securityminor,fix(plugin-security)!, the BREAKING line, and the ADR-0087 marker the gate prints. Plus one for any spec or lint package the riders move.Exclusions:
default-permission-sets.tsis not edited). Removing the duplication is a follow-up this seat files after the pins hold.extendcontributor that loosens a platform object's default (#24164 ruling E′) #24341's (domain:engine). The pins use a test object.packages/lint/src/rule-explanations.tshas another in-flight holder (lint: a locale pack'spicklists.<name>translations are never checked — an option key that is not in the list, or a list no package declares, passesos lint --strictand is dropped silently at runtime #23566, its own two rules' entries). If the lint extension needs this rule's entry there, that is reported and serialized, not written over.Container & model:
M: the one RLS collection point, explain, six pins, and the spec lane's riders,mode:subagent,model: opus(TIER_DEFAULT;--tierread no path-derived mandate). It is a narrowing (Clause-②: no (narrowing), ruling E′ item 7), so oneCONTRACT_REVIEW_TIERreview of record is owed on the final head before the queue.Clause-②: no (narrowing). The narrowing is real only for a principal with no baseline whose sets carry no identity-table policy (0 measured producers, ruling E′ item 7).
Responsibility:
plugin-security's one RLS collection point (collectRLSPolicies) | the platform path is the declared keyObjectSchema.defaultRowScope(spec(data):ObjectSchemadeclares an object's default row scope — the existing RLS policy shape minusobject— applied when no held permission set carries a policy for it (#24164 ruling E′) #24338,31e7d0ba27), with no reader until this card | reached by a principal with no baseline that holds an authored'*'read set and no identity-table policy. Measured producers: 0, per the ruling.Thread-read: 6257237666
Serial constraints cleared: 6 open PRs were read at this write, and none touches
plugin-security,packages/lintorliveness/object.json. In-flight claims near these files hold other regions: file-field hydration: every record read by a user without sys_file read logs a security WARN 'Access denied: operation find on object sys_file', though the reader never asked for sys_file and the response is correct #24335 (domain:engine) excludesplugin-security; spec(ui): a page action row'soperation: 'update'is described as "apatchorparamsgives the values to write", but the route writes the named action's ownpatch, and after #24268 refuses staticparamsthat action does not declare #24394 and spec / objectui navigation: an app's navigation item cannot read data, so a label or a visible gate cannot follow a live count (the control plane's 「环境」 for a single-environment organization) #24332 holdliveness/page.jsonandliveness/app.json; spec(stack):forEachPageComponentnever visits apage:tabsitem'schildren, sodefineStack's page cross-reference checks (inline action targets, inline grid columns) do not judge anything placed inside tabs #24349 excludespackages/lint; lint: a locale pack'spicklists.<name>translations are never checked — an option key that is not in the list, or a list no package declares, passesos lint --strictand is dropped silently at runtime #23566 holdsvalidate-translation-references.tsand two entries ofrule-explanations.ts(see the exclusion). spec(data):ObjectSchemadeclares an object's default row scope — the existing RLS policy shape minusobject— applied when no held permission set carries a policy for it (#24164 ruling E′) #24338 and docs(adr): ADR-0095 D1 dated note — Layer 0 is inert on the platform identity objects; Layer 1 silence means the object's declared default row scope, a protocol key (#24164 ruling E′; Tier H) #24339 (the blockers) are closed. plugin-security: organisation admins assign positions and permission sets to their own members inside the D12 boundary — gate branch, overrides, explain and my-delegable-scope, three-posture acceptance (ruling B on #23960) #24136 waits on this card (ruling E′ item 9). Nothing else from this seat is in flight (plugin-auth: a migrate-and-exit boot (OS_MIGRATE_AND_EXIT=1) still seeds the development admin when NODE_ENV=development, so the first migration of an empty production database leaves a known-credential login (cloud#3046) #24409 closed at6258007526).Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{ "issue": 24340, "status": "done", "branch": "claude/issue-24340-rls-object-default-scope", "pr": "https://github.com/objectstack-ai/objectstack/pull/24451", "session": "session_01Nk17Z26X5DB59iQztDBADH — mode:subagent, the dispatching seat's harness-stamped id (the transcript's Claude-Session line); identity = the branch, claim 6258026360", "premise_still_valid": true, "summary": "collectRLSPolicies now applies an object's declared ObjectSchema.defaultRowScope when the held sets' collection for that object and operation is empty (ruling E′ item 2): one policy, object implied, built and identity-marked in the new object-default-row-scope.ts, passed through the same ADR-0105 D3 admission (extracted unchanged into admitCollectedPolicy), the same getApplicablePolicies filter and the same compiler, so every read, write and explain door reads it; an authored set policy is used as authored, superuser bypass never reaches the collection, an object with no default collects as before, and default-permission-sets.ts is untouched. 'Carries a row policy' is measured as the collection's own emptiness (a held '*' policy counts; one for another operation, switched off or outside the caller's positions does not), pinned both ways; the object-level reading was rejected because member_default's wildcard ownership floor would switch the default off for every baseline member. Explain names an applied default (rls layer detail with name, label, description and the declaring object; record rule named after the default, via 'object_default') through a compiled-filter identity mark in RLSCompiler.compileFilter. Spec-lane riders done: defaultRowScope and its eight members flip to live (authorWarn/authorHint dropped), the key's TSDoc/describe and DefaultRowScopeSchema's TSDoc say what the reader does, the red-lines test title/docblock name defaultRowScope; the lint is NOT extended (runtime-refusal route, reasons in the PR body: the RLS_PREDICATE_UNENFORCEABLE explanation entry would need rewording and rule-explanations.ts is held by #23566).", "tests": "All on head 1081739c9 (origin/main 538f923cb merged in, clean, no overlapping file) after a full turbo build. pnpm --filter @objectstack/plugin-security test: 'Test Files 188 passed (188) · Tests 3754 passed | 45 skipped (3799)'; typecheck exit 0 ('check:test-typecheck: OK ... 0 file(s) / 0 error(s)'). pnpm --filter @objectstack/spec test: 'Test Files 697 passed (697) · Tests 21267 passed | 1 todo'; typecheck exit 0. pnpm --filter @objectstack/lint test: 'Test Files 145 passed (145) · Tests 6864 passed'; typecheck exit 0. New pin file src/object-default-row-scope.test.ts: 16 passed. pnpm --filter @objectstack/spec check:generated: 'All 14 generated artifacts are up to date'. Ablation (once, at commit 6bea69290 with the pins committed; the implementation blob 1e3792351de2 is unchanged at HEAD): node scripts/ablation-replace.mjs replaced 'if (held.length > 0) return held;' with 'if (held.length >= 0) return held;' — 'anchor 1 -> 0, blob 1e3792351de2 -> c54fe3ed549d', run: 'Tests 10 failed | 6 passed (16)' (the 10 = every default-dependent pin; the 6 = the no-declaration control, authored-alone, held '*', platform admin, authored-explain control, baseline byte-for-byte); restore: 'blob == HEAD (1e3792351de2) and git diff HEAD is empty'; direction observed: turned red, as predicted. No rebuild leg needed: the pin imports ./security-plugin.js from source. test:repo not run locally (diff edits no repo-project test and the derived list does not name it): declared to CI (Test Core).", "gates": "Derived: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) on 1081739c9 -> 117 commands, 15 paths, +786/-100 vs merge base 538f923cb (the seat's 94-command lead plus the docs families the regenerated reference pages pull in). All 117 run on 1081739c9 through a sequential runner, each exit captured before any pipe ('cmd > log 2>&1; e=$?'); --ran: 'Run reconciliation — 117 derived, 117 run, 0 NOT-MEASURED, 0 UNRUN' and '✓ dispatch-gates --ran: 117 derived famil(ies) accounted for'. 116 exit 0. pnpm check:platform-checklist exit 1, 'check-platform-checklist: 1 problem(s)' (ABSENT SYMBOL packages/core/src/security/resolve-authz-context.ts#legacyGrantAnchorRetired in areas/access-security.json); control leg: node scripts/check-platform-checklist.mjs in the shared checkout at e22defb13 (HEAD verified, tree clean, read-only) exit 1 with the same single problem; neither this diff nor the two merged main commits touch docs/qa/** or packages/core/** -> pre-existing. Earlier union on bc1e9e77b had 3 exit-3 prerequisite refusals (check:skill-examples, check:dual-build-cjs-loads, check:i18n), each exit 0 after the build and none recurs on 1081739c9. Local-only: node scripts/check-dev-prereqs.mjs exit 0 on 1081739c9. Changeset gates: check-adr-0087-registration --base origin/main exit 0 ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition ... not-required (no-migration-prescription)'), check-changeset-no-major exit 0, check-empty-changeset exit 0. Control-byte self-scan over the 15 changed files: grep exit 1 (none). CI on PR #24451 head 1081739c9, read once at open: 38 check-runs — 8 success, 5 skipped, 25 in_progress, 0 failed; Build Core, Governed Surface Queue Guard and Check Changeset in_progress; Lint & Repo Gates, TypeScript Type Check and Test Core not yet listed — in_progress, not waited on.", "mcp_calls": "0", "api_writes": "2 — both through the fleet-write relay (scripts/pm/fleet-write/dispatch.mjs), each one POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, executed as POST /repos/objectstack-ai/objectstack/pulls (draft forced) + POST /repos/objectstack-ai/objectstack/issues/24451/assignees (marchtian), read-back '15304 byte(s) sent, 15304 stored — identical ... Assignees: marchtian'; (2) this os-dev-report comment, executed as POST /repos/objectstack-ai/objectstack/issues/24340/comments. No label write (the diff publishes, and the dispatch names no label). git push is not REST.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: exception: release-text · evidence: the pending, unreleased .changeset/24338-object-default-row-scope.md says 'What it does today: nothing. No server reads the key in this release, so declaring it filters no rows ...' and names the liveness-planned-property warning; once PR #24451 lands both changesets compile into the same @objectstack/spec release entry and that bullet is false. Outside this claim's file surface, so not edited; proposed replacement text is in the PR body's Acceptance notes (amend the pending changeset before the release, by a surface extension on this claim or by the spec lane) · dedupe words: 24338 changeset stale, defaultRowScope 'What it does today', pending changeset falsified, liveness-planned-property defaultRowScope", "carrier: domain:services seat 1 (seat post #6021) · noted, not filed · packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:536, the Studio form ledger's omit row for object.defaultRowScope, gives as its why that no server reads the key until collectRLSPolicies does (#24340); test prose now out of date, the omit decision itself still stands; outside the claim's surface", "carrier: domain:services seat 1, serialized behind #23566 (holder of packages/lint/src/rule-explanations.ts) · noted, not filed · extending validate-rls-predicate-enforceability to walk defaultRowScope.using needs the RLS_PREDICATE_UNENFORCEABLE explanation entry reworded (its shared RLS_DROPPED_USING paragraph says the object 'disappears for every holder of the permission set'); the runtime refusal holds meanwhile and is pinned", "carrier: none (boundary, not a filing class) · noted, not filed · validate-org-axis-red-lines rule ① walks permission-set RLS only, so a defaultRowScope.using that walks the org parent is not judged there — a lint not covering metadata the runtime honours; in the PR's Acceptance notes", "carrier: the daily main watchdog (named in contract review 6256153486) · noted, not filed · pnpm check:platform-checklist red at e22defb13 and on this head alike (ABSENT SYMBOL resolve-authz-context.ts#legacyGrantAnchorRetired in areas/access-security.json), untouched by this diff" ], "files_changed": [ ".changeset/24340-default-row-scope-live.md", ".changeset/24340-rls-object-default-scope.md", "content/docs/references/api/metadata.mdx", "content/docs/references/data/object.mdx", "content/docs/references/system/migration.mdx", "packages/lint/src/validate-org-axis-red-lines.test.ts", "packages/plugins/plugin-security/src/explain-engine.ts", "packages/plugins/plugin-security/src/object-default-row-scope.test.ts", "packages/plugins/plugin-security/src/object-default-row-scope.ts", "packages/plugins/plugin-security/src/rls-compiler.ts", "packages/plugins/plugin-security/src/security-plugin.ts", "packages/spec/liveness/object.json", "packages/spec/liveness/state-counts/object.md", "packages/spec/src/data/object.zod.ts", "packages/spec/src/security/rls.zod.ts" ], "line_budget": { "additions": 786, "deletions": 100, "files": 15, "against": "merge base 538f923cb (git diff --numstat 538f923cb HEAD)", "per_file": { ".changeset/24340-default-row-scope-live.md": "+10/-0", ".changeset/24340-rls-object-default-scope.md": "+20/-0", "content/docs/references/api/metadata.mdx": "+1/-1 (generated)", "content/docs/references/data/object.mdx": "+1/-1 (generated)", "content/docs/references/system/migration.mdx": "+2/-2 (generated)", "packages/lint/src/validate-org-axis-red-lines.test.ts": "+8/-5", "packages/plugins/plugin-security/src/explain-engine.ts": "+64/-5", "packages/plugins/plugin-security/src/object-default-row-scope.test.ts": "+439/-0", "packages/plugins/plugin-security/src/object-default-row-scope.ts": "+81/-0", "packages/plugins/plugin-security/src/rls-compiler.ts": "+22/-0", "packages/plugins/plugin-security/src/security-plugin.ts": "+59/-19", "packages/spec/liveness/object.json": "+36/-38", "packages/spec/liveness/state-counts/object.md": "+1/-1 (generated)", "packages/spec/src/data/object.zod.ts": "+31/-23", "packages/spec/src/security/rls.zod.ts": "+11/-5" }, "precedent": "#20023 +510/-1, #20033 +394/-0" }, "deviations": [ "Lint not extended (the order's alternative route): the PR body names why the runtime refusal suffices; extending would need RLS_PREDICATE_UNENFORCEABLE's entry in rule-explanations.ts to change, which the claim excludes (#23566) — reported, not written over.", "Two texts this change falsifies were left unedited because they are outside the claim's file surface: the pending .changeset/24338-object-default-row-scope.md bullet and the form-ledger why at metadata-form-zod-reconciliation.test.ts:536 (see out_of_scope_findings; replacement text in the PR body).", "origin/main (538f923cb, two commits, no file overlap) was merged into the branch before opening the PR, per AGENTS.md Multi-agent §10; build state refreshed, check:generated re-run, the three packages' suites and the full derived union re-run on the merged head.", "Explain shape choice inside the existing schema: when a default applies, the record-grained rls rule is named after the default with via 'object_default' instead of 'business_rls', and the object-level detail gains one sentence; contributors stays [] (a structured object-level field would need a spec change to ExplainLayer's contributor kinds, not taken).", "label/description of defaultRowScope are graded live because explain now prints them to the administrator (README display-key rule); they had no reader otherwise.", "Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and an emoji PR footer; AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) and its session-URL PR footer were used instead, as AGENTS.md takes precedence. No model identifier is in any commit, the PR or this comment.", "The pin file gained one typing-only commit after the ablation (bc1e9e77b, the explain rls-layer accessor); no assertion changed." ], "spec_lane_files": [ "packages/spec/src/data/object.zod.ts", "packages/spec/src/security/rls.zod.ts", "packages/spec/liveness/object.json", "packages/spec/liveness/state-counts/object.md (generated, gen:liveness-counts)", "packages/lint/src/validate-org-axis-red-lines.test.ts", "(spec-derived, not under packages/spec: .changeset/24340-default-row-scope-live.md, and content/docs/references/{data/object,api/metadata,system/migration}.mdx generated by gen:docs)" ], "call_sites": [ "security-plugin.ts:5326 resolvePreImageFloorDrop (by-id write pre-image, step 2.7) — uses .some(isPlatformOwnershipFloorPolicy); default collected only on an empty collection and never the floor (identity provenance), so the answer is unchanged", "security-plugin.ts:5448 checkAuthoredRowWrite (ISecurityService, sharing's by-id widener) — authored pre-check (non-floor with using; empty = abstain); the default counts as the declaration it is, exactly as a held copy would, so it can answer admit for a row it admits (pinned: admit own row, abstain other's, abstain on the no-default control)", "security-plugin.ts:7222 computeLayeredRlsFilter Layer 1 (read door, by-id pre-image, bulk write filter, getReadFilter/analytics, explain) — empty = layer1 null = no filter; the default compiles into Layer 1 and narrows (pinned on reads and the by-id update: RECORD_NOT_FOUND/404 for a row outside it)", "security-plugin.ts:7369 derive-from-select (update/delete with no write-class using) — re-collects select; a select default scopes the derived write as a held select policy does", "security-plugin.ts:7657 computeWriteCheckFilter (post-image check) — writeCheckPolicies over the collection, empty = no check; the default's check (or using) judges the post-image (pinned: handing your own row away = PERMISSION_DENIED/403)", "security-plugin.ts:8907 assertControlledByParentWrite master leg — .some(isPlatformOwnershipFloorPolicy); unchanged for the same reason as :5326", "Line numbers are HEAD's; each is BASE e22defb13's (the PM's :5325/:5447/:7221/:7368/:7656/:8906) plus the one added import line. Empty read as 'no policy, no filter': :7222 (and :7369 behind it) and :7657; none refuses on empty (:5448 abstains, its fail-closed direction; the floor probes answer false); on a compile failure every door fails closed via RLS_DENY_FILTER." ] }objectstack-fleet commentedon Oct 19, 2026
Release-scope note · epic PM
session_01Y5583bs2RDd53MH6kM1ca3(os-steve) · 2026-10-19T04:40Z.target:v18is removed from this card.The maintainer wrote in the epic seat's chat: 「把你建议的那批卡移出 v18」. 18.0 ships for fresh installs, on the
singleposture only, as soon as possible (ruling records 6259608309 and 6259698357 on #15194).This changes only the release target. Your work in flight is not stopped. It lands whenever it is ready and ships in the next release after it merges.
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{ "issue": 24340, "round": "patch round 1 (rework record 6259617731)", "status": "done", "branch": "claude/issue-24340-rls-object-default-scope", "pr": "https://github.com/objectstack-ai/objectstack/pull/24451", "head": "7702f3ca2b0c5899763cca4b8fa96e84de0d1fbb", "merge_commit": "7702f3ca2 (origin/main 26501ae24 merged by scripts/pm/os-regen-merge.sh; no rebase, amend or force-push)", "patch_commit": "ef9a7a01e", "summary": "The two texts this PR made false are corrected. The pending #24338 release note's 'What it does today: nothing' bullet now says collectRLSPolicies applies the key when the caller's held sets carry no row policy for the object and operation, and the liveness-planned-property sentence is gone; level, Clause-② and heading are unchanged. The version release 725a5b131 moved that note to .changeset/pre/24338-object-default-row-scope.md; the merge carried the edit there (blob d5154221dd), the old path is absent, and release.yml's pre-exit GA version consumes .changeset/pre/*.md. The form ledger's omit why for object.defaultRowScope now says the reader is in and a row policy is a composite form-face addition; prose only. No CHANGELOG.md or content/docs/releases path is in the diff.", "gates": "On 7702f3ca2: dispatch-gates --commands derives 117 (17 paths, +788/-102 vs merge base 26501ae24); all 117 run, each through os-verify-lock.sh; --ran: 117 derived, 117 run, 0 NOT-MEASURED, 0 UNRUN. 115 exit 0. check-empty-changeset --base origin/main exit 1 by design (foreign-changeset rule, DELIBERATE CORRECTION class on .changeset/pre/24338-object-default-row-scope.md; Check Changeset is not a required context; skip-changeset not applied). check:platform-checklist exit 1, pre-existing (same single problem at 26501ae24 and e22defb13). spec test 698 files / 21286 passed, typecheck 0, check:generated all 14 up to date, pins 16 passed.", "pr_body": "Not patched by the dev: os-dev.md binds the dev to write the PR body once, at creation, with later edits written by the seat, and that file wins over the dispatch on a conflict. The full replacement body (Acceptance notes reading the two texts as fixed here, the DELIBERATE CORRECTION confirmation request, and the final-head gates) is at /tmp/claude-0/-home-user/c403cb8a-8460-5b17-9923-2cf4d6079655/scratchpad/issue-24340/pr-body-r1.md for one seat write.", "api_writes": "1 — this comment, through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/objectstack/issues/24340/comments). git push is not REST.", "mcp_calls": "0" }objectstack-fleet commentedon Oct 19, 2026
ACCEPT → PR #24451 comment
6260939382·domain:servicesseat 1 (#6021) ·session_01Nk17Z26X5DB59iQztDBADH· 2026-10-19T06:15Z. Head7702f3ca2b. Contract review PASS6260875372atCONTRACT_REVIEW_TIER(the narrowing face). The same record confirms the DELIBERATE CORRECTION of #24338's pending note.Check Changesetenters the queue red by design; the ACCEPT gives the three conditions. Every other check on the head is green or a roster skip. It lands through the merge queue, andFixes #24340closes this card at the merge.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Landed ·
domain:servicesseat 1 (#6021) ·marchtian·session_01Nk17Z26X5DB59iQztDBADH· 2026-10-19T06:55Z. PR #24451 merged through the queue as94e60c00fat 2026-10-19T06:49Z.Fixes #24340closed this card ascompleted.main:git merge-base --is-ancestor 94e60c00f origin/mainexits 0, and the control31ce45c07exits 0. The PR head7702f3ca2bexits 1, as a squash merge should.packages/spec/src/data/object.zod.ts, also carries main's own feat(spec,objectql)!: a top-level action bound to another package's object is listed on that object as served, and an unregistered target fails the boot #24458 (fb0d85b7f), and the PR's hunks are present unchanged.pm:dispatchedis removed and the card is unassigned (marchtian). The read-back matched.Blocked-by: #24341and is filed for triage.extendcontributor that loosens a platform object's default (#24164 ruling E′) #24341: the per-(object, operation) reading that landed, and the pins on the real declarations that card owes.domain:spec, after lint: a locale pack'spicklists.<name>translations are never checked — an option key that is not in the list, or a list no package declares, passesos lint --strictand is dropped silently at runtime #23566.pm:blocked, now withBlocked-by: #24341. Onmain, no platform object declares a default yet (unblock note on plugin-security: organisation admins assign positions and permission sets to their own members inside the D12 boundary — gate branch, overrides, explain and my-delegable-scope, three-posture acceptance (ruling B on #23960) #24136).Generated by Claude Code