Skip to content

Commit ecab008

Browse files
xvyimuclaude
andcommitted
test(gates): rewire orphaned state-io/fs-io unit tests + add anti-regression wiring gate
Root cause: c22dbc9 (2026-07-21, "docs: Codexveil GitHub identity README", a rebase-friendly replay) silently reverted the `test:state-io` and `test:fs-io` scripts that 3f36e56 had added 80 minutes earlier, along with their references inside `test:unit`. Both test files stayed in the tree and still passed when invoked directly, so nothing looked broken -- but neither ran under `npm test` or CI for weeks. The green gate did not cover them. docs/reports/2026-07-21-five-layer-internal-opt-report.md section 5 claimed both were "merged into test:unit"; that claim was true at 3f36e56 and false from c22dbc9 onward. Errata added rather than rewriting history. What changed: - package.json: restore `test:state-io` + `test:fs-io`; put both back in the `test:unit` chain. Unit gates 15 -> 18. - scripts/check-test-wiring.mjs (new) + `test:wiring`, wired into `test:unit`: fails when (a) a `*.test.mjs` / `*.test.ps1` exists in the repo but no package.json script runs it (orphan test), or (b) a script exists but is unreachable from `test:unit`. Read-only repo scan (readdir/readFile only); no install state, no network, no CDP. `test:control` is whitelisted as live-only (needs real CDP; CLAUDE.md already documents it as out of CI); packages/contracts owns its own runner via `test:contracts` and is skipped. - CLAUDE.md + AGENTS.md command lists kept in sync (repo requires same outline). - docs/CHANGELOG.md: Unreleased section extended to 08-03 with root cause. Verification (real exit codes, this machine, pnpm 11.5.0 / Node ESM): - `pnpm install --frozen-lockfile` -> 0 (lockfile up to date, 4 entries pass supply-chain policy; no dependency or lockfile change in this commit) - `pnpm run typecheck` -> 0 - `pnpm audit --registry=https://registry.npmjs.org --audit-level=high` -> 0 ("No known vulnerabilities found"); `pnpm audit --prod` -> 0; `pnpm run audit:deps` -> 0. Dependabot open security alerts: 0 (unchanged). - `pnpm run test` -> 0, now including `test:wiring` (all passed), `test:state-io` (pass) and `test:fs-io` (pass), which had never run in CI. - Gate proven able to fail, not just to pass: unwiring `test:state-io` reproduces the exact c22dbc9 regression -> exit 1 "orphan test"; dropping `test:fs-io` from the `test:unit` chain -> exit 1 unreachable. Restored -> 0. Not done / out of scope: - No publish, no `publish-runtime.ps1`, no asar or installed-runtime touch; no repack or replacement of any Codex Desktop file. SKIN_VERSION and the 1.3.25 product line are unchanged (ADR 0003 stamp authority untouched). - doctor / smoke / live CDP probes not run (need a running Codex + CDP 9335); this commit changes no injector, runtime or theme byte, so PROJECT section 9.2 requires only the unit gates. - docs/BASELINE.generated.md left stale (fullHead 707d739, themeCount 11 from the pre-arina-only era). It is generated by write-baseline.ps1 against live install state, so refreshing it is a publish-path action, not a code change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 393383f commit ecab008

6 files changed

Lines changed: 132 additions & 3 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ npm run test:themes-contracts # themes normalizeColors ⊂ contracts palette
3434
npm run test:store
3535
npm run test:adapter
3636
npm run test:deps
37+
npm run test:wiring # 每个 *.test.* 都有 script 且可从 test:unit 达到
3738
npm run test:freshness
3839
npm run test:cdp-url
3940
npm run test:catalog-budget

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ npm run test:themes-contracts # themes normalizeColors ⊂ contracts palette
3535
npm run test:store
3636
npm run test:adapter
3737
npm run test:deps
38+
npm run test:wiring # 每个 *.test.* 都有 script 且可从 test:unit 达到
3839
npm run test:freshness
3940
npm run test:cdp-url
4041
npm run test:catalog-budget

docs/CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,18 @@
66
77
---
88

9-
## Unreleased — maintenance on 1.3.25 product line(2026-07-20 → 07-28
9+
## Unreleased — maintenance on 1.3.25 product line(2026-07-20 → 08-03
1010

