Skip to content

Commit 6c28be1

Browse files
authored
fix: watcher backoff, stale update version, selector provenance, testable Windows artifact (#387)
Four verified fixes plus one design note. The unverifiable managed CDP profile was split out to #390. - #218: the macOS watcher's discovery backoff capped at a flat 500ms regardless of how long the CDP endpoint had been gone, so an exited Codex left it polling twice a second. Reproduced on the maintainer's own machine: 82 `fetch failed` lines in 4m22s. The ceiling now escalates with continuous outage (500ms → 5s after 10s → 30s after 60s), cutting ten idle minutes from ~1200 polls to 55. Not 'stop the watcher on exit', which is what breaks live switching after a reopen (#200). - The client announced updates to the version it was already running. `check-update-macos.sh` reads the VERSION file beside itself, and the client preferred the deployed engine's copy — which lags indefinitely whenever the engine install is refused because Codex is open. Prefer the bundled copy for the update check only. - CI compiled the Windows Setup.exe as a build check and discarded it, leaving every Windows-only fix unverifiable. It is now published as an artifact like the macOS DMG. `pull_request` also no longer filters on base branch, so a stacked PR still gets tests and an installer. - `verifiedAgainst` claimed Codex 26.727 while the contract covered 26.818, making any doctor report quoting it misleading. Rewritten per-build with explicit evidence strength (maintainer/reporter/fixture) and a gaps list, plus `tools/check-selector-provenance.mjs` and a CI gate. - `docs/compat-profile-design.md`: design only, for a signed hot-updatable selector profile. Verified: 124/124 portable Node tests, sync --check, both payload checks, Swift typecheck (macOS 14.4 SDK), full CI green including PowerShell 5.1 and 7. No version bump, so the release guard treats this as version_unchanged and publishes nothing.
1 parent 40d1f97 commit 6c28be1

13 files changed

Lines changed: 827 additions & 37 deletions

File tree

.github/workflows/ci.yml

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ name: CI
33
on:
44
push:
55
branches: [main]
6+
# Deliberately unfiltered by base branch. With `branches: [main]` a pull
7+
# request stacked on another branch ran no checks at all, so a change split
8+
# out for separate review also lost its tests and its installer artifact —
9+
# which is the one thing a reporter needs in order to verify it.
610
pull_request:
7-
branches: [main]
811

912
permissions:
1013
contents: read
@@ -124,6 +127,27 @@ jobs:
124127
test "$macos_injector" = "$expected"
125128
test "$windows_injector" = "$expected"
126129
130+
- name: Check selector contract provenance
131+
shell: bash
132+
env:
133+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
134+
run: |
135+
# Every Codex release since 26.715 moved DOM that the shared contract
136+
# binds to, and the provenance block silently stayed on 26.727 until
137+
# 26.818. Changing selectors without restating what they were checked
138+
# against is what makes doctor output untrustworthy later.
139+
# An array, not an unquoted variable: the base argument is optional
140+
# and a bare $base_file would either vanish or word-split.
141+
args=()
142+
if [[ -n "$BASE_SHA" ]]; then
143+
git fetch --depth 1 origin "$BASE_SHA"
144+
base_file="$(mktemp)"
145+
if git show "$BASE_SHA:tools/selectors.json" > "$base_file" 2>/dev/null; then
146+
args+=("$base_file")
147+
fi
148+
fi
149+
node tools/check-selector-provenance.mjs "${args[@]}"
150+
127151
- name: Run portable Node.js regressions
128152
shell: bash
129153
run: |
@@ -184,6 +208,21 @@ jobs:
184208
(Get-Item -LiteralPath $artifact).Length -le 0) {
185209
throw 'Windows installer artifact is missing or empty.'
186210
}
211+
"INSTALLER_PATH=$artifact" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
212+
213+
# The macOS job has always published its DMG, so a macOS fix can be handed
214+
# to a reporter straight from the pull request. Windows compiled the
215+
# installer purely as a build check and then discarded it, which left every
216+
# Windows-only fix unverifiable: the maintainer has no Windows host, and a
217+
# reporter cannot test a branch without an installer. Publish it too.
218+
- name: Upload Setup.exe artifact
219+
if: matrix.executable == 'powershell.exe'
220+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
221+
with:
222+
name: CodexDreamSkin-setup
223+
path: ${{ env.INSTALLER_PATH }}
224+
retention-days: 7
225+
if-no-files-found: error
187226

188227
macos-tests:
189228
name: macOS repository regressions

TASK_PROGRESS.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,61 @@
11
# Task Progress
22

3+
## Unified compatibility pass (2026-08-27)
4+
5+
Branch `codex/unified-compat`, based on `origin/main@40d1f97` (v1.5.16 + #386).
6+
7+
- [merged] PR #386 (horizontal overflow) squashed to `main` as `40d1f97` after
8+
full CI green and a local `node --test windows/tests/injector-window-readiness.test.mjs`
9+
run at 11/11. It closed a real cross-platform drift: macOS already gated
10+
`pass` on `!result.documentOverflow?.x`; Windows computed `documentOverflow`
11+
and ignored it. #287 is superseded by it; #298 is closed by it.
12+
- [split out] The managed CDP profile change moved to its own branch
13+
`codex/windows-cdp-profile` so it stops blocking the verified work here. It is
14+
the only change in this pass that cannot be verified without a Windows host.
15+
Original note kept below for context:
16+
- [moved to codex/windows-cdp-profile] Adopted PR #363's managed CDP profile.
17+
Nothing ever passed `-ProfilePath`, so `--user-data-dir` was never sent, and
18+
Chromium 136+ ignores `--remote-debugging-port` for the default data
19+
directory. The launcher now defaults to `%LOCALAPPDATA%\CodexDreamSkin\cdp-profile`
20+
and an explicit `-ProfilePath` remains an override. The regression asserts
21+
exactly one managed `--user-data-dir` argument and that the directory exists
22+
before launch. **This host has no `pwsh`; the PowerShell suites have not been
23+
run locally — CI's PowerShell 5.1/7 jobs are the first real execution.**
24+
Known behavior change: the managed profile requires a one-time Codex sign-in
25+
inside it; recorded in `windows/CHANGELOG.md`.
26+
- [implemented] `tools/check-selector-provenance.mjs` plus a CI step: changing
27+
`tools/selectors.json` selectors now requires moving `verifiedAgainst` in the
28+
same commit. The base file is compared but not shape-validated, so the
29+
introducing commit can pass its own gate.
30+
- [corrected] `verifiedAgainst` claimed 26.727 while the contract already
31+
covered 26.818. It now lists every Codex build per platform with explicit
32+
evidence strength (`maintainer` / `reporter` / `fixture`) and a `gaps` list.
33+
**Everything after 26.803 is reporter evidence; there is no maintainer
34+
re-verify on Windows since 26.727.**
35+
- [design only] `docs/compat-profile-design.md` specifies a signed, hot-updatable
36+
selector/Safe-CSS profile served from `api.dreamskin.cc`, so a Codex DOM change
37+
stops requiring a full client release. Not implemented.
38+
- [verified locally] `node tools/sync-runtime-assets.mjs --check`, `node --test`
39+
across `tools` (10), `windows` (28) and `macos` (76) suites, and
40+
`git diff --check` all pass.
41+
- [triaged] Full sweep of all open issues on 2026-08-27. Closed with evidence:
42+
#80 (accent contrast — `readableAccentInk` computes from the resolved accent
43+
against both backdrop extremes; the legacy Windows `applyProfile`/
44+
`--dream-accent-ink` path no longer exists and both platform assets are
45+
byte-identical), #354 (fixed by #357 in v1.5.13; reporter was on 1.5.2),
46+
#373 (v1.5.16). Every remaining issue got an explicit disposition: accepted,
47+
accepted-but-blocked, needs-info, folded into another issue, or declined with
48+
a stated reason and a written trigger for revisiting. #388 was split out of
49+
#379 for the wallpaper library/rotation request. #189/#346 were linked:
50+
`codesign --verify --deep --strict` re-verifies nested binaries, so one bad
51+
nested signature in an official build is an unfixable dead end for users;
52+
local check on ChatGPT 26.727.51351 passes both modes, so it is
53+
version-specific. Proposed fix is to keep the requirement check and demote
54+
`--deep` to an optional strictness mode.
55+
- [not done] No release, no version bump, no macOS Swift change. #374, #376,
56+
#371 remain open; #378 sections 4-7 were already covered by v1.5.16 while
57+
sections 1-3 were already in `main`.
58+
359
## Issue #373 complete Codex 26.818 hotfix and v1.5.16 (2026-08-27)
460

561
- [objective] Complete the portions of #373 that v1.5.15 did not ship, validate

