Skip to content

Commit f4eb433

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-ybjm3h
2 parents c5f1351 + a8e7393 commit f4eb433

47 files changed

Lines changed: 1447 additions & 281 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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/spec': patch
3+
'@objectstack/plugin-security': patch
4+
'@objectstack/plugin-auth': patch
5+
---
6+
7+
feat(plugin-security,spec,plugin-auth)!: a delegated admin's write outside their own scope answers `403 DELEGATION_SCOPE_VIOLATION` with the gate's reason
8+
9+
When a delegated administrator's write on `sys_user_position` or `sys_user_permission_set` fell outside their own `adminScope`, the delegated-administration gate knew exactly why ("business unit … is outside the delegated subtree", "… is not in the scope's allowlist"), but threw it as a plain `403 PERMISSION_DENIED`. A client could not tell it from a denial whose reason must stay hidden, so the console showed only its fixed "no permission" line.
10+
11+
Clause-②: yes (widening: the ADR-0112 ledger registers `DELEGATION_SCOPE_VIOLATION` under `@objectstack/plugin-security`)
12+
Metadata-protocol: none
13+
14+
<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, object definition, stored row or stored shape is added, removed, renamed or re-shaped, and no exported declaration is removed or narrowed, so there is nothing for `objectstack migrate meta` to rewrite. What changes is the error code three of the gate's refusal reasons answer with: every write that was refused is still refused, with the same 403 status and the same reason, and every write that was admitted is still admitted. The remedy is on the consumer's side, reading `code`; never a rewrite of a package source or a stored row. The other categories are closed on facts: all three packages publish (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and nothing exported is removed or narrowed (not runtime-interface-only, type-surface-only or runtime-export-retired). -->
15+
16+
**BREAKING** (a wire-answer change), shipped as `patch`: no metadata protocol kind moves.
17+
18+
- **`@objectstack/plugin-security`.** Three reasons describe the caller's own delegated scope: a business unit outside the delegated subtree (the new row's or the existing row's), a permission set not in the scope's allowlist (one the position distributes, or one granted directly), and a target user outside the subtree. They now throw `DelegationScopeViolationError`: status `403`, code `DELEGATION_SCOPE_VIOLATION`, and the message `[Security] Outside your delegated scope: delegated 'OP' on OBJECT rejected — REASON (scope from 'SET').` Every other refusal of the gate keeps `403 PERMISSION_DENIED`, unchanged: the audience anchor, principal-less writes, `sys_member` being tenant-only, the organization boundary, plain CRUD with no scope, filter writes, delegation of duty, a scope without `manageAssignments`, an unanchored assignment, adminScope containment, a stored grant whose set is unnamed or not in the catalog, a grant naming no target user, and a target user with no business unit.
19+
- The message does not open with `[Security] Access denied`, which `mapDataError`, `isPermissionDeniedError` and the explain route read as `PERMISSION_DENIED`. The REST door (`mapDataError`) and the runtime dispatcher (`resolveThrownHttpError`) carry the new code as it was thrown.
20+
- **`@objectstack/spec`.** One ADR-0112 ledger row, `DELEGATION_SCOPE_VIOLATION`, registered as its own meaning. It is not a synonym of `PERMISSION_DENIED`, and no waiver is needed.
21+
- **`@objectstack/plugin-auth`.** The invitation door reads the new code as the gate's verdict too, so an invitation placed outside the issuer's scope still answers `403 INVITE_PLACEMENT_OUTSIDE_ISSUER_SCOPE`, not the `503` read-fault answer.
22+
- For a client: a `403` with code `DELEGATION_SCOPE_VIOLATION` carries a message about the caller's own scope, which is safe to show. A `403 PERMISSION_DENIED` stays opaque. A client that matched `PERMISSION_DENIED` to catch these three refusals should also match the new code.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
fix(cli): `os create plugin NAME --in-repo` scaffolds extend `tsconfig.base.json`, so `tsc` reaches the scaffold's own `src/`
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** The in-repo plugin's `tsconfig.json` extended the monorepo root `tsconfig.json`. That file is the type-check program for `scripts/`, and its `exclude` (`packages`, `apps`, `examples`, `ui`) resolves against the root, so a scaffold under `packages/plugins/` inherited it and matched none of its own files: `tsc` stopped with TS18003, "No inputs were found".
11+
- **After.** The emitted `tsconfig.json` extends `tsconfig.base.json`, the file every package in the monorepo extends, declares `types: ["node"]` and its own `exclude: ["dist", "node_modules"]`, the same shape as a sibling such as `plugin-email`. `tsc --showConfig` for the scaffold and for `plugin-email` differs only in each package's own file list.
12+
- The default (standalone) placement is unchanged.
13+
14+
What to do: nothing for new scaffolds. An in-repo plugin scaffolded earlier: change its `tsconfig.json` `extends` from `../../../tsconfig.json` to `../../../tsconfig.base.json`, and add `"types": ["node"]` and `"exclude": ["dist", "node_modules"]`.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
fix(cli): when `os dev` shifts off a busy port, the config reads the port the server binds
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** The development auto-shift ran in the `serve` child, after `os dev`'s compile and the child's own config load had both read the requested port. On `examples/app-showcase` with 41310 held, `os dev -p 41310` printed `Port 41310 is in use — serving on 41311 instead`, yet `GET /api/v1/meta/connector/showcase_status_api` served `http://127.0.0.1:41310` and `showcase_declarative_connector_ping` answered `400 FLOW_FAILED`. The same held for a port from `OS_PORT`, and for a direct `os serve --dev` (or `os serve` under `NODE_ENV=development`), whose dispatch dialled the busy port.
11+
- **Now.** `os dev` runs the same walk before it compiles. On a shift it prints the same notice, exports the port it landed on as `OS_PORT`/`PORT` (the channel a port flag already uses) for the compile, the watch recompiles and the server, and hands the server that port. `os serve` exports its shifted port the same way before it loads the config and before a direct `os serve --dev` compiles. The same boots now serve and ping the port they bound, and the ping answers 200.
12+
- Not covered: a port taken between `os dev`'s probe and the server's bind makes the server shift once more. It says so and its own config reads the new port, but `/api/v1/meta` keeps the probed one until the next recompile.

‎.claude/agents/os-dev.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,7 @@ model: opus
270270
- driver/kernel 启动路径同此;那时跑 `--project integration` 并在报告里说明,否则写已声明给 CI。
271271
- 层是测出来的,不是列出来的:spawn CLI 或启动 driver 的测试按 `packages/cli/vitest-tiers.ts` 落。
272272
- 分区 pin `test/vitest-tiers-partition.test.ts` 在某个测试文件两层皆无或皆有时变红。
273-
- 用 `git push -u origin claude/issue-<n>-<slug>` 推上去,网络失败退避重试;pre-push 拒卡片 trailer。
274273
- Draft PR 指向 `main`,正文首行 `Fixes #<n>`;合并不应关卡时用 `Part of #<n>`,并说明留下哪一半。
275-
- PR 开出后合 main 只因冲突、main 致 CI 红、出队,后只跑 §10 复检。
276274
- 全量 dogfood 只在终 head 跑一次,仅当标题带 `!` 或 `Clause-②:` 带 `(narrowing)` 臂(yes/no 皆算)。
277275
- 命令同 CI:按 ① 建闭包,锁下 `pnpm --filter @objectstack/dogfood test`;⛔ 无 `OS_TEST_SHARD`、无路径。
278276
- 红 = 本改动的后果(陈旧 fixture)同 PR 修,余照报;⛔ 永不跳过或收窄;计数记 `dogfood_battery`。
@@ -281,12 +279,14 @@ model: opus
281279
- ⛔ 永不 `Fixes` 一张还在决策箱的卡:合并会静默关掉它,而收件箱过滤只读 open。
282280
- ⛔ 不写否定式的关单句,它照样关掉点名的卡:解析器无视否定,只匹配关键词 + `#<n>`。
283281
- 关键词是 `fix/fixes/fixed/close/closes/closed` 与 `resolve/resolves/resolved`;让它们远离其它卡号。
284-
- 写 `#<n> is not addressed here`、`out of scope: #<n>` 或 `#<n> remains open`。
285282
- 卡片关系只在 PR 正文声明一次:commit ⛔ 不带卡片 trailer,其 trailer pair 一律 model-free。
286283
- harness 归属提醒凭其优先级句让位本文件;harness 自写含模型名 trailer 只报,⛔ 不仿不改史。
287284
- 受管路径全在 `.claude/**` 者 Tier S,达档复核 PASS 在案即由席位入队落地;余皆 Tier H 等人批。
288-
- 受管面 PR 正文带 `## 维护者速读(草稿)` 节,中文、业务角度,席位意见留空;两层同欠。
289-
- 五段固定:改了什么/为什么改/风险与代价(含回滚)/席位意见/你要做的;席位定稿成评论。
285+
- 两声明行后 `## 维护者速读` 六行,受管与否都带,中文、业务角度,不写席位意见。
286+
- 六行固定:改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的。
287+
- 先看 ≤3 文件按风险排,点名风险 hunk;两分钟验证 = 一条命令或一次点击,写预期所见。
288+
- 需要你做的恰一个动作,非受管面写「无,席位落地」;席位定稿成评论。
289+
- 英文节序:What changes/Tests/Acceptance notes/Verification;日志读数每子题折进 details/summary 块。
290290
- 触 `skills/**`(对外发布的技能包)的 diff:PR 正文报两个读数,并默认拒绝小功能大扩写。
291291
- 两个读数缺一不可:被改文件的整文件 before/after,与整包 before/after(全部 SKILL.md 之和)。
292292
- 行数为准,姊妹门禁定义 token 计数后同报 token。

‎.claude/skills/pm-dispatch/SKILL.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,13 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
185185
- 另含 `ui/docs/adr/**`+`ui/.claude/**`+`ui/skills/**`+`ui/AGENTS.md`+`ui/CLAUDE.md`+`ui/.github/workflows/**`。
186186
- 路径面命中 Tier H ⇒ ACCEPT 换终局四件套,混合 diff ⛔ 不按比例判;要拆让 dev 单独开 PR。
187187
- 改动 >3000 行(含生成物)同换终局四件套,⛔ 无 Tier S 例外;读数 = PR additions+deletions。
188-
- ① 席位记录一条落 PR,两层同欠:Tier H 置顶速读五段,其下 `## 席位核对`;卡只留指针行。
188+
- ① 席位记录落 PR 先于 `pr_ready`/入队且钉 head:速读六行+席位意见,其下 `## 席位核对`。
189189
- ② PR 悬着等人批,同笔挂 `needs-user-decision`;人工直合即审核记录,授权批准 ⇒ 席位落地。
190190
- 指针 `ACCEPT → PR #n comment <id>`;技能面 hunk 复核随面定档:席未达档起隔离达档复核。
191191
- 核对六项(席位读此):形态/前提实测/门禁 check-runs/接受的偏差/范围外去向/落地条款。
192-
- 速读五段固定:改了什么/为什么改/风险与代价(含回滚)/席位意见/你要做的(一个动作)。
193-
- 草稿归 dev:受管面 PR 正文带 `## 维护者速读(草稿)`,中文、业务角度,席位意见留空。
194-
- 终稿 = 席位对照自己读的 diff 校正草稿、填席位意见,置顶席位记录;维护者只读评论。
192+
- 速读六行固定:改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的。
193+
- 草稿归 dev:每个 PR 正文带 `## 维护者速读` 六行,中文、业务角度,不写席位意见。
194+
- 终稿 = 席位对照自己读的 diff 校正六行、加席位意见行,置顶席位记录;维护者只读评论。
195195
- PR 上的标签 = 待维护者审阅,不入六态;与请审同为等人批清单,随获批或撤回判决离开。
196196
- 批准判定单源 = 队列守卫常量 `GOVERNED_APPROVERS`:授权账户 APPROVED 即算,⛔ 不卡 `commit_id`。
197197
- 批准后再推亦不过期;席位落地 = 过落地前检、清标、ready、auto-merge,踢出/变基同法。

0 commit comments

Comments
 (0)