Skip to content

Commit 0852cee

Browse files
authored
Merge branch 'main' into fix/meta-resync-default-permission-sets
2 parents b7aef77 + ff648ad commit 0852cee

336 files changed

Lines changed: 10869 additions & 1102 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
'@objectstack/spec': major
3+
'@objectstack/rest': minor
4+
'@objectstack/plugin-security': minor
5+
'@objectstack/lint': minor
6+
'@objectstack/metadata': minor
7+
'@objectstack/platform-objects': patch
8+
---
9+
10+
ADR-0090 follow-through wave: enforce book audience at the read layer; finish the D2/D3 cleanup the P1 rename missed.
11+
12+
- **rest**: `/meta/book`, `/meta/doc`, and `/meta/book/:name/tree` now ENFORCE
13+
the ADR-0046 §6.7 audience model (ADR-0049 — no unenforced security
14+
properties): anonymous callers see only `public` books/docs;
15+
`{ permissionSet }`-gated books require the caller to hold the named set;
16+
a doc's effective audience is the union over the books that CLAIM it
17+
(unclaimed docs default to `org`; orphan rendering never inherits `public`).
18+
Gated evaluation fails CLOSED when holdings cannot be resolved. `doc`/`book`
19+
single-item reads bypass the shared meta cache (per-caller gate vs shared ETag).
20+
- **spec**: new pure helpers powering that gate — `audienceAllows`,
21+
`resolveDocAudiences`, `docAudienceAllows`, `resolveBookClaimedDocs`
22+
(+ `AudienceCaller`/`AudienceBook` types). BREAKING (launch window):
23+
`METADATA_FORM_REGISTRY` keys `role`/`profile` are gone — `position` is the
24+
registered form (the `position` type had LOST its form layout in the P1
25+
rename); `EnvironmentArtifactMetadataSchema` declares `positions` instead of
26+
retired `roles`/`profiles`.
27+
- **plugin-security**: the `security` service exposes
28+
`resolvePermissionSetNames(ctx)` — the same resolution as data-plane
29+
enforcement, for the docs gate.
30+
- **metadata**: artifact ingestion maps `positions → 'position'` (the stale
31+
`roles → 'role'` mapping matched nothing since the P1 rename, silently
32+
dropping compiled positions from metadata registration).
33+
- **lint**: books join the D3 role-word scan (their `audience` is a
34+
permission-model reference now), and a new advisory rule
35+
`security-book-audience-unknown-set` flags a `{ permissionSet }` audience
36+
naming a set the stack does not declare (runtime fails closed — the typo
37+
cost is "nobody can read the book", so say it at author time).
38+
- **platform-objects**: metadata-form translations regain `position` (all four
39+
locales) and drop the retired `role`/`profile` groups, with a vocabulary
40+
regression test.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
"@objectstack/plugin-hono-server": patch
3+
"@objectstack/plugin-security": minor
4+
"@objectstack/spec": minor
5+
---
6+
7+
Two ADR-0090 D5 closures (#2752, #2753):
8+
9+
**`GET /me/apps` sources the engine registry.** Stack apps are registered
10+
into the engine registry (runtime AppPlugin), not the metadata service —
11+
`metadata.list('app')` returned `[]` for every principal, leaving
12+
`tabPermissions` and `AppSchema.requiredPermissions` with no enforced
13+
consumer. The endpoint now reads `registry.getAllApps()` (same authority as
14+
the meta routes, nav contributions merged) with the metadata service as an
15+
additive fallback; the capability and tab filters are unchanged and now
16+
actually run.
17+
18+
**The default baseline binds to the `everyone` anchor.** `member_default`
19+
carried `allowDelete` on its `'*'` grant — an anchor-forbidden bit — so
20+
bootstrap refused the `everyone` binding on every boot and the baseline
21+
flowed only through the separate fallback channel D5 explicitly rejected.
22+
Two aligned changes:
23+
24+
- `describeHighPrivilegeBits` (spec) is calibrated to the exact ADR-0090 D5
25+
bit list (VAMA, delete/purge/transfer, systemPermissions). A plain `'*'`
26+
wildcard is no longer high-privilege by itself; the wildcard ban moves to
27+
the GUEST tier where D9 specifies it (`describeAnchorForbiddenBits`).
28+
- `member_default` drops `allowDelete` from the wildcard. **Behavior
29+
change:** deleting records is no longer a baseline right — members keep
30+
create/read/edit-own; domains that want member deletes grant them per
31+
object via an ordinary position-distributed set. The owner-scoped delete
32+
RLS stays as a narrowing defense for members who receive a delete bit
33+
elsewhere.
34+
35+
With the baseline anchor-safe, bootstrap's existing binding path succeeds:
36+
"what new users get" is now literally "what is bound to `everyone`" — same
37+
table, same audit, same explain path (proven by the new
38+
`me-apps-and-everyone-baseline` dogfood).
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
'@objectstack/objectql': patch
3+
---
4+
5+
fix(data): resolve field `defaultValue`s BEFORE the `beforeInsert` hook (#2703)
6+
7+
Declarative field defaults (including the `current_user` token) were resolved
8+
by `applyFieldDefaults` *after* the user `beforeInsert` hook ran. A hook that
9+
DERIVED one field from another therefore read a stale `null` for any field that
10+
was about to be defaulted — e.g. `sales_person: Field.user({ defaultValue:
11+
'current_user' })` left `sales_person == null` inside the hook, so a derived
12+
`current_status` computed to `unassigned` unless the client passed the field
13+
explicitly.
14+
15+
`applyFieldDefaults` now runs at record-initialization time, before
16+
`beforeInsert`, matching the industry-standard order of execution (Salesforce
17+
field defaults / ServiceNow dictionary defaults are populated before before-
18+
triggers; engine-owned generation — autonumber sequences, encryption, timestamps
19+
— stays after the hook). The hook still has final say: it runs after and may
20+
override any defaulted field. Defaults still only fill fields left `undefined`,
21+
so client-supplied values are untouched, and the caller's input object is no
22+
longer mutated in place.
23+
24+
Behavior note: a `beforeInsert` hook can no longer distinguish "client omitted
25+
field X" from "field X received its default" for fields that declare a
26+
`defaultValue` — the hook now always sees the resolved default. This matches how
27+
Salesforce/ServiceNow behave (before logic sees a fully-initialized record) and
28+
is the intended fix.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@objectstack/spec": minor
3+
"@objectstack/platform-objects": minor
4+
"@objectstack/plugin-security": minor
5+
---
6+
7+
Position assignment panels as pure SDUI (ADR-0090 follow-through).
8+
9+
- `RecordRelatedListProps` gains `relationshipValueField` (default `'id'`): which parent-record field the junction's `relationshipField` stores — the generic affordance for name-keyed junctions (`sys_user_position.position` stores `sys_position.name`). Used for both the list filter and the Add-picker's parent-side value.
10+
- `sys_user` detail page gains a **Positions** tab (assign positions to a user; Add picker stores the position machine name via `valueField: 'name'`; the D12 delegated-admin gate's denials surface in the dialog).
11+
- New `sys_position` detail page (shipped by plugin-security): **Holders** (name-keyed via `relationshipValueField: 'name'`) and **Permission Sets** (bindings) tabs — zero bespoke UI; ADR-0091 validity columns slot in later as plain column additions.
12+
13+
Renderer note: the generic `record:related_list` Add-picker and `relationshipValueField` support land in objectui alongside the ^14 alignment; with older renderers these tabs degrade to read-only lists.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
"@objectstack/rest": minor
3+
---
4+
5+
feat(rest): colour select/radio cells in xlsx exports with their option colour
6+
7+
The data export route (`GET /data/:object/export`) now carries a select /
8+
radio field's option `color` into the generated Excel workbook as the cell's
9+
**font colour** (white cell background), so an exported sheet reads like the
10+
in-app coloured badges instead of plain black text. csv / json output is
11+
unchanged.
12+
13+
- `export-format.ts` gains `toArgb()` (hex `#RGB` / `#RRGGBB` → exceljs ARGB
14+
`FFRRGGBB`, `undefined` for anything not plain hex) and `cellFontColor()`
15+
(resolves the matched select/radio option's colour for one cell; returns
16+
`undefined` — i.e. leave it unstyled — for non-option fields, unmatched
17+
values, colourless options, or invalid hex). `ExportFieldMeta.options` now
18+
carries the option `color`.
19+
- `createXlsxStream(res, useStyles)` takes the flag through to exceljs'
20+
`WorkbookWriter`; the route enables styling and sets `cell.font.color`
21+
per-cell only for xlsx.
22+
23+
Styling is heavier than a bare value dump, so it is gated behind a **10 000-row
24+
cap** (`STYLE_ROW_CAP`): exports whose effective limit exceeds it stream
25+
without colours (all rows intact) and set `X-Export-Styles: dropped`; coloured
26+
exports set `X-Export-Styles: applied`. This mirrors the "formatted export has a
27+
lower ceiling than a raw dump" pattern used by Salesforce / ServiceNow. The
28+
existing 50 000-row hard cap is unchanged.
29+
30+
Closes #2757.

.github/workflows/lint.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ jobs:
5858
- name: Doc/skill authoring guard
5959
run: pnpm check:doc-authoring
6060

61+
# ADR-0090 D3 vocabulary ratchet: "role" is reserved-forbidden in docs
62+
# and skills. Existing occurrences are frozen in the baseline (better-auth
63+
# boundary, ARIA samples, educational mentions); NEW occurrences fail.
64+
# Improvements ratchet the baseline down via --update.
65+
- name: Reserved-word ("role") docs ratchet
66+
run: pnpm check:role-word
67+
6168
# Authorization resolution must stay single-sourced (resolveAuthzContext,
6269
# @objectstack/core). Guards against a duplicate resolver copy drifting on a
6370
# security path (the REST-vs-dispatcher sys_user_role drift) and against an

.github/workflows/release.yml

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,6 @@ jobs:
6262
run: bash scripts/build-console.sh
6363

6464
- name: Downstream backward-compat smoke (live hotcrm)
65-
# TEMPORARY (ADR-0090 v2 launch window) — advisory, not blocking.
66-
# ADR-0090 P1 (#2697) removed the sharing recipients `role` /
67-
# `role_and_subordinates` from @objectstack/spec with NO compatibility
68-
# aliases (docs/adr/0090 D3 — a deliberate, launch-window break). The
69-
# latest published hotcrm tag (v1.2.2) still authors those values, so
70-
# this gate necessarily goes red on a break the spec took on purpose,
71-
# and no adopting hotcrm release exists yet to bump HOTCRM_REF to. Keep
72-
# the step running for signal, but don't let it block the v2 publish.
73-
# RESTORE (make blocking again): once hotcrm ships a release adopting
74-
# the v2 recipient vocabulary, bump HOTCRM_REF below to that tag and
75-
# delete this `continue-on-error` line so a red here blocks publish.
76-
continue-on-error: true
7765
# Pre-publish gate (#2035): the about-to-publish @objectstack/spec must
7866
# not break a real third-party consumer pinned to a published release.
7967
# Clones objectstack-ai/hotcrm@${HOTCRM_REF}, installs it (published
@@ -82,11 +70,12 @@ jobs:
8270
# The deterministic in-repo floor is @objectstack/downstream-contract;
8371
# this is the live ceiling.
8472
env:
85-
# v1.2.1: hotcrm dropped the ObjectSchema `detail` block removed by
86-
# ADR-0085 (hotcrm#423) — the intended break, explicitly digested
87-
# downstream. Bump this ref whenever a deliberate spec surface
88-
# removal ships a matching hotcrm release.
89-
HOTCRM_REF: v1.2.2
73+
# v2.0.0: hotcrm adopted the ADR-0090 Permission Model v2 vocabulary
74+
# (role→position recipients, profiles removed, explicit OWD —
75+
# hotcrm#438), closing the launch-window advisory period from #2719.
76+
# Bump this ref whenever a deliberate spec surface removal ships a
77+
# matching hotcrm release.
78+
HOTCRM_REF: v2.0.0
9079
run: bash scripts/downstream-smoke.sh
9180

9281
- name: Create Release Pull Request or Publish to npm

.objectui-sha

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7a68d78f2a0c3c1f99dafd39b75b4f117a24917b
1+
397dcad73eaf6fdaa1740bff15f9d8faf45c4232

content/docs/ai/actions-as-tools.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ await aiService.chatWithTools(messages, tools, {
210210
actor: {
211211
id: currentUser.id,
212212
name: currentUser.displayName,
213-
roles: currentUser.roles,
213+
positions: currentUser.positions,
214214
permissions: currentUser.permissions,
215215
},
216216
conversationId,

content/docs/ai/agents.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ instead — see the callout in the [AI Overview](/docs/ai).)
4646

4747
Every deployment serves MCP at `/api/v1/mcp` by default (a core platform
4848
capability — set `OS_MCP_SERVER_ENABLED=false` to opt out), with two
49-
authentication tracks:
49+
authentication tracks: The in-product entry point is the
50+
**Setup → Connect an Agent** page: per-client connect snippets, the portable
51+
SKILL.md download (`GET /api/v1/mcp/skill`), and API-key minting.
5052

5153
- **OAuth 2.1 (interactive clients — recommended).** Each deployment is its
5254
own spec-compliant authorization server: the endpoint publishes

0 commit comments

Comments
 (0)