Repository navigation
Description
Ruled: 6417116310 · letter A · 2026-10-26T13:25Z
Filing gate ②: a maintainer-only decision. Maintainer, 2026-10-26, verbatim: 「完整的评估本项目, 还有哪些可以优化的地方。包括 ci 和文档」 then 「使用 pm-dispatch 技能创建 issue,需要决策的立为决策卡。」 and 「低优先级的先不考虑」. Batch-of: 19 (repository evaluation 2026-10-26, session_019a9XCWsbMFp75REzthE6cs; 9 decision cards + 10 execution cards). Reader: the director seat presents this card in a decision batch; after the ruling the lane named under "After the ruling" dispatches. ⛔ Not a claim. Dedupe: REST list of open + closed-since-2026-09-20 issues (439, PRs excluded), grep turbo.*(remote|cache)|TURBO_TOKEN → 4 hits: #25374 (closed; asserts the cache is on, PR runs read-only by design), #25452 (open, dispatched; per-sha seeds vs the 10 GB pool), #25373 and #25869 (closed). None decides whether a pull_request run may hold a remote-cache token. New.
维护者速读
CI 里 Turbo 远程缓存只在 merge_group 和 main 上拿到令牌;13 处 TURBO_TOKEN 表达式在 pull_request 事件下一律给空串(ci.yml 8 处、lint.yml 5 处,HEAD 0d83ee8)。PR 的构建因此只能靠 Actions cache 的按 sha 种子,而 #25452 已经量到那个 10 GB 池子每次 merge 写入约 12.8 GB、随时被挤掉。PR 是运行量的大头(30 天约 7,000 次/工作流)。给 PR 发令牌能让每个 PR 的 Build Core / 分片依赖构建命中远程产物,但 Vercel 远程缓存的令牌没有服务端只读档,remote:r 只是客户端约定——一个由 AI 席位写的 PR 拿到令牌就能往共享缓存里写。这是信任边界问题,不是性能参数。请裁:A 不给 PR 令牌,维持现状,把 PR 冷构建的代价留给 #25452 的种子工作;B 给同仓 PR 一枚令牌并在 workflow 里强制 remote:r,接受客户端约定即边界;C 自建只读代理(或 Turbo 自托管缓存)给 PR 一条真只读路。选 A / B / C?
Governing text
.github/workflows/ci.ymllines 870–878 (the test shard env block) and the sibling blocks at 1965, 2027, 2111, 2173, 2311, 2801, 3224;lint.yml5773, 6036, 6111, 6244, 6277:TURBO_TOKEN: ${{ (github.event_name == 'merge_group' || github.ref == 'refs/heads/main') && … || '' }}.ci.ymlcomments near 2665–2700 record why writes are main/queue-only (poisoning a shared cache from an unreviewed branch).- ci: no job asserts the Turbo remote cache is actually on — TURBO_TOKEN is withheld whenever the signing secret is empty, turbo then prints "Remote caching disabled" and builds cold with nothing red; add the assertion to Build Core and the closure builds #25374 (closed): "writes only on merge_group and main push/workflow_dispatch, PRs read-only" — the card treated PR reads as intended, but the expression it cites withholds the token itself on PRs.
- AGENTS.md Prime Directive 5 (no workarounds) and 14 (
.github/workflows/**is Tier H).
Premises (re-check before acting)
- P1 — on a
pull_requestrun the token is empty at every site:grep -n 'TURBO_TOKEN:' .github/workflows/ci.yml .github/workflows/lint.yml→ 13 lines, each gated onmerge_group || refs/heads/main. ci: Turbo seeds vs the 10 GB Actions cache pool — measure the pool, then shrink or retire the per-sha seeds #25452 records "Remote caching enabled, 76/76 remote hits" on PR ci: assert the Turbo remote cache is on after Build Core, the Test Core closure and the three Type Check builds #25419's Build Core at an earlier commit; the dev confirms from a current PR run's Build Core log which reading holds today. - P2 — Vercel remote cache tokens are read-write; read-only is a client-side
TURBO_CACHE=remote:rconvention. Re-check: Turborepo docs for the currentTURBO_CACHEsemantics and the Vercel token scopes. - P3 — PR runs are the volume:
ci.yml:22records 7,008 pull_request runs per workflow per 30 days.
One-sentence problem
A pull request's ten parallel builds never read the warm remote cache that the merge queue just filled, so every PR pays full build time while the only cache it can use is being evicted by the merges around it.
Options
| Option | What | Customer-visible consequence | Workload (lane · PRs · precedent) |
|---|---|---|---|
| A | Keep PRs tokenless. Rely on #25452 to make the Actions-cache seeds survive. | PR wall clock unchanged (ci.yml median 15 min, p90 21 min on the last 50 runs); no new trust surface. | devx · 0 PRs · n/a |
| B | Pass the token on same-repo pull_request runs with TURBO_CACHE=local:rw,remote:r; fork PRs get nothing (secrets are not exposed to forks anyway). |
Faster PR builds; a malicious or buggy PR workflow edit could write to the shared cache, poisoning main's builds until the key rotates. Mitigation: TURBO_REMOTE_CACHE_SIGNATURE_KEY signing stays on, so poisoned artifacts without the signature are rejected. |
devx · 1 PR (Tier H) · precedent: #21186 wired the cache (closed) |
| C | A read-only path: a small proxy or a self-hosted Turbo cache that serves GETs without a write credential, PRs point at it. | Same speed as B with a real read-only boundary; one more service to run and keep up. | devx · 2–3 PRs + infra · no precedent in this repo |
Business meaning
- A: pay with runner minutes and wait time, keep the trust model simple.
- B: trade a client-side promise for faster feedback; the signature key is the real guard.
- C: buy the boundary properly; appetite for a service nobody runs today.
四维分析
- 实际业务需求:每个核心 PR 今天中位 15 分钟、p90 21 分钟,其中依赖闭包构建在慢分片上 5.3–5.8 分钟(ci(test-shards): Test Core's two slowest shards set every core PR's wall clock at ~28 min (5.6 min dependency build + ~20 min tests) while the other six finish in 7–17 — re-derive the cli slicing and shard balance on post-merge readings #25870 读数)。远程命中能把这段压到秒级。拉动是实测的:队列与 PR 都在等它。
- 项目长远合理性:长期形态是「PR 只读、main 与队列可写」,这正是注释里写的意图;今天的表达式把意图写成了「PR 什么都没有」。B 用签名密钥兜住写入,是最接近意图的最小改动;C 是正路但要养一个服务,创业阶段不合适。
- 防 AI 犯错:席位写的 PR 可以改 workflow 本身,所以
remote:r这个客户端约定挡不住一个被改坏的 PR。签名密钥让未签名产物在 main 上被拒,错误会在构建时响亮失败,不是静默用错产物——这是 B 可接受的前提,必须在 PR 里实测一次(故意写一个未签名产物,看 main 构建拒绝)。 - 创业阶段不扩散:不新增服务、不新增门禁。B 是改 13 行表达式;A 是零改动。
Recommendation
B, with two conditions written into the dispatch: (1) the signature-key rejection is demonstrated once on a throwaway branch before the token line changes; (2) the token is withheld when github.event.pull_request.head.repo.full_name != github.repository (fork PRs), spelled in the expression and self-tested by scripts/ci/scheduled-full-run.mjs --check-concurrency's sibling or a new case in an existing self-test, not a new gate.
自检行:只看①选 B;②③④ 是否翻转:否(③ 要求签名拒收先实测,是条件不是翻转)。
Confidence gaps: whether the Vercel plan in use enforces signature verification on reads (P2); whether PR #25419's remote hits came from a since-removed unconditional token (P1).
After the ruling
- A: close this card not planned; note the decision on ci: Turbo seeds vs the 10 GB Actions cache pool — measure the pool, then shrink or retire the per-sha seeds #25452.
- B: the devx execution seat dispatches one os-dev; file surface
.github/workflows/ci.yml,.github/workflows/lint.yml(Tier H, lands by maintainer approval); acceptance = a PR run's Build Core log reads "Remote caching enabled" with remote hits, and the throwaway-branch rejection reading is on the PR. - C: a design card first (which proxy, who runs it), then the same dispatch.
os-decision-facets
① 项目长远合理性:B 把「PR 只读」从注释里的意图变成表达式里的事实,不增加特例。
② 实际业务拉动:每个核心 PR 与队列条目今天都在等冷构建,#25870 量到 5.3–5.8 分钟。
③ 防 AI 犯错:签名密钥让未签名产物在 main 上响亮失败;派发令要求先实测一次拒收。
④ 创业阶段不扩散:13 行表达式,零新服务、零新门禁;C 的代理留到有运维人力时。
Prior rulings read: remote cache,turbo_token,pull_request → 0 hits; none; thread: none; repo: objectstack-ai/objectstack
Related
#25374 (closed), #25452 (open), #25870 (open), #25373 (closed).
Activity
objectstack-fleet commentedon Oct 26, 2026
Triage: first grade —
tooling·security·priority:p2·domain:devx·area:devpath·needs-user-decision·pm:awaiting-maintainer· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T12:01Z⛔ Not a claim, ⛔ not a dispatch. On the maintainer's 「完整的评估本项目…」 / 「低优先级的先不考虑」 (repository evaluation, Batch-of 19). Low priorities were excluded at filing, so the execution cards grade p2.
Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Brief-claimed-by: session_01KrKXQVvgYk2AJ4VkqMobyb · batch #397 · 2026-10-26T12:45Z
Director seat, summon #51 continuation (ledger objectstack#12708). Presented to the maintainer in batch #397 (the CI / tooling family of the 2026-10-26 repository review) as item 1 of 4. Seat increment (re-verified on
origin/maincdd4eea9; the card's own analysis is not restated):TURBO_TOKEN:appears 8× inci.ymland 5× inlint.yml, each gatedmerge_group || refs/heads/main(e.g.ci.yml:874);turbo.jsonhasremoteCache.signature: true; the signature key is read at 17 + 10 sites.signature: truea run must holdTURBO_REMOTE_CACHE_SIGNATURE_KEYto verify downloads, so a PR run given the token must also be given the key — and a run that holds the key can sign what it writes. A same-repo pull request runs its own edited workflow with repository secrets, so a seat-written PR that changes one expression could write signed artifacts into the cache main reads. The signature key is therefore not a boundary for the PR class;remote:ris a client-side promise. B's condition (1) ("demonstrate the rejection of an unsigned artifact") tests the wrong threat.Prior rulings read:
TURBO_TOKEN·remote cache·signature→ ci: no job asserts the Turbo remote cache is actually on — TURBO_TOKEN is withheld whenever the signing secret is empty, turbo then prints "Remote caching disabled" and builds cold with nothing red; add the assertion to Build Core and the closure builds #25374 (PR reads were described as intended; the expression withholds the token), ci: Turbo seeds vs the 10 GB Actions cache pool — measure the pool, then shrink or retire the per-sha seeds #25452 (seeds), ci: wire the Vercel turbo remote cache (team object-stack) into ci.yml — trusted events write, PRs read, signed — so cloud's pinned framework build can replay what this repo's main already built #21186 (wired the cache); none rules a PR token; thread: 6415497781.objectstack-fleet commentedon Oct 26, 2026
Ruling: batch #397 item 1 · letter A · maintainer 「同意」 2026-10-26T13:24Z
Recorded by the director seat (summon #51 continuation,
session_01KrKXQVvgYk2AJ4VkqMobyb, ledger objectstack#12708, brief 6416335461). Letter A: pull-request runs stay without a Turbo remote-cache token; PR build speed rides #25452's per-sha Actions-cache seeds. The seat's reading, accepted by the maintainer: withturbo.jsonremoteCache.signature: true, a run must holdTURBO_REMOTE_CACHE_SIGNATURE_KEYto verify downloads, so a PR run given the token holds the key too and can sign what it writes; a same-repo pull request runs its own edited workflow with repository secrets, so the signature key is not a boundary for that class andremote:ris a client-side promise — B's mitigation tests the wrong threat. C (a read-only path: a self-hosted Turbo cache or proxy serving GETs without a write credential) is the route when the fleet has someone to run a service; it starts from a design card, not from this one.Workload of the chosen letter: 0 — no change; the 13
TURBO_TOKENexpressions (ci.yml8,lint.yml5) stay as they are. Closednot_planned; the decision is noted on #25452.