Skip to content

fix: watcher backoff, selector provenance gate, and a testable Windows artifact - #387

Merged
Fei-Away merged 8 commits into
mainfrom
codex/unified-compat
Aug 27, 2026
Merged

fix: watcher backoff, selector provenance gate, and a testable Windows artifact#387
Fei-Away merged 8 commits into
mainfrom
codex/unified-compat

Conversation

@Fei-Away

@Fei-Away Fei-Away commented Aug 27, 2026

Copy link
Copy Markdown
Owner

拆分后的版本:这个 PR 里的每一项都已在本机或 CI 上验证过,可以直接合。 唯一需要 Windows 实机确认的受管 CDP profile 已移到 #390

1. macOS watcher 空转把机器拖卡(#218

在维护者本机复现了,不是转述报告者:

$ grep -c 'fetch failed' ~/Library/Application\ Support/CodexDreamSkinStudio/injector-error.log
82
first: 2026-08-27T03:15:31.522Z
last:  2026-08-27T03:19:53.655Z

4 分 22 秒里 82 行,而实际轮询频率是每秒约 2 次(日志被 2 秒节流了)。根因是退避上限写死 500ms,不管端点已经消失多久:

discoveryDelayMs = Math.min(500, Math.round(discoveryDelayMs * 1.6));

改成上限随连续失败时长升级:10 秒内维持 500ms(重载 / 导航 / renderer 重启照常快速重连),10 秒后 5s,60 秒后 30s。

空闲时长 修复前 修复后
10 分钟 1200 次 55 次
1 小时 7200 次 155 次
8 小时 57600 次 995 次

错误日志写入间隔同步跟随退避,挂一整夜不会撑大 injector-error.log

特意没有采用「Codex 退出就停掉 watcher」#264 / #353 两个 draft 的方向):短暂中断同样会让目标发现失败,而 watcher 正是重新上色的东西,停掉它就是 #200 报告的「关闭重开后热切换失效」。

回归:macos/tests/discovery-backoff.test.mjs,6 项。

2. CI 发布 Windows Setup.exe 产物

Windows job 一直在编译 Setup.exe 做构建检查,然后丢掉;只有 macOS 会上传 DMG。结果是每一个 Windows-only 修复都无法验证——维护者没有 Windows 主机,而报告者又没法测一个分支。

现在 Setup.exe 和 DMG 一样作为 artifact 上传(保留 7 天,if-no-files-found: error)。#390 就是第一个受益者。

3. 选择器合同 provenance 闸门

verifiedAgainst 到今天还写着 26.727.40816,而合同里的选择器早已扩展到 26.818。任何引用它的 doctor 报告因此都是误导性的。

  • 重写成逐版本列表,每条显式声明证据强度:maintainer / reporter / fixture,另加 gaps 记录已知未验证面。
  • 如实记录:26.803 之后每一条都是报告者证据,Windows 自 26.727 起没有维护者本机 renderer 复核。
  • tools/check-selector-provenance.mjs + CI 步骤:改 selectors[] 而不动 verifiedAgainst → 构建失败。base 文件只比较不校验形状,所以引入规则的这个提交能通过自己的闸门。

4. docs/compat-profile-design.md(仅设计)

签名的、可热更新的 selector / Safe CSS 兼容档案,从 api.dreamskin.cc 分发,让上游 DOM 变更不再需要发整版客户端。含 fail-closed 校验顺序、「只允许覆盖 selector 字符串」的有界降级面、「档案是数据不是代码」的硬边界。

Validation

  • node --test macos/tests/*.test.mjs windows/tests/*.test.mjs tools/*.test.mjs121/121 PASS
  • node tools/sync-runtime-assets.mjs --check:PASS
  • node {macos,windows}/scripts/injector.mjs --check-payload:PASS
  • 全仓 node --checkgit diff --check:PASS
  • provenance 闸门对真实 origin/main base 跑通,负例在单测里覆盖
  • 上一版(含 CDP 改动)四个 CI job 全绿,包括 PowerShell 5.1 / 7

Scope boundary

不改主题包、配色或用户设置。不修改官方 Codex 二进制或 app.asar。不发版、不改版本号。

Nothing in the shipped Windows path ever passed -ProfilePath, so the launcher
never sent --user-data-dir, and Chromium 136+ ignores --remote-debugging-port
for its default data directory. The debugging endpoint therefore never opened
on current Codex builds and the skin simply did not appear. Default to a
persistent managed %LOCALAPPDATA%\CodexDreamSkin\cdp-profile, keep an explicit
-ProfilePath as an advanced override, and assert exactly one managed
--user-data-dir argument plus an existing directory before launch.

Adopted from #363, whose reporter verified the fix on Store Codex 26.803.10989.0
/ Chromium 151.0.7922.76 with a real theme injection. The managed profile costs
a one-time Codex sign-in inside it; that is recorded in the changelog.

Also stop the selector contract's provenance from rotting. verifiedAgainst still
claimed 26.727.40816 while the contract had been extended through 26.818, which
makes every doctor report that quotes it misleading. Rewrite it as a per-build
list that states its own evidence strength (maintainer / reporter / fixture),
add the gaps we know about, and add tools/check-selector-provenance.mjs plus a
CI step so changing selectors without restating what they were checked against
fails the build.

docs/compat-profile-design.md records the structural fix this points at: a
signed, hot-updatable selector profile, so an upstream DOM change stops
requiring a full client release. Design only, not implemented.
The target-discovery backoff capped at a flat 500ms regardless of how long the
endpoint had been gone, so once Codex exited the watcher kept polling roughly
twice a second for as long as it stayed loaded and wrote `fetch failed` to the
error log every 2s. Reporters saw the machine become noticeably sluggish with
no Codex running at all.

Keep the fast ramp for short outages — a reload, navigation, or renderer
restart also fails discovery, and the watcher is what repaints the skin
afterwards — and escalate the ceiling only once discovery has failed
continuously: 5s after 10s of outage, 30s after 60s. Ten idle minutes drop from
about 1200 polls to 55; an idle hour from 7200 to 155. Error logging follows the
same curve so an overnight watcher cannot fill injector-error.log.

Deliberately not "stop the watcher when Codex exits": that is what breaks live
theme switching after a reopen (#200).
The Windows job compiled the installer as a build check and discarded it, so a
Windows-only fix could not be handed to a reporter for field verification: the
maintainer has no Windows host, and a reporter cannot test a branch without an
installer. macOS has published its DMG all along.
It is the only change in this pass that cannot be verified without a Windows
host, and bundling it here meant the provenance gate, the #218 watcher fix and
the CI installer artifact could not ship until someone found a Windows tester.
@Fei-Away Fei-Away changed the title fix(windows): restore CDP on Chromium 136+ and pin selector provenance fix: watcher backoff, selector provenance gate, and a testable Windows artifact Aug 27, 2026
check-update-macos.sh answers "which version am I running" by reading the
VERSION file next to itself, and the client preferred the copy inside the
deployed engine. That copy can lag the app indefinitely: the engine installs
asynchronously after launch, and the installer refuses outright while Codex is
open — this machine's own start-error.log shows four such refusals in ninety
seconds today, right after updating to 1.5.16.

While that refusal stands the app bundle is the new version and the engine is
still the old one, so currentVersion comes back stale, updateAvailable turns
true, and the client notifies about the exact version it is already running.

Prefer the bundled copy for the update check only, keeping the deployed engine
as a fallback. The app bundle is the one source that cannot be stale. Every
other script keeps resolving installed-first, because for those the deployed
engine is the thing actually doing the work.
With `pull_request: branches: [main]` a PR based on another branch ran no
checks at all, so splitting a change out for separate review silently cost it
its tests and its Setup.exe artifact.
@Fei-Away
Fei-Away merged commit 6c28be1 into main Aug 27, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant