跳到正文

classic-vibe-mac

在浏览器标签页里写 C,按下 Build & Run,一台 System 7 的 Macintosh 就带着你刚编译出来的 68k 程序启动,程序就摆在桌面上。

Screenshot of classic-vibe-mac
编辑截图, 28 Sep 2026classic-vibe-mac ↗

这是什么

一个静态网页,同时是一套完整的经典 Macintosh 开发环境。你在 CodeMirror 面板里改 C,按下 Build & Run;页面用 Retro68 工具链的 WebAssembly 版本编译它,链接成 68k Mac 二进制,写进一个磁盘镜像,然后拿这个镜像启动 Basilisk II。一台 System 7.5.5 的桌面会带着那个程序出现,大约一秒。不需要安装、本机不需要工具链、没有后端。改一下源码再按一次按钮,Mac 就带着改动重启。

谁做的在 UNDRR(联合国减灾署)工作,常驻波恩。他 2011 年就注册了 GitHub,有一百一十一个公开仓库,几乎全是单人做的小型浏览器工具:两个 PDF 工具、一个注意力热力图玩具、一个 Johnny Castaway 屏保的网页复刻、一个给七到九岁孩子做的乘法表游戏。这是其中体量最大的一个,也是唯一一个需要自己写编译器工具链的。

制作过程

8 个阶段
  1. 01

    整条流水线,从头到尾

    所有东西都发生在浏览器里,文件从 GitHub Pages 提供:HTML、JavaScript、WebAssembly 模块,以及一个分块的 HFS 磁盘镜像。一个 service worker 垫片提供了 Pages 无法设置的跨源隔离头,`SharedArrayBuffer` 因此可用,模拟器才能跑起来。按下 Build & Run 后,页面把你的源码交给 wasm 版 `cc1`,跑四段流水线——预处理与编译、汇编、链接、再把 ELF 输出转成 MacBinary——把 `SIZE` 资源接到产物上,用它在 HFS 模板的新副本上打补丁,起一个 web worker,然后拿引导盘和刚构建出来的那个盘实例化 Basilisk II。最有意思的是这份分工:**JavaScript 管网络,Mac 管渲染和事件循环**。从模拟器往回通信走的是一个挂载的 extfs 卷,Mac 看到的是 `:Unix:`,所以 C 里的 `cvm_log()` 会落到一个文件里,宿主轮询它并显示到控制台面板——**Mac 内部刻意没有任何通用 socket**。另有一条可选开启的以太网路径藏在 URL 参数后面,把模拟器 worker 桥接到一个 Cloudflare Durable Object 中继。可选用的示例架上有二十六个示例,它们进阶的是 Toolbox 接口面而不是规模——踩坑记录里正有一条的标题就叫这个。从画一个字符串的窗口,到双缓冲的弹跳小球,直到 **Glypha III:John Calhoun 1992 年的街机游戏**——以 MIT 许可整体 vendor 进来,带着原始精灵美术、十七个音效和约六千六百行当代 C。这位 John Calhoun,正是作者的另一个仓库 `johnny_web` 里被网页复刻的那个 Johnny Castaway 屏保的作者——也就是说,**这已经是他第二次把 Calhoun 的作品往前带一代了**。

  2. 02

    提交日志里,作者是机器,共同作者是人

    三百零九次提交中有二百三十八次带共同作者尾注,而名字几乎都是 Ken Hawkins——仓库所有者本人。**提交是 agent 写的,它把人类列为共同作者。** 五月那一批的每一条提交都以同一段结尾:一行短横线,然后是 `Co-authored-by: Ken Hawkins <khawkins98@gmail.com>`。这和本档案其他地方记录的情形正好相反——那边是人提交、助手挂在旁边署名;这里助手是记录上的作者,人是协作者。把它和第二个事实放在一起看——`package.json` 里一直有一个 Anthropic SDK 的开发依赖,直到九月才移除,而文档描述的是并行派发多个各带不同简报的 agent——这看起来就不太像一个人在用聊天窗口,而更像一个人跑着一小层编排、让它去提交。

  3. 03

    五审阅者红旗流程

    仓库里最可复用的东西是一份讲过程而不是讲产品的文档。在任何大块工作启动之前——任何以周计的估算、任何触及基础设施的、任何引入新架构维度的——并行派发五个独立 agent,各给一个不同的视角,各写一份按严重度排序的批评。视角从一份菜单里挑:领域专家、范围与项目管理、安全与滥用、基础设施可行性、法律与知识产权、编辑器与用户体验、编译与运行时、热重载手感。由编排者汇总并决定。多个审阅者共同给出的高严重度发现是叫停项;共同给出的中severity发现变成缩小范围的要求;低severity的记为后续事项。文档明确写了:三个视角是下限,**五个是甜点**——再多,汇总本身就变成一个项目。

  4. 04

    它叫停的两个 Epic,和一个原封放行的

    同一份文档报告了这套流程的三次结果,都在 2026 年 5 月,而失败的那两次比成功那次更有教益。一个「通过 WebSocket 中继实现真正的 Mac TCP/IP」的 Epic 被关闭:三位审阅者各自独立地发现了三个不同的致命问题——设计在 OSI 层次上就错了、Cloudflare 的服务条款禁止看起来像 VPN 的服务、而目标浏览器其实是仍在售的共享软件。原本的估算是乐观了五到十倍。另一个「带 C 编译的浏览器内 IDE」的 Epic 也被关闭:其中一个阶段需要 OAuth 作用域和一个令牌交换中继,也就是说需要一个后端,这和项目自己「不要后端」的约束冲突;后面的阶段又默认存在一个实际上并不存在的浏览器内 HFS 写入器;而把整套编译器工具链移植到 WebAssembly 的诚实成本是四到九个人月。它被换成一个更小的 Epic,改用资源编译器的 WebAssembly 版本来做资源分支编辑——文档里形容为**「用大约百分之五的力气覆盖大约百分之七十的情感承诺」**。第三个 Epic 过了同样的五个审阅者,原样通过。这个模式值得抄,恰恰因为**三分之二的想法是被它杀掉的**。

  5. 05

    十万字关于出错的事

    仓库里有一份十八万三千字符的踩坑记录,按日期组织、每条一个短标题,从项目第一天一直写到九月。开头是作者标记为「先读这些」的十二个故事,而它们的标题本身就是这个项目难度的总结:**尽早搭测试夹具,因为结构性检查会撒谎**;**发布到 staging 就是启动测试,因为无头环境模拟不了运行时**;编译器不是可重入的;链接脚本里的某条指令会静默覆盖输入目标文件的符号;在绕过垃圾回收器的时候,对比一次规范构建是你手上杠杆最高的诊断手段。正文是大约一百条同类条目,具体到对别人完全无用、对下一个尝试这件事的人无比宝贵:模拟器读的型号标识是 gestalt ID 减六;资源分支列表工具打印列的顺序是这一种而不是另一种;System 7.5.5 没有公开的单文件下载,所以引导盘得自己从存档拼;GitHub Pages 设不了模拟器需要的隔离头;模拟器的某一层用的是 GPL-2.0,而不是原本以为的更宽松的许可。

  6. 06

    tag 列表里的那些标签

    对一个三颗星的项目来说,这份 tag 列表异常说明问题。十二个 tag 里有十一个的名字是 `autonomous-` 加 2026 年 5 月中的一个日期,再加一句已完成事项的简短描述——给移植过来的应用加埋点、退役预编译构建路径、关闭某个特定 issue、发布调试控制台、一轮许可压力测试。它们读起来像是一段 agent 自主工作、边走边打里程碑的运行留下的记录,而踩坑记录里有一条也确实用同样的话提到了「autonomous 模式那次运行」。第十二个 tag 归档了一个 compile-server 分支。这里完全没有任何版本发布:changelog 改把历史按日期分成里程碑,并且明确这么说了,因为这里面没有东西是版本化的。

  7. 07

    两个仓库,以及那个不是重点的

    编译器工具链住在隔壁仓库 `wasm-retro-cc`,描述是「编译成 WebAssembly 的经典 Macintosh 68k C 编译器」。它许可兼容、零颗星、比本项目晚两天创建,而 README 特意说明里面没有任何东西是本项目专用的——那是 Retro68 的编译器、binutils 和 MacBinary 转换器,用 Emscripten 构建,可以单独复用。把它拆出去是正确的决定,也意味着真正有意思的东西分散在两个仓库里,而其中一个多数读者不会打开。许可状况也值得记下来:本仓库是 GPL-3.0,而踩坑记录里有一条讲的就是发现某个依赖其实是 GPL-2.0,而不是原本以为的更宽松许可。

  8. 08

    然后到了九月,尾注被剥掉了

    最后一次实质性提交在 2026 年 9 月 24 日,标题是「用 commit-msg hook 剥掉 AI 共同作者尾注」。它加了一个 git hook,移除指向 Anthropic 地址的 `Co-authored-by` 行和「Generated with Claude Code」页脚,并顺手清掉这些删除留下的空行。这次改动里有两个细节值得注意,因为它们说明作者想过。**hook 匹配的是邮箱域名而不是「claude」这个词**,所以一位名叫 Claude 的人类协作者不会被误删。文档里还记下了设了 hooks 路径之后、本地目录里原有的 hook 会停止运行这个副作用。同一个提交把 Anthropic SDK 从开发依赖里移除。按时间顺序读下来,这个仓库记录的是一段这样的过程:项目开始时 agent 是记录上的作者,整个夏天由它们并行开发、并围绕它们建起了一套评审流程,然后到了九月,披露被关掉了。

相关档案

全部档案 →