1111
> 产品线版本仍为 **1.3.25**;安装态 runtimeId 以 doctor 为准(例 `1.3.25-107b0e` / 本机 `1.3.25-da2adc`)。下列为扫描落地 + 任务卡收口 + DAY 文档,****改 SKIN_VERSION 产品线号。
1212
13+
### 测试接线修复 + 防复发门(2026-08-03)
14+
15+
- **根因**`c22dbc9`(2026-07-21「docs: GitHub identity README」的 rebase 重放)静默回退了 `3f36e56` 80 分钟前加的 `test:state-io` / `test:fs-io` 两条 script 及其在 `test:unit` 中的引用。两个单测文件仍在仓内、单独跑也过,但**数周未进 `npm test` / CI** —— 绿门并不覆盖它们。
16+
- **修复**:两条 script 重新接线并归位到 `test:unit``npm test` 单元门 15 → 18 条)。
17+
- **防复发**:新增 `scripts/check-test-wiring.mjs` + `npm run test:wiring`(已进 `test:unit`)。发现 (a) 仓内存在 `*.test.mjs` / `*.test.ps1` 但无 script 运行(孤儿测试),或 (b) script 存在但从 `test:unit` 不可达,即失败。`test:control` 为 live-only(需真 CDP,CLAUDE.md 已注明不进 CI),白名单豁免;`packages/contracts` 自带 runner,不在扫描范围。
18+
- **门有效性已实证**:分别人为制造上述两类违例,门均 exit 1;恢复后 exit 0。
19+
- 勘误:[`reports/2026-07-21-five-layer-internal-opt-report.md`](./reports/2026-07-21-five-layer-internal-opt-report.md) §5 原称两测「并入 test:unit」,已加勘误说明。
20+
1321
### 安全 / 依赖审计路径(2026-07-28)
1422

1523
- **解开 npmmirror audit DEFER**:CI `themes-gate` 增加 `pnpm audit --registry=https://registry.npmjs.org --audit-level=high`(hard-fail);本地 `npm run audit:deps``scripts/audit-local.mjs`

docs/reports/2026-07-21-five-layer-internal-opt-report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ L4 执行 runtime injector · core/cdp · discover
9898

9999
|| 内容 |
100100
|----|------|
101-
| 测试 | `test:state-io` · `test:fs-io` 并入 `test:unit` |
101+
| 测试 | `test:state-io` · `test:fs-io` 并入 `test:unit`**勘误 2026-08-03**:本条当时成立于 `3f36e56`,但 80 分钟后 `c22dbc9`("docs: GitHub identity README" 的 rebase 重放)把两条 script 连同 `test:unit` 里的引用一并回退,导致两个单测数周未进 `npm test` / CI。已于 2026-08-03 重新接线,并加 `test:wiring` 门防复发) |
102102
| 注释 | core `index.mjs` 标明 state-io 包内 |
103103
| 报告 | 本文 |
104104

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
"test:store": "node packages/themes/theme-store.test.mjs",
2121
"test:adapter": "node packages/themes/dream-adapter.test.mjs",
2222
"test:deps": "node scripts/check-package-deps.mjs",
23+
"test:wiring": "node scripts/check-test-wiring.mjs",
2324
"test:control": "node packages/runtime/scripts/control-plane.test.mjs",
2425
"test:freshness": "node packages/core/state/state-freshness.test.mjs",
26+
"test:state-io": "node packages/core/state/state-io.test.mjs",
27+
"test:fs-io": "node packages/runtime/scripts/fs-io.test.mjs",
2528
"test:cdp-url": "node packages/runtime/scripts/cdp-url-guard.test.mjs",
2629
"test:cdp-exposure": "node packages/core/cdp/cdp-exposure.test.mjs",
2730
"test:catalog-budget": "node packages/runtime/scripts/theme-catalog-budget.test.mjs",
@@ -35,7 +38,7 @@
3538
"typecheck:contracts": "pnpm --filter @codex-skin/contracts typecheck",
3639
"build:contracts": "pnpm --filter @codex-skin/contracts build",
3740
"audit:deps": "node scripts/audit-local.mjs",
38-
"test:unit": "npm run test:themes && npm run test:themes-contracts && npm run test:store && npm run test:adapter && npm run test:deps && npm run test:freshness && npm run test:cdp-url && npm run test:cdp-exposure && npm run test:catalog-budget && npm run test:stamp && npm run test:theme-load && npm run test:payload-builder && npm run test:catalog-quality && npm run test:probe-kit && npm run test:version-retention",
41+
"test:unit": "npm run test:themes && npm run test:themes-contracts && npm run test:store && npm run test:adapter && npm run test:deps && npm run test:wiring && npm run test:freshness && npm run test:state-io && npm run test:fs-io && npm run test:cdp-url && npm run test:cdp-exposure && npm run test:catalog-budget && npm run test:stamp && npm run test:theme-load && npm run test:payload-builder && npm run test:catalog-quality && npm run test:probe-kit && npm run test:version-retention",
3942
"test": "npm run test:unit && npm run test:contracts",
4043
"typecheck": "npm run typecheck:contracts"
4144
},

