Skip to content

Commit ea96c27

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-16464-shard-timings-refresh-workflow
2 parents da33db7 + 4500bc5 commit ea96c27

13 files changed

Lines changed: 1238 additions & 149 deletions

File tree

.claude/agents/os-dev.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ model: opus
126126
- ① 先 build 依赖闭包:`pnpm --filter '<pkg>^...' build` 是新 worktree 的第一条命令。
127127
- 跳过它产出的失败,读起来与你的改动弄坏了 import 一模一样。
128128
- ② 受影响包自己的 `pnpm test` / `pnpm typecheck`,用 `--filter` 圈定。
129-
- 受影响包 = CI 会测的包,清单读 `TURBO_SCM_BASE="$BASE" pnpm exec turbo ls --affected`
130-
- ⛔ 不按改了哪些包猜:普通 import 被改模块的包也在清单里,欠它们的测试
129+
- 受影响包 = 本包;import 方只在公开面变化时欠测试:spec 契约、发布的 `exports`、线上形状
130+
- 公开面字节不变 ⇒ 只欠本包测试与派生门禁,⛔ 不给每个 import 方补测试
131131
- `packages/cli` 只欠 `unit` 层(见 Definition of done 的测试条)。
132132
- ③ 派发词点名的门禁族,加上你看得出被牵连的。
133133
- 新 fake engine ⇒ `check:engine-double-contract`;新错误码 ⇒ `check:error-code-casing`
@@ -207,11 +207,10 @@ model: opus
207207
- 它必须答 exit 0;否则阴性作废,`git fetch --deepen` / `--unshallow` 到控制腿转 0 再重读。
208208
- ⛔ 控制腿别挑浅窗内的近亲:控制 commit 至少与被测那个同深;两条腿的退出码都进报告。
209209
- `--is-shallow-repository` 是便宜的触发器不是判据,判据是控制腿。
210-
- 反向验证(回退修复,看诊断变化)先 commit 修复:恢复只是 `git checkout <your-branch> -- <path>`
211-
- 对着未提交的编辑,`git checkout origin/main -- <path>` 不留任何恢复点。
210+
- 反向验证与消融是一次性证明:前后运行引在 PR 正文与报告里;⛔ 不留永久测试文件。
211+
- 两者都先 commit 修复再回退或变异:恢复腿指向 `HEAD`,`HEAD` 必须先装着你的实现。
212+
- 未提交时 `git checkout origin/main -- <path>` 不留恢复点,`git checkout HEAD -- <path>` 删的正是实现。
212213
- 恢复机制与字节一致性证明规则见 AGENTS.md;从已 commit 的状态重跑,红/绿数字才可信。
213-
- 消融同一条,先 commit 再变异:恢复腿指向 `HEAD`,`HEAD` 必须先装着你的实现。
214-
- 对未提交的实现,一次完美的 `git checkout HEAD -- <path>` 删的正是实现本身,事后检查全绿。
215214
- 通则:失效形态是 exit 0 且什么都没做的清理步骤,只能靠观察状态验证,永不靠读退出码。
216215
- 恢复腿与变异腿完全对称,四条硬线如下。
217216
- ① 恢复写 `git checkout HEAD -- <path>`,⛔ 永不裸 `git checkout -- <path>`:裸恢复从索引取。
@@ -225,6 +224,7 @@ model: opus
225224
- 拒收类用例断言信封,不断言 throw 本身;最小断言集是错误的 `code``status`(ADR-0112 信封)。
226225
- 单独的 `expect(...).toThrow()` 不是拒收测试:抛裸 `Error` 的未修复 driver 照样绿。
227226
- 从不抛错的传输层转红时,它指向缺陷之外。
227+
- 提示、裁决与错误文案 ⛔ 不 pin,除非消费者解析其原文;断言种类、退出码或具名主体。
228228
- 措辞本身即契约的地方,在 `code`+`status` 之上再断言 message 首句,永不取而代之。
229229
- 键与值的可达性判据:守某个键是真实编写面 → 断言 fixture 上无 `unrecognized_keys`
230230
- 守某个值的判定 → 要求完整 `safeParse` 绿;拒键与拒值是两个不同的事实。

