Skip to content

fix(example-crm): grant crm_opportunity_line_item in crm_sales_user, matching its master crm_opportunity (#8164) - #8228

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-8164-crm-line-item-grant
Aug 12, 2026
Merged

fix(example-crm): grant crm_opportunity_line_item in crm_sales_user, matching its master crm_opportunity (#8164)#8228
os-zhuang merged 3 commits into
mainfrom
claude/issue-8164-crm-line-item-grant

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#8164

What

crm_opportunity_line_item is a master-detail child of crm_opportunity
(sharingModel: 'controlled_by_parent', inlineEdit: 'grid' on the
Opportunity form) but was not granted object-level CRUD in any app-crm
permission set. Record-level access always follows the master (ADR-0055),
but object-level CRUD is a separate gate the platform never derives —
so every role-bound (non-admin) user, including the three positions this
app ships to demonstrate selling, got a silent 403 the moment they tried
to add/edit a product line on an Opportunity they otherwise fully own. The
platform's own build-time lint (security-master-detail-ungranted)
already flagged this independently.

Added crm_opportunity_line_item to crm_sales_user's objects map in
examples/app-crm/src/security/sales-positions.ts, with the same grant
shape as its master crm_opportunity
:
{ allowRead: true, allowCreate: true, allowEdit: true, allowDelete: false }.

Why this shape

The lint rule's own name (security-master-detail-ungranted) and the
runtime's ADR-0055 derivation both say the line item's access is meant to
follow its master. Matching the parent's grant exactly (not inventing an
independent CRUD policy) keeps that relationship visible in the metadata:
the same users who can already create/read/edit an Opportunity can now
also create/read/edit its line items, with delete withheld on both (line
items are removed by editing the Opportunity's grid, not by a standalone
delete permission — consistent with the master).

Also regenerated examples/app-crm/access-matrix.json (the ADR-0090 D6
capability snapshot objectstack build checks on every compile) via
objectstack build --update-access-matrix — never hand-edited. The build
fails on undocumented capability drift, and granting the line item is
exactly such a drift; the new entry records the object's own
sharingModel (controlled_by_parent), not the master's
public_read_write.

Tests

  • New coverage: examples/app-crm/test/smoke.test.ts gained one new,
    purely-additive test —
    grants crm_opportunity_line_item in crm_sales_user, matching its master crm_opportunity (#8164) — asserting the child's grant exists and equals
    the master's. No existing test was touched or re-spelled; there was zero
    prior coverage of crm_sales_user's grant contents before this PR.
    Confirmed the test discriminates: red on the ablated (pre-fix) code
    (expected undefined to be defined), green on the fix.
  • objectstack verify --app examples/app-crm/objectstack.config.ts --rls:
    every position persona's probeBlocked count dropped from 1 (the line
    item — the sole gap [finding] The CRM example binds none of its 3 declared positions to crm_sales_user, so a user holding one is 403 on every CRM object #8060 deliberately left open) to 0
    (memberVisible: 6, not proven — expected, since the object is
    effectively controlled_by_parent on a public_read_write master, so
    the object gate opening makes it visible by design), with zero RLS
    holes. Aggregate across the 3 position personas: probeBlocked 3 → 0,
    memberVisible 15 → 18, holes 0 → 0. Full before/after readout in the
    dev report.
  • Build-time lint: security-master-detail-ungranted no longer fires for
    crm_opportunity_line_item (confirmed present on the ablated build,
    absent on the fix).
  • Ablation: committed the fix first, then reverted both
    sales-positions.ts and access-matrix.json together (reverting only
    the grant would itself trigger an access-matrix drift failure),
    re-ran verify --rls and the build lint, confirmed both returned
    exactly to the pre-fix baseline, then restored from the committed
    branch (no git stash).

Not in scope

packages/plugins/plugin-security/** — this PR stays entirely inside
examples/app-crm/**, per the card's scope.


Generated by Claude Code

…matching its master crm_opportunity (#8164)
crm_opportunity_line_item is a master-detail CHILD of crm_opportunity
(sharingModel: 'controlled_by_parent', inlineEdit: 'grid' on the
Opportunity form), but crm_sales_user granted object-level CRUD on 5
CRM objects and never the line item. Record-level access always
follows the master (ADR-0055), but object-level CRUD is a SEPARATE
gate the platform never derives -- every role-bound (non-admin) user
got a silent 403 the moment they tried to add/edit a product line on
an Opportunity they otherwise fully own. The platform's own
build-time lint (security-master-detail-ungranted) already flagged
this independently.
Grant shape matches the master exactly:
{ allowRead: true, allowCreate: true, allowEdit: true, allowDelete: false }.
Adds one purely-additive test asserting the grant exists and equals
the master's -- there was zero prior coverage of crm_sales_user's
grant contents.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEVB6w7D7uCszR9Mw1BL73
…ant (ADR-0090 D6)
Companion to the sales-positions.ts fix (#8164): the ADR-0090 D6
access-matrix snapshot gate fails the build on any undocumented
capability drift, and granting crm_opportunity_line_item is exactly
such a drift. Regenerated with `objectstack build --update-access-matrix`
per the gate's own instructions -- the diff is the review artifact:
crm_sales_user gains create/read/edit on crm_opportunity_line_item,
recorded with the object's OWN sharingModel (controlled_by_parent),
not the master's.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEVB6w7D7uCszR9Mw1BL73
@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 12, 2026 9:29pm

Request Review

@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tests tooling labels Aug 12, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 12, 2026 21:39
@os-zhuang
os-zhuang added this pull request to the merge queueAug 12, 2026
Merged via the queue into main with commit c4624f0Aug 12, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-8164-crm-line-item-grant branch August 12, 2026 21:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

crm_opportunity_line_item has no object-level CRUD grant in any app-crm permission set — the platform's own build lint already flags it

2 participants

@os-zhuang@claude