Repository navigation
docs(spec): the runAs description names a system run's refused identity-assignment writes, with its ADR-0087 D3 entry - #25564
Conversation
…tity-assignment writes, with its ADR-0087 D3 entry The flow runAs describe and its source comment say that a system run's governed writes to sys_user_position, sys_user_permission_set, sys_invitation and sys_member are refused with PERMISSION_DENIED, and name the remedies. A protocol-18 semantic entry records the change of meaning. Derived reference pages regenerated. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yig39ZouqWBxABRfM5FN4
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 144 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 74bc6aa80f1c163ab83ef1fedc75dc67e730d643 && git checkout 74bc6aa80f1c163ab83ef1fedc75dc67e730d643
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 041a120b6189e9b174a3f4b8cf6202f22ace2bbd 5a9bf414cf27e307083bde55bd0f66884398d2a8 && git checkout -B drift-repro 041a120b6189e9b174a3f4b8cf6202f22ace2bbd && git merge --no-ff 5a9bf414cf27e307083bde55bd0f66884398d2a8
node scripts/docs-audit/affected-docs.mjs --json 041a120b6189e9b174a3f4b8cf6202f22ace2bbd
|
Contract reviewServed-tier: Record rendered at 2026-10-24T01:00Z against card #25554 (body and both comments), PR #25564 (body, five files, net diff against ① Derived judgmentsJ1 — the describe sentence states what #25483 enforces, no more and no less: RIGHT. Compared clause by clause against the merged code on
J2 — the ADR-0087 D3 semantic entry: RIGHT. Shape per J3 — regenerated projections: RIGHT. The two J4 — the changeset marker. The changeset is not declared breaking (no BREAKING banner, no ② Semver level
③ Boundary flagsDev deviations (three) and
Escalations, none of which blocks this head:
Check-runs on the head, read at 2026-10-24T00:58Z: 40 runs, 24 success, 6 skipped (opt-in and matrix placeholders), 10 in_progress. Success includes every gate this face owes: Implemented-by: VERDICT: PASS |
席位核对 · ACCEPT ·
|
…commit e649c2d91e (objectui#13440) (objectstack-ai#13445) <!-- claude-projects-attribution: {"github_login":"os-sales"} --> _Requested by **Sales** · [project thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuQx8xAyxeMnyWkn84sEzqVb)_ Closes objectstack-ai#13440 Clause-②: no Metadata-protocol: none Unblocks #13285 and #13305 (both wait for a pin that carries 18.1.0). ## The pin - `.objectstack-sha` moves from `915e94049922` to `e649c2d91e270e3da26820bcfe54a28445305a75`, the objectstack 18.1.0 release commit ("chore: version packages", objectstack-ai/objectstack#25606). It carries objectstack-ai/objectstack#25483 and objectstack-ai/objectstack#25564, which objectstack-ai/objectstack#25251 required to ship in one release, so the same-release hold is met. - **Ancestry, read in the objectstack checkout:** `git merge-base --is-ancestor e649c2d91e origin/main` exits 0, and the bump script reported "(on origin/main)". The `@objectstack/cli@18.1.0` tag peels to `e649c2d91e`. - **The path:** `scripts/bump-objectstack.sh e649c2d91e270e3da26820bcfe54a28445305a75 --no-commit` (so the commit carries the repo's trailer pair), which fetched the sha and built and packed the 7 packages, then `pnpm install`. - **What moved:** `.objectstack-sha` and the seven packed manifests in `.objectstack-pin/`. The manifests change only version strings, `18.0.0` to `18.1.0` (the package's own version and its `@objectstack/*` dependencies). - **`pnpm-lock.yaml` is unchanged.** `pnpm install` left it byte-identical, and `pnpm install --frozen-lockfile` exits 0. So the Dependabot lockfile PRs (objectstack-ai#13438, objectstack-ai#13439) do not overlap this one. - `pnpm objectstack:status` answers `✓ built at the pin e649c2d91e27`. ## What the move breaks, and the follow-through in this PR Each item below is red on the bare pin move and was fixed here because the failing check names its own fix. Nothing else changed. ### 1. The `external` tripwire in `@object-ui/auth` (objectui#13427) `packages/auth/src/__tests__/org-roles.test.ts` › "spells external itself only while the pin predates objectstack#25515" went red as designed. Its message prescribes the swap, and this PR does exactly that: - `packages/auth/src/org-roles.ts` re-exports the spec's `MEMBERSHIP_ROLE_EXTERNAL` as `ORG_ROLE_EXTERNAL` instead of spelling the literal. **The exported name and its value (`'external'`) are unchanged**, and its declared type is still the literal `"external"`, so `@object-ui/auth`'s public face does not move (Clause-② stays `no`). `ORG_ROLE_LABELS` is typed `Record<OrgRole, …>` again, with the `| typeof ORG_ROLE_EXTERNAL` union dropped. - The tripwire test is deleted, with its now unused `@objectstack/spec` namespace import. - `InviteMemberDialog.roleCap.test.tsx` and `org-i18n-holdouts-4474.test.tsx` now expect `external` in the invite dialog's role list. This is the spec's 18.1.0 vocabulary reaching `ORG_ROLES`, which objectui#13427's changeset already described: "Wherever the installed spec lists the role, the invite dialog offers it to every issuer who can invite". It is not a choice made by this card. The owner list gains `external`, and a delegated admin or an unloaded membership is offered `['member', 'external']`. The delegate test's name now says "offered member and external only". ### 2. `check:installed-pin-claims`: 34 present-tense "installed 18.0.0" stamps The gate is blocking through `scripts/__tests__/check-installed-spec-pin-claims.test.ts`. The packed manifests now say 18.1.0, so it flagged 34 lines in 24 files, all comments or test description strings. No code changes. **Every stamp was re-measured before it moved.** The method: - The spec SOURCE diff between the two pins, `git diff --name-only 915e9404 e649c2d91e -- packages/spec/src`, touches only `automation/flow.zod.ts` (the `runAs` description text and a comment), `data/object.zod.ts` (the new `membershipKeys` key and its extension refusal), `identity/*` (the `external` role), `kernel/execution-context.zod.ts` and `security/explain.zod.ts` (descriptions and comments), `security/rls.zod.ts` (`MembershipKeysSchema`), `stack.zod.ts` (the duplicate membership-key refusal), `index.ts` (two new exports) and one semantic migration entry, plus tests. - No claim's subject is in that set. The subjects are `ui/*` rows (`ViewDataSchema`, `ComponentPropsMap['object-calendar']` and `['object-kanban']`, list-view conditional formatting and card style rules, the chart and dashboard `aria` tombstones, record components, form section groups, master-detail details, the gantt `navigation` tombstone), `FieldSchema` and `SelectOptionSchema`, and flow node config (`waitForInput`, the single-brace mark, the canvas-seed save errors). The two claims about non-spec sources were checked at their own source: `packages/lint/src/validate-expressions.ts` (the `current_user` CEL message `celAuthoring.ts` quotes) has an empty diff between the pins, and the trigger packages changed only `CHANGELOG.md` and `package.json`. - The test files that carry a claim, or pin a source file's claim, ran green at the new pin (see the verification comment). Each stamp moves `18.0.0` to `18.1.0`. A stamp that carries its re-measure list (`objectui#12030, … objectui#13261`) gains `objectui#13440`, the same form objectui#13261 used. After the restamp the gate answers `34 at the pin` and `OK`. ### 3. `e2e/live/ci/backend.env` `scripts/__tests__/ci-cd-pipeline-doc.test.ts` › "pins OBJECTSTACK_VERSION to the version the lockfile resolves" went red. `OBJECTSTACK_VERSION` moves `18.0.0` to `18.1.0`. Its `@objectstack/cli@18.1.0` tag peels to this pin's commit, so the live lane's backend and the console's pin are the same release. ### 4. The `ObjectSchema` key-count guards in `app-shell` `MetadataService.retiredObjectEnabled.test.ts` (line 157) and `MetadataService.specKeyObjectPayload.test.ts` (line 171) count the keys `ObjectSchema` accepts, and both went red with `expected 47 to be 46`. Spec 18.1.0 adds one key, `membershipKeys` (objectstack-ai/objectstack#25513). **Measured, not assumed:** the spec's own `authorable-surface/data.json` gains exactly `data/Object:membershipKeys` between `915e9404` and `e649c2d91e`, and loses nothing. Both guards now expect 47, and each carries a comment with that basis, in the same form as the earlier re-point (objectui#12971). The two files pass, 25 tests. ## Changeset `.changeset/13440-objectstack-pin-e649c2d.md` declares `'@object-ui/auth': patch`. The published module now imports `MEMBERSHIP_ROLE_EXTERNAL` from `@objectstack/spec`, so it needs a spec that exports it (18.1.0 or later). `node scripts/objectstack-floor.mjs --dry-run` exits 0 and moves 37 ranges to `^18.1.0` at publish, so the published ranges will require that version. `check-changeset-presence.mjs` and `check-changeset-no-major.mjs` pass. ## Acceptance notes - The restamped comments in published packages (`types`, `core`, `plugin-calendar`, `plugin-designer`, `plugin-detail`, `plugin-kanban`, `app-shell`) change no behaviour, so the changeset names `@object-ui/auth` only. - After merge the seat reads #13285 and #13305 against the new pin, as the card says. - [x] Adds no product-specific widget, dialog, hook or string (see AGENTS.md › Product-neutral renderer) Session: `https://claude.ai/code/session_01CErMSwkRLW7c7jk23MHfVP` 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01CErMSwkRLW7c7jk23MHfVP --------- Co-authored-by: Claude <noreply@anthropic.com>
Requested by Tesla · project thread
Closes #25554
Part of #25251
Clause-②: no (describe text and ADR registration only; no accept-set change)
Metadata-protocol: semantic (records the change of meaning #25483 landed)
This is PR2 of ruling 6354966423 (letter B) on #25251, the spec-lane half. PR1, #25483, made the data middleware refuse a
runAs: 'system'flow's governed writes to the four identity-assignment objects. The publishedFlowSchema.runAsdescription still told authors that a system flow keeps system privileges without exception. Under ADR-0087 this PR has to ship in the same release as #25483.What changed
packages/spec/src/automation/flow.zod.ts, therunAsdescribe. One new sentence: elevation does not reach identity assignment. A system run's governed writes (insert, update, delete, transfer, restore, purge) tosys_user_position,sys_user_permission_set,sys_invitationandsys_memberare refused withPERMISSION_DENIED. Declareuserfor position holdings and permission-set grants, so the ADR-0090 D12 delegated-administration gate judges the user the run acts for. Change invitations and memberships through the organization endpoints.runAs. The same rule stated exactly as feat(plugin-security)!: a flow that runs as system writes no identity assignment #25483 enforces it: refused ahead of theisSystembypass for a context carryingisSystemAND aflowRunId,403, nothing written. Reads of the four objects and writes to every other object are unchanged, and so is a platform writer whose system context carries no flow run.flow-run-as-system-identity-assignment-write-refused(entries/semantic/18.…), the entry feat(plugin-security)!: a flow that runs as system writes no identity assignment #25483's changeset left to this PR. It has no D2 conversion: changingrunAschanges what every other node of the flow may do, and an endpoint call is not a rewrite of a node.gen:docs(content/docs/references/automation/flow.mdx,content/docs/references/api/automation-api.mdx).gen:skill-refsandgen:skill-docsproduced no change.registry.ts,spec-changes.jsonand the upgrade guide are generated and git-ignored, so nothing is committed for them.25554-runas-identity-assignment-wording.md,@objectstack/specminor, with the registered ADR-0087 disposition marker.Acceptance notes
check-governed-merges --testanswers GOVERNED (Tier H) fordocs/adr/0073-automation-execution-identity.md, so per the card it goes as a separate maintainer-merged PR. The draft text is in the os-dev-report on spec: the runAs describe still says a system flow keeps system privileges, but its writes to the four identity-assignment objects are now refused (#25251 PR2) #25554.minor, notpatch. Ruling A (2026-10-21, on changeset levels: minor only when the metadata protocol changes (added / narrowed / semantic), patch otherwise; release notes grouped by what the reader must do (maintainer ruling A, 2026-10-21) #25105), ascheck-changeset-no-major.mjsenforces it, requires at least one moved package atminorforMetadata-protocol: semantic. The dispatch order saidpatch; the gate wins.service-automation/src/engine.tsstill quotes the earlier ruling text ("system flows keep system privileges") as a citation of that ruling. It is left alone.Verification
At head
5a9bf414cf:node scripts/pm/dispatch-gates.mjs --commandsderived 109 families for this diff. 108 exit 0.pnpm check:dual-build-cjs-loadsis NOT MEASURED locally (exit 3: it needs every workspace package built), and CI measures it.pnpm --filter @objectstack/spec buildgreen.vitest run src/migrations src/automation/flowin@objectstack/specpassed 25 files and 1122 tests.check:spec-changes,check:upgrade-guide,check:docs,check:skill-refs,check-adr-0087-registration --base origin/mainandcheck-changeset-no-major --base origin/mainare green.🤖 Generated with Claude Code
https://claude.ai/code/session_013yig39ZouqWBxABRfM5FN4
Generated by Claude Code