Skip to content

fix(plugin-auth)!: a user preference's owner is fixed when it is saved - #29685

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-57qwum
Nov 3, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-57qwum

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread
Fixes #29630

Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none — no skill or doc teaches changing a saved preference's owner; the one page that names the rule, content/docs/permissions/system-context.mdx row 26c, is updated here

维护者速读

  • 改了什么:sys_user_preference 加一条保存规则:更新时若把 user_id 改成另一个用户,一律 403 PERMISSION_DENIED(系统写除外),与 feat(platform-objects): sys_personal_view, a reader's own named list views, with the owner's delete door (server step) #29575 给 sys_personal_view 的规则同形;两个对象现在共用 plugin-auth 里的一份检查 owner-fixed-rule.ts。
  • 为什么改:用户偏好属于保存它的那个用户,规则此前没有对这个对象成立。
  • 风险与回滚:只收窄一种写入(改偏好的属主);只改 value 的写入、回显原属主的写入、系统写入都不受影响。回滚即 revert 本 PR。
  • 先看:packages/plugins/plugin-auth/src/owner-fixed-rule.ts(共用检查)、user-preference-hooks.ts(注册与对象判定)、personal-view-hooks.ts(改为调用共用检查,文案不变)。
  • 两分钟验证:pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/user-preference-owner-fixed.dogfood.test.ts,预期 7/7 通过,三种调用者各一条 403。
  • 需要你做的:无,席位落地。

What changes

  • New rule on sys_user_preference. A non-system update that sends a user_id other than the stored one is refused 403 PERMISSION_DENIED, for a member, an organization administrator and a platform administrator alike. The refusal names the row and the user_id column and echoes no other id. An update that echoes the stored user_id, or does not send it, saves as before; a system write is not judged.
  • One shared check. packages/plugins/plugin-auth/src/owner-fixed-rule.ts (isOwnerChange, ownerFixedError, assertOwnerUnchanged) now holds the rule for both sys_personal_view and sys_user_preference. personal-view-hooks.ts calls it instead of its inline copy; its behaviour and message are byte-identical. Not exported from a published entry.
  • Registration. user-preference-hooks.ts registers one beforeUpdate hook on sys_user_preference, in AuthPlugin and in IdentityObjectsPlugin, beside the personal-view hooks, so the rule travels with the table.
  • Docs. content/docs/permissions/system-context.mdx row 26c covers both objects and anchors on the shared check (register regenerated).
  • Changeset. .changeset/29630-user-preference-owner-fixed.md: @objectstack/plugin-auth patch, BREAKING banner, both declaration lines, ADR-0087 not-required.