.claude/skills/pm-dispatch/references/platform-readings.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@
7171
- 同一 PR 第二次被踢 ⇒ 停止重投,按签名四分支重判。
7272
- 吞吐两则:合并队列落地 ≈ 每 PR 15–30 分钟且串行,⛔ 不据还没落提前判异常。
7373
- 单容器重验证(build 加 test)并发甜点 ≈3,排批按它定上限。
74+
- 入队事件与队列 ref 迟 1–3 分钟才出现 ⇒ 轮询预算按 3 分钟,⛔ 不按 1 分钟判没挂上。
75+
- ready 翻转触发检查重跑 ⇒ 入队落在翻转之后约一分钟,那段空窗不是挂载失败。
76+
- `behind` 的 PR 照常入队:落后于 main 不是入队否决,⛔ 不为它先跑 update-branch。
77+
- `check_suite.completed` 会命名过期 head,check-run 也只属最后一次 push ⇒ 用前先重读当前 head。
7478

7579
## API 配额
7680

@@ -212,6 +216,8 @@
212216
- `list_issue_types` 对本集成 403 而 `issue_write type:` 正常,是读权限缺口。
213217
- ⇒ 直接写已知好值(`Bug`/`Feature`/`Task`),写侧报错才是真信号;列表 403 不等于类型不可用。
214218
- 非法值是响错还是静默丢弃未实测,写非已知值前先小样验证。
219+
- ⛔ 控制词不取卡片逐字标题:逐字标题回 0,同题自然语言回 14 条含该卡,零仍不是读数。
220+
- MCP `get_comments` 结果超 ~100 KB 溢出成文件:返回体是路径不是评论,读前先判形。
215221

216222
## 读数陷阱
217223

@@ -330,6 +336,21 @@
330336
- 计数答至今发生过没有,不答机制在不在:零计数只作弱先验。
331337
- 判 required 集为空要读 ruleset 的 required 集本身,或看队列合并是否真在等检查。
332338
- ⛔ 别处写下的计数值一律先复测再用。
339+
- MCP `issue_write create` 落库丢掉正文尾部的署名页脚块,正文其余部分完好。
340+
- 同一路径吃掉标题里的尖括号跨度 ⇒ 标题占位写裸词(NAME / :id),⛔ 不写尖括号形。
341+
- 建卡改走 REST `POST /issues` 页脚存活;回读后 `PATCH /issues/{n}` 重送正文逐字节存下。
342+
- CI job 的失败 step 不必与 job 名一致 ⇒ ⛔ 不由 job 名推原因,先读 step 名再下结论。
343+
- Actions 日志保留把老 job 截到 post-job cleanup ⇒ 归档只剩清理输出时原因不可断言。
344+
-`packageManager` 的目录里 corepack 运行时解析 registry `latest` ⇒ 同 SHA 前绿后红是 tag 移了。
345+
- 自测写出的 fixture `package.json` 必带根清单的 `packageManager`,否则 CI 里解析到 latest。
346+
- `COREPACK_DEFAULT_TO_LATEST=0` 单用修不了已记 12.x 的 store ⇒ 环境变量不是补救。
347+
- corepack 下载 pnpm 可在检查体开跑前崩 undici(`assert(!this.paused)`)⇒ 是基础设施红。
348+
- 落地探针除命名代码形外,还必须在落地前的 tip 上读出不同值,否则它分不开两棵树。
349+
- `check-governed-merges` 浅克隆上拒答而非少报,并报未审计仓数;补救 `git fetch --shallow-since=`
350+
- 前台 `sleep` 被 harness 拒 ⇒ 等待写成带 until 条件的前台阻塞等待,⛔ 不写 sleep 轮询循环。
351+
- 分支删除被拒有第二形态:代理回 403,与既有 send-pack 断连同处置 ⇒ 不可删,⛔ 不重试。
352+
- `merge-tree` 套不套 `.gitattributes` 驱动随本克隆注册与否变,驱动 exit 0 只说驱动收下了。
353+
- ⇒ 驱动管辖的路径上冲突证明跑两遍,第二遍带 `-c merge.os-regen.driver=false` 关掉驱动。
333354

334355
## 闭合关键词解析(PR 正文写侧)
335356

