Skip to content

fix(plugin-security): the shipped sets stop copying the identity objects' declared row scopes - #24761

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-24484-sets-stop-copying-identity-defaults
Oct 20, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-24484-sets-stop-copying-identity-defaults

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24484
Clause-②: no

The follow-up that ruling E′ item 3 (comment 6250748677 on #24164) names, as #24340's body records it. organization_admin (and so the derived organization_admin_no_bypass), member_default and viewer_readonly no longer carry copies of the row scope that thirteen identity objects now declare as their own defaultRowScope (declared by #24341). The thirteen are the seven SCIM projection tables, sys_verification and sys_jwks, and the select self scope of sys_account, sys_team_member, sys_oauth_access_token and sys_oauth_refresh_token. The security plugin's collection applies an object's default wherever the caller's held sets carry no policy for that object and operation, so rows do not move. Explain now names the object's default (via: 'object_default') where it named a set's copy, as triage (6261582931) recorded the ruling accepts. No schema, via value, object declaration, core code or packages/spec file changes. It runs after #24136 (landed as b27f8de), which edited the same file; this branch starts on the tree that holds it.

Per-copy byte-identity readings (taken on origin/main 4a2ca57)

Each shipped set's policy for each candidate object, compared field by field (name, object, operation, using, check, positions, enabled) with the object's declared defaultRowScope plus object. Sets read: organization_admin, organization_admin_no_bypass, member_default, viewer_readonly.

Family Objects Copies read Identical Disposition
SCIM sys_scim_user, sys_scim_subject, sys_scim_projection_grant, sys_scim_identity_tombstone, sys_scim_group, sys_scim_group_member, sys_scim_connection_binding 28 28 dropped
credential sys_verification, sys_jwks 8 8 dropped
_self, select sys_account, sys_team_member, sys_oauth_access_token, sys_oauth_refresh_token 16 16 dropped
ledger sys_audit_log (sys_audit_log_org) 4 4 kept, measured (below)

56 of 56 copies were byte-identical. The only '*' policies in any shipped set are member_default's owner_only_writes (update) and owner_only_deletes (delete), so after the drop each holder's select collection on the thirteen objects is empty and collects the default. On the head, the same reading finds only the four ledger copies left among the candidates, and the must-keep rows unchanged.

What stays, and why

  • Pointer 6280763190's must-keep list, untouched: sys_user_self (all) in member_default, and every all self scope (sys_session, sys_two_factor, sys_user_preference, sys_api_key, sys_device_code, sys_oauth_consent, sys_oauth_application, sys_organization). A member's update and delete collections hold the '*' ownership floor, so the default is never collected there. Also kept, outside the pointer's may-drop list: sys_user's two select policies in organization_admin and viewer_readonly (they differ from the merged sys_user declaration), and viewer_readonly's select spellings of objects whose default is all (they differ by operation).
  • sys_audit_log_org: kept, although it is byte-identical and on the pointer's may-drop list. I measured it before dropping it. When no organization wall is enforced, collectRLSPolicies strips the platform's own tenant policies by provenance (isPlatformTenantPolicy). platform-tenant-policies.ts builds those provenance keys from the shipped sets' policies. With no set carrying the copy, the ledger's identical default is no longer recognised as the platform's. It survives the single strip as an authored tenant policy and narrows the ledger read. Ablation A3 below measures it under single: the viewer, both organization-admin variants and their agents went from all three ledger rows to one. sys-audit-log-row-scope.test.ts already pins the provenance half (the shipped copies are non-empty and each is a platform tenant policy). The helper's comment now says why the copy stays.

Rows: the before/after served-ids run

New pin: packages/plugins/plugin-security/src/objects/shipped-sets-identity-default-row-scope.test.ts, modelled on default-row-scope-declarations.test.ts. It runs on the real SecurityPlugin + ObjectQL + SqlDriver, with the real declarations and a security catalog of the shipped sets. It covers three deployment shapes: walled with the platform baseline, walled with no platform baseline (fallbackPermissionSet: null, each principal resolves only the set it names), and single. The personas are a member_default holder, a viewer_readonly holder, both organization-admin variants, a user in no organization, delegated MCP agents for the member, the viewer and the organization admin (ruling B, 6278784335), and the platform administrator as a control. Each persona's served ids are asserted against the ids written out per family, on all 14 touched objects. They are also asserted equal to what the same deployment serves with the dropped copies spread back into the sets, on those objects plus sys_user and the eight all objects.

Tree Served-ids block (9 tests: 3 shapes x listed ids, copies restored, control) After-state pins (explain, census, authored policy, no copy in any set)
origin/main 4a2ca57, sets unchanged 9/9 pass (plus the two keep pins and the private-object control) 20 red, by design: the copies are still there
head e12d958 9/9 pass all pass: 32/32 in the file

Explain moves, as ruled

Pinned per family against the copies-restored tree:

  • SCIM: a member's record-grained explain of its own sys_scim_user row gives the rule sys_scim_user_self with via: 'object_default'. The tree with the copy gives business_rls and no via. The object-level read of sys_scim_group names object default row scope 'sys_scim_group_none' as its source, for all four set holders.
  • _self: the same for sys_account / sys_account_self.
  • credential: the object-level reads of sys_verification and sys_jwks name sys_verification_none / sys_jwks_none. The verdict is unchanged.
  • sys_audit_log_org: not moved, because the copy is kept (above).
  • Census: every human persona collects the object default on exactly the thirteen dropped objects' select, and on no other object or operation. With the copies restored, none. A delegated agent's own leg collects none (ruling B, whole).

An authored policy still wins

A member who also holds an application set authoring sys_account id == null reads no sys_account row, and its collection is exactly that policy. The control is the same member without the set, who reads its own row under the default. Measured change: on 4a2ca57 this holder read its own row, because member_default's copy was OR-ed in. An authored scope that left out the holder's own rows was widened back to them. Now the authored policy alone decides on that object and operation. This only ever narrows. No package or example in this tree authors a set policy on any of the thirteen objects (git grep over packages, examples and apps, tests excluded: 0 hits).

Ablations (each pin red under the forbidden change, then restored)

Mutations went through scripts/ablation-replace.mjs, which checks the anchor count, the replacement count and the blob change on disk. Restores used git checkout HEAD -- PATH and were proven by the blob hash equalling HEAD's and by an empty git diff HEAD. No build was involved: the pin imports the sets from source. All four ran on 48e8f6d.

Leg Forbidden change Red Green, as expected
A1b drop member_default's sys_oauth_application_self (all) keep pin (member write collection), census served ids
A2 drop member_default's sys_user_self (all) keep pin (write collection), sys_user_self stays all served ids
A3 drop sys_audit_log_org from all three sets served ids under single (viewer, org admins and their agents: 3 ledger rows to 1), census, ledger-copy pin served ids walled, both shapes
A4b re-add sys_account_self (select) to member_default no-copy pin, _self explain pin, census, authored-policy pin served ids

The first A1 and A4 attempts never ran. The tool refused both before any test, because their replacement overlapped their anchor. They were re-anchored as A1b and A4b.

Also in this diff

  • Existing pins restated where they asserted the copies stay: default-row-scope-declarations.test.ts (the thirteen declare the literal written out and no set carries it; the behaviour-neutrality comparison covers the ten declarers the sets still carry; the census expects the thirteen select defaults), object-default-row-scope.test.ts (sys_scim_user moves out of its byte-for-byte block into a block showing the declaration is the scope), and rbac-objects.test.ts (the member_default roster).
  • private-credential-row-scope.test.ts: the header says where the scope now lives, and the agent_x persona (pointer 6280763190, last bullet) now has the shape its one producer emits: userId is the delegating member, onBehalfOf.principalKind: 'human'. scim-projection-row-scope.test.ts gets the same header correction, and its agent comment now states ruling B.
  • Outside the claim's declared file surface, comment-only, declared here: the 13 packages/platform-objects/src/identity/*.object.ts comments above each defaultRowScope said "the shipped sets still carry it". That is false after this change. Only // lines changed; no declaration byte moved, and the declaration pin reads each value.
  • Deliberate correction of a pending release note: .changeset/24341-identity-objects-default-row-scope.md. Three sentences stated that the shipped sets carry each policy, which this change makes false before that note ships. They now say each object's policy is either still carried or collected as the identical default. check-empty-changeset is red on purpose: its own text says a deliberate correction is confirmed on the PR, not routed around. Please confirm this correction. The other platform-objects: the identity objects declare their default row scope (the literals the shipped sets copy today), and the registry refuses an extend contributor that loosens a platform object's default (#24164 ruling E′) #24341 note sentences are about that change's own effect and stay true.
  • New changeset: .changeset/24484-sets-stop-copying-identity-defaults.md, @objectstack/plugin-security patch.

Tests and gates (head e12d958)

  • pnpm --filter @objectstack/plugin-security test: 191 files, 3926 passed, 45 skipped, exit 0.
  • pnpm --filter @objectstack/plugin-security typecheck (tsc, scripts, test layer): exit 0. File-scoped eslint --no-inline-config on the seven touched plugin-security files: exit 0.
  • pnpm --filter @objectstack/platform-objects test: 1195 passed. typecheck: exit 0.
  • Closure build pnpm --filter '@objectstack/plugin-security^...' build: exit 0, run on the merged tree.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 67 families. Each was run locally with its exit captured before any pipe. 64 exited 0. check-empty-changeset --base origin/main exited 1: the deliberate correction above. check:dual-build-cjs-loads and check:i18n exited 3, PREREQUISITE NOT MET (they need a whole-repo build), so they are NOT MEASURED here and CI's Lint & Repo Gates measures them. --ran reconciliation: 67 accounted for, 65 run, 2 NOT MEASURED, 0 UNRUN.
  • test:repo was not run locally; CI's Test Core runs it.

Acceptance notes

  • Provenance reads the sets, not the declarations. platform-tenant-policies.ts keys "this is the platform's tenant policy" off the shipped sets. The ledger's copy therefore cannot go until provenance also recognises an object's declared default. Dormant today, because the copy stays. Carrier: none.
  • Agents on private objects: a read ceiling's '*' does not reach sys_verification, sys_jwks, sys_oauth_access_token or sys_oauth_refresh_token, so the agent personas are refused there before any row scope applies. That is identical on both trees, and the pin lists it.

Generated by Claude Code

… shipped sets before dropping their row-scope copies

The served-ids block passes on the base tree; the after-state pins (explain,
census, authored policy, no copy in any set) are red there by design.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
…cts' declared row scopes

Drops the select-spelled copies of the row scope thirteen identity objects
now declare as their own defaultRowScope (the seven SCIM projection tables,
the two credential tables, and the self scope of sys_account,
sys_team_member and the two OAuth token tables) from organization_admin
(and so its no-bypass variant), member_default and viewer_readonly. Keeps
sys_user's policies, every all-spelled self scope, and sys_audit_log_org,
whose copy is the provenance that lets the wall-less posture strip the
object's identical default.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
…opies; the objects' comments and the changesets say where the scope lives

The existing pins that asserted the shipped sets keep their copies now
assert the thirteen declarations stand alone and are the scope every
holder reads under; the credential and SCIM pins' headers say so, and the
credential pin's agent takes the shape its one producer emits. The
platform objects' comments drop the claim that the sets still carry the
copy, and the sibling changeset's state sentences stay true after this
change.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/platform-objects, @objectstack/plugin-security, touching 44 documentable anchor(s).

12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/validation-rules.mdx (via sys_account (literal, a string literal in rowLevelSecurity))
  • content/docs/deployment/cli.mdx (via sys_account (literal, a string literal in rowLevelSecurity))
  • content/docs/deployment/self-hosting.mdx (via sys_account (literal, a string literal in rowLevelSecurity))
  • content/docs/permissions/access-recipes.mdx (via rowLevelSecurity (symbol, a field of const object baseDefaultPermissionSets))
  • content/docs/permissions/authentication.mdx (via sys_account (literal, a string literal in rowLevelSecurity), sys_verification (literal, a string literal in privateCredentialRowScope))
  • content/docs/permissions/authorization.mdx (via sys_jwks (literal, a string literal in privateCredentialRowScope), sys_oauth_access_token (literal, a string literal in rowLevelSecurity), sys_oauth_refresh_token (literal, a string literal in rowLevelSecurity), sys_verification (literal, a string literal in privateCredentialRowScope))
  • content/docs/permissions/permission-metadata.mdx (via rowLevelSecurity (symbol, a field of const object baseDefaultPermissionSets))
  • content/docs/permissions/permission-sets.mdx (via rowLevelSecurity (symbol, a field of const object baseDefaultPermissionSets))
  • content/docs/permissions/rls.mdx (via rowLevelSecurity (symbol, a field of const object baseDefaultPermissionSets))
  • content/docs/permissions/sharing-rules.mdx (via rowLevelSecurity (symbol, a field of const object baseDefaultPermissionSets))
  • content/docs/protocol/objectql/security.mdx (via rowLevelSecurity (symbol, a field of const object baseDefaultPermissionSets))
  • content/docs/ui/setup-app.mdx (via sys_jwks (literal, a string literal in privateCredentialRowScope), sys_verification (literal, a string literal in privateCredentialRowScope))

⛔ 8 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/implementation-status.mdx (via rowLevelSecurity (symbol, a field of const object baseDefaultPermissionSets))
  • content/docs/releases/v15.mdx (via sys_jwks (literal, a string literal in privateCredentialRowScope))
  • content/docs/releases/v17/17-0.mdx (via sys_account (literal, a string literal in rowLevelSecurity), sys_team_member (literal, a string literal in rowLevelSecurity))
  • content/docs/releases/v17/17-1.mdx (via sys_account (literal, a string literal in rowLevelSecurity))
  • content/docs/releases/v17/17-3.mdx (via sys_team_member (literal, a string literal in rowLevelSecurity))
  • content/docs/releases/v17/17-5.mdx (via rowLevelSecurity (symbol, a field of const object baseDefaultPermissionSets), sys_account (literal, a string literal in rowLevelSecurity), sys_jwks (literal, a string literal in privateCredentialRowScope), sys_verification (literal, a string literal in privateCredentialRowScope))
  • content/docs/releases/v17/17-6.mdx (via rowLevelSecurity (symbol, a field of const object baseDefaultPermissionSets), sys_jwks (literal, a string literal in privateCredentialRowScope))
  • content/docs/releases/v17/17-7.mdx (via sys_account (literal, a string literal in rowLevelSecurity), sys_verification (literal, a string literal in privateCredentialRowScope))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 17 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 580fd4ed3c98a1752dfb0041b45cc713644fef7e → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 902318f3a63d9f459a47ce7d2fbf210d40c15757 — the merge of head e12d958692f49558a8b0e5341a00c3016c1d39d8 into base 580fd4ed3c98a1752dfb0041b45cc713644fef7e, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 902318f3a63d9f459a47ce7d2fbf210d40c15757 && git checkout 902318f3a63d9f459a47ce7d2fbf210d40c15757
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 580fd4ed3c98a1752dfb0041b45cc713644fef7e e12d958692f49558a8b0e5341a00c3016c1d39d8 && git checkout -B drift-repro 580fd4ed3c98a1752dfb0041b45cc713644fef7e && git merge --no-ff e12d958692f49558a8b0e5341a00c3016c1d39d8

node scripts/docs-audit/affected-docs.mjs --json 580fd4ed3c98a1752dfb0041b45cc713644fef7e

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 580fd4ed3c98a1752dfb0041b45cc713644fef7e → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:services seat 1 · 2026-10-20T05:36Z

os-justin · session_011TdZ8GjixNZFLz1xJD9zzK (seat post #6021). Reviewed against GitHub and origin/main 795ff708d8, not against the report (os-dev-report on #24484, posted with PR #24761). Disclosure read first: the PR, the changesets and the report state which row policies move and which stay, and give no recipe for reading another user's identity rows. Head under review:

e12d958692f49558a8b0e5341a00c3016c1d39d8
  1. Shape. Draft, base main, first line Fixes #24484, line-start Clause-②: no in the body and the new changeset. The body names 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, [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, 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 and platform-objects: the identity objects declare their default row scope (the literals the shipped sets copy today), and the registry refuses an extend contributor that loosens a platform object's default (#24164 ruling E′) #24341 with no keyword beside any of them. 22 files, +850/−385, none on a governed surface (governedPathsIn answers []). The trial merge onto origin/main is clean. No contract review is owed: no published schema, no governed text, and no Clause-②: yes or (narrowing) declaration of this PR's own. The no (narrowing) line inside the corrected platform-objects: the identity objects declare their default row scope (the literals the shipped sets copy today), and the registry refuses an extend contributor that loosens a platform object's default (#24164 ruling E′) #24341 note is platform-objects: the identity objects declare their default row scope (the literals the shipped sets copy today), and the registry refuses an extend contributor that loosens a platform object's default (#24164 ruling E′) #24341's own, reviewed with PR feat(platform-objects)!: the identity objects declare their default row scope, and the registry refuses an extend that sets one #24623.

  2. What was dropped, read by the seat on both trees (named policies in default-permission-sets.ts, origin/main against the head):

    • Gone: sys_account_self, sys_team_member_self, sys_oauth_access_token_self and sys_oauth_refresh_token_self (3 → 0 each); the seven SCIM policies; sys_verification_none and sys_jwks_none. The two helpers are no longer spread.
    • Unchanged, every must-keep item of pointer 6280763190: sys_user_self and sys_user_org_members (3 and 3), and the all literals sys_session_self, sys_two_factor_self, sys_user_preference_self, sys_api_key_self, sys_device_code_self, sys_oauth_consent_self, sys_oauth_application_self and sys_organization_self.
    • Kept against the pointer's may-drop list: sys_audit_log_org (1 → 1). This is the measured exception the dispatch provided for. Tenant-policy provenance (platform-tenant-policies.ts) is read off the sets, and ablation A3 shows the single ledger read moving without the copy.
  3. Rows do not move; explain does.

    • The per-copy byte-identity reading covers 56 of 56 copies.
    • The new real-stack pin (shipped-sets-identity-default-row-scope.test.ts) asserts served ids per persona, agents included, under isolated with the baseline, isolated without it, and single. It passes 9 of 9 on origin/main with the sets unchanged and 32 of 32 on the head.
    • Explain names via: 'object_default' for one object per dropped family.
    • Ablations A1b, A2, A3 and A4b each turn red as predicted, with the restore proven by blob equality.
  4. Prose read sentence by sentence against the diff.

    • .changeset/24484-sets-stop-copying-identity-defaults.md:
      • the thirteen objects named;
      • "byte-identical";
      • the collection rule;
      • the three postures, the agent and the no-baseline case, as pinned;
      • the explain sentence;
      • the two "Kept" bullets, which match point 2;
      • the authored-policy paragraph.
    • The corrected .changeset/24341-identity-objects-default-row-scope.md: "carried", the second paragraph's either/or, and the baseline bullet's either/or. These are exactly the three sentences this PR made false. The rest of that note is unchanged and still true.
    • The 13 platform-objects identity files: only // lines change; no declaration byte moves. Declared to domain:engine on [PM seat] domain:engine — 🟢 os-project-manager #6367 (6283774501).
  5. Gates. At ACCEPT, every check-run on this head is success or an expected skip, except Check Changeset, which is red by design. Its one failing step is "Reject an empty-frontmatter changeset added by this PR": check-empty-changeset.mjs's foreign-changeset refusal on .changeset/24341-identity-objects-default-row-scope.md, the DELIBERATE CORRECTION class. The same job's "Require a changeset", ADR-0087 disposition, live allow-major read and major guard all pass. The three conditions for queueing past a designed red hold:

    • the gate's own source says this class stays red on the pushed branch (check-empty-changeset.mjs "Past a designed red", and its remedy text "this gate stays red either way");
    • pr-automation.yml triggers on pull_request only, never merge_group;
    • this comment records the gate and the reason.

    Locally, 65 of 67 derived families were run (64 exit 0; check-empty-changeset exit 1, the same designed red). 2 were NOT MEASURED (exit 3, prerequisite); CI's lint lane measures them.

  6. The dev's open questions, answered by the seat.

  7. Accepted deviations.

  8. Out-of-scope dispositions. Tenant-policy provenance read off the sets rather than off object declarations is dormant while the copy is kept: Acceptance notes. It becomes owed only if sys_audit_log_org's copy is ever to leave the sets.

  9. Landing clause. Tier S: not governed, under the human-merge threshold. Next step: ready + auto-merge through the relay once every other check on the current head is green or an expected skip, carrying Check Changeset's designed red under point 5. A push resets this reading.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 20, 2026 05:38
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 20, 2026 05:38
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit 7d45c8e Oct 20, 2026
40 of 41 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24484-sets-stop-copying-identity-defaults branch October 20, 2026 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

2 participants