Skip to content

Commit 4651c28

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-19496-instrument-discipline-charter
# Conflicts: # .claude/skills/pm-dispatch/SKILL.md
2 parents fec2177 + ea64bbc commit 4651c28

17 files changed

Lines changed: 1439 additions & 242 deletions
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/lint': minor
3+
---
4+
5+
`lintLivenessProperties` now reports a liveness ledger it could not read, instead of going silent.
6+
7+
`loadWarnMap` returned the same empty map for two different facts: "this metadata type's ledger classifies nothing as warn-worthy" and "there is no ledger". A missing `<type>.json` and a file whose JSON is broken both returned an empty map with no log, no throw and no other signal, so losing or corrupting ONE file under the `liveness/` directory `@objectstack/spec` ships switched every author warning for that type off in silence — indistinguishable from that type simply having no warnings.
8+
9+
The contrast that makes it a defect rather than a design sits one frame up: the DIRECTORY-level failure is loud by construction (the rule returns `[]` and everything depending on it goes red). Loud by directory, silent by file.
10+
11+
What changes for consumers:
12+
13+
- A new rule id, `LIVENESS_LEDGER_UNREADABLE` (`'liveness-ledger-unreadable'`), exported from the package root beside the four verdict ids. It is not a fifth verdict: the other four grade a property the ledger DID classify, this one says the classification never arrived, so a finding carrying it means no other finding about that metadata type can be trusted. Compare `f.rule` against the constant rather than retyping the slug. It cannot be silenced per finding: the CLI has no per-rule suppression, and `suppressWarnings` is a dashboard-widget key (`spec/src/ui/dashboard.zod.ts`) while this finding's subject is a ledger rather than an authored item, so there is nothing to carry it. The remedy is the one the finding's own hint names — repair or reinstall `@objectstack/spec`.
14+
- `lintLivenessProperties` raises exactly one such finding per unreadable type, per run — never one per authored item — ahead of the walk's own findings, and keeps walking every type whose ledger IS readable. On an intact installation nothing changes: no ledger is missing, so no finding is added.
15+
- A ledger that parses but is not a ledger (a bare `null`, an array, a scalar, or a document with no `props` record) is the same reported fault. Reading `.props` off a parsed `null` used to be a `TypeError` — a throw from a rule whose contract is that it never throws, through the one input an author cannot influence.
16+
17+
`authorWarnedProperties` still answers the empty set for a ledger it cannot read — a decision procedure returning a set has no way to report a failed read — and that is unchanged for a missing file and for broken JSON. One input does move: a ledger document that parses to `null` used to make it THROW, and it now returns the empty set like the other two. `os lint` runs both halves in one pass, so the run states the fault once rather than never.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
'@objectstack/lint': minor
3+
---
4+
5+
**BREAKING for runtime metadata writes** — the ADR-0090 D3 vocabulary freeze (`security-role-word`) now runs at the runtime publish gate for all six collections it judges, so `position` and `app` writes are gated for the first time (#19370)
6+
7+
Clause-②: no (narrowing)
8+
9+
`validateSecurityRoleWord` moves from `surfaces: ['cli']` to
10+
`['cli', 'runtime-publish']` and declares
11+
`runtimeTypes: ['object', 'permission', 'book', 'position', 'app']` — the write
12+
type of every collection it judges. `position` and `app` join
13+
`TYPE_TO_STACK_KEY` in `runtime-gate.ts` so the gate can build a per-write
14+
snapshot for them.
15+
16+
**The refusal set grows.** A runtime metadata write — Studio's designer, REST
17+
`/meta`, an MCP/AI author — that carries the reserved word `role` in a
18+
security-relevant identifier or label is now refused with the 422 lint envelope
19+
instead of stored. Concretely, these used to succeed at that door and no longer
20+
do:
21+
22+
- an object, field, action or field-group header named or labelled for `role`;
23+
- a permission set named or labelled for `role` (e.g. `role_manager`);
24+
- a documentation book named or labelled for `role`;
25+
- a **position** named or labelled for `role` (e.g. `sales_role`);
26+
- an **app** named or labelled for `role` (e.g. `role_hub`).
27+
28+
The platform vocabulary the rule freezes is unchanged and so is its fix-it text:
29+
`permission_set` for capability, `position` for distribution, `business_unit`
30+
for hierarchy. Nothing is renamed, retired or added — this is the same rule,
31+
with the same rule id and the same findings, now enforced at the fourth door as
32+
well as by `os validate` / `os build` / `os lint`.
33+
34+
**Nothing changes for the three CLI commands.** Both security entries have run
35+
on all three since the #8310 split, and their union is byte-identical to before.
36+
37+
**Stored rows are untouched** (#4463 D4: the gate blocks new writes, never the
38+
read path), and `OS_ALLOW_UNLINTED_METADATA_WRITES=1` remains the migration-window
39+
escape hatch for a tenant that authored one of these names before this landed.
40+
41+
Why the two types were held back until now, and why the wait ended: `position`
42+
and `app` are `allowRuntimeCreate: true`, so a position called `sales_role` could
43+
be minted through the one entrance a tenant has while an object of that name was
44+
refused. Under #7220 one rule id sits on ONE side of the wall, so the rule was
45+
split out and held back whole rather than wired for a subset of its collections.
46+
Mapping the two write types is what lets it cross, also whole.
47+
48+
Deliberately NOT done: carrying `positions` / `apps` as `RuntimeStackContext`
49+
collections. A collection joins that context because some rule resolves
50+
references into it; this rule resolves nothing — it judges each identifier and
51+
label on its own — so a sibling position tells it nothing about the written one
52+
and its finding cancels in the gate's differential either way. Carrying them
53+
would cost the publish door one indexed `sys_metadata` read per write for no
54+
verdict change.
55+
56+
<!-- adr-0087: not-required (no-migration-prescription) nothing is retired, renamed or added: no authorable key changes, no stored shape is rewritten, and `objectstack migrate meta` has nothing to reach. The rule, its rule id, its vocabulary and its fix-it text are unchanged since ADR-0090 D3; only the surface it runs on widens. An affected tenant renames its own metadata, which is tenant data rather than a spec migration. -->

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ model: opus
9191
5. ⛔ 永不按进程名杀(`pkill -f` 会带走并行 agent 的运行);记下你启动的 PID,只对它操作。
9292
6. **整条流水线在前台跑。** build 与 test 都是本任务的步骤:阻塞运行、读真实输出、继续。
9393
- 宿主事实:你启动的后台作业、watcher、你请求的通知都不会唤醒你;结束一轮就是结束。
94-
- 有可展示内容即 commit、push 并开 draft PR,不等验证结束;验证结果到达即写进报告。
94+
- 每个可编译小步即 commit + push;有可展示内容即开 draft PR;接管只认远程分支最后 sha。
9595
- 带具名缺口的 PR 是交付进行中的常态,未读到的判决写 `NOT MEASURED: <family>, reason: …`。
9696
- 平台事实:容器把前台命令钉在约 10 分钟上限,超时 SIGTERM 杀掉(`exit 143`)。
9797
- 上限划定前台里放什么:重活走规则 1 的锁;仓级扫描归 CI(见本地验证范围节)。

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
178178
- 放行认门禁 job 的结论(`completed: success`),⛔ 不认聚合读数。
179179
- advisory 门禁红着进 main 是共享损伤,任何车道发现都立即止血并立单,见 landing-operations B。
180180
- 仪器纪律(硬门禁面、只报告面、新增授权、工具位)见 `references/instrument-discipline.md`。
181-
- dev 自己死了不等于维护者中止:子代理消失是正常死法,走死认领回收。
181+
- dev 自己死了不等于维护者中止:子代理消失是正常死法,走接管(见认领节)。
182182
- 维护者中止只认原话或宿主回报 stopped by the user,⛔ 不据推断立无重启条件的门。
183183
- 共享基础设施修复入队前按症状复查 main,不按 issue 号。
184184
- 立卡者不查重、只附 3–5 查重词;真撞上重复,先比数值与作用域再决定关哪个。
@@ -470,15 +470,16 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
470470
- 阻塞项无主 ⇒ 被挡席认领做掉,不限大小;在该卡走完整认领、尊重其热文件串行队。
471471
- 阻塞项在飞 ⇒ 等:`pm:blocking` 在其车道排最前、等待者写该卡;p0/p1 优先级沿链传递。
472472
- 取卡前置 = `docs/NORTH-STAR.md`「优先级」第 3 条:产品仓开放 P0/P1 每次取卡现读。
473-
- 取卡全序:`priority:p0` > `pm:blocking` > `target:` 板上项 > p1 > p2 > p3 > 无级;同级先 `Bug` 再卡龄。
473+
- 取卡全序:维护者直派插队卡(出处三件)> 契约面卡(判据见 `references/lanes/spec.md`)> 标签序。
474+
- 标签序:`priority:p0` > `pm:blocking` > `target:` 板上项 > p1 > p2 > p3 > 无级;同级先 `Bug` 再卡龄。
474475
- `pm:blocking` 级内先按解锁扇出(从 `Blocked-by:` 反向索引现算,⛔ 扇出数不落标签)。
475476
- 全序每级取既有信号现读/现算,零逐卡维护;优先非豁免;无级/缺 `Path:` 轮报记分诊缺口。
476477
- 解锁那一刻 PM 自己的判断最不可信:裁决收窄或关掉了那张卡是假设不是前提。
477478
- 该假设以机制假设身份进派发令,被证伪就在同一张卡公开更正。
478479

479480
### 认领(先认领后动工)
480481

481-
- 共享身份下 assignee 只答有无认领;身份只认正文 session ID,⛔ 不认作者字段。
482+
- 共享身份下 assignee 只答有无认领;身份只认正文 session ID,⛔ 不认作者字段,接管同此。
482483
- assignee 字段归 PM:原子对 step 1 设,dev 席恒不写它;跨账号 assignee 不是你 ⇒ 永不碰。
483484
- 释放是显式动作:让卡离手者同笔清 assignee + `Release:` 行(会话/因/去向);下一任重新认领。
484485
- 部分落地(PR 带 `Refs #N (item k)`,⛔ 不 `Fixes`)即释放:合入同笔回 `pm:queue` + 清 assignee。
@@ -498,16 +499,12 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
498499
- ③ 竞态复读:认领评论上墙后重读全线程;认领评论时间戳是唯一仲裁。
499500
- 更早的评论带不同 session ID/分支 ⇒ 你输了,回 `already claimed — yielding` 另选。
500501
- 让行是交接不是退场:连同让行评论交出已诊断的一切与已取的板面读数,赢家不必重扫。
501-
- dev 侧早推分支,远程分支是在飞工作最硬的证据。
502-
- 死认领回收:认领 >~24h ⇒ 疑死;判死主腿 = 搜引用本卡的 PR、读其 `merged`/`merged_at`。
503-
- ⛔ 判死不读 closes-list;承诺分支缺席与提交扫描失效只能支持判死、永不单独确立。
504-
- 零引用 PR ⇒ 停下发问,⛔ 不判什么都没落地。
505-
- 回收前先救工作树:向任何派发 worktree 提交前先过存活/所有权检查。
506-
- 或对树最新 mtime 过明确年龄阈值;⛔ 不凭 GitHub 侧静默动手。
507-
- 过栏后,派发 worktree 的未提交改动先 WIP commit 到派发分支并 push,sha 记进回收评论。
508-
- WIP commit 标 INCOMPLETE AND UNREVIEWED;续派者 diff 它,⛔ 不无审续建。
509-
- WIP 信息只写观察到的(脏路径/行数/sha),⛔ 不写席位行为的现在时断言。
510-
- 再评论询问,静默一窗后释放回队(`Release:` 行载因);有带提交活分支的认领永不回收。
502+
- dev 每个可编译小步即 push:容器随会话回收,未 push 的树救不回,可交接的只有远程分支。
503+
- 认领人不可达(token 耗尽/会话结束/身份退役)⇒ 接管:一条评论四件齐,⛔ 不判死活。
504+
- ① 跨账号 `Release:` 点名被撤认领的 id 与 session ID,带出处三件(谁的指令/原话/在哪说)。
505+
- ② assignee 同笔换人(`--unassign 旧 --assign 新`);③ 新 `Claim:`:新 session、续用分支与远程 sha。
506+
- ④ 交接记录:旧分支最后已 push 的 sha + 一句状态;读者只验①③形状,缺一件即非撤销。
507+
- C9 只剩一种红:无任何 `Release:` 的跨账号 `Claim:`(真抢卡);线程上每条活认领都要点名。
511508
- 误伤活席位 ⇒ 令其追加式更正,落 PR 正文不落分支历史。
512509

513510
### 派发
@@ -584,7 +581,6 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
584581
- 已有远程分支/PR 不豁免探针。
585582
- ④ 活着 ⇒ 进度与阻塞点;回 no active task; resumed from transcript ⇒ 生前已死,询问即复活。
586583
- ⑤ 判据永远取正向证据:分支、PR、报告、探活回包。
587-
- 45 分钟是发探针的门槛,⛔ 不是判死的门槛。
588584
- 判死只有三类依据:探针回包表明已死;宿主明确回报 stopped;超过本车道基线且连续静默。
589585
- 基线 = 本车道实录派发 → 推分支/开 PR 的端到端耗时,三五单即可用,⛔ 非本文任何常数。
590586
- `mode:cloud` 的 ~2h 静默是本轮收集边界(记 `blocked` 移步下轮),也不是判死。

‎.claude/skills/pm-dispatch/references/core-rules.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,17 @@
105105
- 同批独立性按文件面不相交判,⛔ 不按包;同区域硬串行,冲突交合并队列仲裁 ⛔ 不手排。
106106
- 家族派发须过五门:同缺陷同修法、同包区域、成员皆已裁、逐成员可核、点名排除清单。
107107
- 两张以上排队卡共享热文件时,必须以五门为判据显式回答折叠还是串行。
108-
- 取卡全序:插队卡、有下游依赖者的卡、板上项、p1、p2、p3、无级;同级缺陷卡先再卡龄。
108+
- 取卡全序:维护者直派插队卡(出处三件)> 契约面卡(判据见 `references/lanes/spec.md`)> 标签序。
109+
- 标签序:`priority:p0` > `pm:blocking` > `target:` 板上项 > p1 > p2 > p3 > 无级;同级先 `Bug` 再卡龄。
109110
- 认领原子对:一次标签写入完成认领与状态对调,随后留 `Claim:` 开头的评论并重读全线程。
110-
- 更早的他会话认领即让行并交出已诊断的一切;认领逾一天且无合并证据即疑死。
111-
- dev 自死不等于维护者中止,需显式信号;回收前先救工作树,有提交的活分支 ⛔ 永不回收。
111+
- 更早的他会话认领即让行并交出已诊断的一切;认领人不可达即接管,⛔ 不判死活。
112+
- dev 自死不等于维护者中止,需显式信号;接管一条评论四件齐,只救已 push 的分支。
112113
- 一单一次派发,档位逐卡显式传参;语义面卡恒契约复审档施工,契约复核只 spec、skills 欠。
113114
- 派发词 ⛔ 不整段粘贴 issue 正文,只带增量,并要求子代理自查正文完整性。
114115
- 派发词分三区:裁决不可重裁、机制假设须实测、建议路线可换,⛔ 不把假设写成裁决。
115116
- 标准非协商条款 ⛔ 不抄进派发词,清单、路径与行号在派发那一刻从树上取。
116117
- 危害断言必须有读数,测不了写成问题 ⛔ 不写成栅栏;技能包卡必带净增行数预算。
117118
- 收集先扫 GitHub 且报告缺席 ⛔ 永不读作成功;探活先确认 Routine 启用,在飞再重挂加速器。
118-
- 45 分钟是发探针的门槛而非判死门槛,判死只认探针回包、宿主信号或超实测基线。
119119
- 停摆永不自愈,按梯度复位、三次即判不可靠重派;报告丢失时按草稿 PR 直接验收。
120120
- 复核对 GitHub 核验 ⛔ 不对自述核验,逐项过清单并亲核形态、范围与整包价值密度。
121121
- CI 收敛读数只属于复核侧;判决三种:验收落卡、返工最多两轮、升级走决策通道。

0 commit comments

Comments
 (0)