Repository navigation
plugin-security: under one master-reference declaration, a write to a controlled_by_parent detail is not held to ADR-0055's master invariant on every write shape (reach measured; detail withheld under the disclosure ruling) #25068
Description
Filing gate ①, the security exception ("may alter data"). Filed by the domain:services PM seat, seat 1 (seat post #6021, session_011TdZ8GjixNZFLz1xJD9zzK). It comes from an out-of-scope finding by #25044's dev (PR #25066), measured on a real stack. ⛔ Not a claim. Unlabelled and ungraded, for triage.
Disclosure: the maintainer's ruling recorded on #9387 (5323310017) applies. This card records only:
- that the reading exists, and where it lives;
- the governing text.
⛔ No probe, declaration shape, request, write shape or served value goes on this card, its PR or any comment. The seat holds the detail privately and hands it to the fix dev in the dispatch order.
Reader, and what they do: triage grades the card. Then the domain:services lane (plugin-security) re-measures the reach and fixes it.
Where it lives (origin/main 688052e147)
- The gate:
packages/plugins/plugin-security/src/security-plugin.ts, thecontrolled_by_parentdetail write gate (assertControlledByParentWrite, step 2.8). - The reading: for one authorable declaration of a detail's master reference, the gate holds the master invariant on one write shape and not on others. Measured for a member and an organization admin, a write on another shape landed and left a detail that no principal's read can match.
- Not this card: plugin-security: one more write to a controlled_by_parent detail is decided without asking for edit access on every master it involves, where ADR-0055 §3 requires it (reach measured; detail withheld under the disclosure ruling) #25044 / PR 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, plugin-security: one write path to a controlled_by_parent detail does not ask for edit access on its master, where ADR-0055 §3 requires it (reach measured; detail withheld under the disclosure ruling) #25012 / 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 and plugin-security: the controlled_by_parent detail write gate may not ask the master-edit question on one more write shape, where ADR-0055 §3 requires it (reach not measured; detail withheld under the disclosure ruling) #25049 are other readings in the same gate family. This card is the same region, and serial after them.
Governing text
- ADR-0055 §1 (
docs/adr/0055-master-detail-controlled-by-parent.md): acontrolled_by_parentdetail derives its access from exactly one required master. - ADR-0055 §3 (
:60): "editing/deleting/creating a detail requires edit access to its master".
Status of the reading
- Reach was measured on a real
ObjectQL+SqlDriver+SecurityPluginateddffc3390. It is unchanged by 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 and PR 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. The readings are private.
Dedupe
Two MCP search_issues queries over this gate family returned 26 and 5 hits. Their exact terms name the shape, so the seat holds them privately under the disclosure ruling.
The nearest hits:
- plugin-security: one more write to a controlled_by_parent detail is decided without asking for edit access on every master it involves, where ADR-0055 §3 requires it (reach measured; detail withheld under the disclosure ruling) #25044, plugin-security: the controlled_by_parent detail write gate may not ask the master-edit question on one more write shape, where ADR-0055 §3 requires it (reach not measured; detail withheld under the disclosure ruling) #25049 (open, other readings in the family);
- objectql: a master-detail row whose master is missing can be neither read nor removed through any door — the admin answers 404 by id, queries omit it, and the integrity audit only reports it #24964 (closed,
domain:engine: reading and removing a row whose master is missing); - plugin-security: the controlled_by_parent detail write gate's master-edit check may not ask the same question as the master's own write door (reach not measured; detail withheld under the disclosure ruling) #24986, plugin-security: a controlled_by_parent detail is readable by a caller with no read on its master object, where ADR-0055 refuses it (detail withheld under the disclosure ruling) #24962, plugin-security: one write path to a controlled_by_parent detail does not ask for edit access on its master, where ADR-0055 §3 requires it (reach measured; detail withheld under the disclosure ruling) #25012, finding(security): every write to a controlled_by_parent detail TWO levels below its root master answers 403 "master … not editable" for a principal who edits that master — a regression against 17.7.0, measured on hotcrm's crm_quote_line_item #23093 and other closed gate cards.
None of them covers this reading. New.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 21, 2026
Triage (R557): p1 ·
domain:services·area:access·bug+security·pm:queue. ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-21T07:56Zcontrolled_by_parentfamily. A write under one master-reference declaration is not held to ADR-0055's master invariant on every write shape. Reach is measured.6309201286) enumerates every read and write door as one table. This card's door goes into that table, and one PR closes both if their doors overlap. It is serial with PR 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 (plugin-security: one more write to a controlled_by_parent detail is decided without asking for edit access on every master it involves, where ADR-0055 §3 requires it (reach measured; detail withheld under the disclosure ruling) #25044) and the rest of the family insecurity-plugin.ts.5323310017) applies to this card, its PR and every comment. Reach is recorded only as an abstract statement. Detail is withheld pending the maintainer.Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Claim: PM loop round 29 · 2026-10-21T08:30Z
Session:
session_011TdZ8GjixNZFLz1xJD9zzKAccount:
os-justin(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-25049-cbp-write-shape-master-edit(shared with #25049's claim6310196529: one dev, one PR)Worktree:
objectstack-issue-25049Domain:
domain:servicesSeat:
domain:services#1File surface:
packages/plugins/plugin-security/src/security-plugin.ts: thecontrolled_by_parentdetail write gate (assertControlledByParentWrite) and its docblocks only;.changeset/25049-*.md(or25068-*.md).⛔
packages/objectql/**, ⛔packages/spec/**, ⛔computeControlledByParentFilter(#24932's region). ⛔ No master scope composed into a write (#24932's ruling B6303721609). (Stop on breach; explain in the report.)Container & model:
M,mode:subagent, the same dev as #25049's claim (no second dev; the seat's concurrency is unchanged)Clause-②: no
Responsibility:
a write under one master-reference declaration is not held to the master invariant on every write shape (reach measured, detail private) | ADR-0055 §3 requires edit access to the master for every detail write | every deployment with controlled_by_parent objectsThread-read: 6311012946
Serial constraints cleared:
Triage R557 folds this card into #25049's door table and says one PR closes both if their doors overlap. #25049's claim already names this card in its first step. PR #25066 (#25044) landed as f46ef4d70e and PR #25076 as a39389eb51; no other open PR of this seat touches security-plugin.ts.Order: this card's door is one row of #25049's door table. If the doors overlap, #25049's PR closes both cards. If they do not, the dev names this card's door in its report, and the seat amends or releases this claim before anything else is written.
⛔ The #9387 disclosure rule (
5323310017) applies to this card, its PR and every comment.Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Claim amendment ·
domain:servicesseat 1 ·session_011TdZ8GjixNZFLz1xJD9zzK· 2026-10-21T09:08Z · amends claim 6311604756.Clause-②: no (narrowing)
The declaration moves from
notono (narrowing). PR #25090 refuses two write shapes to acontrolled_by_parentdetail that used to land: a batch create under a master the caller may not edit, and an update that empties the master reference where required-field validation does not judge it. A caller that relied on either write landing is refused now, so the accept set narrows. It ships as a BREAKING minor under the amended 「甲」 (6303865025), with a!title, its ADR-0087 disposition and the full dogfood battery on the final head (run ond8dc243c15). It owes a contract-tier review before the queue.Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
ACCEPT → PR #25090 comment
6312852285·domain:servicesseat 1 ·session_011TdZ8GjixNZFLz1xJD9zzK· 2026-10-21T09:40ZGenerated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Landed ·
domain:servicesseat 1 ·session_011TdZ8GjixNZFLz1xJD9zzK· 2026-10-21T10:11Z · ⛔ The #9387 disclosure rule applies.added_to_merge_queue2026-10-21T09:42Z →merged2026-10-21T10:09Zorigin/mainby content3782b2df6e(one parent,8f59304ddc), 3 files.security-plugin.tscarries the[#25049]batch-create loop and the emptied-reference ruleState change in this stroke:
pm:dispatchedoff and the assignee cleared. The card closedcompletedon the merge.What changes: a batch create of a
controlled_by_parentdetail asks for edit access on the master of every row it names, through the one chain walk, before anything is written. An update whose body empties the master reference meets the empty-master rule, on every update shape (ADR-0055 §1, §3). Each is a verdict, not a scope (#24932's ruling B). This is a BREAKING minor (contract review6312751822, ACCEPT6312852285).Follow-ups, both abstract and for triage: #25092 (outside the detail write gate) and #25095 (in the same gate region, after this merge).
Generated by Claude Code