Skip to content

Commit 87f686f

Browse files
committed
Merge origin/main into claude/project-thread-hzucdt
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jU8C3iu8fsXdU4Rm8o
2 parents d9c3c22 + 6924440 commit 87f686f

78 files changed

Lines changed: 3424 additions & 277 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/plugin-auth': patch
3+
'@objectstack/plugin-security': patch
4+
'@objectstack/platform-objects': patch
5+
'@objectstack/spec': patch
6+
'@objectstack/cli': patch
7+
---
8+
9+
feat(plugin-auth): a SCIM group push grants the ordinary permission set its group mapping names
10+
11+
Clause-②: yes (widening)
12+
Metadata-protocol: none
13+
14+
An identity provider that pushes a group over SCIM now changes what its members may do. A platform administrator saves a mapping from one SCIM connection's group to one permission set with `POST /api/v1/auth/admin/scim/set-group-mapping` (`connectionId`, `groupExternalId` or `groupDisplayName`, `permissionSet`), and deletes one with `POST /api/v1/auth/admin/scim/delete-group-mapping` (`mappingId`). Both run the platform-admin gate the SCIM credential door runs. A connection whose credential names no organization, or whose credentials name two, is refused `400 VALIDATION_FAILED` on `connectionId`, so every mapping, and every grant it makes, belongs to exactly one organization. Only live credentials (active and unexpired, as the bearer verifier reads them) decide that organization: a credential revoked in one organization does not block a mapping once a new one is minted in another. Saving the same group and set again is idempotent; if that mapping was saved while another organization owned the connection, the save is refused `409 RESOURCE_CONFLICT` naming the old mapping, which must be deleted first. The rows live in the new platform object `sys_idp_group_mapping`, which the data API only lists.
15+
16+
When a group's membership changes, `@better-auth/scim`'s projection grants each member the mapped set in the organization that owns the connection, inside the SCIM transaction, written by the connection (`svc:scim:CONNECTION_ID` in the audit trail). Removing a member removes that grant. A group nobody mapped grants nothing. The projection asks the boundary again before every grant it writes, so a set that becomes an administrator set after its mapping was saved grants nothing. A grant the projection did not make, such as one an administrator made by hand, is never removed. Saving or deleting a mapping applies it to the group's current members at once.
17+
18+
A mapping can never make anyone an administrator. The save refuses, with `400 VALIDATION_FAILED` on `permissionSet`, every set outside the organization boundary: `admin_full_access`, the organization-administrator sets, the agent ceiling sets, a set with a superuser wildcard or an `adminScope`, and a set with a capability not scoped to the organization. The judgement is plugin-security's own delegated-administration predicate, now served as the optional `ISecurityService.organizationBoundaryFailure(setName)` member. Without a security service the save is refused `503` and nothing is granted.
19+
20+
The break-glass guard's refusal no longer tells operators that "the SCIM group mapping is too broad": a group mapping never moves administrator standing, so the sentence now says to grant another account standing first.
21+
22+
The v18 organization-ownership ceremony (`os migrate organization-ownership`) inventories `sys_idp_group_mapping` with the attribution fate, like the other organization-owned rows written with their organization: a database that holds the new table plans and applies as before.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/mcp': patch
3+
---
4+
5+
fix(mcp): `agent_prompt` reads an agent through the protocol read `GET /api/v1/meta/agent/NAME` serves, so an agent a package ships is advertised and served
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: none
9+
10+
The stdio MCP server's `agent_prompt` read agents from the kernel `metadata` service, which holds no copy of an agent a package ships (it lives in the registry the protocol reads). So a boot whose only agent came from a package did not list `agent_prompt`, and a get could not serve that agent, while `GET /api/v1/meta/agent/NAME` served it. Now, where the host's read has both the protocol's reads (the `protocol` service does), `agent_prompt` resolves the agent through its by-name read (`getMetaItem`) and the roster that decides whether the prompt is advertised reads its merged listing (`getMetaItems`), with the metadata service's completeness verdict kept on top as for skill prompts. One reader per host, decided once for both the roster and the get: a host without both reads reads the metadata service exactly as before, for both. A miss still answers `Error: Agent "X" not found`, and a by-name read refused `SERVICE_UNAVAILABLE` still answers the outage text with no instructions.
11+
12+
`McpMergedMetadataRead`, the type of `MCPServerRuntime.bridgePrompts`' second parameter, gains an optional `getMetaItem(request)` member; a host that passes an object without it is read as before, the roster included.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/service-package': patch
3+
'@objectstack/metadata-protocol': patch
4+
'@objectstack/cloud-connection': patch
5+
---
6+
7+
fix(cloud-connection): an unmanaged copy is recorded from the row the import wrote, not from a store re-read at install
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
Before this, the install-local door tied an unmanaged copy's record to the environment base's `sys_packages` row by reading the package store right after the import. That read swallows an ordinary fault and answers null, so a passing store fault at that moment left the copy with no record, and the purge, reseed and count doors answered 404 for a copy that was installed.
13+
14+
- **`publish` states the stamp it writes.** The package service now makes one UTC stamp per write, in the shape the column already stores (`YYYY-MM-DD HH:MM:SS`, what `CURRENT_TIMESTAMP` and `UTC_TIMESTAMP()` produced), and binds it as the row's `created_at` and `updated_at` in both dialects' upserts. `publish` answers `{ success: true, version, createdAt }`, where `createdAt` is the value the row holds after the write. On a re-publish of an existing (id, version) row the stored `created_at` stays the earlier one, as before (the update arm sets only `updated_at`), so `publish` reads that row's `created_at` before its upsert and answers it. When that read faults, the answer names no `createdAt`. The table DDL and the latest index are unchanged.
15+
- **The import carries the base row, door-only.** The in-process `importUnmanagedPackage` answer gains `baseInstance: { version, createdAt }` when the package store stated them. It is outside the published `ImportUnmanagedPackageResponse`: the schema's parse strips it, and no REST answer carries it.
16+
- **install-local ties the record from that answer.** The install no longer reads the package store. A package store that states no stamp fails closed as before: `copyRecord.written: false`, and the copy doors answer 404. The purge-time re-read and its comparison are unchanged.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ Seat: `domain:<x>#<n>` (the seat number this PM sits on; absent = seat 1)
310310
File surface: `<预期触碰的目录>` (stop on breach; explain in the report)
311311
Container & model: `<S 级机械卡 / M / L>`, `mode:subagent | mode:cloud | mode:thread`, `model: <档位词,引当次 --tier 输出;天花板拼 CONTRACT_REVIEW_TIER,同行引其 MANDATORY 路径行或 reason:(逐卡裁决,或常设档位令的 id 与时刻);⛔ 不抄模型 id>`, `effort: <medium | high | xhigh | max,按卡难度定>`
312312
Clause-②: yes | no
313-
Metadata-protocol: added | narrowed | semantic | none (the kind of change to what a metadata author can write; the level table is AGENTS.md's Post-Task Checklist step 3; the dev copies it beside `Clause-②:` into the PR body and every changeset body; a kind other than `none`, or a `(narrowing)` arm, owes the PR body a third line the dev writes from the diff: `Skills-docs: updated here | follow-up owed — FILES | follow-up #N | none — REASON`; `updated here` = this diff teaches it in `skills/**` or the docs and the package's evals exercise it; the seat files the owed card and rewrites `owed — FILES` to `#N` before ACCEPT, through the PR-body line of `references/seat-post-protocol.md`)
313+
Metadata-protocol: added | narrowed | semantic | none (the kind of change to what a metadata author can write; levels: AGENTS.md's Post-Task Checklist step 3; the dev copies it beside `Clause-②:` into the PR body and each changeset body; a kind other than `none` or a `(narrowing)` arm owes the PR body a third line, from the diff: `Skills-docs: updated here | follow-up owed — FILES | follow-up #N | none — REASON`; `updated here` = the diff teaches it in the docs (in `skills/**` only on `domain:skills` PRs, else `follow-up #N`) and its evals exercise it; the seat files the owed card, rewriting `owed — FILES` to `#N` before ACCEPT via `references/seat-post-protocol.md`'s PR-body line)
314314
Responsibility: `<whose code produces the risk, and the repo it lives in (objectstack ⇒ a platform card carries the fix, references/cross-repo-coordination.md rule 1) | the platform path that already covers it, or none | who reaches it, and whether anyone uses it today>` (defect cards and cards whose premise is a failing behaviour, else `n/a — not a defect card`; the dispatch prompt's ruling section repeats the three answers)
315315
Thread-read: <id of the newest comment on the card at the moment this claim is written, or none>
316316
Premise: holds | corrected <comment id> | inbox (the pre-dispatch premise reading, dispatch-runbook.md 派发前置)

‎.claude/skills/pm-dispatch/references/dispatch-runbook.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
- 全量 = 会话、分支、文件面、交付物、串行约束;成员卡各留一行指针指向锚卡。
3434
- 标签翻转照旧逐卡执行:省的是评论正文写量,不是状态写入。
3535
- 验收与解锁扫描的读侧不变,仍逐卡读。
36-
- 写序:标签翻转只在认领评论写成功之后,两笔不原子。
37-
- 评论失败而标签已翻即制造一记真半状态;顺序反了就带重试义务,重试未成即回滚标签。
36+
- 写序:认领一笔 `label-write --comment-file`,评论排首、标签随后;执行器首败即停,标签不先翻。
37+
- 评论存而标签未翻 ⇒ 非零退出;补标签用不带 `--comment-file` 的 `label-write`,⛔ 不重发评论。
3838

3939
## 巡查常设项(每席巡查与总监席摘要)
4040

‎.claude/skills/pm-dispatch/references/execution-duties.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
- 工作项面:卡片、裁决与分诊评论列的工作项逐条对树核验是否仍未完成,⛔ 不对卡核验。
1919
- `batch` 等待不填槽,四态见 SKILL.md 该行;同批独立按文件面不相交判,⛔ 不按包或 check 名。
2020
- 软顶:本席已验收等人批的 Tier H PR 逾 5 ⇒ 停派 Tier H,S/非受管照常,轮报列积压;两周复测。
21+
- 每轮按现 head 核本席终局四件套 PR:席位记录/`pm:awaiting-merge`/请审/终版速读,缺件同轮补。
22+
- 四件读数器:本地 `node scripts/pm/check-half-states.mjs` H43/H48 只读受管 H;超行无受管 PR 自读。
2123
- single-writer 路径 = `SINGLE_CLAIM_PATHS` 所枚举;共享其它路径是普通并发,后落地方解冲突。
2224
- 第 N 单派发前读 `scripts/pm/os-verify-lock.sh --status`:到达深度 ≥ `LOCK_DEPTH_HOLD`(= 2)即等。
2325
- 到达深度 = `queue N:` 行数 + 1(待派 dev 的运行算作到达);`state:` holder 与 `parked` 行不计。
@@ -67,9 +69,9 @@
6769

6870
- 归属 = session ID,⛔ 不认作者字段;`Account:` 派生自 assignee,不等即半态、持卡席下笔补正。
6971
- 卡 assignee 归 PM,dev 恒不写;PR 派认领账号,交棒同笔改;在飞 PR = `head:claude/` 正文点名本卡。
70-
- 部分落地(PR 带 `Refs #N (item k)`,⛔ 不 `Fixes`)即释放:合入同笔回 `pm:queue` + 清 assignee。
72+
- 部分落地(PR 带 `Refs #N (item k)`,⛔ 不 `Fixes`)即一笔释放:`Release:`+回 `pm:queue`+清 assignee。
7173
- `Release:` 行点名已落与余项;余项整落 `ui/**` 外他席树 ⇒ 点名其改标,拆分加 `pm:retriage`。
72-
- 派发前按序执行原子对:① Assign @me,并把 `pm:dispatched` 与摘 `pm:queue` 放进同一次标签写入。
74+
- 派发原子对一笔 `label-write`:① `--assign` @me 加 `pm:dispatched` 摘 `pm:queue`,`--comment-file` 带②。
7375
- step ① 之后获得 assignee 的直接弃出本批。
7476
- ② 认领评论照抄 SKILL.md 模板:`Branch:`、`Clause-②:`、`Metadata-protocol:` 各占一行且行首。
7577
- 条款②入队闸门只读认领评论且只读行首键:句中提及与另发评论皆读不到,卡即不可读。
@@ -177,12 +179,10 @@
177179

178180
- 你是记录在案的复核人:对 GitHub 核验,⛔ 不对报告的自述核验。
179181
- 逐项判据展开在 `references/review-checklist.md`,每份报告对着它过。
180-
- PR 改已有 `.changeset/*.md` ⛔ 不打 `skip-changeset`;确认件只认派发席记录(`landing-operations.md`)。
181182
- 绿色输出≠ 该绿证明了被测风险:拒收断言、全绿方向与时序、pin 翻转、边界后收益。
182183
- 触 `skills/**` 的 PR 加问整包价值密度:从整包加载的客户 agent 座位读,⛔ 不从作者座位读。
183184
- 超派发预算或小功能大扩写 ⇒ REWORK,⛔ 不因已经写好了放行。
184185
- 判决 ACCEPT:PR 评论 `## 席位核对`,清单结论+抽查读数+偏差,⛔ 不复述其叙事;卡留指针行。
185-
- `mcp_calls` 点名写工具(`settings.json` deny 清单 + `update_pull_request`)⇒ 拒收,⛔ 不带注放行。
186186
- 判决 REWORK:逐项反馈,同认领重派;补丁轮优先 SendMessage 续派原 dev;最多 2 轮,第三次升级。
187187
- 判决 ESCALATE:见〈升级与决策〉;熔断任一命中亦判,PR 暂停,答复前 ⛔ 不派下一轮。
188188
- 熔断三条:独立安全复审连续 2 轮不过;diff 超首次复核规模 2 倍;上轮修复引入新 HIGH。

‎.claude/skills/pm-dispatch/references/landing-operations.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@
4848

4949
- 车道 PM 的权责三件:验收(复核清单)、首次入队、确认 MERGED;落地执行在案判决,非自批。
5050
- 首次入队:ACCEPT 后挂 6–9 分钟 flip 定点,到点核门禁 job 结论。
51-
- 绿即经中继 op `pr_ready` + `automerge_enable` 落地,拼写见 `rest-channel.md`;未绿按阶梯重挂定点。
51+
- 绿即落地评论与 `pr_ready`+`automerge_enable` 一笔中继:`label-write --land`;未绿按阶梯重挂定点。
5252
- 写节流拒(exit 10) ⇒ 待 `node scripts/pm/write-pace.mjs --status` 现空槽再重试。
5353
- CI success webhook 不可靠:⛔ 不坐等,也 ⛔ 不忙轮询;定点文本照定时器写法纪律。
54-
- undraft 撞 429 ⇒ 落地入等 `resets at` 态:该刻记卡,到点一次重试;⛔ 不报阻塞、不自定节奏。
54+
- undraft 429 ⇒ 卡记 `resets at`,到点一次重试裸 op(`rest-channel.md`),⛔ 不重发评论、不报阻塞。
5555
- 落地遭分类器拒 ⇒ 停手、报维护者、卡上记命令与拒因;⛔ 不换拼法、不退回 ccr 绕过。
5656
- 落地评论 = 两个读数的探针表 + 一行状态变更(摘标/关卡/收口),⛔ 不复述 `## 席位核对`。
57-
- 转 ready/入队前跑 `check-governed-merges.mjs --pr N`:受管面照两层;>3000 行(含生成物)照 Tier H。
57+
- 转 ready/入队前跑 `check-governed-merges.mjs --pr N`,判定读全文 ⛔ 不 tail/head;四件套照 SKILL.md。
5858
- ⛔ 两层不由席位批准;清标即落地同受此闸,漏判会被队列守卫在 merge group 里拒收。
5959
- 挂 auto-merge 的同一动作里重测生成物漂移,⛔ 不复用更早的零;非零 ⇒ 按 A 的固定序。
6060
- ready + 全绿 ≠ 已入队:队列从不主动拉 PR,入队是显式动作。

‎.claude/skills/pm-dispatch/references/seat-lifecycle.md‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,18 @@
4949

5050
## Epic 子树车道
5151

52-
- 大开发与跨层功能点(spec/运行时/前端)整体委托一个专职 PM 会话:`/pm-dispatch epic:#<n>`。
53-
- 委托信号成对落地:父单打 `pm:epic` + 正文写会话 ID 与声明的文件领地。
54-
- epic 立卡挂父单、打 `pm:epic`,⛔ 永不 `pm:queue`;`label:pm:epic` 即父子保留全集,域座位不取。
55-
- 队列 = 子树 open 未认领 sub-issue,每轮重读不缓存;其它 PM 候选获取跳过整棵子树。
56-
- 域座位批次选择读一次 `label:pm:epic` 索引,避开领地相交;epic PM 不兼任 `domain:*` 座位。
57-
- 认领纪律全套照做:先写标签、再 `Claim:`、再全线程重读;认领后 `pm:epic` 留在卡上。
58-
- 离开子树(交车道或转 spec 座位)同一笔标签写摘 `pm:epic` 加 `pm:queue`。
59-
- `pm:epic` + `pm:queue` 同卡 = 半状态(保留兼移交);后者即已移交,取回走全套认领协议含重读。
60-
- 单车道仓(无 `domain:*`)开卡即认领合法;多车道仓域标签前置照旧,子树标记即保留。
61-
- 新立 spec/公共契约 sub-issue 照旧转 spec 座位队列,epic 侧写 `Blocked-by:`;衍生按三分留席。
62-
- 每次分流留一行审计评论;父单维护 checklist 汇总评论,决策仍锚在具体 sub-issue。
63-
- 收尾四步、僵尸回收与领地防撞细则见 `references/seat-post-protocol.md`。
52+
- 专题 = 父单 + 里程碑表 + 负责车道(立卡车道):它拆卡并保持表现值;⛔ 无默认专题席。
53+
- 表四列 `Milestone | Child | Blocked-by | Lane`,一里程碑一组行,子卡格 `#<n>` 加一句话。
54+
- 父单挂 `tracking` + `area:*`,无 pm 状态标签与 `pm:epic`,永不派发;决策锚在子卡。
55+
- 子卡一律 `sub_issue_add` 挂父单、住车道队列,默认无 `pm:epic`;先后只写子卡 `Blocked-by:`。
56+
- 滚动拆卡:只当前与下一个里程碑是卡,余为表行;高风险专题拆前贴小结,维护者裁 go/no-go。
57+
- 专题次序 = 路线图表(云:`cloud/ROADMAP.md`;本仓列每日看板,直至有表);⛔ 无每账号上限。
58+
- 暂停:父单 `pm:on-hold` + `Restart-when:`(原话+理由);同笔当前与下个里程碑未起工子卡同挂。
59+
- 在飞子卡逐卡呈维护者:默认契约复审 PASS 者落地,余停手转 `pm:on-hold`。
60+
- 后续里程碑子卡先把目标、要裁与链接并入表,再关 not_planned。
61+
- 父单留检查点评论(发运/冻结件现状/风险);重启新读它后按表重拆,仍成立的旧卡可重开。
62+
- 总览单列暂停专题(天数、`Restart-when`);逾 30 天问一次,不续则父单连余卡关 not_planned。
63+
- 须一手到底的链:写成一卡有序步骤,或具名集中例外打 `pm:epic`(细则 `seat-post-protocol.md`)。
6464

6565
## 轮次报告与节奏
6666

0 commit comments

Comments
 (0)