scripts/check-test-wiring.mjs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* Test wiring gate (PROJECT §9.2 · DoD 1).
3+
*
4+
* Fails if a *.test.mjs / *.test.ps1 file exists in the repo but no package.json
5+
* script runs it, or if a wired script is not reachable from `test:unit`.
6+
*
7+
* Why: 2026-07-21 `c22dbc9` ("docs: GitHub identity README", a rebase replay)
8+
* silently dropped `test:state-io` + `test:fs-io` that `3f36e56` had added 80
9+
* minutes earlier. Both unit tests kept passing on demand but never ran in
10+
* `npm test` or CI for weeks — a green gate that proved nothing about them.
11+
* An orphan test is worse than no test: it reads as coverage without being it.
12+
*
13+
* Contract packages own their own runner (`test:contracts` → vitest/node --test
14+
* inside the workspace package), so packages/contracts is out of scope here.
15+
*
16+
* Run: node scripts/check-test-wiring.mjs | npm run test:wiring
17+
*/
18+
import { readdir, readFile } from "node:fs/promises";
19+
import { join, relative } from "node:path";
20+
import { fileURLToPath } from "node:url";
21+
22+
const repoRoot = join(fileURLToPath(import.meta.url), "..", "..");
23+
24+
// Owned by `test:contracts` via the workspace filter, not by a root script.
25+
const SKIP_DIRS = new Set(["node_modules", "dist", "vendor", ".git", "versions"]);
26+
const SKIP_PREFIXES = ["packages/contracts/"];
27+
// Live-only gates: need a running Codex / CDP / loopback control plane.
28+
// Documented as "不进 CI" in CLAUDE.md; wired but deliberately outside test:unit.
29+
const LIVE_ONLY_SCRIPTS = new Set(["test:control"]);
30+
31+
async function walkTests(dir, acc = []) {
32+
let entries;
33+
try {
34+
entries = await readdir(dir, { withFileTypes: true });
35+
} catch {
36+
return acc;
37+
}
38+
for (const e of entries) {
39+
const p = join(dir, e.name);
40+
if (e.isDirectory()) {
41+
if (SKIP_DIRS.has(e.name)) continue;
42+
await walkTests(p, acc);
43+
} else if (e.isFile() && /\.test\.(mjs|ps1)$/.test(e.name)) {
44+
acc.push(relative(repoRoot, p).replace(/\\/g, "/"));
45+
}
46+
}
47+
return acc;
48+
}
49+
50+
const pkg = JSON.parse(await readFile(join(repoRoot, "package.json"), "utf8"));
51+
const scripts = pkg.scripts || {};
52+
53+
const testFiles = (await walkTests(repoRoot)).filter(
54+
(f) => !SKIP_PREFIXES.some((p) => f.startsWith(p)),
55+
);
56+
57+
/** Scripts that directly invoke a given test file. */
58+
function scriptsRunning(file) {
59+
return Object.entries(scripts)
60+
.filter(([, cmd]) => cmd.replace(/\\/g, "/").includes(file))
61+
.map(([name]) => name);
62+
}
63+
64+
/** Script names reachable from `test:unit` via `npm run <name>` chains. */
65+
function reachableFromUnit() {
66+
const seen = new Set();
67+
const queue = ["test:unit"];
68+
while (queue.length) {
69+
const name = queue.shift();
70+
if (seen.has(name)) continue;
71+
seen.add(name);
72+
const cmd = scripts[name];
73+
if (!cmd) continue;
74+
for (const m of cmd.matchAll(/npm run ([\w:-]+)/g)) queue.push(m[1]);
75+
}
76+
return seen;
77+
}
78+
79+
const reachable = reachableFromUnit();
80+
81+
let failed = 0;
82+
function ok(cond, msg) {
83+
if (!cond) {
84+
failed += 1;
85+
console.error("FAIL:", msg);
86+
} else {
87+
console.log("ok:", msg);
88+
}
89+
}
90+
91+
ok(testFiles.length > 0, `discovered test files (${testFiles.length})`);
92+
ok(Boolean(scripts["test:unit"]), "test:unit script exists");
93+
94+
for (const file of testFiles) {
95+
const runners = scriptsRunning(file);
96+
if (runners.length === 0) {
97+
ok(false, `${file} — no package.json script runs it (orphan test)`);
98+
continue;
99+
}
100+
const live = runners.filter((r) => LIVE_ONLY_SCRIPTS.has(r));
101+
if (live.length === runners.length) {
102+
console.log(`ok: ${file} — live-only via ${live.join(", ")} (outside test:unit by design)`);
103+
continue;
104+
}
105+
const inUnit = runners.filter((r) => reachable.has(r));
106+
ok(
107+
inUnit.length > 0,
108+
`${file} — wired (${runners.join(", ")}) and reachable from test:unit`,
109+
);
110+
}
111+
112+
if (failed > 0) {
113+
console.error(`\ncheck-test-wiring: ${failed} failed`);
114+
process.exit(1);
115+
}
116+
console.log("\ncheck-test-wiring: all passed");

0 commit comments

Comments
 (0)