Skip to content

Commit 6d611d6

Browse files
jstar0claude
andcommitted
feat: make automatic folding work end to end on a real corpus
The transparent session filesystem could fold, but nothing survived contact with a real store: a single refusal anywhere in a cycle left the system unable to make progress again, and none of it healed on its own. Folding 2,800 sessions surfaced five separate ways to get permanently stuck, each of which silently blocked every later cycle. Enrollment, in the order they bite: - A staged canonical snapshot that already existed was refused even when it was byte-identical to its source, so a migration that failed after staging could never be retried. That stranded 237 sessions folded into the pack with their originals still on disk — the duplicate 884 MB behind "Space saved: Zero KB". - One failed migration returned out of the cycle, skipping reclamation, while the pack build earlier in that same cycle had already written a new generation. One stuck session meant the store grew every cycle and never shrank. Reclamation now runs regardless; only deleting a user's original waits for a clean cycle. - Every candidate was priced against the storage budget, and each price is a full store scan — before the batch limit was consulted, so a batch of five still scanned the store ~2,880 more times. Planning went from 0.42s to over 200s and was killed, which is why the Host reported "0 remaining" and stopped folding entirely. - A pack build refused for free space left manifests pointing at unpacked objects. The doctor reported it, the gate then blocked every candidate, and nothing repacked: one tight disk locked the system permanently. The cycle now repacks once before closing that gate. - Batch size is measured rather than assumed. A cycle rebuilds the whole pack whether it folds one session or two hundred, so the batch is sized from the observed rebuild and per-session cost to keep that toll a quarter of the work, with a manual override in the Auto fold pane. The Host used to write a fixed 1 back whenever any switch was flipped, which quietly reset a tuned batch and made every later cycle pay a full rebuild per session. Reliability of the mount itself: - A mount-table probe that ran out of time was reported as recovery. It proves nothing — MNT_NOWAIT contends with unrelated volumes and with the scheduler, and a mount that actually went away is reported by a lookup that completes. The production store logged 12,776 of these, each surfacing as a "needs attention" alert that healed itself within half a minute. The supervisor now asks the backend directly and keeps its last conclusive reading. - Status publication no longer waits on reconciliation, so a supervisor that is merely busy stops reporting itself as gone, and the steady-state mount-table read runs once every five intervals instead of every one. - A status channel that stops advancing is a monitoring read gap, not an outage: it degrades the indicator and only escalates once sustained. A component that reports failure still alerts at ten seconds. - Read-ahead extends its horizon only once a read advances into the adjacent block. Unconditional look-ahead charged every lookup the price of a scan, which once every session is pack-backed turns listing sessions into rereading the store. Verified against the live store: 205 sessions whose originals had been retired read back byte-identical to the SHA-256 recorded at retirement, 666 MB in total, every JSONL record still parseable. Repo hygiene: drop a stale 17 MB build of codexfold committed by accident, ignore Xcode build output, and extend the sanitisation check to docs so a maintainer's home path cannot ship in them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent aa68bff commit 6d611d6

79 files changed

Lines changed: 11541 additions & 601 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.

‎.cfbin‎

-16.2 MB
Binary file not shown.

‎.gitignore‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/codexfold
22
/codexfold.exe
3+
/.cfbin
34
/dist/
45
/*.sqlite
56
/*.sqlite-shm
@@ -8,5 +9,10 @@
89
*.test
910
.DS_Store
1011
.worktrees/
12+
/.tmp/
13+
14+
# Xcode build products and derived data
15+
platform/darwin/fskit/build/
16+
DerivedData/
1117
xcuserdata/
1218
*.xcuserstate
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# CodexFold 外部 AI 真实验收与本机试运行指南
2+
3+
更新:2026-09-09。继续使用这一份文档,不再另开版本编号。目的是真实省盘、Codex正常工作、异常准确提醒并恢复;不是公开分发审计。本文不构成生产安装授权。
4+
5+
## 1. 当前做到哪里
6+
7+
先读 [修复记录](repair-status-2026-09-09.md) 最上方最新结论,那里保存具体数据与证据路径;旧章节仅是历史经过。接手时核对live PID、二进制SHA、mount、store和SQLite,不把历史PASS当成当前状态。
8+
9+
已经真实完成:自动fold/pack/migrate及原始snapshot、重复loose、旧pack回收;较大真实历史读取和UI fork;同一Desktop后台接口archive/unarchive/fork/delete;第三方API多轮调用;回收后base不变、delta续写;共享child真删除且parent不变;有delta的会话回退为普通文件后同一Desktop继续回复。
10+
11+
性能已有实测改善:3000文件重复ReadDir由356–573ms降到22.8–24ms,完整枚举中位28.605ms、native8.36ms;首次仍约645ms。35.8MB managed warm读取接近原生,append+fsync p95约4.98ms。这些不是长期或所有负载零开销保证。
12+
13+
后续已完成module退出恢复及真实续写、daemon重启后删除不复活、全namespace退出回退、后台重开普通目录中的Desktop并续写、压缩并发读取测量。普通目录中的Codex分页历史迁移有字节完全一致的对照;具体结果见修复记录23:32节。
14+
15+
并发测试还发现并修复活跃writer阻塞loose回收时的误报及漏重试:占用时等待,释放后无新fold也回收,保留原有删除证明。尚未完成原生提醒窗口真实显示/恢复/去重、UI点击闭环、全部故障矩阵、长时生产规模负载。最新Host和busy回收代码的安装状态分别确认。
16+
17+
生产没有安装本轮修复。9月9日22:52附近30秒累计采样,旧生产daemon约41.25%单核、隔离daemon约1.07%;数据量和工作负载不同,不能直接当优化倍数,生产热点仍要查。
18+
19+
证据主要在 `.tmp/desktop-real/evidence`、`.tmp/repair-space-20260909/evidence`、`.tmp/repair-directory-enabled-20260909/evidence`;当前源码回归结果见修复记录。保留已完成的结论与未完成的缺口,不重新做全部无关审计。
20+
21+
## 2. 当前操作边界
22+
23+
- 生产Codex/CodexFold、数据和凭据仅只读。生产安装、替换、重启、namespace切换另需批准;不关闭SIP、不重置全部扩展。
24+
- 测试用本机第三方endpoint/key的隔离副本,模型`gpt-5.6-terra`,纯API,不用OAuth或Cockpit。密钥不放命令参数、日志或报告。
25+
- 独立Desktop保留原版app.asar;独立bundle ID、CODEX_HOME、Electron data;克隆移除生产URL schemes,防止链接打开生产任务。
26+
- 当前主代理自己完成,不用subagent;不抢鼠标、不置前、不发键盘、不点菜单。computer-use只后台读取/截图;同一Desktop真实后台接口可以验证语义行为,但不是点击UI证据。
27+
- 当前原生告警代码会激活窗口;不干扰用户约束下不得触发它。此项保留未执行,不能隐藏窗口后声称弹窗通过。
28+
- archive可恢复,delete真删除。异常、删除、回退只作用于已标记的隔离副本;每次用PID/启动时间/命令/store/mount共同确认目标。
29+
- 保留已有dirty改动,不自动commit/push。安装、sudo或系统设置的授权按本次用户范围和工具要求处理,不由历史文档扩大。
30+
31+
## 3. 固定候选和入口
32+
33+
```text
34+
Verification App: ~/Applications/CodexFoldVerification.app
35+
App ID: vip.jstar.codexfold.fskitacceptance108
36+
Module ID: vip.jstar.codexfold.fskitacceptance108.module
37+
Module path: Contents/Extensions/CodexFoldFSKitModule.appex
38+
FS type: codexfoldverification
39+
```
40+
41+
108是稳定身份后缀,已测试源码build109。系统授权已完成;不重复创建身份或无故重注册。重建/替换bundle可能重置扩展授权,Host-only修复避免触及运行中的module。
42+
43+
构建器为`scripts/build-codexfold-verification-app.sh`。先查当前`--help`和生成manifest,核对签名、Info.plist、live module路径。构建不是安装,helper/Host/module分别绑定SHA。
44+
45+
生产App是`~/Applications/CodexFoldFSKit.app`,helper是`~/Library/Application Support/CodexFold/codexfold`。生产namespace已active,不套用“尚未接入”的旧初始化步骤。PID/挂载/managed清单/policy应现查。
46+
47+
新run root要短、私有、与生产分离;Desktop的`$CODEX_HOME/ipc/ipc.sock`短于104字节。推荐当前runner,不混用旧Cockpit preparer的layout:
48+
49+
```sh
50+
cd <repo>
51+
scripts/run-real-fold-acceptance.sh --help
52+
scripts/run-real-fold-acceptance.sh \
53+
--frontend native-fskit \
54+
--candidate-app ~/Applications/CodexFoldVerification.app \
55+
--fskit-type codexfoldverification \
56+
--model gpt-5.6-terra --performance \
57+
--run-root /private/tmp/cf-real-UNIQUE
58+
```
59+
60+
UNIQUE换为本次唯一值,调用前目录不存在。可用`--session ID`固定真实归档源切片。数据在`$RUN_ROOT/work/codex-home`,证据在`$RUN_ROOT/evidence`。runner退出清理work和凭据。
61+
62+
helper、cwd、serve/supervisor控制日志放内置盘`/private/tmp/codexfold-acceptance-runtime.*`;退出时复制日志再清理。此前repo外置盘eject使旧测试代码映射和FD revoked,出现高CPU;不要重复该布局。数据盘丢失仍可能不可读,不能声称代码放内置盘消除了磁盘故障。
63+
64+
`--skip-resume`只用于无需模型的回归,必须记模型未执行。`--desktop-app PATH`要求独立Desktop,等待`evidence/desktop.done`;只在真实Desktop步骤完成后写done,不能绕过验证。首次onboarding可能依赖焦点;当前禁止置前,优先继续已初始化的隔离profile,不反复开新窗口。
65+
66+
手动接管时别让旧runner超时删除活跃store。核对身份后终止旧控制runner,由接手者负责现有进程、mount、策略和清理;不要把SIGSTOP的旧runner误CONT。
67+
68+
## 4. 认证:实测成功的组合
69+
70+
```toml
71+
model = "gpt-5.6-terra"
72+
forced_login_method = "api"
73+
cli_auth_credentials_store = "file"
74+
75+
[model_providers.main]
76+
# name/base_url/wire_api沿用本机可用的第三方provider
77+
requires_openai_auth = true
78+
# 无env_key
79+
```
80+
81+
隔离auth.json仅apikey模式和key副本,无OAuth tokens。上述`requires_openai_auth=true`使用文件认证,不是OAuth。保留env_key但进程无对应环境变量时,本机版本会报Missing environment variable,即使auth.json有效;runner已对齐无env_key的实测成功组合。
82+
83+
先用隔离短任务一次纯文本调用确认,再复用任务测试。不重复花费请求排查同一错误。按本次turn ID匹配task_complete,不能拿旧回复当成功。
84+
85+
后台证据来自目标Desktop renderer→electronBridge→该Desktop app-server,绑定隔离路径和进程;另起CLI属于CLI证据。fork后确认新child ID/SQLite;delete后确认行/路径消失。`thread/unsubscribe`不保证关闭rollout writer,本机实测归档才释放。
86+
87+
## 5. 真实会话矩阵
88+
89+
每项记录:动作前后时间、session/parent/child ID、SQLite route、size/SHA、base/delta、实际响应或截图、doctor。不要复制会话正文到报告。
90+
91+
| 行为 | 完成条件 |
92+
| --- | --- |
93+
| Desktop新建、连续多轮 | 每轮实际第三方请求成功,同一rollout追加、JSONL有效 |
94+
| 真实归档数据自动fold | 实际pack/migrate/reclaim完成,snapshot/重复loose退休,包含旧包和delta的allocated净下降 |
95+
| 回收后打开大历史 | 真实内容可读、可展开/搜索,记录打开/恢复时间,不只stat |
96+
| 回收后resume/append | base前缀不变、delta增长;普通append不产生全量backing |
97+
| native及managed归档/恢复 | SQLite/目录同步变化,SHA不变,再次可打开 |
98+
| native及managed fork | child独立ID/inode和正确历史,父子分别续写互不修改 |
99+
| 共享pack回收 | 父子完整SHA不变、旧generation释放;reader lease暂留,释放后无新fold周期也能回收 |
100+
| managed真delete | SQLite/route/manifest/catalog消失、其他任务不变;后续fold/恢复/重开后不复活 |
101+
| 活跃writer | 迁移/回退被保护,合法append保持最新字节;不绕过锁 |
102+
| truncate/random write/乱序append/COW | 与普通文件语义对照,必要时才生成backing,非法状态不误发布 |
103+
| 开关与无新任务周期 | on/off/on正确,off取消后续步骤,空周期0/0,完成任务不残留waiting |
104+
| 单任务回退 | 有delta的managed完整恢复普通文件、SHA不变,原Desktop续写成功 |
105+
| 隔离Desktop重开 | 同一profile/数据库任务可恢复,不加载生产数据 |
106+
| 隔离组件重启 | daemon/module/supervisor恢复,不擅自重启Desktop,不改变生产进程 |
107+
| 全namespace回退 | 所有managed恢复普通文件再deactivate,目录/SQLite一致,停止测试FSKit后仍可读 |
108+
| Codex历史格式迁移 | legacy→paginated可以合法重写历史;同一原文件在普通目录的原版Codex作对照,不把不明SHA变化当正常 |
109+
110+
## 6. UI与自动策略
111+
112+
实际UI选项:检查间隔30m/1h/2h,稳定窗口1h/6h/24h,归档范围与自动fold开关。30s/1s只供隔离后端加速,不是UI选项。
113+
114+
一个任务工作进度0/6→6/6,最后一步是验证和回收;多任务读实际cycle_total,不硬编码0/5或旧公式。普通writer/reader占用显示waiting-reclaim,等待释放后下一周期重试,不当故障;未完成回收保持5/6,等待/idle不虚假动画。
115+
116+
验证点击→policy写入→backend重读→status→UI重读。后台写policy不能替代点击闭环。改interval重算next_check_at;stable_for不到不选;scope放宽仍保护writer。关闭工作周期取消后续任务,完成队列扣除已处理数。
117+
118+
状态栏正常不常绿、深浅色文字可读;保留用户认可布局,Liquid Glass不等于过透明。图表分别显示逻辑、真实物理占用、净节省和采样时间。导出/复制/Reveal/历史/设置持久化分别测,全部只改测试设置。
119+
120+
## 7. 故障与性能矩阵
121+
122+
记录before/fault/during/recovered及occurrence ID;恢复后实际读/hash,关键故障后在同一Desktop续写。手工status模拟只证明读取逻辑,不是实际backend恢复。
123+
124+
| 情况 | 预期 |
125+
| --- | --- |
126+
| 1–9秒不可用 | 自恢复、不弹持续窗、不借旧时间误算10秒 |
127+
| 连续超过10秒、再次发生 | 每次一个窗口,原因/影响/建议明确,恢复结案,第二次新occurrence |
128+
| daemon crash/挂起 | 对应恢复按实际服务配置生效;SIGSTOP不冒充crash |
129+
| socket/resource不可用 | 准确识别、恢复可用;普通目录不能冒充mount |
130+
| module/helper/supervisor退出 | 只恢复对应测试组件,身份/mount不变,生产不变 |
131+
| Host异常退出/用户Quit/双monitor | 单presenter;异常恢复和用户Quit区分;错过恢复的历史正确结案 |
132+
| policy缺失/损坏/version/symlink/FIFO | 非法policy停止自动工作并报原因;合法恢复后重读,不沿用旧值 |
133+
| status丢失/损坏/stale/sequence停滞/身份错 | 不继续假healthy;所有通道新鲜真实健康才清ack/结案 |
134+
| probe超时/并发或mount变化 | 时间/并发有界,不反复生成恢复任务,身份错保持错误 |
135+
| 空间不足/峰值预算超限 | 写入前拒绝,保留原文/base/delta,恢复空间后可重试,不占满实际磁盘演练 |
136+
| 睡眠/唤醒/磁盘消失 | 仅获准不打扰用户时真实演练,否则未执行;进程重启不替代断电 |
137+
| 删除与旧包恢复 | 删除不复活、共享数据不误删;legacy缺删除证明则保留但不阻断其他GC |
138+
139+
性能分别记录普通APFS基线、managed冷/热读、append+fsync、3000文件枚举、idle和压缩并发CPU,注明样本量/时长、首次与重复、p50/p95。native reference必须是真正普通文件,不能也走生产FSKit。高CPU先核对FD/代码映射是否revoked,再查热点,不以healthy代替读取。
140+
141+
## 8. 清理和交付
142+
143+
关闭隔离策略;释放writer并回退所有managed(包括fork);停止测试Desktop和supervisor、卸载测试mount、停止daemon,然后namespace deactivate。确认两个目录已是普通目录后才清理work及凭据,避免通过symlink误删挂载内容。保留小型脱敏证据和SHA。需要继续验证时明确保留对象和负责人,不留下无人管理的控制runner。
144+
145+
结果分已通过/失败/部分验证/未执行,写明根因和下一步。无需因截图sidecar缺失抹掉已经发生的语义动作,但不能补写虚假UI通过。beta/preview标签不是不能本机试用的理由;未测项也不能藏掉。
146+
147+
## 9. 本机生产试运行:单独批准后执行
148+
149+
前提:第5–7节必要行为补齐,明确剩余限制和用户接受范围。是本机几天试用,不是公开可分发审核。
150+
151+
1. 记录候选helper/Host/module身份、当前生产namespace/managed清单、policy、可用空间、SQLite及配置元数据。备份限必要路由和恢复信息,不无期限再复制全量session库。给出确切安装目标和回退方法,获得安装窗口批准。
152+
2. 用户结束或暂停工作,正常退出生产Codex,确认writer关闭,再走现有服务安装入口。已有namespace不重复初始化、不改第三方认证。用当前`fs service --help`、`fs namespace --help`和[维护指南](maintainer-guide.md)确认参数及dry-run。
153+
3. 先验证服务/mount和所有已managed完整可读,再打开Codex。初始自动策略关闭,先验证旧任务和新建,再启用用户选的间隔/稳定窗口/范围,不留隔离30s/1s配置。
154+
4. 首批真实归档fold后验证净allocated下降、恢复/续写/fork/归档/delete和CPU。异常时停止扩大处理,不为了观察而频繁弹窗。
155+
5. 试用时间灵活,不强制24小时或7天,不占固定7天备份。保留正在使用的reader generation与必要事务数据,已证明可删的重复对象及时回收。
156+
157+
应急处理:
158+
159+
- 读写正常、仅状态异常:关闭自动fold、保留mount/service、导出诊断,不自动重启Codex。
160+
- 单组件故障:既有恢复机制尝试恢复,超过10秒准确提示;实际读/续写后才算恢复。
161+
- 持续不可读:在获准窗口暂停Codex写入,先恢复当前service/mount;仍不稳定则完整回退所有managed为普通文件,再停用namespace,核对SHA/SQLite后恢复Codex。不能先卸载承载唯一数据的mount。
162+
- 回退空间不足:停止新增工作,明确所需空间,安全清理无关空间或迁到用户指定位置;不能删除唯一pack/delta腾空间。
163+
- 数据证明不一致:保留pack/delta/state/journal,逐条恢复;不伪造published.json、不覆盖旧证据。全部最新字节物化后才清理旧存储。
164+
165+
成功标准是用户真实工作持续正常、实际净省盘、性能可接受、告警准确,不是测试数量或文档编号。

‎docs/history-repair-2026-09-10.md‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# 两条生产任务历史读取修复
2+
3+
用户明确授权“自己验证、自己修”。本次授权只用于修复这两条任务及还原修复过程的附带状态;未重启Codex、app-server或CodexFold,未安装候选App/helper,未修改凭据。
4+
5+
## 已恢复
6+
7+
- `01a03c6c-f052-76b3-bfe4-0ea30aa82904`(MacALL):原始分段、数据库都有最新正文,但生产已加载任务返回空items;同一数据的离线原版app-server可正常读取。通过原生归档/恢复释放该任务加载状态后,生产最新三轮items从0/0/0恢复2/2/39。原文件8,370,070bytes,SHA与操作前一致,没有修改正文或手工修改该任务索引。
8+
- `01a07688-b3e6-7a20-802a-63fda3916afa`:当前分段`01a081d4-9850-75f0-99a9-55aa69519783`在2026-09-09 19:33:04与19:34:36连续出现ordinal8933。19:34:30–33有真实thread/resume日志;23:33:22生产日志报expected ordinal8934, got8933。索引停在byte18012332/ordinal8934,文件仍继续写入。
9+
- 先复制两任务完整21段历史及对应SQLite行到独立普通目录验证。对第二条从第二个8933起将2334条记录ordinal加1,逐条断言除ordinal外JSON完全相同,没有删除记录。生产应用前再次冻结最新副本,确认没有其他history_base引用该尾段。
10+
- 用原版Codex在副本中resume(无turn/start、无模型请求)重建索引;25个task_started全部匹配25条索引记录,无缺项。原生产索引只有10轮,后续15轮恢复。
11+
- 两任务暂时归档,检查生产app-server已释放目标writer、挂载和底层文件内容一致后,修复第二条原文编号及对应分段索引。SQLite用BEGIN IMMEDIATE,只替换该分段的thread_turns/thread_items/projection/realtime行,备份旧索引;原文写入保留inode,写后fsync及完整SHA校验。未改其他分段的正文。
12+
- 第二条原文36,201,120→36,201,121bytes;before SHA `866fb6247a783a2e05193c81a1d1ec89b093bd7306345c62357d0100e8ef10e9`,after SHA `870ef7e7191fd080b2ce3aaa211035270f8a89501dd03ef2001e82d46c9813d7`。索引推进到byte36201121/ordinal11268。
13+
- 恢复两任务未归档状态后,生产读取接口最新三轮为216/1/7条items,不再停在19:30。进一步每个任务读取连续三页、每页10轮,覆盖故障前后和旧分段。一个原本没有user/final item的中断轮次仍为空,不虚构正文。
14+
15+
## 状态与备份
16+
17+
Codex原生archive会连带归档未归档的spawn descendants。本次连带改变了26个历史子任务,已按父子关系与此次archived_at精确选择,全部恢复未归档;没有启动它们执行任务。两主任务也均archived=0。修复停止的运行任务没有自动重新执行。
18+
19+
持久回退副本位于:
20+
21+
`~/Library/Application Support/CodexFold/Recovery/history-20260910/`
22+
23+
包含原始分段、该分段旧索引、修复前后SHA、生产三页读取结果和26个子任务归档状态还原记录。目录权限700。离线诊断副本位于`/private/tmp/codexfold-history-repair-20260910`,不含生产凭据。
24+
25+
生产PID保持:Codex48614、app-server48783、daemon1013、supervisor1882。历史读取恢复已验证;尚未验证用户重新打开画面的实际渲染,也没有在生产任务插入测试消息。
26+
27+
## 仍需查明的触发原因
28+
29+
重复编号发生在恢复任务后,但尚未证明是Codex计数恢复缺陷、并发写入或CodexFold可见性参与触发。MacALL通过卸载任务状态恢复,不能据此认定其底层索引损坏。CodexFold同路径替换后的监听缺陷有独立失败实测,当前候选修复尚未完成真实挂载验证。恢复这次数据不等于防复发和CodexFold完整生产验收完成。

0 commit comments

Comments
 (0)