Skip to content

docs(scripts): 四条 metadata-protocol 基线条目的处方指向一个不存在的动作 (#4987) - #5624

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-4987-baseline-closes-cycle
Aug 5, 2026
Merged

docs(scripts): 四条 metadata-protocol 基线条目的处方指向一个不存在的动作 (#4987)#5624
os-zhuang merged 1 commit into
mainfrom
claude/issue-4987-baseline-closes-cycle

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4987

scripts/engine-double-contract.baseline.json 是 shrink-only 手写台账,一条条目的 closes 就是给下一个作者的处方。四条 metadata-protocol 条目写的是「add @objectstack/objectql to devDependencies」——这个动作做不到:@objectstack/objectqldependencies 已含 @objectstack/metadata-protocol(workspace:*),反向加边按构造即成环。按现在这句去做的人会加 devDependency、跑 turbo、撞环、回退,然后才发现处方本身是错的 —— 而台账存在的意义正是让他不必重新发现这件事。

不是引用旧测量,而是在本分支重测

把边加进 metadata-protocol 的 devDependencies,turbo 2.10.7 对 buildtest 两个 task graph 都直接拒绝(exit 1),随后回退(工作树已确认干净,package.json 不在本 PR diff 内):

 WARNING  Circular package dependency detected: @objectstack/objectql, @objectstack/metadata-protocol
  x Cyclic dependency detected:
  | 	@objectstack/objectql#build, @objectstack/metadata-protocol#build
  | The cycle can be broken by removing any of these sets of dependencies:
  | 	@objectstack/metadata-protocol#build -> @objectstack/objectql#build
  | 	@objectstack/objectql#build -> @objectstack/metadata-protocol#build

判据本来就在同一个文件里:packages/spec/src/contracts/data-engine.test.ts 那条 EXEMPT 早就写了「it cannot be pinned even in principle ... the import would invert the dependency」,只是从没套用到 metadata-protocol 这四条上。

改了什么

只改这四条的 why/closes 台账文字(protocol-publish-drafts-org-scope / protocol.runtime-authoring-gate / protocol.save-flow-canonicalization / sys-metadata-repository.recorded-by):

未做也未声称做的事:本 PR 文件面只是台账文字,四个文件各自的 fake delete 当前是否被驱动没有探测,why 里如实写明。按台账自己的规则这不改变结论 —— 那是关于单个文件的论证,不是关于契约的论证(#5206 那条做了逐文件探测,本次没有,不冒充)。

边界

⛔ 不实施下沉代码改动(engine-core 域,见 #4987 分诊的越界提示)、⛔ 不增删条目、⛔ 不改 file/unguarded/kind 定位与判定字段、⛔ 不改检查脚本。

下沉本身已另开 #5619(未指派)承接 —— 顺带记一笔:#4867 / #4981 / #5206 三条的 closes 写的是「tracked as #4987」,而 #4987 的真实文件面只是措辞;#5619 的完成范围里包含把那三处引用改指过去(整批删除这 7 条时自然消失)。

另:该文件不在 .gitattributes 的 merge driver 列表里(那里显式注明 shrink-only ratchet 基线「deliberately absent」),是手写台账不是生成物,正常编辑。

验证

pnpm check:engine-double-contract(self-test + 扫描)改动前后输出逐字节一致(diff 无输出):

OK  self-test: separates engine doubles from driver doubles, accepts only the producer's predicate ...
engine doubles: 58 in 57 test file(s) — 24 pinned to ObjectQL.delete's dispatch predicate, 34 in the shrink-only baseline.
  x PINNED: packages/runtime/src/action-execution-calldata-not-found.test.ts declares 2 engine double(s) ... (lines 69, 102).
check-engine-double-contract: 1 problem(s).

判据即「无新增问题」:pinned/baseline 计数 24/34 不变,仅剩的 1 个 problem 是 #5604(action-execution-calldata-not-found.test.ts 69/102 行,#5584 引入,cli 座位在修)—— main 上本来就红,与本改动无关。

  • JSON 语法:JSON.parse 通过,条目数 32 不变;round-trip 字节一致,故 diff 恰好 8 增 8 删(4 条 × why+closes)。脚本断言了 $comment、条目数、每条的 file/unguarded/kind 未变,并在遇到非预期原文时直接抛错而不是改错条目。
  • 范围核对:文件里「add @objectstack/objectql to devDependencies」处方从 12 处降到 8 处,剩下 8 处全部属于 plugin-reports / plugin-security / plugin-webhooks / service-datasource —— 计算 objectql 的传递依赖闭包(14 个包)确认这四个包都不在闭包内,故它们的 devDependency 处方是真的可行,不该动。闭包内只有 metadata-protocol。
  • node scripts/check-nul-bytes.mjs:OK(5533 个跟踪文本文件,无裸控制字节);另做门禁盲区自扫 grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]',干净。
  • 无 changeset:scripts/ 台账文字修正,非发布面 → 走 skip-changeset 标签路线(未写空 frontmatter changeset)。

已知环境红,与本 PR 无关:ESLint job 全仓红即 #5604 签名(非必需检查,不追);Check Changeset 首 run 可能因标签竞态红(#5580,同轮在修)。


Generated by Claude Code

`scripts/engine-double-contract.baseline.json` 是 shrink-only 手写台账,一条条目的
`closes` 就是给下一个作者的处方。四条 metadata-protocol 条目写的是「add
@objectstack/objectql to devDependencies」—— 这个动作**做不到**:
`@objectstack/objectql` 的 `dependencies` 已含 `@objectstack/metadata-protocol`
(`workspace:*`),反向加边按构造即成环。

不是引用旧测量,而是在本分支重测:把边加进 metadata-protocol 的 devDependencies,
turbo 2.10.7 对 `build` 与 `test` 两个 task graph 都直接拒绝(exit 1),随后回退:

    WARNING  Circular package dependency detected:
             @objectstack/objectql, @objectstack/metadata-protocol
    x Cyclic dependency detected:
    |    @objectstack/objectql#build, @objectstack/metadata-protocol#build

判据本来就在同一个文件里 —— `packages/spec/src/contracts/data-engine.test.ts` 那条
EXEMPT 早就写了「it cannot be pinned even in principle ... the import would invert
the dependency」,只是从没套用到 metadata-protocol 这四条上。

- `why` 改成实测的环,并写明**不能**因此升级成 EXEMPT:不存在的是 devDependency
  这条**路线**,条目本身靠下沉谓词仍可关闭,所以按台账自己的规则保持 DEBT。
- `closes` 改成唯一存在的路线:把 `assertEngineDeleteDispatch` 下沉到两边都已依赖
  的包。已静态核实其可行:objectql 与 metadata-protocol 都已依赖
  `@objectstack/metadata-core`,而后者 `dependencies` 只有 spec + zod、不含
  objectql,故不引入新环;生产者 `packages/objectql/src/engine-delete-dispatch.ts`
  **没有任何 import**,下沉是一次搬移而非重构。`spec/contracts` 作为另一候选保留,
  但注明仅当谓词属于契约层才对,不要默认选它。
- 措辞与 #4867 / #4981 / #5206 三条早先已改好的同族条目对齐,但**不写硬编码的
  sibling 计数** —— 那三条写的「four/five sibling」在同族增至 7 条后已经漂移,
  本次改用免计数措辞,避免再漂。

未做也未声称做的事:本 PR 文件面只是台账文字,四个文件各自的 fake delete 当前是否被
驱动**没有探测**,`why` 里如实写明。按台账自己的规则这不改变结论 —— 那是关于单个
文件的论证,不是关于契约的论证。

下沉代码本身属 engine-core 域,不在本 PR 文件面,已另开 #5619 承接(三条同族条目
`closes` 里「tracked as #4987」的引用也在那里改指)。

验证:`pnpm check:engine-double-contract` 改动前后输出**逐字节一致** —— 仍是
24 pinned / 34 baseline、仅 1 个 problem(#5604 的 action-execution-calldata-not-found,
cli 座位在修,与本改动无关)。JSON round-trip 字节一致,故 diff 恰好 8 增 8 删;
条目数、`file`/`lines`/`unguarded`/`kind` 与 `$comment` 由脚本断言未变。

Fixes #4987

Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE

Co-authored-by: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 5, 2026 8:47pm

Request Review

@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 5, 2026 — with Claude
@github-actions github-actions Bot added the size/s label Aug 5, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 5, 2026 21:00
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 0c57315 Aug 5, 2026
20 of 22 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4987-baseline-closes-cycle branch August 5, 2026 21:12
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
…objectstack-ai#5579) (objectstack-ai#5642)

该段给出的唯一理由是「One raw control byte makes grep treat the whole file as
binary: zero matches, no signal」——而这条只对 NUL 成立。在容器内独立复现(样本用
printf 生成,未粘贴裸字节;GNU grep 3.11 + ripgrep 14.1.0):

    U+0000  grep: binary file matches            rg: binary file matches (found "\0" ...)
    U+0001  grep: 2:searchable line              rg: 2:searchable line
    U+007F  grep: 2:searchable line              rg: 2:searchable line

即门禁扫描面里除 NUL 之外的每个字节(含 objectstack-ai#5460 纳入门禁、objectstack-ai#5577 补进自扫字符类的
DEL)都不会让文件被当成二进制。危害只写这一条的后果不是文字不精确:agent 写出一枚
非 NUL 控制字节、自扫命中后去核对指令,会发现唯一被陈述的判据不成立,从而把门禁的红
判成误报。

`scripts/check-nul-bytes.mjs` 脚本头早就把两侧分开论证好了(objectstack-ai#5157 段),本次把散文
口径搬过去对齐:

- binary-file / zero-matches 那条点名 NUL,并标明是实测结论;
- 其余扫描面字节引脚本头写清的三条:渲染为空(代码对每个读者说谎)、两种拼写互不
  命中(文件里是字节,不是你会去搜的转义文本)、事故源不挑字节值;
- 补一句直接堵住上述推理:「不是 NUL、grep 还能搜到」永远不构成把门禁红或自扫命中
  读成误报的理由;
- 危害论证指向脚本头「引用它,不要重新推导」,不在此处再抄一遍论证细节。

顺带修同段两处陈旧:

- 「this repo has paid four times」的硬编码计数改为免计数措辞——该族已多于四例,
  objectstack-ai#5624 刚因同样的漂移把台账里的 sibling 计数改成不含数字的表达;
- 「a `0x01` that `check:nul-bytes` does not scan for (objectstack-ai#5157)」的现在时已错:objectstack-ai#5157
  正是把该字节纳入扫描面的那一单,改为过去时的事实句。

未做(留档而非顺手扩面):单源化——让字符类与危害论证不再手抄多处——是 objectstack-ai#5484 正文
留下的方向,本 PR 只修散文口径,不动 `scripts/check-nul-bytes.mjs`、不动 objectstack-ai#5577 刚
补的自扫字符类、不动 objectstack-ai#5630 刚加的 Toolchain traps 条目。

纪律:全程未向任何文件写入裸控制字节,散文沿用该文件与脚本头既有的 `0x01`/`0x7f`
十六进制写法(不含反斜杠转义,不会被编辑工具 materialise)。
`node scripts/check-nul-bytes.mjs` 绿;改动文件自扫
`grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` 无命中;`cat -A` / `od -c` 复核新增
行无意外字节。

`.claude/` 文档-only,无用户可见变更,走 skip-changeset 标签路线。

Fixes objectstack-ai#5579

Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
…tstack-ai#5650)

* docs(os-dev): PR 收尾清单增补 `skip-changeset` 硬步骤,以回读 PR labels 为闭环 (objectstack-ai#5559)

测试-only / 工作流-only / `.claude/`-only 的 PR 不写 changeset,靠 `skip-changeset`
标签豁免 Check Changeset。此前收尾清单对这一步只字未提,dev 在 PR 正文「声明」了
标签却没有真正挂上,objectstack-ai#5533/objectstack-ai#5538 两例各烧一轮门禁红并由 PM 手工兜底 ——
「声明了 ≠ 执行了」正是 declared ≠ enforced 的 agent 版。

新增条目把它写成与「PR 正文以 Fixes 开头」同级的硬步骤,并按实测校正了三处认知:

- 没有任何自动化会替你挂:`.github/labeler.yml` 九条规则里没有 `skip-changeset`,
  2026-08-05 六例(objectstack-ai#5533/objectstack-ai#5538/objectstack-ai#5542/objectstack-ai#5624/objectstack-ai#5642/objectstack-ai#5645)的该标签全部来自 agent,
  `github-actions[bot]` 一次都没挂过 —— 它只负责 `size/*`、`documentation`、
  `tests`、`ci/cd`。
- 只「加一个标签」,不要整集写入:整集写入会把 bot 刚挂的 `size/*` /
  `documentation` / `tests` 抹掉(objectstack-ai#5533/objectstack-ai#5538/objectstack-ai#5642/objectstack-ai#5645 的 agent `unlabeled`
  事件即此),反向也成立 —— objectstack-ai#5533 的标签 15:46:44 落、15:46:45 就被
  `github-actions[bot]` 的写入抹掉,只活了一秒。
- 闭环是回读而不是 POST:Check Changeset 在 PR 打开后数秒就实时读一次标签
  (objectstack-ai#5580),`opened` run 通常早于标签落地 —— objectstack-ai#5542 的 dev 做对了全套,仍留下
  一条红 `opened` run,之后每一次 run 才被标签豁免。所以要在 bot 平息后回读
  标签、并把读到的列表写进报告。

* docs(os-dev): 首 run 颜色按实测改成双向竞态,不再预设为红 (objectstack-ai#5559)

本 PR(objectstack-ai#5650)自己按新硬步骤执行时,实测结果与原措辞相反:标签 22:03:54 落地,
`opened` run(workflow run 31051251795,创建于 22:03:38)的 Check Changeset job
直到 22:04:35 才启动并做实时标签回读 —— 早于回读 41 秒落地的标签被读到,该 run
直接 **success**,并非预设的红。

于是这一句改为如实描述竞态:首 run 的颜色取决于 runner 启动耗时与 POST 落地
的先后,两个方向都有实例(objectstack-ai#5542 红、objectstack-ai#5650 绿)。指导语随之从「预期会红」改为
「尽快落标签,把首 run 的颜色当信息而不是自己的判决」。

预设方向被自己的实测证伪时如实记录,而不是让措辞压着实测走。

---------

Co-authored-by: os-zhuang <hr@objectstack.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants