Skip to content

Commit d29c214

Browse files
os-zhuangclaude
andauthored
feat!: adopt ObjectStack Permission Model v2 (ADR-0090 / spec 13) (#438)
- Bump all @objectstack/* deps ^12.3.0 → ^13.0.0 - Sharing recipients: role → position; role_and_subordinates → position (positions are flat in v2 — hierarchy belongs to the business-unit tree, which this app does not model, so the grants target the director/manager position directly) - Role hierarchy → flat CrmPositions (config key roles → positions); territory teams join the position list - Profiles: isProfile removed (ADR-0090 D2 retired the concept); the six permission bundles remain plain permission sets - Explicit sharingModel authored on all 15 objects per the D7 security posture linter (values follow the old decorative OrganizationDefaults: campaign/product/knowledge public_read, contact controlled_by_parent, rest private); dead OrganizationDefaults export removed - Approval flow approvers keep type 'role' — spec 13's sole D3 exception (better-auth sys_member.role) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent b0af232 commit d29c214

31 files changed

Lines changed: 530 additions & 522 deletions

objectstack.config.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
AccountTeamSharingRule, TerritorySharingRules,
2323
OpportunitySalesSharingRule,
2424
CaseEscalationSharingRule,
25-
RoleHierarchy,
25+
CrmPositions,
2626
} from './src/sharing/index.js';
2727

2828
import { allHooks } from './src/hooks/index.js';
@@ -96,9 +96,8 @@ export default defineStack({
9696
CaseEscalationSharingRule,
9797
...TerritorySharingRules,
9898
],
99-
roles: RoleHierarchy.roles.map((r: { name: string; label: string; parentRole: string | null }) => ({
100-
name: r.name,
101-
label: r.label,
102-
parent: r.parentRole ?? undefined,
103-
})),
99+
// ADR-0090 D3: positions are flat capability-distribution groups — the v1
100+
// role hierarchy's parent links are gone (hierarchy belongs to the
101+
// business-unit tree, which this app does not model).
102+
positions: CrmPositions,
104103
});

package.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@
2626
},
2727
"packageManager": "pnpm@10.33.0",
2828
"dependencies": {
29-
"@objectstack/account": "^12.3.0",
30-
"@objectstack/cli": "^12.3.0",
31-
"@objectstack/driver-memory": "^12.3.0",
32-
"@objectstack/driver-sql": "^12.3.0",
33-
"@objectstack/driver-sqlite-wasm": "^12.3.0",
34-
"@objectstack/metadata": "^12.3.0",
35-
"@objectstack/objectql": "^12.3.0",
36-
"@objectstack/runtime": "^12.3.0",
37-
"@objectstack/service-analytics": "^12.3.0",
38-
"@objectstack/service-automation": "^12.3.0",
39-
"@objectstack/spec": "^12.3.0"
29+
"@objectstack/account": "^13.0.0",
30+
"@objectstack/cli": "^13.0.0",
31+
"@objectstack/driver-memory": "^13.0.0",
32+
"@objectstack/driver-sql": "^13.0.0",
33+
"@objectstack/driver-sqlite-wasm": "^13.0.0",
34+
"@objectstack/metadata": "^13.0.0",
35+
"@objectstack/objectql": "^13.0.0",
36+
"@objectstack/runtime": "^13.0.0",
37+
"@objectstack/service-analytics": "^13.0.0",
38+
"@objectstack/service-automation": "^13.0.0",
39+
"@objectstack/spec": "^13.0.0"
4040
},
4141
"optionalDependencies": {
4242
"better-sqlite3": "^12.11.1"

pnpm-lock.yaml

Lines changed: 427 additions & 453 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/objects/account.object.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ export const Account = ObjectSchema.create({
99
pluralLabel: 'Accounts',
1010
icon: 'building',
1111
description: 'Companies and organizations doing business with us',
12+
13+
// ADR-0090 D1/D7: OWD is an authored decision. Owner + sharing rules (team/territory grants below).
14+
sharingModel: 'private',
1215
// ADR-0079: render-only `titleFormat` retired in favor of `nameField`,
1316
// which names a real field. The former template composed two local fields, so
1417
// a `display_title` formula field reproduces it for the record title.

src/objects/campaign.object.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export const Campaign = ObjectSchema.create({
1313
pluralLabel: 'Campaigns',
1414
icon: 'megaphone',
1515
description: 'Marketing campaigns and initiatives',
16+
17+
// ADR-0090 D1/D7: OWD is an authored decision. Campaign catalog is org-visible; only owners edit.
18+
sharingModel: 'public_read',
1619
// ADR-0079: render-only `titleFormat` retired in favor of `nameField`,
1720
// which names a real field. The former template composed two local fields, so
1821
// a `display_title` formula field reproduces it for the record title.

src/objects/campaign_member.object.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ export const CampaignMember = ObjectSchema.create({
1616
icon: 'user-plus',
1717
description: 'Membership and response tracking for marketing campaigns',
1818

19+
// ADR-0090 D1/D7: OWD is an authored decision. Follows the campaign team's ownership; lookup child (not master-detail).
20+
sharingModel: 'private',
21+
1922
// @objectstack 12: the dead object-level `enable.trackHistory` flag was
2023
// removed (ADR-0049) — per-field history is opt-in via `Field.trackHistory`
21-
// (ADR-0052), set on `status` below. Master-detail children still inherit the
22-
// master's sharing automatically (sharingModel enum has no controlled-by-parent).
24+
// (ADR-0052), set on `status` below.
2325

2426
highlightFields: ['crm_campaign', 'crm_lead', 'crm_contact', 'status', 'response_date'],
2527

src/objects/case.object.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ export const Case = ObjectSchema.create({
1010
icon: 'life-buoy',
1111
description: 'Customer support cases and service requests',
1212

13+
// ADR-0090 D1/D7: OWD is an authored decision. Owner + escalation sharing rule.
14+
sharingModel: 'private',
15+
1316
fieldGroups: [
1417
{ key: 'basic', label: 'Case Information', icon: 'info' },
1518
{ key: 'origin', label: 'Origin & Routing', icon: 'route' },

src/objects/contact.object.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ export const Contact = ObjectSchema.create({
99
pluralLabel: 'Contacts',
1010
icon: 'user',
1111
description: 'People associated with accounts',
12+
13+
// ADR-0090 D1/D7: OWD is an authored decision. Master-detail child of crm_account — inherits the account's sharing.
14+
sharingModel: 'controlled_by_parent',
1215

1316
fieldGroups: [
1417
{ key: 'identity', label: 'Identity', icon: 'user' },

src/objects/contract.object.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export const Contract = ObjectSchema.create({
1313
pluralLabel: 'Contracts',
1414
icon: 'file-pen-line',
1515
description: 'Legal contracts and agreements',
16+
17+
// ADR-0090 D1/D7: OWD is an authored decision. Legal documents — owner only.
18+
sharingModel: 'private',
1619
// ADR-0079: render-only `titleFormat` retired in favor of `nameField`.
1720
// Original template was '{contract_number} - {crm_account.name}'. The
1821
// `crm_account.name` segment is a lookup DOT-WALK, which a formula field

src/objects/forecast.object.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export const Forecast = ObjectSchema.create({
2727
pluralLabel: 'Forecasts',
2828
icon: 'trending-up',
2929
description: 'Periodic pipeline snapshot by owner used for revenue forecasting.',
30+
31+
// ADR-0090 D1/D7: OWD is an authored decision. Forecasts are personal to the owner.
32+
sharingModel: 'private',
3033
// ADR-0079: render-only `titleFormat` retired in favor of `nameField`.
3134
// The former template led with `{owner}`, a lookup — DROPPED here because a
3235
// formula cannot dot-walk a lookup (ADR-0072). The `display_title` formula

0 commit comments

Comments
 (0)