Resumed from claude/project-thread-rq4y7m@34568e18 (the previous card thread's hand-off), merged with origin/main 7dae3b3c. The last commit trims the shared check's header and the changeset's "Who sees it" line to the rule alone.

Why

The card asks for the sys_personal_view shape (#29575) on this object: a user's preferences are their own UI state, and their owner is fixed when they are saved.

Tests

On 424e9295:

  • packages/plugins/plugin-auth/src/user-preference-hooks.test.ts: registration; a member, an org admin and a platform admin are refused 403; the message names the row and column and no other id; clearing the owner is a change of owner; controls: echo, value-only update, system write, another object.
  • packages/qa/dogfood/test/user-preference-owner-fixed.dogfood.test.ts (REST): member, org admin and platform admin cannot hand their own preference to another user (403, the row keeps its owner); controls: echo saves; the owner reads, lists and updates their own preference.
  • pnpm --filter @objectstack/plugin-auth build && test && typecheck under the lock: VERDICT command-exit 0, Test Files 216 passed (216), Tests 3703 passed, 10 skipped.
  • Full dogfood battery (pnpm --filter @objectstack/dogfood test, the (narrowing) arm) under the lock on the full build: VERDICT command-exit 0, Test Files 400 passed, 3 skipped (403); Tests 3304 passed, 28 skipped (3332).
  • Ablations (previous card thread, on 3bbd5da8, each red then restored with the blob equal to HEAD and git diff HEAD empty): dropping the rule call reds the 5 unit refusal pins and keeps the 5 controls green; making the shared check always false reds 6 pins across both objects; guarding the registration (plugin-auth rebuilt, dist-preflight marker present, then --absent after restore) reds the member and org-admin REST pins. The platform-admin REST pin stays green under that last ablation because an existing write check already refuses that caller's write there; the unit pin holds the rule on its own for that caller. Since then only comments and the changeset changed in tested files.

Acceptance notes

  • Clause-②: no (narrowing): one accepted write is now refused, and no public export or published type moves.
  • The ablation readings are carried from the hand-off report on the card; the tested code is unchanged since.

Verification

Derived gates on 424e929
  • Full build under the lock (pnpm build --concurrency=2): VERDICT command-exit 0, Tasks 117 successful, 117 total. History unshallowed for the teardown self-test fixture.
  • node scripts/pm/dispatch-gates.mjs --commands (9 paths vs merge base 7dae3b3c): 95 families. Each run once, stdin from /dev/null, exit captured before any pipe: 95 exit 0, including check-plugin-teardown-shape.mjs --self-test (48 cases), spec check:skill-examples (282 prose examples), check:dual-build-cjs-loads (115 entry points across 70 packages), check:system-context-census and check:type-check-debt (0 raw tsc errors above the ledger).
  • dispatch-gates --ran: 95 derived, 95 run, 0 NOT-MEASURED, 0 UNRUN.
  • pnpm lint: exit 0.

Generated by Claude Code

One shared owner-fixed rule (owner-fixed-rule.ts) now holds the check
sys_personal_view carried inline, and sys_user_preference registers it
too: a non-system update that sends a user_id other than the stored one
is refused 403 PERMISSION_DENIED, for every caller but the system.

Claude-Session: https://claude.ai/code/session_01ADHkmTNFJm8TGKvyoyKegL
Co-authored-by: Claude <noreply@anthropic.com>
… REST

The rule is judged on the hook for every non-system caller, and over
the real showcase stack for a member, an organization administrator
and a platform administrator, with the echo and own-row controls. The
system-context census row covers both per-user objects on the shared
check, and the changeset records the narrowing.

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

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-auth, touching 16 documentable anchor(s).

41 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 06881565bdced6c949f05d59f0059a3ff7409924.

⛔ 7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 cross-cutting symbol(s) contributed no route anchor: packageId (4 routes)
  • 1 anchor(s) matched too much of the corpus to be a work list: PERMISSION_DENIED (literal, 51 pages)
  • 5 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 216 client-bound route-ledger rows — the other 156 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 156: 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; 101 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 — 22 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 06881565bdced6c949f05d59f0059a3ff7409924 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 2be4faf22c491ddabe4c8430a1b493aabf6aa216 — the merge of head 424e92959b28d0338b269e7452de3c3fc994fad6 into base 06881565bdced6c949f05d59f0059a3ff7409924, 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 2be4faf22c491ddabe4c8430a1b493aabf6aa216 && git checkout 2be4faf22c491ddabe4c8430a1b493aabf6aa216
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 06881565bdced6c949f05d59f0059a3ff7409924 424e92959b28d0338b269e7452de3c3fc994fad6 && git checkout -B drift-repro 06881565bdced6c949f05d59f0059a3ff7409924 && git merge --no-ff 424e92959b28d0338b269e7452de3c3fc994fad6

node scripts/docs-audit/affected-docs.mjs --json 06881565bdced6c949f05d59f0059a3ff7409924

⚠️ 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 06881565bdced6c949f05d59f0059a3ff7409924 → 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

domain:server#2 · os-warren · session_01JQwxbXKRzgnVvjzxaYxLUf

Contract-review-claim: session_01JQwxbXKRzgnVvjzxaYxLUf · head 424e929 · 2026-11-03T01:27Z

Face: Clause-②: no (narrowing) ⇒ CONTRACT_REVIEW_TIER, scope = the diff outside prose. Served by an independent at-tier subagent; the seat posts its record.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · os-warren · session_01JQwxbXKRzgnVvjzxaYxLUf

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 424e92959b28d0338b269e7452de3c3fc994fad6
Local-runs: none
Scope: packages/plugins/plugin-auth/src/auth-plugin.ts, packages/plugins/plugin-auth/src/identity-objects-plugin.ts, packages/plugins/plugin-auth/src/owner-fixed-rule.ts, packages/plugins/plugin-auth/src/personal-view-hooks.ts, packages/plugins/plugin-auth/src/user-preference-hooks.ts, packages/plugins/plugin-auth/src/user-preference-hooks.test.ts, packages/qa/dogfood/test/user-preference-owner-fixed.dogfood.test.ts + packages/platform-objects/src/identity/sys-user-preference.object.ts, packages/plugins/plugin-auth/src/manifest.ts, packages/plugins/plugin-auth/src/index.ts, packages/plugins/plugin-auth/src/identity-write-guard.ts, packages/plugins/plugin-security/src/system-write-guard.ts, packages/plugins/plugin-security/src/objects/default-permission-sets.ts, packages/objectql/src/engine.ts, packages/objectql/src/engine-hook-dispatch.ts, packages/rest/src/data-action-routes.ts, ui/packages/data-objectstack/src/userState.ts, content/docs/permissions/system-context.mdx (row 26c, read), .changeset/29630-user-preference-owner-fixed.md (read for ②)

Narrowing face on card #29630, a security card. Per the security-findings rule (triage 6590532575, #29111) this record states the rule the PR restores and the judgments of it, and nothing of mechanism, reproduction or path. Read: the card body and all six comments (triage, two claims, two dev reports, the hand-off), the PR body, its file list, the net diff against main at this head, the check-runs on this head, and the precedent record 6590198210 on PR #29575. The cited code was read on origin/main. Nothing was built, run or re-run.

① Derived judgments

The rule the PR restores. A sys_user_preference row's owner is fixed when the row is saved: a non-system update that sends a user_id other than the stored one is refused 403 PERMISSION_DENIED, for a member, an organization administrator and a platform administrator alike; an update that echoes the stored owner or does not send it saves as before; a system write is not judged. It is the rule PR #29575 gave sys_personal_view, now held by one shared check.

Change the diff implies Judgment
New beforeUpdate rule on sys_user_preference, refusing a change of user_id for every non-system caller Right. It is the shape the card asks for (step 2), the shape the precedent ruled right on main for the twin object, and the reason row 26c already states on main applies to this object as written (sys-user-preference.object.ts declares the same owner-column defaultRowScope, user_id is a plain lookup, and no hook on main names the object). The refusal is judged before any write, keeps the ADR-0112 pair, names the row and the column and echoes no other id (pinned).
An echo of the stored owner, or a payload without user_id, is not judged Right. The console's own writer (userState.ts) updates { value } only and creates in the caller's name, so the one shipped consumer is untouched; the echo control is pinned at both levels.
The system skip (ctx.session.isSystem === true) Right as a platform-writer skip. No shipped system writer updates this object (git grep on main: no src writer names it outside its object file, the permission sets and the manifest). The same incompleteness the precedent named as its S1 is carried over unchanged; see ④.
Every non-system update dispatch reaches the rule with ctx.previous bound Right on main. The by-id path binds the prior row before the before-phase dispatch; the predicate (multi) path dispatches per matched row with that row as previous (engine-hook-dispatch.ts); the REST import and batch run under the caller's own context. The rule's !previous early return therefore never admits a non-system change on a shipped path. Unchanged from the precedent's reading.
sys_personal_view moved onto the shared check Right, behaviour-preserving. isOwnerChange is the inline predicate verbatim (same sameId normalisation, same sets test, same system skip), ownerFixedError with { label: 'Personal view', noun: 'view' } renders the former message byte-for-byte, and the call sits at the same point of validateOnUpdate, before the config check. The ctx.object guard stays in the caller, so assertOwnerUnchanged is never reached for another object. The 10 skipped and 3703 passing plugin-auth tests on the dev report include personal-view-hooks.test.ts unchanged.
ownerChangeError removed from personal-view-hooks.ts; owner-fixed-rule.ts and user-preference-hooks.ts added No public surface moves. Neither module is reachable from packages/plugins/plugin-auth/src/index.ts or the package exports on main or on this head, and ownerChangeError has no consumer on main outside its own file.
Registration in AuthPlugin.start and IdentityObjectsPlugin.start, under distinct packageIds Right. It mirrors the personal-view registration line for line, so the rule travels with the table on both kernels; IdentityObjectsPlugin.init() refuses a composition beside AuthPlugin on main, so the hook is never bound twice. The comment's claim that the manifest registers both objects is true (manifest.ts lists SysUserPreference and SysPersonalView).
Row 26c of system-context.mdx widened to both objects, anchor moved to owner-fixed-rule.ts#isOwnerChange, register row regenerated Right in substance; check:system-context-census is green on this head (Lint, docs and code guards).
Unit pins (user-preference-hooks.test.ts) Hold the rule on its own: three callers refused, message shape, clearing the owner refused, four controls, and the registration shape (one beforeUpdate hook, nothing on create or delete). The fake-engine harness is right for a hook whose whole input is the context.
REST pins (user-preference-owner-fixed.dogfood.test.ts) Right in shape: three callers refused on their own row, two administrators do not reach a member's row by id, echo and value-only controls. Note: the dogfood job in ci.yml is push/schedule-gated, so on this PR head the three shards are skipped and the only reading of these pins is the dev's local battery on the card (6592969538: 400 files, 3304 tests, 0 failures). Not re-judged here; the push run after merge is the CI reading.
Dev's claim that the platform-admin REST pin stays green when the registration is ablated Not re-judged (read-only). It makes no claim the record relies on: the unit pin holds the rule for that caller.

Check-runs on this head, as read: 49 runs; 31 success, 12 skipped (opt-in, matrix-gated or ui-path-gated, the dogfood shards among them), none failed, and 6 pending at the time of the last read, all of them Test Core shards (7/8 and 8/8 had already concluded success). Green: Build Core, Build Docs, Check Changeset, PR-body guards, Governed Surface Queue Guard, Check PR Size, Spec property liveness, Type Check (debt ledger, consumer, source), Lint (ESLint, PM dispatch-gates, docs and code guards, CI/release/engine gates, query-options erasure, tooling self-tests), Live E2E UI, Test UI, Dogfood Regression Gate (umbrella). The seat lands only once Test Core concludes; nothing in this diff touches a core package other than plugin-auth, whose own suite the dev ran green on this head.

② Semver level

  • @objectstack/plugin-auth: patch holds. Metadata-protocol: none is true (no key, value, type or default an author writes against moves), and AGENTS.md's rule is that a non-protocol change, widening or breaking, ships patch with its BREAKING banner and ADR-0087 disposition; the precedent (feat(platform-objects): sys_personal_view, a reader's own named list views, with the owner's delete door (server step) #29575) shipped the same rule at patch. The changeset carries the banner, both declaration lines and exactly one adr-0087: not-required (no-migration-prescription) marker, and Check Changeset is green.
  • Clause-②: no (narrowing) holds. The line answers whether the card widens the accept set or the public surface (clause2-line.mjs): it does neither. One accepted write is now refused on one object, no export, route, object or type is added, and the only removed symbol is module-internal. The card body's yes (narrowing) was the filing seat's guess; both claims and the precedent's reading correct it, and (narrowing) is the arm that carries BREAKING either way.
  • No @objectstack/platform-objects line is needed: the object file is unchanged.
  • The changeset prose matches the tree: now, unchanged, shared rule, who sees it, remedy. The remedy sentence is the one the error message gives.

③ Boundary flags

Dev deviation 1 (one commit beyond the hand-off, comment and changeset wording only): verified; 424e9295 touches the shared rule's header comment and one changeset line, no code. Right, and the trim to the rule alone is what triage 6590532575 asks.

Dev deviation 2 (PR template headings folded into the os-dev order): prose, not reviewed.

Dev deviation 3 (no (narrowing), not yes): right; ② above.

Dev deviation in the hand-off (the delta's engine-rig shape not used for the unit pins): accepted; the fake-engine harness is the right instrument for a context-only hook, and the real-stack face is the REST file.

Open question, step 3 of the card (the class: close per object here, or one platform-level card for the update floor): escalated to the seat, with this reading of main in support of the dev's option B. Every other platform object whose default row scope is its own user_id column lives in the better-auth bucket, whose user-context writes identity-write-guard.ts refuses outright; member_default grants allowEdit on exactly sys_user, sys_user_preference and sys_personal_view; so the shipped population the rule has to close is the two system-data objects, and this PR closes the second. What the per-object rule cannot reach is an app-authored object scoped on its own owner column, which is the platform-level question the dev names. The dev's census itself is off-board and was not read. Fixes #29630 is right if the seat files the floor card through the advisory channel; otherwise Part of.

Open question in the hand-off (closing keyword): the same answer.

Escalated: the step-3 ruling, above. Nothing else.

④ Design judgment

The narrowing, read against the published spec on main: the object's own docblock, defaultRowScope and row 26c all declare a preference as the user's own row, and no spec, skill or doc on main offers changing a saved preference's owner as a capability (Skills-docs: none in the PR body is true; the one page that names the rule is row 26c, updated here). So the refusal makes the object's own declared posture real at the consumer rather than taking a declared capability away. The consumer that exists (userState.ts) never sends user_id on an update. Nothing declared and undelivered is being narrowed around.

I tried to break the rule four ways, each read in the cited code on main and found closed: a dispatch without previous (none on a non-system path, ① above); a second registrar binding it twice (refused at init); a bulk payload diverging per row (the batch payload is one, judged per matched row); and a value that normalises to the stored id (the sameId normalisation treats only undefined, null and '' as the same absent value, and an echo is the declared no-change). A private finding was not needed: no further gap was found on this head.

Must-fix: none.

Should-fix

  • S1 (carried from the precedent's S1 on feat(platform-objects): sys_personal_view, a reader's own named list views, with the owner's delete door (server step) #29575, still unapplied on main). Row 26c, owner-fixed-rule.ts, personal-view-hooks.ts and user-preference-hooks.ts describe the system skip only as "no shipped system writer does". The precedent record asked for one sentence saying the rule does not bind app-authored automation elevated to system, the family row 1b's flow-system-identity-write-refusal.ts names, so the row says what is really lost. This PR widens the row to a second object and copies the sentence; it is the place to add the one line, for both objects at once. Docs and comments only; no behaviour change.
  • S2 (carried from the precedent's S2). packages/plugins/plugin-auth/src/manifest.ts still says sys_personal_view's "save validation and default flag live in personal-view-hooks.ts"; the default flag is gone, the owner rule now lives in owner-fixed-rule.ts, and the adjacent SysUserPreference entry names no rule. One comment.
  • S3. user-preference-hooks.ts's docblock says "a create is the row scope's (a preference created in another member's name is refused there)". True on main for the member, and the precedent pinned it for the twin object, but this PR carries no create pin for sys_user_preference. Either add the one REST control or drop the parenthesis; the rule under review is the update rule and is unaffected.

None of the three changes the accept set or is a reason to hold the PR.

Implemented-by: claude/project-thread-57qwum
Reviewed-by: session_01JQwxbXKRzgnVvjzxaYxLUf

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · os-warren · session_01JQwxbXKRzgnVvjzxaYxLUf

维护者速读

席位核对

ACCEPT · domain:server#2 · head 424e92959b28d0338b269e7452de3c3fc994fad6 · 2026-11-03T01:46Z · contract review of record at CONTRACT_REVIEW_TIER (narrowing face): PASS 6593114814 on this head, by an independent at-tier subagent adopted by this seat

  1. Shape: draft, base main, Fixes #29630, Clause-②: no (narrowing), Metadata-protocol: none (body and changeset agree; patch, BREAKING banner, adr-0087 not-required). 9 files, +405/-33, not governed (check-governed-merges --pr 29685 0 of 9), resumed from the previous seat's branch rq4y7m@34568e18 per Release 6591667134.
  2. Premise measured: dev report 6592969538 (step-1 readings held privately per the security-findings rule); unit pins for member, org admin and platform admin plus echo/value-only/system controls; three ablations red and restored.
  3. Gates on this head: check-runs pinned to 424e9295: 50 total, 38 success, 12 skipped, 0 failed. Dev: 95/95 derived gates exit 0, --ran 0 unrun, pnpm lint 0, plugin-auth 216/216 files, dogfood battery VERDICT 0 (the dogfood CI job is push/schedule-gated, so the post-merge push run is its CI reading).
  4. Accepted deviations: the last commit trims the shared check's header and the changeset to the rule alone (comment/prose only, verified by the review); the personal-view check moves onto the shared rule with a byte-identical message.
  5. Out of scope: review should-fixes S1 (system-skip sentence for elevated app automation, row 26c and docblocks) and S2 (stale manifest.ts comment) are plugins: extract one shared own-row delete door from the unreact door and the personal-view door, which are near line-for-line copies #29636's items (a) and (b); S3 (one docblock claim without a pin) is carried to plugins: extract one shared own-row delete door from the unreact door and the personal-view door, which are near line-for-line copies #29636 by comment. The class question (step 3) is ruled B and handled privately; no public card. Skills-docs: not owed.
  6. Landing: not governed, under 3000 lines ⇒ the seat lands it (pr_ready + automerge_enable). At MERGED: landing record on security(row scope): an org member can re-parent a sys_user_preference row to another member by PATCHing its user_id, planting a preference the victim then reads as their own #29630 (closed by Fixes), pm:dispatched removed, card thread released.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 3, 2026 01:48
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 2ea6042 Nov 3, 2026
51 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-57qwum branch November 3, 2026 02:07
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/m tests tooling

Projects

None yet

1 participant