@@ -342,6 +363,7 @@
342363
- PR body 与 squash commit message 是两个独立解析源:commit 干净不等于 body 干净,只查 commit 会漏。
343364
- 误关的卡以 `completed` 状态对一切只看 open 的过滤与巡检隐身,无机械守卫覆盖这条路径。
344365
- 消费侧检查 = 合并后读 `closed_by_pull_requests`,在复核清单里。
366+
-`Fixes` PR 自动关闭的卡保留 `pm:*` 标签;`Refs` PR 不关卡 ⇒ 状态转移归席位不归平台。
345367

346368
## 断粮检测与跨墙恢复(5 小时用量墙)
347369

@@ -360,3 +382,7 @@
360382
- 更新端带 `model`,但只有每次起新会话的 Routine 吃得到。
361383
- 自绑的保持所绑会话的档位直到绑定解除 ⇒ ⛔ 不据更新端能改档推自绑的档位可改。
362384
- UI 建的 Routine agent 改不动:更新端只受理自己建的。
385+
- 用量墙杀死席位与全部在飞子代理而 worktree 存活;`SendMessage` 幂等重启文本可整批复活。
386+
- 定时器只在轮次之间投递:可迟到数分钟,也可在轮内被吃掉而无通知 ⇒ 醒来先重读状态。
387+
- 撞墙期间到点的定时器在重置后按序补投 ⇒ ⛔ 不为它们重建 Routine。
388+
- 墙杀后别席可能在共享分支留下抢救 WIP 提交:未过门禁的分支尖不是复核输入。

.github/workflows/ci.yml

Lines changed: 28 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -371,15 +371,17 @@ jobs:
371371
- name: Install dependencies
372372
run: pnpm install --frozen-lockfile
373373

