Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/one-app-rule-adr-0019-citation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@objectstack/spec": patch
---

fix(spec): the one-app-per-package refusal cites the record it means, `ADR-0019 (app-as-consumer-unit) D3`

`ADR-0019` names **two** records in this repository — `0019-app-as-consumer-unit` (D3 = a `type: 'app'` package defines at most one app) and `0019-approval-as-flow-node` (D3 = deprecating `ApprovalProcessSchema`). Both have a D3, and `stack.zod.ts` cited the bare number for both, so an author following the refusal's own citation was as likely to reach the wrong decision record as the right one.

The three citations of the app-cap rule now name the record:

- the `STACK_SINGLE_APP_VIOLATION` message — the only one an app author ever sees;
- the `validateSingleApp` docblock;
- the `StackSingleAppViolationError` docblock.

Only the message tail changed: `An 'app' package must define at most one app, but found N (…)` is untouched, so any consumer matching on that prefix is unaffected. The rule, the refusal's condition and `defineStack`'s behaviour are unchanged.

The approvals-side citations are deliberately left bare — repo-wide ADR-number disambiguation is tracked separately.
4 changes: 3 additions & 1 deletion content/docs/ui/audience-based-interfaces.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ description: The same data serves different audiences. Give end users a curated

The built-in permission sets already encode this split: `member_default` and `viewer_readonly` do **not** carry `studio.access` / `manage_metadata`, so Studio and schema-design surfaces are invisible to them; `admin_full_access` carries both, while `organization_admin` gets `setup.access` only — the Setup shell is reachable but Studio and schema design stay hidden from org-scoped admins (see [`default-permission-sets.ts`](https://github.com/objectstack-ai/objectstack/blob/main/packages/plugins/plugin-security/src/objects/default-permission-sets.ts)).

**One package, one app.** A package of `type: 'app'` exposes **exactly one** App — `defineStack` refuses a second at load, per ADR-0019 (app-as-consumer-unit) D3. More audiences therefore never mean more apps: they are navigation groups **inside** that one app, gated by `requiredPermissions` exactly as below.

### 1. Gate the app and its navigation

```ts
Expand Down Expand Up @@ -68,6 +70,6 @@ This is the lesson from Airtable's surfaces: the **Automations tab is visible to

## See also

- Decision records: **ADR-0047** (run modes), **ADR-0066** (unified authorization, dual-surface gates).
- Decision records: **ADR-0019** (app-as-consumer-unit — one app per package), **ADR-0047** (run modes), **ADR-0066** (unified authorization, dual-surface gates).
- [Who can see data / automation / interface](/docs/permissions/access-recipes).
- Guide: [Security](/docs/permissions); reference: [App schema](/docs/references/ui/app), [Permission schema](/docs/references/security/permission).
7 changes: 4 additions & 3 deletions packages/spec/src/stack.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1460,7 +1460,7 @@ function validateNamespacePrefix(config: ObjectStackDefinition): string[] {
}

/**
* Validate the "at most one App per package" rule — ADR-0019 (D1/D3).
* Validate the "at most one App per package" rule — ADR-0019 (app-as-consumer-unit) D1/D3.
*
* A consumer package (`manifest.type === 'app'`) must not define **more than
* one** app — that is the banned "suite contains apps" shape. Fold the apps
Expand All @@ -1479,7 +1479,7 @@ function validateSingleApp(config: ObjectStackDefinition): string[] {
const names = apps.map((a) => a.name).join(', ');
return [
`An 'app' package must define at most one app, but found ${apps.length} (${names}). ` +
`Fold them into one app with multiple tabs, or split into separate packages (ADR-0019 D3).`,
`Fold them into one app with multiple tabs, or split into separate packages ADR-0019 (app-as-consumer-unit) D3.`,
];
}

Expand Down Expand Up @@ -1943,7 +1943,8 @@ class StackNamespacePrefixInvalidError extends StackRefusalError {

/**
* [ADR-0112 · #15963] An `app` package declares more than one app — the banned
* "suite contains apps" shape, ADR-0019 D3 — {@link validateSingleApp}.
* "suite contains apps" shape, ADR-0019 (app-as-consumer-unit) D3 —
* {@link validateSingleApp}.
* Spelled `_VIOLATION` like the ledger's other rule-violation refusals
* (`UNIQUE_VIOLATION`, `EXTERNAL_SCHEMA_MODE_VIOLATION`).
*/
Expand Down
Loading