Skip to content

Commit 025f6de

Browse files
docs(spec): the runAs description names a system run's refused identity-assignment writes, with its ADR-0087 D3 entry (#25564)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} --> _Requested by **Tesla** · [project thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoVgqGdGWoJtcHYXUFi5Es7m)_ 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 published `FlowSchema.runAs` description 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`, the `runAs` describe.** One new sentence: elevation does not reach identity assignment. A system run's governed writes (insert, update, delete, transfer, restore, purge) to `sys_user_position`, `sys_user_permission_set`, `sys_invitation` and `sys_member` are refused with `PERMISSION_DENIED`. Declare `user` for 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. - **The comment block above `runAs`.** The same rule stated exactly as #25483 enforces it: refused ahead of the `isSystem` bypass for a context carrying `isSystem` AND a `flowRunId`, `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. - **ADR-0087 D3 semantic entry** `flow-run-as-system-identity-assignment-write-refused` (`entries/semantic/18.…`), the entry #25483's changeset left to this PR. It has no D2 conversion: changing `runAs` changes what every other node of the flow may do, and an endpoint call is not a rewrite of a node. - **Derived pages regenerated** with `gen:docs` (`content/docs/references/automation/flow.mdx`, `content/docs/references/api/automation-api.mdx`). `gen:skill-refs` and `gen:skill-docs` produced no change. `registry.ts`, `spec-changes.json` and the upgrade guide are generated and git-ignored, so nothing is committed for them. - **Changeset** `25554-runas-identity-assignment-wording.md`, `@objectstack/spec` `minor`, with the registered ADR-0087 disposition marker. ## Acceptance notes - **ADR-0073 amendment not in this PR.** `check-governed-merges --test` answers GOVERNED (Tier H) for `docs/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 #25554. - **Changeset level is `minor`, not `patch`.** Ruling A (2026-10-21, on #25105), as `check-changeset-no-major.mjs` enforces it, requires at least one moved package at `minor` for `Metadata-protocol: semantic`. The dispatch order said `patch`; the gate wins. - `service-automation/src/engine.ts` still 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 --commands` derived 109 families for this diff. 108 exit 0. `pnpm check:dual-build-cjs-loads` is NOT MEASURED locally (exit 3: it needs every workspace package built), and CI measures it. - `pnpm --filter @objectstack/spec build` green. `vitest run src/migrations src/automation/flow` in `@objectstack/spec` passed 25 files and 1122 tests. - `check:spec-changes`, `check:upgrade-guide`, `check:docs`, `check:skill-refs`, `check-adr-0087-registration --base origin/main` and `check-changeset-no-major --base origin/main` are green. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_013yig39ZouqWBxABRfM5FN4 --- _Generated by [Claude Code](https://claude.ai/code/session_013yig39ZouqWBxABRfM5FN4)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6abee03 commit 025f6de

5 files changed

Lines changed: 92 additions & 7 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
docs(spec): the flow `runAs` description says a `system` run's writes to the four identity-assignment objects are refused, and the ADR-0087 D3 entry records that change of meaning
6+
7+
Clause-②: no (describe text and ADR registration only; no accept-set change)
8+
Metadata-protocol: semantic (records the change of meaning #25483 landed)
9+
10+
<!-- adr-0087: registered flow-run-as-system-identity-assignment-write-refused -->
11+
12+
`@objectstack/plugin-security` now refuses a `runAs: 'system'` flow's governed writes (insert, update, delete, transfer, restore, purge) to `sys_user_position`, `sys_user_permission_set`, `sys_invitation` and `sys_member` with `PERMISSION_DENIED`: the data middleware refuses them from a context that carries `isSystem` and a `flowRunId`, ahead of its system bypass. The published `FlowSchema.runAs` description still said a system flow keeps system privileges without exception. It now names the refusal and its remedies.
13+
14+
- **`runAs` description.** One sentence: a system run's governed writes to the four objects are refused with `PERMISSION_DENIED`. Declare `user` for 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. The source comment above the key says the same and adds that a platform writer whose system context carries no flow run is unchanged.
15+
- **ADR-0087 D3 entry** `flow-run-as-system-identity-assignment-write-refused` in protocol 18's semantic list, so `os migrate meta` and the upgrade guide list the change for an author upgrading a flow that wrote those rows. It carries no conversion: changing `runAs` changes what every other node of the flow may do, and moving an invitation or membership write to an endpoint is a new call, so no rewrite can make the choice for the author.
16+
17+
No key, default or accepted value changes, and every flow that parsed before parses the same way.

0 commit comments

Comments
 (0)