Skip to content

fix(plugin-security)!: an update of a controlled_by_parent detail also asks for edit access on the master its body names (ADR-0055 §3) - #25066

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-25044-cbp-every-master-edit
Oct 21, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-25044-cbp-every-master-edit

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #25044

Clause-②: no (narrowing)

What changed

ADR-0055 §3 requires edit access to the master for every write to a controlled_by_parent detail, and it reads a create's master from the master id in the incoming body. A write can involve more than one master. For one shape of write, the master-detail write gate (assertControlledByParentWrite, step 2.8) asked the master-edit question of only one of the masters the write involves. It now asks every one of them.

  • packages/plugins/plugin-security/src/security-plugin.ts, inside assertControlledByParentWrite only. The gate first asks about the master or masters it already asked about, unchanged. Then it runs the same chain walk on the master id the write's body names, unless that master was already asked. The walk is assertControlledByParentChainEditable, the one walk PR fix(plugin-security)!: an update or delete of a controlled_by_parent detail asks for edit access to its master on every write shape (ADR-0055 §3) #25042 extracted; no second copy exists.
    • A refusal uses the by-id leg's own envelope: 403 PERMISSION_DENIED, naming the caller's detail and operation, with its leg recorded. It is thrown before the write runs, so nothing is written.
    • An insert's master already is its body's, so it is asked once.
    • A write that selects no row involves no master and is not refused.
    • No master scope is composed into any write. computeControlledByParentFilter, packages/objectql/** and packages/spec/** are untouched.
  • packages/plugins/plugin-security/src/controlled-by-parent-every-master-edit.test.ts (new): 17 cases on a real stack. That is ObjectQL over better-sqlite3, the real SecurityPlugin, the real sharing service and middleware, and contexts built by resolveAuthzContext and assembleExecutionContext.
  • .changeset/25044-cbp-every-master-edit.md: @objectstack/plugin-security minor. It is BREAKING under the launch-window convention for an accept-set narrowing. It carries the Clause-② line and the ADR-0087 disposition not-required (no-migration-prescription).

Reach

Reach is stated abstractly here, because the disclosure ruling recorded on #9387 applies. The reading was re-measured on a real ObjectQL + SqlDriver + SecurityPlugin, for a member and for an organization admin. The write landed on origin/main eddffc3390 (before PR #25042) and again on PR #25042's head 033fc75a09 (after it). The probes were throwaway and are not committed. The detail is held privately.

Verification

Every run below is on this branch's head a447bfdb8f, after the final commit, unless it is marked otherwise. Each one ran under the shared verify lock.

  • Build. The dependency closure was built with pnpm --filter '@objectstack/dogfood^...' build (a superset of @objectstack/plugin-security^..., plugin-security itself included). It exited 0.

  • The new pins. controlled-by-parent-every-master-edit.test.ts passes 17/17, beside PR fix(plugin-security)!: an update or delete of a controlled_by_parent detail asks for edit access to its master on every write shape (ADR-0055 §3) #25042's controlled-by-parent-write-shape-master-edit.test.ts (32/32 for the two files together).

  • The package. pnpm --filter @objectstack/plugin-security exec vitest run: Test Files 202 passed (202), Tests 4156 passed and 45 skipped (4201). pnpm --filter @objectstack/plugin-security typecheck exited 0. That covers tsc on both programs and check:test-typecheck, and the new test file is in the tsconfig.test.json program (--listFiles, 1 hit).

  • Dogfood battery (pnpm --filter @objectstack/dogfood test, no shard and no path, owed by the ! title). The lock's verdict was command-exit 0. Test Files: 285 passed, 3 skipped (288). Tests: 2375 passed, 18 skipped (2393).

  • Derived gate families. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 69 families. All 69 ran, and each exit code was captured before any pipe. --ran reports: 69 run, 0 NOT-MEASURED, 0 UNRUN.

  • Ablation, one leg per pinned behaviour. The fix was committed first. Each leg is a mutation made through scripts/ablation-replace.mjs, where the anchor must hit and the blob must change. Each restore is git checkout HEAD --, proven by blob equality with HEAD and an empty git diff HEAD. The subject is imported as ./security-plugin.js from source, so no dist/ sits on the path. The result:

    leg mutation went red
    L1 drop the new question after the by-id leg the refusal pins (7 cases) and the count pin
    L2 drop the new question after the predicate leg the refusal pins (7) and the count pin
    L3 ask the body's master instead of the stored one the stored-master pin and the count pin
    L4 a blanket refusal in place of the walk the lands-as-it-did pins (7), and the refusal and stored-master pins on the envelope
    L5 drop the no-row guard the no-row pin
    L6 drop the already-asked skip the count pin (an insert's master asked twice)

    The first L4 attempt was refused by the tool: its replacement contained its anchor, so the anchor count did not move. It measured nothing and was rerun with a non-overlapping anchor. The unmutated control passed 17/17. The final tree is byte-identical to HEAD.

  • Base. The branch is main at b2890909aa (PR fix(plugin-security)!: an update or delete of a controlled_by_parent detail asks for edit access to its master on every write shape (ADR-0055 §3) #25042 landed) plus this change, merged with origin/main 72518d20f5 before the runs above. Since then main has gained one commit (4bf079c693, plugin-auth), which is disjoint from this diff.

Acceptance notes

  • Two adjacent readings in the same gate family turned up while measuring. Neither is changed here. Both go to the seat privately, for filing under the same ruling.
  • The question is asked of what the body names before the engine's own field strips run. On a reference the engine would strip from the write, a body naming a master the caller may not edit is therefore refused, where before it was dropped. That fails closed. Mirroring the engine's strip rules in the gate would make a second copy of them.
  • The new walk runs on the master id the body names, as the insert's walk already does.

Seat's append (domain:services seat 1, from the dev's patch-round report 6310152347 on #25044; the dev writes its PR body once):

  • Patch round 1 (4b4cfed7af): only the changeset headline changes, narrowed to the update shape this diff covers, per contract review 6310100720 ③.1. No code or test line moved.
  • git diff --stat a447bfdb8f 4b4cfed7af touches only .changeset/25044-cbp-every-master-edit.md (+1/−1). Code is byte-identical, so the dogfood battery and the package and gate runs at a447bfdb8f stand.

Generated by Claude Code

…for edit access on every master it involves

ADR-0055 §3 requires edit access to the master for every detail write and
reads a create's master from the master id in the incoming body. The
master-detail write gate now runs its one chain walk on every master the
write involves: the master(s) its rows are stored under, as before, and
the master id its body names when that is not one of them. A refusal is
the by-id leg's envelope and nothing is written; an insert's master is
asked once; a write that selects no row involves no master.

New real-stack pins in controlled-by-parent-every-master-edit.test.ts and a
BREAKING minor changeset.

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

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/permissions/field-level-security.mdx (via SecurityPlugin (symbol, a top-level class))
  • content/docs/permissions/index.mdx (via SecurityPlugin (symbol, a top-level class))
  • content/docs/plugins/packages.mdx (via SecurityPlugin (symbol, a top-level class))
  • content/docs/ui/forms.mdx (via SecurityPlugin (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx (via SecurityPlugin (symbol, a top-level class))
  • content/docs/releases/v18/18-0.mdx (via SecurityPlugin (symbol, a top-level class))

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 — 16 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 2b1382a5b3887e4abd194411ef77a7cd368c95c2 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 2b1382a5b3887e4abd194411ef77a7cd368c95c2

⚠️ 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 2b1382a5b3887e4abd194411ef77a7cd368c95c2 → 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

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a447bfdb8f2cf4237452adc0e3866074142de5ed
Local-runs: none
Scope: packages/plugins/plugin-security/src/security-plugin.ts, packages/plugins/plugin-security/src/controlled-by-parent-every-master-edit.test.ts, .changeset/25044-cbp-every-master-edit.md + packages/objectql/src/engine.ts, packages/objectql/src/validation/rule-validator.ts, docs/adr/0055-master-detail-controlled-by-parent.md

Inputs: card #25044 (body and every comment through the dev report 6309719274), PR #25066 (body, three-path file list, net diff against main at a447bfdb8f), the check-runs on that head, and the cited code read at the head. Face: narrowing, from the Clause-②: no (narrowing) line on the body and in the changeset; reviewFace returns no path hit on this file list, so the floor is every diff path outside the prose faces, listed first above. Disclosure: the maintainer's ruling on #9387 (5323310017) governs this record — reach is stated abstractly; no probe, grant shape, request body, write-shape name for an unfixed path or served value is written here.

① Derived judgments

Governing text: ADR-0055 §3 (docs/adr/0055-master-detail-controlled-by-parent.md:60) — editing, deleting or creating a detail requires edit access to its master, and a create's master is read from the master id in the incoming body. Ruling B on #24932 (6303721609): no second spelling of a master scope while the mechanism moves to the engine leaf. Line numbers are the head's.

  1. The new question is a VERDICT, with no master set composed into the write — RIGHT. askBodyNamedMaster (security-plugin.ts:9237) reads opCtx.data[rel.fk] as an own property, returns on a null and on a master already in judged, and otherwise calls the walk. It assigns nothing — not opCtx.ast, not opCtx.options.where, not opCtx.data; it throws or returns. computeControlledByParentFilter (:8746) carries no hunk, and the file list holds nothing under packages/objectql/** or packages/spec/**. Ruling B gets no second spelling.
  2. One walk, no second copy — RIGHT. The closure's only callee is assertControlledByParentChainEditable (:9435), PR fix(plugin-security)!: an update or delete of a controlled_by_parent detail asks for edit access to its master on every write shape (ADR-0055 §3) #25042's extraction; the diff adds no walk body, and assertMasterRowEditable keeps its single call site inside that walk (:9476). The predicate leg's loop is unchanged except that its result is bound to predicateMasters so the new question can read it (:9272–:9280).
  3. The refusal is the by-id leg's envelope, and nothing is written — RIGHT. The walk is handed the gate's own masterEditDenied / denyMasterEdit (:9205–:9219): PermissionDeniedError (403 PERMISSION_DENIED), the sentence naming the caller's operation and object, the leg recorded beside the error in MASTER_EDIT_DENIAL_LEG (:9214). The gate runs in the security middleware's pre-phase (step 2.8, :3246 and :3256 for the delegator), which executeWithMiddleware (engine.ts:6808) runs before the executor, so a throw leaves the executor — hooks, strips, validation, the statement — unreached. The new pins compare the envelope to the live by-id refusal of the same caller and master, and read every row back unchanged.
  4. The insert's master is asked exactly once — RIGHT. The insert branch reads masterId from opCtx.data[rel.fk] (:9254); askBodyNamedMaster([masterId]) (:9411) reads the same key and returns on judged.includes. Pinned by the count test (the spy's fourth argument: one entry per insert). The same skip covers a by-id or predicate write whose body names a master already asked. One nit, not a hole: a body spelling the stored master in another scalar type is not includes-equal and asks the same master a second time — one verdict, asked twice.
  5. The fail-closed acceptance note is TRUE. The gate reads the body before the executor's strips run: stripReadonlyFields / stripReadonlyWhenFields (engine.ts:18109, :18143, :18428; rule-validator.ts:2117) sit inside the executeWithMiddleware body (engine.ts:17189), and the #19853 note at engine.ts:4070 records that a master-detail FK that strip takes is a repoint that never lands. So a body naming a master the caller may not edit, on a reference the engine would strip, now answers 403 where the reference was dropped and the write landed. Refusing before the strip is the right side to err on; mirroring the strip rules in the gate would be a second copy of them, and the changeset's "What to do" covers the remedy.
  6. The changeset headline over-claims. "a write to a controlled_by_parent detail asks for edit access on every master it involves" is a universal; the body under it is scoped ("for one shape of write … It now asks it of every master the write involves"). The dev's own report on the card (6309719274, out-of-scope findings F1 and F2; Acceptance note 1 on the PR) records two further readings in the same gate family, unchanged by this PR, so the diff does not make the universal true. It is the sibling's flag (PR fix(plugin-security)!: an update or delete of a controlled_by_parent detail asks for edit access to its master on every write shape (ADR-0055 §3) #25042, whose headline was narrowed before landing). The PR title — the squash subject — and the [#25044] docblock sentence "on every shape it asks EVERY master the write involves" (:9136) carry the same universal. Flagged in ③.1; the changeset body, its Before / Now / Unchanged bullets and the ADR-0087 comment say only what the diff makes true.
  7. The accept-set change, read on the dispatch verdicts (resolveEngineUpdateDispatch / resolveEngineDeleteDispatch, :9264–:9271): a by-id update whose body names a master other than the stored one is now judged on both; a predicate update is judged on each stored master of the rows the caller can read and then on the body's, only when at least one such row was reached (:9280); an insert, and a delete (whose opCtx carries no data, engine.ts:20069), answer exactly as before; a write the engine refuses on its own reject verdict is unchanged. On a delegated write both principals are asked (:3256). The changeset's Before / Now / Unchanged bullets match this reading, "Inserts and deletes" included.
  8. Scope held. The hunks are inside assertControlledByParentWrite and the walk's docblock; packages/objectql/** and packages/spec/** are untouched; no governed path is in the file list (Governed Surface Queue Guard success).

② Semver level

@objectstack/plugin-security minor, headline fix(plugin-security)!:, body **BREAKING**, Clause-②: no (narrowing) on the PR body and in the changeset, ADR-0087 marker not-required (no-migration-prescription) — RIGHT. The diff adds no export, no schema and no authorable spelling; it narrows the accept set of one write door, and no conversion carries it. Under the amended 「甲」 (6303865025 on #6015, rule 2) such a narrowing ships now, in 18.x, as a BREAKING minor in the #24813 changeset shape — which this changeset is: Before / Now / Unchanged / What to do, with the remedy stated (a grant on the master, or a narrower write). The disposition is right: no metadata moves, so nothing for migrate meta to rewrite. Check Changeset reads success on the head. One wording flag on the headline (③.1); the level and the carriers stand.

③ Boundary flags

  1. Headline over-claim (①.6) — for the owning seat, before landing. Narrow the changeset headline and the PR title to the shape the diff fixes, as the sibling's were; the docblock's universal sentence may go with them. A changeset-only hop is a prose-face move (.changeset/ is on REVIEW_SURFACE_EXCLUDED_PROSE_FACES) and keeps this record; a docblock edit moves the reviewed file and re-owes it. Not a FAIL: the level, the carriers and the code are right, and the body text is accurate.
  2. Two further readings in the same gate family (dev report 6309719274, F1 and F2) — held out under the disclosure ruling; escalated to the seat by the dev. One further shape, read from the code of the gate and not measured, is held out under the disclosure ruling; escalated. None is changed by this PR, and none makes this diff wrong.
  3. Dev deviations — answered. BASE moved twice (cut at eddffc3390, fast-forwarded onto b2890909aa after PR fix(plugin-security)!: an update or delete of a controlled_by_parent detail asks for edit access to its master on every write shape (ADR-0055 §3) #25042 landed, merged with 72518d20f5): the reviewed diff is the net diff against the merge base, three paths, and main has since gained one disjoint commit (4bf079c693). The throwaway validation on the pre-merge base committed nothing. The commit trailers follow AGENTS.md's model-free pair. Finding 1 is ③.2.
  4. open_questions: none declared. The fail-closed acceptance note is ①.5 — judged true, noted, no carrier owed.
  5. Docs drift advisory (6309701002): four hand-written pages name SecurityPlugin by symbol; none restates the body-master rule by its inputs. The lane seat's advisory, not a claim of this diff.
  6. Check-runs on the head. Every skipped name is on check-expected-skips.mjs's roster: the full-battery-only jobs (Dogfood Regression Gate matrix template, Dogfood Verify CLI, Temporal Conformance, Console Pin Gate), Build Docs on the docs filter, the two opt-in harnesses, and Auto Label / Check PR Size on a later edited/labeled event run beside their success on the synchronize run. Lint & Repo Gates, TypeScript Type Check, Build Core, Governed Surface Queue Guard, Dogfood Regression Gate (aggregate), Check Changeset and the claim guards: success. Test Core (aggregate) and all six shards: success, read after the last shard completed. 47 check-runs, none pending, none red.

Implemented-by: claude/issue-25044-cbp-every-master-edit
Reviewed-by: session_011TdZ8GjixNZFLz1xJD9zzK

VERDICT: PASS

Changeset text only; no code or test change.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot changed the title fix(plugin-security)!: a write to a controlled_by_parent detail asks for edit access on every master it involves (ADR-0055 §3) fix(plugin-security)!: an update of a controlled_by_parent detail also asks for edit access on the master its body names (ADR-0055 §3) Oct 21, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:services seat 1 · 2026-10-21T07:30Z

os-justin · session_011TdZ8GjixNZFLz1xJD9zzK (seat post #6021). Reviewed against GitHub and origin/main 2b1382a5b3, not against the reports (os-dev reports 6309719274 and 6310152347 on #25044).

Disclosure read first (#9387, 5323310017).

Head under review:

4b4cfed7af4166806b44fcc6a4ec23c75c520300
  1. Shape.
    • Draft, base main, first line Fixes #25044. Title fix(plugin-security)!:, and line-start Clause-②: no (narrowing) in the body and the changeset.
    • 3 files, none on a governed surface (governedPathsIn answers []). The trial merge onto origin/main is clean.
  2. Review of record. The face is narrowing at CONTRACT_REVIEW_TIER. The isolated contract review 6310100720 reads PASS on a447bfdb8f (Served-tier: CONTRACT_REVIEW_TIER, Local-runs: none).
    • This head adds only patch round 1. git diff --stat a447bfdb8f 4b4cfed7af4166806b44fcc6a4ec23c75c520300 touches .changeset/25044-cbp-every-master-edit.md alone (+1/−1). This is a prose-face hop, which keeps the record. The seat confirms it sentence by sentence:
  3. The fix, read in the code (security-plugin.ts, inside assertControlledByParentWrite only).
  4. Pins and ablation.
    • controlled-by-parent-every-master-edit.test.ts has 17 cases on a real stack, for a member and an organization admin.
    • Red before the fix: 9 of 17 failed. Six ablation legs (L1–L6) each go red as designed, and every restore was proven by blob equality.
  5. Gates.
  6. Classification. This brings the runtime to ADR-0055 §3, which reads a create's master from the body, and so a write under a master named in the body. It is an implementation gap, shipped as a BREAKING minor under the amended 「甲」 (6303865025).
  7. Accepted deviations.
  8. Out-of-scope disposition (all abstract, under the disclosure rule).
  9. Landing clause. Tier S: not governed, under the human-merge threshold, a BREAKING minor under the launch-window convention. The next step is ready + auto-merge through the relay once every check on the current head is green or an expected skip. A push resets this reading.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 21, 2026 07:32
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 21, 2026 07:32
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit f46ef4d Oct 21, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-25044-cbp-every-master-edit branch October 21, 2026 08:02
os-steve pushed a commit that referenced this pull request Oct 21, 2026
os-steve pushed a commit that referenced this pull request Oct 21, 2026
…-title, keyed-text, webhook-report cases) meet a NOT NULL tenant column

- plugin-security: an organization-less permission-set catalogue row is
  legacy residue, so six suites relax only sys_permission_set to its pre-18
  shape. Driver-written seeds name the rig's organization
  (readable-ids-nested-compose, controlled-by-parent-master-object-write).
  The lean person-title engine gets the Default Organization.
- rest (stored-expression dropped fields), plugin-webhooks (auto-enqueuer
  report), plugin-approvals (reader-locale labels, keyed text): each lean
  engine gets the Default Organization. The keyed-text out-of-office
  delegation names the deals' organization, where its request opens.

No assertion changed.

Claude-Session: https://claude.ai/code/session_018EszgtA3V2KXBX5tf59Hi4
Co-authored-by: Claude <noreply@anthropic.com>
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/l tests tooling

Projects

None yet

2 participants