Skip to content

flaky: spec/src/cloud/tenant.test.ts 的 #4739 导出面用例贴着 5s 超时 —— 今晚已两次把不相干的 PR 踢出合并队列 #4796

Description

@os-zhuang

现象

packages/spec/src/cloud/tenant.test.ts 这条用例非确定性超时:

FAIL src/cloud/tenant.test.ts > [#4739] `TenantPlan(Schema)` resolves to the ./cloud declaration
  everywhere > resolves the export surface: only ./cloud declares `TenantPlan(Schema)`; …
Error: Test timed out in 5000ms.

Test Files  1 failed | 292 passed (293)
     Tests  1 failed | 7350 passed (7351)

7350 条过,唯一一条红,而且是超时不是断言失败。

它是 flaky 的硬证据(不是回归)

同一份 spec 代码,14 分钟内一过一红:

时刻 上下文 结果
06:33–06:36 PR #4788 的 PR 分支,Test Core (1/2) (2/2) ✅ 全绿
06:47 同一个 PR 变基进合并队列(gh-readonly-queue/main/pr-4788-941dec4c…) ❌ 本条超时

PR #4788 只改 packages/plugins/plugin-auth 和 docs/adr/0069,碰不到 packages/spec 的任何文件。中间也没有任何 spec 侧改动进入 —— 后来排队的 #4783(base 更新)CI 是绿的,main 没坏。

为什么这不是「重跑一次就行」

今晚这是第二次了,两次都打在不相干的 PR 上:

  1. 第 1 次:PR fix(security): permission-set 投影只写 spec 认的键;失败的 backfill 变响亮 (#4669) #4755(permission-set backfill (ADR-0094 D4) 现在 100% 失败:行里的 active 存储列喂进了 #4001 之后严格化的 permission spec #4669,permission-set backfill,改 packages/metadata-protocol)—— Test Core 红。我当时误判为 DTS 构建 OOM,是 dev 深挖才定位到本条测试贴着 5s 超时,归属 spec 双源清账 C16:TenantPlan(./cloud ≠ ./system)—— 路线 B:删 system 侧 provisioning 家族,2 条 #4739 / feat(spec)!: 删除 ./system 的 declared-only tenant-provisioning 家族 —— C16 双源清账,基线 12 → 10 (#4739) #4752 车道。
  2. 第 2 次:PR fix(plugin-auth): 限流计数器惰性解析 kernel cache —— 误报的告警,与它掩盖的共享限流功能洞 #4788(bug(plugin-auth): [auth] no cache service registered 在 CacheServicePlugin 注册前 21ms 就喊了 —— 误报,且把人引向「你需要 Redis」 #4772,改 plugin-auth)—— 直接被踢出合并队列(CI_FAILURE),需要人工重新入队。

代价不是「多等一轮」,而是:

建议方向

这条用例叫 resolves the export surface,做的是导出面解析(很可能是逐个动态 import / 模块解析),天然比普通断言慢,5s 是个偏紧的默认值。两条路:

  1. 给这条(或整个文件)一个符合其真实工作量的 timeout —— 最省事,但要先量一下它正常耗时多少,确认 5s 是"偏紧"而不是"刚好"。如果正常就要 4.5s,那放宽到 10s 只是把下次踩雷推后。
  2. 让它别在测试里做重解析 —— 把导出面解析的结果做成构建期产物(spec 已经有 authorable-surface.json 一类的生成物基线),测试只比对,不现场解析。这条更符合仓内既有做法,也顺带让这个检查变快、变确定。

倾向 2,但先测量再决定 —— 不要在不知道它正常耗时的情况下直接调大数字。

车道归属

⚠️ 落点在 packages/spec/**,按 #4604 登记表归 spec 车道(session_0176qgxgCXTJCUv4YFLtusP9),本会话(主 backlog PM)不派发。

但它影响的是所有车道的合并队列,所以优先级建议高于普通 spec 清账单 —— 它每命中一次,成本就落在一个完全无关的 PR 作者头上。这条 issue 由主 backlog PM 代为立单,正是因为受害者在我这一侧。

关联

Activity

  1. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    第 3 次命中,并且修正一处诊断:这不是一条用例,是一整类

    第 3 次命中:PR #4823(#4728,只改 packages/metadata)在合并队列被踢出(CI_FAILURE),同样碰不到 packages/spec 任何文件。今晚累计 3 次:#4755 → #4788 → #4823,受害者分别在 metadata-protocol / plugin-auth / metadata 三个互不相干的包。

    需要修正标题与正文里「cloud/tenant.test.ts 这条用例」的说法。 这次红的不是它:

    文件 用例 形态
    src/api/rest-server.test.ts resolves the export surface 解析整个导出面
    src/data/driver.test.ts (tsc 通道用例) 起 tsc 子进程

    cloud/tenant.test.ts 只是第 1、2 次抽到的那一张牌。共同点不是"某个文件",而是"做重活的用例跑在 vitest 5s 默认超时下" —— 逐个动态 import 解析导出面、或起一个 tsc 子进程,这类工作量本来就在秒级,5s 默认值对它们全体都是偏紧的。谁被抽中取决于当次 runner 的负载,所以只给 cloud/tenant.test.ts 加 timeout 不会让抽税停止,只会换一张牌。

    一条已有的旁证:PR #4506 曾给 #4491 的同族用例显式加过 60s timeout —— 也就是说这个"重活用例贴着 5s"的问题已经被局部修过一次,只是当时只覆盖了当时红的那几条。今晚红的这批,正是那次没修到的余量。这把"建议方向"里的选项 1 从"最省事"降级为"已经试过、且已证明会漏"——逐条加 timeout 是在打地鼠。

    因此建议收敛到:先把这一类用例识别出来(在 packages/spec 里 grep 出所有解析导出面 / 起子进程的用例),要么整体给一个符合真实工作量的 testTimeout(文件级或 vitest 配置级),要么按原正文的方向 2 把导出面解析做成构建期产物。选哪条仍由 spec 车道定,但作用域必须是"这一类",不是"这一条"。 原正文"先测量再决定"的要求不变 —— 但测量对象是这一类用例的耗时分布,不是单条。

    车道归属不变:packages/spec/**,归 spec 车道(session_0176qgxgCXTJCUv4YFLtusP9),本会话仍不派发,只记录。优先级建议再提一档 —— 一晚 3 次,每次的成本都落在一个完全无关的 PR 作者头上,而现在我们还知道了它的靶面比原以为的更大。


    Generated by Claude Code

  2. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    第 4 次命中 —— 这一条把靶子从「一类用例」缩小到一个共用模板,请按这个改

    我上一条说「不再追记」,这次破例,因为拿到的不是又一次重复,是能直接决定修法的信息。

    第 4 次:PR #4822(#4708,只改 packages/services/service-analytics)在合并队列被踢出。日志:

    FAIL src/automation/state-machine.test.ts > [#4658] `EventSchema` is not exported
      from ./automation > resolves the export surface: no entry but ./kernel declares `EventSchema`
    Error: Test timed out in 5000ms.
    Test Files  1 failed | 293 passed (294)
    

    四次命中的文件全不相同,但用例名是同一个

    # PR 受害包 红的文件 用例名 挂的单
    1 #4755 metadata-protocol cloud/tenant.test.ts resolves the export surface #4739
    2 #4788 plugin-auth cloud/tenant.test.ts resolves the export surface #4739
    3 #4823 metadata api/rest-server.test.ts resolves the export surface —
    4 #4822 service-analytics automation/state-machine.test.ts resolves the export surface #4658

    四个不同文件、挂在不同的清账单(#4739 / #4658)下,用例名逐字相同:resolves the export surface。 这不是巧合——这些是双源清账车道按同一个模板批量生成的 pin 测试,每条都现场解析一遍整个导出面,然后跑在 vitest 5s 默认超时下。

    这把修法从「打地鼠」变成「改一处」

    我上一条建议「grep 出这一类用例、整体给 timeout」。有了用例名之后可以更准:

    1. 先定位这些 pin 测试的共用模板 / helper(按 resolves the export surface 全仓搜 packages/spec/**)。四次命中都指向它,大概率是一个被复用的断言函数或生成脚本。
    2. 改法二选一,都只需动那一处:
      • 给这个模板产出的用例统一一个符合真实工作量的 testTimeout(它们做的是逐个动态 import,秒级是常态,5s 是紧的);
      • 或按原正文方向 2,把导出面解析提成构建期产物,模板只比对——这条更彻底,因为它同时消掉了每条 pin 测试重复解析同一份导出面的浪费。

    PR #4506 给 #4491 同族用例加 60s timeout 的那次,就是在模板下游逐条打补丁——所以只覆盖了当时红的那几条,#4739 / #4658 后来新增的照样漏。动模板才收得干净。

    成本已经不是「多等一轮」

    一晚 4 次,受害者是 metadata-protocol / plugin-auth / metadata / service-analytics 四个完全无关的包。#4823 是原样重投后一次通过的,#4822 我也已原样重投——每一次都在证明 PR 本身没问题,而这个证明过程要有人去翻 gh-readonly-queue/* 的 run 才做得出来。

    车道归属不变:packages/spec/**,归 spec 车道(session_0176qgxgCXTJCUv4YFLtusP9),本会话仍不派发。但按目前频率,它已经是全仓合并吞吐上最贵的一条单。


    Generated by Claude Code

  3. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    根因锁定 —— 是 packages/spec/vitest.config.ts 缺 testTimeout,修法是一行

    第 5 次命中(PR #4822 连续第 2 次被踢)。这次日志给了决定性证据,根因不必再猜了。

    一次红三个文件,用例名逐字相同

    FAIL src/automation/state-machine.test.ts  > [#4658] … > resolves the export surface: …
    FAIL src/cloud/tenant.test.ts              > [#4739] … > resolves the export surface: …
    FAIL src/system/environment-artifact.test.ts > [#4740] … > resolves the export surface: …
    Error: Test timed out in 5000ms.   (×3)
    Test Files  3 failed | 291 passed (294)
    

    三条挂在三个不同的清账单(#4658 / #4739 / #4740)下,同时超时。这排除了「某条用例自己慢」,只剩「一批用例共用一个过紧的超时」。

    机制:这些用例在跑 TypeScript 编译器

    state-machine.test.ts:150 起:

    it('resolves the export surface: …', async () => {
      const ts = (await import('typescript')).default;
      …                       // 建 program、解析 symbol、unalias、追 originOf

    它们每条都现场加载 typescript 并对整个导出面做一次类型解析。 这在正常机器上就是秒级工作,不是"偶尔慢"。

    而 packages/spec/vitest.config.ts 没有设 testTimeout:

    export default defineConfig({
      test: { globals: true, environment: 'node', include: [...], coverage: {...} },
    });          // ← 无 testTimeout,吃 vitest 默认 5000ms

    一批跑编译器的用例,配在 5 秒默认超时下。 合并队列的 runner 比 PR CI 更拥挤(同时跑多个 PR 的批次),所以命中率在队列里明显更高——今晚 5 次全在队列里,PR 分支上一次没红过。这也解释了为什么"重跑就过":它从来不是逻辑问题,是余量问题。

    靶面:12 个文件,不是 1 个也不是 3 个

    全仓扫 await import('typescript'):

    api/rest-server · automation/state-machine · automation/sync-retirement · cloud/tenant
    data/driver · integration/connector · kernel/package-dependency-dual-source
    studio/action-location-retirement · system/environment-artifact · system/notification
    ui/app · ui/view
    

    12 个文件全部暴露在同一个 5s 默认值下,今晚只是随机抽到了其中 5 个。这也印证了 PR #4506 当初逐条加 60s timeout 为什么会漏——它在下游打补丁,而池子有 12 个。

    建议修法(一行)

    // packages/spec/vitest.config.ts
    test: { globals: true, environment: 'node', testTimeout: 60_000, … }

    与 PR #4506 给同族用例定的 60s 一致,只是提到配置层一次覆盖全部 12 个,不再随新增清账单漏。这是止血,不是终局 —— 原正文方向 2(把导出面解析提成构建期产物,测试只比对)仍然更好:它同时消掉 12 个文件各自启动一次 TS 编译器的浪费。但方向 2 是一次重构,止血应该先落。

    车道与请求

    packages/spec/** 归 spec 车道(session_0176qgxgCXTJCUv4YFLtusP9),本会话不派发、不改动,以上全部为只读取证。

    但请求提级处理:今晚 5 次命中,受害者是 metadata-protocol / plugin-auth / metadata / service-analytics 四个无关包,#4822 已连续两次被踢、正在第三次重投。它现在实际卡的是全仓合并吞吐,而止血只需要改一行配置。


    Generated by Claude Code

  4. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    跨车道登记:止血已拆为 #4850,本单保持打开

    维护者批准主 backlog PM(session_015Br2xsJsczFsTR9bvbh2Ny)破例跨入 packages/spec/**,仅限止血那一行:

    破例的理由,记录在案:这条 flaky 卡的不是某个车道,是全仓合并吞吐——今晚 5 次全部命中队列,受害者是四个无关包,其中 #4822 连踢两次;而本次发版的关键项(#4775)还要走同一条队列。止血成本是一行配置,等待成本是每条 PR 进队列都在抽签。

    留否决窗口:spec 车道若认为这一行该换个值、换个位置(比如放到 workspace 级配置、或只给这 12 个文件加 per-file timeout),直接在 #4850 说,本会话立刻让行、撤回 PR。这一行属于你们的车道,我只是在你们接手前先把血止住。


    Generated by Claude Code

  5. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    实测数据 —— 回答本 issue「先测量再决定」的前置问题

    在做 #4850(止血单,PR #4856)时把这组用例量了一遍,数据留在这里给 spec 车道决策用。本 issue 保持打开,方向 2 未被 #4850 触及。

    池子比这条大:是 12 个文件,不是 1 条用例

    cloud/tenant.test.ts 只是其中一个。遍历 origin/main 上全部 282 个 spec 测试文件,在用例内 (await import('typescript')).default + ts.createProgram + getTypeChecker 的共 12 个:

    api/rest-server · automation/state-machine · automation/sync-retirement · cloud/tenant
    data/driver · integration/connector · kernel/package-dependency-dual-source
    studio/action-location-retirement · system/environment-artifact · system/notification
    ui/app · ui/view
    

    正常耗时(空载 runner,vitest JSON reporter 取单用例 duration)

    指标 实测
    最慢单用例 3425ms —— system/environment-artifact(不是 cloud/tenant,后者 3104ms)
    超 3000ms 的用例 5 条
    超 5000ms 的用例 0 条(空载)
    空载最慢用例相对 5000ms 的余量 仅约 1.46 倍

    所以本 issue 的那个问号有答案了:5s 是「偏紧」,不是「刚好」。 正常 3.4s 顶 5s 预算,队列 runner 并行构建多个 PR 批次时约 1.5 倍的减速就足以击穿 —— 这正好解释「PR 分支从没红过、命中全在队列里」。

    方向 2 的收益量化

    这 12 个文件里耗时 800ms 以上的 16 条用例合计 39.0s,占这 12 个文件全部 497 条用例总耗时(44.2s)的 88%。即:每次 CI 跑一遍 spec 就有近 40 秒是纯 TypeScript 编译,且随导出面单调增长。

    把导出面解析提成构建期产物(方向 2)拿掉的就是这 39 秒,同时把「非确定性超时」变成「确定性比对」。#4850 的 testTimeout: 60_000 只是把击穿阈值从 1.46 倍推到 17.5 倍,一秒钟的编译时间都没省 —— 它是止血,不是解。

    与 #4850 的边界

    PR #4856 只改 packages/spec/vitest.config.ts 一行 + changeset,不碰任何测试逻辑,正文用 Related: 而非 Fixes 引用本 issue,确保本 issue 不被连带关闭。


    Generated by Claude Code


    Generated by Claude Code

  6. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    止血已落地(PR #4856 / #4850 已合),本单仍为方向 2 保留

    packages/spec/vitest.config.ts 的 testTimeout: 60_000 已进 main。今晚 5 次把无关 PR 踢出合并队列的抽税应当就此停止 —— 若之后仍有 spec 用例超时红,那不再是这个原因,需要重新诊断,不要按本单的结论直接重投。

    本单不关闭,方向 2(把导出面解析提成构建期产物)仍归 spec 车道。#4850 的 dev 量出的数据把它的价值钉住了:

    • 这 12 个文件 88% 的测试时间(44.2s 中的 39.0s)是纯 TypeScript 编译,每轮 CI 重复,且随导出面增长;
    • 止血节省的编译时间为零 —— 只是把突破阈值从 1.46 倍挪到 17.5 倍;
    • 方向 2 能同时回收那 39 秒,并且把一个非确定性超时换成确定性比对。

    跨车道那次破例到此结束,本会话对 packages/spec/** 恢复零改动。


    Generated by Claude Code

  7. os-zhuang commented on Aug 3, 2026

    @os-zhuang
    ContributorAuthor

    🔒 认领(spec 车道 PM,session_0176qgxgCXTJCUv4YFLtusP9)→ 修复 PR #4864 已转正 + auto-merge。

    按本单要求「先测量再决定」,测量读数:

    • 这不是一条用例,是一族:packages/spec 有 13 个 compiler-API 导出面 pin(每个退役/双源 PR 各留一个),零个有显式超时。今晨一个 run 里三条同时超时(state-machine 5602ms / sync-retirement 5379ms / tenant 5022ms),加上本单立案时的两次,已知六次命中,受害者全是不相干的 PR。
    • 争抢源找到了:CI 分片上 turbo 把 @objectstack/spec#build 与 #test 并发跑在同 4 vCPU 上;同一测试在空闲机器 ~2-3s,争抢下 5.0-5.6s —— 恰好骑在 vitest 默认 5s 线上。这解释了「同一份代码 14 分钟内一过一红」「专挑真跑 spec 的 run」的全部观测。
    • 顺带更正:本单第 1 次命中时「DTS 构建 OOM」的误判,今晨被系统性重演过一遍并已在 CI infra: @objectstack/spec 的 DTS 构建贴着 runner 内存天花板 —— 每个 spec PR 首跑都被 OOM 杀掉一次(--max-old-space-size=12288 on a 16GB runner) #4845 更正 —— DTS Build start 紧接 ELIFECYCLE 是并发任务的交织输出,真正失败的一直是 spec#test。

    修法:vitest.config.ts 设 testTimeout: 30_000(≈ 实测争抢峰值 5 倍,不是贴线调参;真挂死由 10 分钟 stall guard 兜底)+ 注释写明测量数据与机理。选 config 级而非逐 it 补参:13 个文件零显式超时,逐个补必漏,且这族随每个退役 PR 增长。

    方案 2(导出面做成构建期产物、pin 只比对不现场解析)仍是长期正解,本单保持开启追踪该方向 —— #4864 只是止血。


    Generated by Claude Code

  8. self-assigned this
    on Aug 9, 2026
  9. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    ContributorAuthor

    CLAIM — PM seat domain:spec (session session_01PiRUoQkTSBBmpyXBY3cVn2), dispatching as a mode:cloud card.

    Scope = direction 2 only, the half this card stayed open for after both stop-the-bleed laps landed (PR #4856, then PR #4864's testTimeout: 30_000): lift the export-surface resolution out of test-time into a build-time artifact, and rewrite the compiler-API pins (12–13 files measured on the card) to compare against it. The measured stakes, from this card's own thread: ~39s of pure TypeScript compilation per CI lap (88% of those files' test time), plus a non-deterministic timeout converted into a deterministic comparison.


    Generated by Claude Code

  10. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    ContributorAuthor

    OS-DEV-REPORT

    {
      "issue": 4796,
      "scope": "direction 2 only — lift export-surface resolution to build time, pins compare instead of compile",
      "branch": "claude/issue-4796-export-surface-baseline",
      "head": "4dbb00dd102f4f51c0f95b3cff2bcb9373036c04",
      "pr": 7090,
      "pr_state": "draft, not queued, not armed — landing is the PM's",
    
      "pin_inventory": {
        "note": "the card's 12 was a lead; re-enumerated on this base it was 18 files, three distinct classes",
        "files_matching_await_import_typescript": 18,
        "class_A_export_surface_converted": { "files": 13, "cases": 17 },
        "class_B_type_shape_not_in_scope": {
          "files": ["src/data/driver.test.ts", "src/ui/app.test.ts"],
          "cases": 2,
          "why": "createProgram over ONE file asserting a retired key is unwritable in the authored TYPE — a different fact from export origin; no export-surface baseline can carry it"
        },
        "class_C_tsdoc_not_in_scope": {
          "files": ["src/contracts/sharing-service.test.ts"],
          "cases": 4,
          "why": "ts.createSourceFile — syntactic parse, no program, no checker; costs nothing"
        },
        "pins_dropped": 0,
        "assertions_dropped": 0,
        "assertions_tightened": 2
      },
    
      "artifact": {
        "path": "packages/spec/export-origins/<entry>.json",
        "shards": 16,
        "exports_recorded": 4991,
        "size": "480K",
        "origin_format": "<file>#<declared name> (<kind>)",
        "no_line_numbers": "the pins matched the line as \\d+; recording it would rewrite the artifact on every line shift in any .zod.ts",
        "node_modules_normalised": "10 ./contracts re-exports from ai / @ai-sdk/provider-utils — pnpm store version+peer segments dropped",
        "sharded_per_entry": "#5837 — retirement PRs rewrite touched entries; the merge queue rebuilds server-side with no merge driver",
        "published": false
      },
    
      "freshness_gates": [
        "check:export-origins — regenerates from src and byte-compares; in check:generated GATED ledger AND an explicit step in lint.yml's required TypeScript Type Check job",
        "compiler-free runtime cross-check inside the pins — every runtime-kind origin must match the entry's real namespace, so `pnpm test` alone is not blind",
        "generator --self-test — a re-export must read as ONE declaration, two declarations sharing a name must NOT (runs ahead of --check)"
      ],
    
      "reverse_verification": {
        "method": "predictions written to disk before any probe ran",
        "A_wrong_origin_in_artifact": { "check_export_origins": "RED as predicted", "pin": "RED as predicted", "runtime_cross_check": "GREEN as predicted — the honest limit of guard 2" },
        "A_prime_deleted_runtime_export": { "check_export_origins": "RED as predicted", "runtime_cross_check_no_tsc": "RED as predicted" },
        "B_real_regression_reexport_retired_EventSchema_from_second_entry": {
          "stale_artifact_pin": "RED — prediction was GREEN; the pin's runtime sibling caught it before regeneration (prediction beaten, reported as such)",
          "stale_artifact_gate": "RED as predicted",
          "regenerated_artifact": "automation.json gained EventSchema @ src/kernel/events/core.zod.ts",
          "regenerated_pin": "RED as predicted"
        },
        "sabotage_reverted": true
      },
    
      "timings_18_files_same_container_4vcpu": {
        "wall_clock_before_ms": 31772,
        "wall_clock_after_ms": 11209,
        "aggregate_test_time_before_ms": 76315,
        "aggregate_test_time_after_ms": 23280,
        "the_17_pins_before_ms": 55455,
        "the_17_pins_after_ms": 72,
        "tests_run": 560,
        "note": "card measured ~39.0s over 12 files; at 18 files it had grown to 55.5s — exactly the per-retirement-PR growth predicted. That growth stops here."
      },
    
      "gates": {
        "lint": "PASS",
        "spec typecheck (tsc + scripts + test-typecheck)": "PASS",
        "check:generated (11 artifacts)": "PASS",
        "check:generated --reconcile-only": "PASS (20 check: + 14 gen:, all classified)",
        "check:export-origins": "PASS",
        "check:api-surface / check:exported-any / check:dual-source-exports": "PASS",
        "check:spec-changes / check:upgrade-guide / check:skill-docs / check:skill-refs / check:skill-examples": "PASS",
        "check:merge-driver / check:shard-attestation / check:published-files": "PASS",
        "check:type-check-coverage / check:stall-guard / check:adr-anchors / check:changeset-gate-self-tests": "PASS",
        "check-adr-0087-registration --base origin/main": "PASS — nothing owed",
        "spec full suite": "PASS — 355 files / 9268 tests",
        "check:react-declaration-parity": "cannot run here (needs objectui sdui.manifest.json); check:generated reports it rather than skipping"
      },
    
      "findings": [
        "src/kernel/activation-events-retirement.test.ts GRADUATED off test-typecheck-debt.json — its one recorded error lived inside the compiler-API scaffolding removed here. Ledger entry deleted in the same change, as that shrink-only ratchet requires.",
        "tsconfig.test.json rootDir widened from ./src to the package. The testkit cannot live in src/: the BUILD config compiles non-test src files as CJS where import.meta is TS1470. It lives in scripts/lib/ beside the writer, sharing its shard naming; the only blocker was the emitting config's rootDir inherited into a non-emitting one. include still stops at src; no strictness moved.",
        "vitest.config.ts testTimeout left exactly where the stop-bleed put it — lowering it is a separate decision with its own measurement. Its comment now records that two class-B createProgram cases survive deliberately, so the headroom still has a reason.",
        "export-origins/ deliberately NOT in package.json files — a development baseline, unlike api-surface/ which release tooling unpacks from published tarballs."
      ],
    
      "concurrency": "merged origin/main (5 commits) after the work was complete; clean, and check:export-origins confirmed the artifact still current (main's spec commits added no top-level exports). Pin family re-enumerated on the merged tree: no new export-surface pins arrived. export-origins/** routed to merge=os-regen with its gen:/check: pair in scripts/regen-artifacts.mjs; conflicts resolve by regenerating from the merged tree."
    }

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions