Skip to content

Commit 783e231

Browse files
committed
fix: consolidate local session recovery and space-safe notifications
Preserve canonical replacement names, foreground-priority maintenance, deletion isolation, bounded transport health checks, native rollout repair and disk-full append recovery. Keep quarantine bytes when replacement completion is uncertain. Use quiet macOS incident notifications and read-only disk-pressure monitoring. Add regression coverage and dated validation reports; retain local raw evidence outside version control. This commit does not reinstall any production runtime.
1 parent e05409e commit 783e231

76 files changed

Lines changed: 5560 additions & 178 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
/*.sqlite-shm
77
/*.sqlite-wal
88
*.prof
9+
*.profraw
910
*.test
1011
.DS_Store
1112
.worktrees/

‎CHANGELOG.md‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,47 @@ pre-1.0, so a minor version may contain compatibility changes.
66

77
## [Unreleased]
88

9+
### Fixed
10+
11+
- Foreground session opens can ask registered background maintenance to yield
12+
and retry without bypassing writer locks. Copy-on-write budget checks reuse
13+
a shared inventory instead of scanning the store while holding file locks;
14+
current free space is still checked for each operation.
15+
- A failed deletion cleanup no longer blocks unrelated managed-session reloads.
16+
macOS provenance-only metadata changes are accepted only when the original
17+
deletion proof recorded that metadata form; unknown materials remain intact.
18+
- Transport health probes have one bounded deadline. Slow inventory refreshes
19+
no longer manufacture a session-access outage; actual outstanding FSKit file
20+
requests retain the ten-second incident threshold and bounded diagnostics.
21+
- Failed or short appends roll back only that append, and storage budget errors
22+
map to ENOSPC. A verified 256 KiB read-lease reserve can be released under
23+
disk pressure without bypassing reader/collector coordination.
24+
- macOS incidents use quiet, deduplicated system notifications instead of
25+
automatically activating a fault window. Recovery removes notifications;
26+
denied permission does not fall back to a modal window. Read-only disk-space
27+
monitoring still works when status files cannot be written.
28+
- Daemon live updates accept a bounded `--takeover-timeout` for slower stores;
29+
the default remains nine seconds and failed takeovers retain rollback.
30+
- Canonical managed rollouts now support same-namespace backup renames and
31+
native temporary-file replacement used by `jstarctl codex rebind`. Durable
32+
names preserve backups and old readers across replacement/reload; stable
33+
replacements fold in the background, respect the automatic-folding policy,
34+
and do not get enrolled again under their logical Codex thread ID.
35+
- A damaged active rollout no longer keeps the mount down. After the disk
36+
filled, about 10 KB of binary memory was left inside one rollout; the
37+
startup preflight rejected the file, `fs serve` exited, and launchd restarted
38+
it every two seconds with no mount, so Codex hung on a dead
39+
`~/.codex/sessions`. The startup preflight now repairs such files: damaged
40+
records and an unfinished final record are moved to
41+
`<native-root>/.codexfold-native-quarantine/` with a manifest that records
42+
their offsets and SHA-256, and the rollout is rewritten through an atomic
43+
rename of a clone (APFS clones share unchanged blocks, so this needs little
44+
free space). Mode and modification time are kept. A complete final record
45+
that only lacks its newline gets the newline. Anything that cannot be
46+
repaired is logged and served unchanged instead of aborting startup.
47+
- `fs validate-native --repair` applies the same repair by hand while the
48+
filesystem service is stopped. Without the flag the command stays read-only.
49+
950
## [0.4.0-beta.2] - 2026-10-01
1051

1152
### Added

‎README.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ The transparent filesystem preview has these explicit boundaries:
3838

3939
See [the Linux FUSE3 validation](docs/validation-linux-fuse3.md) and [the macOS canary validation](docs/validation-macos-canary.md) for the evidence boundary. The default build remains storage-only; platform mounts require explicit build tags and installed host prerequisites.
4040

41+
### Unreleased local fixes
42+
43+
Development since the release adds foreground-priority storage access,
44+
durable backup/replacement names, isolated deletion cleanup, native-rollout
45+
repair, bounded transport health checks, disk-full append recovery and quiet
46+
macOS system notifications. These changes are not in the published archives.
47+
The dated [replacement compatibility](docs/rebind-compatibility-2026-10-16.md),
48+
[incident recovery](docs/incident-recovery-fix-2026-10-16.md), and
49+
[notification and disk-space](docs/notification-space-safety-fix-2026-10-17.md)
50+
reports distinguish the exact locally installed subsets from the complete
51+
development source. Combining those changes does not by itself validate a new
52+
production binary; this source update does not reinstall the running service.
53+
4154
## Install
4255

4356
Install the versioned preview with Go:
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# 2026-10-16 删除清理阻断与健康检查超时修复
2+
3+
## 生产结果
4+
5+
2026-10-16 15:25(Asia/Taipei)完成 daemon 热更新,15:26 完成 supervisor 更新。没有退出/重启 Codex、ChatGPT 或它们的 app-server,没有卸载原挂载,没有修改自动折叠策略。
6+
7+
- 版本:`v0.4.0-beta.2+recovery-fix`。
8+
- 安装及运行 daemon SHA256:`1b435bfde05365408f192d144d78b35721ded1fcff5fac339bf489da750c0670`。
9+
- 安装 helper 有 Apple Development 签名,identifier 为 `vip.jstar.codexfold.helper`,TeamIdentifier 为 `Y987FUR837`。
10+
- daemon PID 从 1140 换为 39946;supervisor 从 2445 换为 40517。新 supervisor 的 executable 映射为已更新的 helper。
11+
- 原挂载 device/inode 仍为 `805306372 / 2`;热更新命令也验证了 FSID 保持。
12+
- 三条卡在 `renamed` 的删除清理均已自然推进到 `complete`,各自 staging 子目录为空。没有重写生产 receipt,没有强行删除 staging。
13+
- `status/managed.json` 从 14:46 起持续 `recovering` 恢复为 `healthy`,新 registry 已在 15:25:40 发布;多轮后续观察仍健康。
14+
- 自动折叠保持 `enabled=false`,policy 文件 SHA256 与更新前一致。
15+
16+
## 已修复的机制
17+
18+
1. 删除 cleanup 的失败不再使整个 managed reload 提前返回。解除已删会话路由失败仍阻止发布;后续磁盘清理失败单独记录,不再阻断无关会话刷新。
19+
2. macOS 的 `com.apple.provenance` 变化不再被误报为对象类型或权限变化。只允许此前采集器已确认的 provenance 元数据形态;inode、mode、内容 SHA256、未知文件、其他 xattr、ACL、BSD flags 等保护仍保留。原 receipt 自身的整体摘要验证没有取消。
20+
3. 健康检查的 Unix socket 连接、hello 和 Ping 共用两秒 context;完整帧读写有 deadline。超时后关闭连接,防止迟到响应与后续请求错配。
21+
4. 真实 FSKit 请求超过十秒会保存限频现场 goroutine 栈,写到 `~/.codex/fold-store/service/logs/blocked-io.txt`。最多每分钟一次、固定文件覆盖、最多约 4MiB,不记录请求正文。
22+
23+
## 验证
24+
25+
精确候选以此前真实生产源码 `.tmp/rename-compat-20261015/source/` 为基底,只叠加本次十三个文件的差异。没有把工作区其他贡献者尚未部署的 native repair 等改动混入此次生产候选。
26+
27+
- 候选源码 `go test ./... -count=1 -timeout=240s` 全部通过。
28+
- 新增 timeout、provenance、cleanup isolation、stall diagnostic 测试的 race 检查通过。
29+
- 正确签名后的候选在真实隔离 FSKit 挂载上通过两个测试:原命名空间 replacement/reload/后台 fold/后端接管,以及未知删除材料保留时其他会话仍能 reload。
30+
- 生产五条重点会话的首 64KiB 和更新前固定 EOF 前 64KiB SHA256 均保持一致;允许活跃会话追加,不把追加误判为损坏。末轮读取耗时 1–74ms。这是固定区域内容验证,不是全库全字节一致性证明。
31+
- 曾报告 busy 的 `019ec5b9-44d4-7c82-805b-88df6e731993` 额外从生产路径顺序读到 EOF,230,591,060 字节,无文件系统错误。
32+
- app 内置会话读取接口成功取回 `01a03c6c-f052-76b3-bfe4-0ea30aa82904` 和 `01a07688-b3e6-7a20-802a-63fda3916afa` 的最近一轮。前一条 busy 会话通过该接口返回 `thread/items/list is not supported yet`,因此不把它记作 Desktop UI 打开成功,也不把该接口不支持误说成 FSKit busy。
33+
- 客户端与两个 app-server PID 前后完全一致。未占用鼠标,未向上述会话发消息。
34+
35+
## 证据位置
36+
37+
本地 `.tmp/incident-20261016/`:
38+
39+
- `findings.md`:部署前只读诊断及故障时间线。
40+
- `regression.log`、`race.log`、`signed-mounted.log`:候选测试。
41+
- `source/`、`codexfold`:本次精确候选源码与签名二进制。
42+
- `live-update.json`:热更新结果与旧二进制恢复路径。
43+
- `service-before.json`、`service-after.json`:生产 build 与后台 PID。
44+
- `production-before.json`、`production-after-final.json`:会话固定区域和客户端进程对照。
45+
- `busy-session-full-read.json`:busy 会话全文件顺序读取结果。
46+
- `deletion-receipts-before.tar`:三条原 purge receipt 的小型本地证据副本,不含整份会话备份。
47+
48+
## 边界与恢复
49+
50+
14:17 那次最早的客户端阻塞发生在上述三条删除任务之前,旧进程也没有现场栈,不能声称已证明它与后来 cleanup 错误是同一个原因。本次修复了可复现的健康检查无限等待和生产持续 reload 失败;不能据此保证所有未知阻塞永不出现。
51+
52+
若再次阻塞,优先保留 `blocked-io.txt`、当前状态文件及日志,再判断具体卡住的调用。不要通过重写删除凭据、无条件移除 staging、重启客户端来代替诊断。
53+
54+
本次旧 helper 保存在:
55+
56+
`~/Library/Application Support/CodexFold/Recovery/live-update-20261016-072532/codexfold-previous-1556979473`
57+
58+
旧版 SHA256 为 `50c491156e673a9ad291f653b55f8150e416f99acedfd5601d7aa39bf327d101`。它保留当前 namespace 兼容能力,可作为此次变更的紧急回退候选,但包含此次修复前的健康检查和 cleanup 缺陷。回退使用现有 `fs service update-daemon-live <candidate> --apply --json`,先确认自动折叠闲置、当前 daemon/supervisor/mount 可识别且健康;不要覆盖正在执行的二进制或降到不识别当前 namespace 的旧版。需要 supervisor 回退时只终止经 lock 和命令行核实的那个 supervisor,由原 host 接管,不能使用会卸载挂载的 stop/restart 命令。
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# CodexFold 故障通知与磁盘满修复(2026-10-17)
2+
3+
## 本机行为
4+
5+
- 故障默认使用 macOS 系统通知,无声音,不自动打开故障窗口、不抢焦点、不让 Dock 紧急跳动。只有用户点击对应通知才打开详情。
6+
- 通知首次使用安静的 provisional 授权,不主动弹权限对话框;通知权限被拒绝时仅保留状态栏和历史,不回退为模态弹窗。
7+
- 同一故障只发送一次;故障恢复后移除通知。UI 重启后观察到可信健康状态,也会清理上一轮已记录的通知。点击旧通知不会打开另一场故障。
8+
- 这是本机静默通知策略:provisional 通知通常进入通知中心,不保证出现横幅。最终显示仍受系统权限、勿扰和系统通知服务影响。
9+
10+
## 修复内容
11+
12+
1. 完整状态刷新超过 20 秒,不再直接被当作会话访问故障;实际错误仍保留到成功观测,不能由心跳抹掉。
13+
2. 跟踪实际未完成的 FSKit 文件请求,排除 Hello/Ping;请求持续超过 10 秒仍会报告问题,完成后报告恢复。
14+
3. 磁盘满时追加失败/短写,在独占写入锁内回退到本次追加前的 EOF,保留之前成功写入的内容,并向客户端返回失败供其重试。
15+
4. 每个存储目录仅保留 256 KiB 应急预留。读取所需锁文件遇到 ENOSPC 时释放预留并有界重试,不绕过锁,不删除未知材料;它不是会话备份。
16+
5. 存储预算不足向文件系统返回 ENOSPC;磁盘满造成的状态写失败日志按 30 秒限频。
17+
6. UI 通过只读 statfs 检查空间,即使状态文件无法写入也能识别空间耗尽;低于 16 MiB 进入故障判断,恢复到 64 MiB 后退出,避免低位反复闪动。
18+
7. 正常停止引发的 context.Canceled 不再发布新的 reload 故障。
19+
20+
物理空间耗尽时,不能承诺新写入成功;本次保证的是已验证场景中的原有内容保护、错误准确返回、读取应急能力以及释放空间后可重试。系统通知服务自己也可能因权限或系统故障无法展示,程序不会因此重新抢焦点弹窗。
21+
22+
## 已运行的验证
23+
24+
本地证据目录:仓库内 `.tmp/pressure-fix-20261017/`(原始运行材料不随公开源码上传)。
25+
26+
- `regression-final.log`:候选后台全量 Go 回归通过;后续小改另有 `release-cli.log` 与 `reload-final.log`。
27+
- `race.log`:涉及请求健康、状态、追加、预留空间的 race 测试通过。
28+
- `release-mounted.log`:签名后台真实隔离挂载、reload、fold、热接管和 cleanup 失败场景通过。
29+
- `full-volume-vfs.log`:真实 APFS 写满至 ENOSPC,已有/新读取、失败追加回退、释放空间后重试/重开通过。
30+
- `full-volume-mounted.log`:真实 FSKit 挂载 + 满 APFS;原生和已折叠文件可读,失败写入未改变已有落盘内容,释放空间后通过挂载路径追加成功。
31+
- `notification-tests-final.log`:104 项测试全部通过,0 failures / 0 skipped。覆盖通知去重、点击才打开、旧通知隔离、重启后恢复清理,以及真实小 APFS 满盘时无状态写入仍生成通知。
32+
- `notification-channel.log` 与 `notification-status.json`:本机生产 App 的系统通知通道。系统 usernoted 已记录 provisional 授权、接收请求和投递至通知中心;不是把 mock 测试当作系统投递证明。
33+
- `notification-status-after-cleanup.json`:测试通知已撤回,delivered_requests 为空。临时 256 MiB APFS 镜像已卸载并删除。
34+
35+
这不等于所有未知性能问题都已永久消失。后台此前仍有偶发连接超时记录;本次移除了慢扫描制造的假故障,并保留真实长请求的检测。
36+
37+
## 上线与可恢复性
38+
39+
后台从上一版实际生产源码 `.tmp/route-follow-20261016/source` 派生,只叠加本次修复;精确候选在 `.tmp/pressure-fix-20261017/source`。没有把工作区其他未上线变更一起部署。
40+
41+
当前后台版本:`v0.4.0-beta.2+space-safe`,SHA256:
42+
43+
`3faef9d1eb28b9a8d28c448a7c0c2be9586c5e96bc1ecf7499c3c4798a672353`
44+
45+
第一次使用默认 9 秒热接管未确认成功,已自动回退;第二次用 30 秒接管成功。该参数不改变 10 秒故障通知阈值。部署证据见 `live-update-30s.json`。
46+
47+
上一个后台保存在:
48+
49+
`~/Library/Application Support/CodexFold/Recovery/live-update-20261017-150638/codexfold-previous-1431727526`
50+
51+
UI 仅替换主程序、常驻监测程序及匹配签名资源,不改变 FSKit 扩展。监测程序须使用与 SMAppService 登记一致的 signing identifier:`vip.jstar.codexfold.fskitprofileprobe.incident-monitor`,Team `Y987FUR837`。首次刷新后系统曾保留旧身份,再次刷新自身注册后已实际运行;不能仅凭 register() 成功判断常驻正常。
52+
53+
更新前后生产挂载 device/inode 均为 `805306372 / 2`,扩展 SHA256 保持:
54+
55+
`2a897911ffb411fd12002193848af503ab7ce5703c7ec55ab4c6116c9dff704d`
56+
57+
未退出、重启或信号 Codex/ChatGPT/app-server,未卸载生产挂载。五条重点会话的首 64 KiB 与更新前 EOF 处 64 KiB 校验一致;活跃会话可继续追加。这是定点连续性检查,不是对全部历史数据逐字节重新认证。
58+
59+
自动折叠开关仍为关闭,策略文件 SHA256 未变:
60+
61+
`61866193bc35953e23b957371184eb7fc4813fd5d1cb1503c18b38c619c74d74`
62+
63+
UI 恢复材料位于 `Recovery/ui-pressure-20261017-*`,本轮最终替换前备份为 `Recovery/ui-pressure-20261017-notifications-final`。恢复必须成组恢复主程序、监测程序和 CodeResources,不能只换其中一份签名文件。后台恢复或挂载生命周期操作不在本文中自动执行,仍需当次明确授权。
64+
65+
最终生产检查:后台 PID 51231、supervisor PID 51931、菜单栏 PID 75240、monitor PID 75244 均实际运行;service status 的 daemon_running / supervisor_running / mount_healthy / build.healthy 均为 true。PID 是本次快照,不作为下次操作目标。客户端原 PID 2782 和 3316 及启动时间保持;进程清单另出现 computer-use 的 `cua_node/bin/node_repl` 派生的一个 app-server,因此 `processes_unchanged` 为 false,不能把完整清单相同作为结论,`original_processes_intact` 才是原进程未变的定点检查。

0 commit comments

Comments
 (0)