374-
# PRs: only test packages affected by the diff against the PR base.
375-
# spec sits at the root of the dependency graph, so spec-touching PRs
376-
# still run (close to) everything — but the many PRs that don't touch
377-
# spec skip the bulk of the 75-package matrix. Push to main and
378-
# merge-queue builds partition the FULL package list instead: the queue
379-
# result IS the next main, so it gets main's validation, not the PR's
380-
# affected-only subset. (Spec's suite runs here plain / uninstrumented;
381-
# the coverage-instrumented pass lives in the nightly coverage-nightly
382-
# workflow.)
374+
# PRs and merge-queue builds: only test packages affected by the diff —
375+
# against the PR base on pull_request, against the group's own
376+
# `base_sha` on merge_group (#16453). spec sits at the root of the
377+
# dependency graph, so spec-touching diffs still run (close to)
378+
# everything — but the many that don't touch spec skip the bulk of the
379+
# 75-package matrix, and at ~120 queue merges a day that is the bulk of
380+
# the queue's test compute. Push to main partitions the FULL package
381+
# list: it gates nobody, and its full run is the ground truth the
382+
# shard-timings refresh reads. (Spec's suite runs here plain /
383+
# uninstrumented; the coverage-instrumented pass lives in the nightly
384+
# coverage-nightly workflow.)
383385
#
384386
# !@objectstack/dogfood: the ~7½-minute dogfood suite is the dedicated
385387
# Dogfood job's whole purpose, and both jobs run under the same `core`
@@ -436,127 +438,26 @@ jobs:
436438
# `turbo ls`'s internal choice of dot-ness — undocumented, and
437439
# `turbo ls` is experimental (see above). Resolving to a commit here
438440
# leaves turbo no choice to make.
441+
# The selection itself lives in scripts/ci/select-shard-packages.sh:
442+
# environment in, `$RUNNER_TEMP/turbo-ls.json` out. This step only
443+
# exports the event's fields, so the same script -- every branch of
444+
# it -- runs locally and under its self-test (#16453). Per event:
445+
# pull_request affected set against merge-base(origin/<base>, HEAD)
446+
# merge_group affected set against the group's `base_sha`, unioned
447+
# with the same cross-package scans. An EMPTY set is a
448+
# legitimate docs-only group: it reaches the "No packages
449+
# on this shard" exit below, every shard still attests,
450+
# and Test Core is an honest green. It is NOT the #10057
451+
# case, which is pull_request-only (the script says why).
452+
# push unchanged: the FULL list.
439453
- name: Compute this shard's package set
440454
env:
441-
BASE_REF: ${{ github.event.pull_request.base.ref }}
442-
PINNED_BASE_SHA: ${{ github.event.pull_request.base.sha }}
455+
OS_SHARD_EVENT_NAME: ${{ github.event_name }}
456+
OS_SHARD_PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
457+
OS_SHARD_PR_PINNED_BASE_SHA: ${{ github.event.pull_request.base.sha }}
458+
OS_SHARD_MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }}
443459
run: |
444-
SCM_BASE=''
445-
if [ "${{ github.event_name }}" = "pull_request" ]; then
446-
if [ -z "$BASE_REF" ]; then
447-
echo "::warning::This pull_request event carries no base branch, so the affected-set diff base cannot be computed."
448-
else
449-
# `fetch-depth: 0` above already makes this resolve — the fetch is
450-
# the guard for the day that changes, not the normal path.
451-
#
452-
# `git cat-file -e` rather than the more idiomatic strict
453-
# `git rev-parse --verify` spelling. That is history rather than
454-
# style, and it is written down because it used to be a live
455-
# hazard: the pre-#6589 check-shard-attestation.mjs classified a
456-
# job as an aggregate GATE when the script's basename and its
457-
# `--verify` flag merely CO-OCCURRED as substrings anywhere in
458-
# the job's joined `run:` text. This job always carries the
459-
# basename (its `--emit` step at the bottom), so spelling
460-
# `--verify` anywhere in this step — comments included, since
461-
# they were part of `run:` — silently reclassified the shard job
462-
# as a gate.
463-
#
464-
# #6589 closed that by construction. Classification is now by
465-
# INVOCATION: within one command the flag must follow the
466-
# script's own name as an argument, the test is applied per STEP
467-
# and never over the job's joined text, and the lexer drops shell
468-
# comments — all three pinned by that script's `--self-test`,
469-
# which is why this comment can now name the flag at all. Either
470-
# spelling is safe here; `git cat-file -e` stays because
471-
# churning it would buy nothing.
472-
if ! git cat-file -e "refs/remotes/origin/$BASE_REF^{commit}" 2>/dev/null; then
473-
git fetch --no-tags --quiet origin "+refs/heads/$BASE_REF:refs/remotes/origin/$BASE_REF" \
474-
|| echo "::warning::Could not fetch origin/$BASE_REF; the merge-base resolution below will decide."
475-
fi
476-
# `if !` rather than a bare assignment on purpose: these steps run
477-
# under `bash -e`, where a failing command substitution kills the
478-
# step with no message at all.
479-
if ! SCM_BASE=$(git merge-base "refs/remotes/origin/$BASE_REF" HEAD); then
480-
SCM_BASE=''
481-
fi
482-
fi
483-
fi
484-
if [ -n "$SCM_BASE" ]; then
485-
# The drift is printed, not just corrected: nothing in this log ever
486-
# said which commit the affected diff started from, which is why the
487-
# decay was invisible.
488-
DRIFT=$(git rev-list --count "$PINNED_BASE_SHA..$SCM_BASE" 2>/dev/null || echo '?')
489-
echo "Affected-set diff base: $SCM_BASE (merge-base of origin/$BASE_REF and HEAD)"
490-
echo "Frozen payload base.sha: $PINNED_BASE_SHA -- $BASE_REF has moved $DRIFT commit(s) since it was frozen, and that drift is exactly what this step used to charge to this PR."
491-
TURBO_SCM_BASE="$SCM_BASE" pnpm exec turbo ls --affected --output=json > "$RUNNER_TEMP/turbo-ls.json"
492-
# `turbo ls --affected` answers "which packages does the dependency
493-
# GRAPH reach from this diff" — and some suites read files the graph
494-
# does not connect them to. spec's api-methods-batch-conformance scan
495-
# walks every `*.object.ts` in the monorepo while spec declares no
496-
# dependency on the packages it judges (nor should it: the scan reads
497-
# source text precisely to avoid inverting the spec -> * direction).
498-
# A platform-objects-only diff therefore left it unrun, and #7769
499-
# landed a violation on `main` that only PRs touching `spec` ever saw.
500-
# Measured on turbo 2.10.7: 51 packages affected by that diff, spec
501-
# not among them.
502-
#
503-
# So packages that declare a cross-package input radius are unioned
504-
# back in when the diff touches it. The declarations, and the static
505-
# detector that refuses to let a new cross-package scan go
506-
# undeclared, live in the script (`pnpm check:cross-package-test-inputs`).
507-
#
508-
# Failure here falls back to the FULL package list, never to the
509-
# affected-only set: same posture as the merge-base fallback below
510-
# (#6195), and the same reason — the full list is a strict superset,
511-
# so doubt costs minutes rather than coverage. This is the FILTER
512-
# CONTRACT's half 1 applied one layer down.
513-
if ! git diff --name-only "$SCM_BASE" HEAD > "$RUNNER_TEMP/changed-files.txt" \
514-
|| ! node scripts/check-cross-package-test-inputs.mjs \
515-
--union-into "$RUNNER_TEMP/turbo-ls.json" \
516-
--changed "$RUNNER_TEMP/changed-files.txt"; then
517-
echo "::warning::Could not union cross-package scans into the affected set; falling back to the full package list for this shard."
518-
pnpm exec turbo ls --output=json > "$RUNNER_TEMP/turbo-ls.json"
519-
# A fourth signal the failure branches do not cover: a producer that
520-
# exits 0 with a wrong, plausible, EMPTY answer. Only that `git
521-
# diff`'s exit STATUS was checked, never its emptiness, so a
522-
# merge-base resolving to something wrong-but-valid gave an empty
523-
# changed-file list -> zero affected packages -> a green shard that
524-
# tested nothing, with every log line reading like a normal quiet PR
525-
# (#10057). The shard attestation (#6082) does not cover it: it
526-
# attests "shard N ran and every step passed", which is exactly what
527-
# a shard that tested nothing does.
528-
#
529-
# Empty is decidable as BROKEN here, and only here: a pull_request
530-
# always differs from its merge-base. At the partitioner zero is
531-
# frequently the CORRECT answer (a docs-only PR genuinely affects no
532-
# package, and with 6 shards a small change legitimately leaves
533-
# shards empty), so a blanket "red on empty" belongs there least of
534-
# all -- this is the one place selection-failed and nothing-selected
535-
# can be told apart.
536-
#
537-
# Scoping this to pull_request needs no `github.event_name` test:
538-
# SCM_BASE is assigned only inside the `pull_request` guard above, so
539-
# this whole `[ -n "$SCM_BASE" ]` branch is unreachable on push and
540-
# merge_group. Those keep taking the full-list path in the `else`
541-
# below, by design.
542-
elif [ ! -s "$RUNNER_TEMP/changed-files.txt" ]; then
543-
echo "::warning::The diff against merge-base $SCM_BASE listed no changed files, which a pull_request cannot legitimately produce; falling back to the full package list for this shard rather than selecting nothing (#10057)."
544-
pnpm exec turbo ls --output=json > "$RUNNER_TEMP/turbo-ls.json"
545-
fi
546-
else
547-
# Falling back to the FULL package list, never to the frozen
548-
# base.sha. This is not the #4690 silent-skip anti-pattern: that is
549-
# about a gate PASSING on input it could not read, and the full list
550-
# is a strict superset of the affected one — this shard still runs
551-
# everything it would have run and more. Cost is minutes; the
552-
# alternative is a red Test Core on a PR with nothing wrong with it.
553-
# Push and merge-queue builds take this branch by design (the queue
554-
# result IS the next main, so it gets main's validation).
555-
if [ "${{ github.event_name }}" = "pull_request" ]; then
556-
echo "::warning::Could not resolve merge-base(origin/$BASE_REF, HEAD); falling back to the full package list for this shard rather than diffing from the frozen base.sha (#6195)."
557-
fi
558-
pnpm exec turbo ls --output=json > "$RUNNER_TEMP/turbo-ls.json"
559-
fi
460+
bash scripts/ci/select-shard-packages.sh
560461
node scripts/partition-test-shards.mjs "$RUNNER_TEMP/turbo-ls.json" \
561462
--shard ${{ matrix.shard }}/6 --exclude @objectstack/dogfood \
562463
> "$RUNNER_TEMP/shard-packages.txt"

0 commit comments

Comments
 (0)