Skip to content

Commit 70f3423

Browse files
committed
chore: ADR-0055 Accepted + changeset; fix pre-existing app-showcase typecheck
- ADR-0055 status → Accepted (implemented in this PR, P0–P2). - changeset for the controlled-by-parent feature (spec/plugin-security/verify minor). - fix(app-showcase): task-triage userFilters tabs were missing the now-required pinned/isDefault/visible fields (a pre-existing main breakage from #2048 that reddened TypeScript Type Check on every PR) — examples typecheck now green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVdnfUAx85amkerym26vdx
1 parent 5122c29 commit 70f3423

3 files changed

Lines changed: 20 additions & 5 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@objectstack/plugin-security": minor
3+
"@objectstack/spec": minor
4+
"@objectstack/verify": minor
5+
---
6+
7+
Master-detail "controlled by parent" permissions (ADR-0055).
8+
9+
A detail object can now declare `sharingModel: 'controlled_by_parent'`: its read/write access is derived from its master record, with no authored RLS.
10+
11+
- `@objectstack/spec`: `controlled_by_parent` added to the authorable `object.sharingModel` enum.
12+
- `@objectstack/plugin-security`: reads inject `masterFK IN (accessible master ids)` (resolved from the master's own RLS, reusing the existing filter machinery — zero RLS-compiler changes); by-id writes (insert/update/delete) to a detail now require edit access to its master, closing the #1994-class by-id hole for derived access.
13+
- `@objectstack/verify`: related-record **topological synthesis**`deriveCrudCases` no longer skips objects with required relations; it builds the object dependency graph, orders it topologically, and threads real target ids, so relationship-dense objects (and the master-detail RLS proof) are verifiable. Honest `blocked` verdicts remain for required-reference cycles and external/missing targets.
14+
15+
v1 limits (per ADR-0055): the accessible-master id set is unbounded (large-tenant scale is a documented future limit), and master-detail chains are single-level (not transitively traversed).

docs/adr/0055-master-detail-controlled-by-parent.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0055: Master-detail "controlled by parent" permissions — derived access via pre-resolved master-id membership
22

3-
**Status**: Proposed (2026-06-19)
3+
**Status**: Accepted (2026-06-19) — implemented in this PR (P0–P2)
44
**Deciders**: ObjectStack Protocol Architects
55
**Builds on**: [ADR-0049](./0049-no-unenforced-security-properties.md) (enforce-or-remove), [ADR-0054](./0054-runtime-proof-for-authorable-surface.md) (prove-it-runs)
66
**Surfaced by**: an audit of master-detail permission semantics — `OWDModel.controlled_by_parent` is **declared but unenforced** (zero runtime consumers; not reachable through the object's `sharingModel` enum; the RLS compiler is relationship-blind). This is *false compliance* (ADR-0049) and *unproven liveness* (ADR-0054).

examples/app-showcase/src/pages/task-triage.page.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ export const TaskTriagePage: Page = {
3333
element: 'tabs',
3434
showAllRecords: true,
3535
tabs: [
36-
{ name: 'in_progress', label: 'In Progress', filter: [{ field: 'status', operator: 'equals', value: 'in_progress' }] },
37-
{ name: 'urgent', label: 'Urgent', icon: 'flame', filter: [{ field: 'priority', operator: 'equals', value: 'urgent' }] },
38-
{ name: 'in_review', label: 'In Review', filter: [{ field: 'status', operator: 'equals', value: 'in_review' }] },
39-
{ name: 'done', label: 'Done', filter: [{ field: 'status', operator: 'equals', value: 'done' }] },
36+
{ name: 'in_progress', label: 'In Progress', pinned: false, isDefault: false, visible: true, filter: [{ field: 'status', operator: 'equals', value: 'in_progress' }] },
37+
{ name: 'urgent', label: 'Urgent', icon: 'flame', pinned: false, isDefault: false, visible: true, filter: [{ field: 'priority', operator: 'equals', value: 'urgent' }] },
38+
{ name: 'in_review', label: 'In Review', pinned: false, isDefault: false, visible: true, filter: [{ field: 'status', operator: 'equals', value: 'in_review' }] },
39+
{ name: 'done', label: 'Done', pinned: false, isDefault: false, visible: true, filter: [{ field: 'status', operator: 'equals', value: 'done' }] },
4040
],
4141
},
4242

0 commit comments

Comments
 (0)