docs/compat-profile-design.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# 兼容档案(compat profile)设计草案
2+
3+
状态:**草案,未实现**。本文只定义契约与边界,供后续按 P1 落地。
4+
背景问题见 `tools/selectors.json``verifiedAgainst.gaps` 与 issue #277
5+
6+
## 为什么需要它
7+
8+
近两个月的失效时间线:
9+
10+
| Codex 版本 | 破坏点 | 修复方式 |
11+
| --- | --- | --- |
12+
| 26.715.9757 → 26.715.10079 | owl runtime 把 `--remote-debugging-port` URL 编码进 `codex://` path,CDP 完全打不开 | #235,启动器加直连回退 |
13+
| 26.721.x | 首页 `home-icon` 渐进渲染,被判成注入校验失败 | #306,改用 `[role="main"]` 容器 |
14+
| 26.727.40816 | 主表面 / 顶栏语义类改成 CSS Modules | 合同追加 `data-app-shell-*``_MainContentSurface_` 前缀 |
15+
| 26.803 | Owl composer 换成 `_ComposerLayoutBody_` / `_ComposerLayoutFooter_` | #359,追加前缀别名 |
16+
| 26.814 | 输入框壳迁到 `_ComposerLayoutRoot_`,旧别名误绑到内层 footer | #372 / #373,v1.5.15 + v1.5.16 |
17+
| 26.818 | composer 底部白框、链接与 Mention 分色、消息透明度 | #377 / #378,v1.5.16 |
18+
19+
两周内被迫连发 v1.5.13 / 14 / 15 / 16。每一次修的都只是**几个字符串**,但交付粒度是**整个安装包**
20+
用户必须重新下载 DMG / Setup.exe,而在这之前他们的皮肤是坏的。#373 里有用户等不及,
21+
自己打包 `CodexDreamSkin-Setup-v1.5.15.zip` 传到 issue 里让别人下载——这既是安全事故风险,
22+
也是"发版粒度不对"最直白的证据。
23+
24+
结构性结论:**选择器契约的变更频率由上游决定,不该由我们的发版节奏决定。**
25+
运行时其实已经具备解耦条件——安装后的 injector 是在运行时从磁盘读
26+
`assets/selectors.json` 的(`macos/scripts/injector.mjs``windows/scripts/injector.mjs`),
27+
它只是没有第二个来源。
28+
29+
## 契约
30+
31+
### 接口
32+
33+
```
34+
GET https://api.dreamskin.cc/v1/compat/profile
35+
?platform=macos|windows
36+
&client=<客户端语义版本>
37+
&codex=<探测到的 Codex 版本,可缺省>
38+
```
39+
40+
响应(`application/json`,无重定向,走现有 `BoundedCommunityHTTPClient` 的有界读取):
41+
42+
```jsonc
43+
{
44+
"schema": "dreamskin-compat-profile/1",
45+
"revision": 7, // 单调递增;客户端只接受 > 本地已缓存值
46+
"issuedAt": "2026-08-27T00:00:00Z",
47+
"expiresAt": "2026-09-26T00:00:00Z", // 过期即整份作废,回落到内置合同
48+
"minClient": "1.5.16", // 低于此版本的客户端必须忽略本档案
49+
"selectors": [ /* 与 tools/selectors.json 的 selectors[] 同构 */ ],
50+
"css": "", // 可选,必须整体通过 dreamskin-safe-css/1
51+
"signature": "ed25519:…" // 对上面全部字段规范化后的签名
52+
}
53+
```
54+
55+
### 客户端合并规则
56+
57+
1. **启动时异步拉取,永不阻塞换肤。** 拿不到、超时、解析失败 → 用内置合同,静默继续。
58+
2. **fail-closed 校验顺序**:HTTPS 固定 origin → 有界读取 → 签名 → `schema``revision`
59+
单调 → `expiresAt` 未过期 → `minClient` 满足 → 每条 selector 的 `key` 在内置合同里存在。
60+
任何一步不过,整份丢弃,不做部分应用。
61+
3. **只允许覆盖 `selector` 字符串**,不允许新增 key、不允许改 `tier`、不允许改 `required`
62+
降级面因此是有界的:最坏情况等于"某个锚点选不中",也就是今天已经能处理的 L2 缺失。
63+
4. **`css` 必须整份通过既有 `dreamskin-safe-css/1` 白名单**,与社区主题走同一个验证器。
64+
验证失败 → 丢弃整份档案(不是只丢 CSS)。
65+
5. **绝不接受脚本、URL、文件路径、命令。** 这是硬边界:档案是数据,不是代码。
66+
服务端也不得把用户可控内容拼进档案。
67+
68+
### 服务端
69+
70+
- 档案由维护者手工发布,进 git,走和迁移一样的部署流程;**不接受任何自动生成或用户投稿**
71+
- 私钥不在仓库、不在 VPS 的 web 进程里;签名在本地完成,服务端只分发已签名的静态 JSON。
72+
- 边缘可缓存(与 `/v1/themes/*/preview/thumbnail` 同策略),但 `revision` 变更必须能在
73+
分钟级生效,缓存 TTL ≤ 5 分钟。
74+
75+
## 与现有机制的关系
76+
77+
- `tools/selectors.json` 仍是**唯一可编辑源**,档案由它导出,不新增第二处手写选择器。
78+
- `tools/check-selector-provenance.mjs` 已经强制:改选择器就必须同步 `verifiedAgainst`
79+
导出档案时应带上同一份 provenance,让 doctor 能报告"当前生效的合同来自哪里、验到哪个版本"。
80+
- 档案不替代发版。启动器 / CDP / PowerShell / Swift 层面的问题(#235#363#378 第 1~3 节)
81+
仍然只能靠发版修——档案覆盖的只有 DOM 漂移这一类,而这一类恰好是最频繁的。
82+
83+
## 未决问题
84+
85+
1. 签名密钥的轮换与吊销策略(至少要能在客户端内置多个可信公钥)。
86+
2. Doctor / 状态面板如何展示"正在使用远端档案 rev N",让用户报 issue 时能说清。
87+
3. 是否需要用户可关闭的开关(默认开、可关,还是默认关)。倾向默认开 + 可关,
88+
因为默认关等于没做。
89+
4. 档案与 `runtime/dom-fixtures/*` 的关系:远端换了选择器之后,本地 fixture 会立刻失真,
90+
需要一条"档案里的选择器必须在至少一份 fixture 上有命中记录或显式标注为未覆盖"的规则。

macos/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
### 修复
1212

13+
- 修复客户端在已经是最新版时仍然提示「发现新版本」、且「当前版本」显示为旧版本号的问题。更新检查脚本是靠读取自身同级的 `VERSION` 来判断"你正在运行哪个版本"的,而客户端此前优先执行**已部署引擎**里的那份副本——引擎是在启动后异步安装的,并��只要 Codex 正在运行,安装就会被直接拒绝(`Close Codex before installation so config.toml cannot be rewritten while the app is saving it`)。这个拒绝一旦持续,App 已经是新版本、引擎还停在旧版本,于是更新检查把旧版本号当作"当前版本"上报,客户端就会对着自己正在运行的这个版本弹出更新通知。现在更新检查一律优先使用 App 包内的副本(已部署引擎仅作为兜底),因为只有 App 包不可能是陈旧的。
14+
- 修复 Codex 退出后 injector watcher 仍以每秒约 2 次的频率轮询已消失的 CDP 端点、导致系统明显卡顿的问题(#218)。目标发现的退避上限此前是固定 500ms,无论端点已经消失多久;现在短暂中断(重载、导航、renderer 重启)仍按原来的快速节奏重连,只有在连续失败 10 秒后才升到 5 秒、60 秒后升到 30 秒。空闲 10 分钟的轮询次数从约 1200 次降到 55 次,1 小时从 7200 次降到 155 次。错误日志的写入间隔同步跟随退避,不再固定每 2 秒往 `injector-error.log` 写一行。未采用"直接停掉 watcher"的方案——那会让 Codex 重开后无法重新上色(参见 #200)。
1315
- 完整补齐 Codex 26.818 主题兼容(#373,感谢 @QingYe-05 的 Windows 实机源码证据):清除 sticky composer 的两层原生渐变,约束 Markdown 宽表,映射真实用户气泡,并改善流式思考、命令详情、新版动作按钮、横向壁纸和顶部栏的可读性;同时移除叠在原生界面上的固定品牌/状态伪文案。
1416
- 修复新版 Codex 把输入框壳迁移到 `_ComposerLayoutRoot_` 后,Dream Skin 误把 `_ComposerLayoutFooter_` 标记为 composer、导致主题输入框样式只落在底部工具栏的问题;同时排除 `/avatar-overlay` 与 Pet composition surface,并在发现旧注入时执行移除与验证,避免主题壁纸污染透明 Pet 窗口形成矩形背景。
1517
- 修复透明或极端显式强调色下按钮文字对比度不稳定的问题:共享渲染器现在按真实 composer 面板表面和最坏背景计算前景色,并正确处理 alpha 与 RGB 夹取(#351)。

macos/assets/selectors.json

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,55 @@
22
"schema": "codex-dream-skin-selectors/1",
33
"description": "双端统一选择器契约。scope 与 required 来自双端 DOM 快照实测;shell-main 与 header-tint 另由 2026-07-31 Codex 26.727.40816 真实 macOS renderer 复核。doctor / CI 应逐条 querySelector 并按 tier 判级:L1 缺失=皮肤主行为受损,L2 缺失=精修静默降级。",
44
"verifiedAgainst": {
5-
"date": "2026-07-31",
6-
"chromium": "150.0.7871.124(双端快照);150.0.7871.182(macOS 真实 renderer 复核)",
7-
"codexVersionMac": "26.715.61943(历史快照);26.727.40816(真实 renderer 复核)",
8-
"codexVersionWindows": "版本号未记录;bundle hash 与 mac 不同(ModelPickerTrigger mac=1fm6a win=1w4ob),确认存在版本错位",
9-
"fixtures": [
10-
"runtime/dom-fixtures/codex-dom-fixture-darwin-watch.json (15 状态)",
11-
"runtime/dom-fixtures/codex-dom-fixture-win32-watch.json (7 状态)",
12-
"Codex 26.727.40816 signed macOS app, live app:// renderer"
5+
"date": "2026-08-27",
6+
"codexVersions": [
7+
{
8+
"version": "26.818",
9+
"platform": "windows",
10+
"evidence": "reporter: #378 本机已验证补丁与 #373 @QingYe-05 的实机源码证据;v1.5.16 据此扩展 composer / Markdown / message 绑定,维护者未做本机 renderer 复核"
11+
},
12+
{
13+
"version": "26.814.41407",
14+
"platform": "macos",
15+
"evidence": "reporter: #373 @GreenLv 的 loopback CDP 只读 DOM 与样式读数,确认 _ComposerLayoutRoot_ 是真实输入框壳、_ComposerLayoutFooter_ 只是其内层 footer"
16+
},
17+
{
18+
"version": "26.814.5517.0",
19+
"platform": "windows",
20+
"evidence": "reporter: #376 复现环境;该版本设置页外观切换后的混色问题仍未修复,settings/appearance 路径未验证"
21+
},
22+
{
23+
"version": "26.803.10989.0",
24+
"platform": "windows",
25+
"evidence": "reporter: #363 实机探测(Chromium 151.0.7922.76);受管 --user-data-dir 下 CDP、app://-/index.html renderer 与真实主题注入验证全部通过"
26+
},
27+
{
28+
"version": "26.803",
29+
"platform": "both",
30+
"evidence": "reporter: #359 Owl composer 布局报告;_ComposerLayoutBody_ / _ComposerLayoutFooter_ / _ComposerHomeUtilityBar_ 前缀别名已并入合同"
31+
},
32+
{
33+
"version": "26.727.40816",
34+
"platform": "macos",
35+
"evidence": "maintainer: 已签名 macOS app 的 live app:// renderer 实测复核(Chromium 150.0.7871.182),shell-main 与 header-tint 逐条 querySelector"
36+
},
37+
{
38+
"version": "26.715.61943",
39+
"platform": "macos",
40+
"evidence": "fixture: runtime/dom-fixtures/codex-dom-fixture-darwin-watch.json(15 状态,Chromium 150.0.7871.124)"
41+
},
42+
{
43+
"version": "unrecorded",
44+
"platform": "windows",
45+
"evidence": "fixture: runtime/dom-fixtures/codex-dom-fixture-win32-watch.json(7 状态);bundle hash 与 mac 不同(ModelPickerTrigger mac=1fm6a win=1w4ob),确认双端存在版本错位"
46+
}
1347
],
14-
"verdict": "双端运行时继续共用一份 selector/CSS 合同。26.727.40816 将主表面和顶栏的旧语义类改为 CSS Modules,同时提供稳定 app-shell data 属性;合同保留旧类并优先增加稳定属性。Windows 26.727 仍需真实 renderer 验收。"
48+
"verdict": "双端运行时继续共用一份 selector/CSS 合同。26.727.40816 起主表面与顶栏的旧语义类改为 CSS Modules,26.803~26.818 又把输入框壳迁到 _ComposerLayoutRoot_;合同的做法始终是保留旧锚点、追加稳定属性或模块名前缀,绝不匹配完整 hash。注意 26.803 之后每一条证据都来自报告者,维护者没有对应平台的本机 renderer 复核——不要把它们当成与 26.727 同等强度的验收。",
49+
"gaps": [
50+
"Windows 26.727 之后至今没有维护者本机 renderer 复核",
51+
"#376:26.814.5517.0 上 Codex 设置→外观切换深浅色后主题混色,settings/appearance 路径的锚点行为未验证",
52+
"#371:Windows 顶部与底部区域配色与主区不一致,尚未确认是 header-tint 未命中还是原生层未被覆盖"
53+
]
1554
},
1655
"appearanceSignal": {
1756
"dark": "html.electron-dark",

0 commit comments

Comments
 (0)