career-ops 本地优先 Web 前端演进全解读:从 v0.2.0 实验性 UI 到 v0.10.0 的变更全景与源码级技术拆解
本文围绕 web/CHANGELOG.md 记录的 career-ops 官方 Web 体验(一个 local-first、opt-in alpha 的求职面板)从 v0.2.0 到 v0.10.0 的完整演进,梳理其本地优先架构、Agent 编排安全模型、锁与数据一致性、Unicode 与状态机正确性等核心议题,并结合当前仓库源码(web/ 目录与相关 providers、templates)逐项印证实现细节。读完你将掌握:这个"不另起引擎、不加服务器"的 Web UI 是如何长出来的、每次发布在修什么、以及如何用这份 CHANGELOG 作为索引直接定位到对应实现代码。
一、这份 CHANGELOG 讲的是什么
career-ops 本体是一个跑在 AI 编程 CLI(Claude Code、Codex、OpenCode、Antigravity、Grok Build 等)里的开源 AI 求职工具链:扫描职位门户、把职位清单评成一份带全局 1–5 分的 A–H 结构化报告、定制简历、追踪投递。web/ 目录是它的实验性 Web UI(alpha),定位是"对 CLI 读写文件的本地优先视图"。
web/README.md 给出了三层关键约束:
- 本地优先、无并行引擎:Web 直接读写 CLI 用到的同一批文件(
data/pipeline.md、data/applications.md、reports/、config/),"no parallel engine, no separate database, no server"; - 永不自动提交:Apply 流程只起草与预填,"you always press the button";
- 增量、可剥离:从不动核心的打包、CI 与发布自动化,"The CLI works exactly the same without it"。
web/CHANGELOG.md 正是这个子包从 2026-07-05(v0.2.0,experimental local-first web UI 首次落地,对应 Issue #1451)到 2026-09-03(v0.10.0)约两个月的版本记录。值得注意的是,发布区间内合入的提交涵盖了 web、dashboard、oferta、apply、providers、tracker、scan、compliance、deps、keys、patterns 等众多 scope——也就是说这份文件实际是一份"以 web 版本号为界、横跨多个功能域的变更档案",读它可以看到一个本地优先 UI 在安全、并发、数据正确性与可访问性上被反复打磨的完整过程。
二、本地优先架构概览(阅读源码的起点)
在逐版本展开前,先建立架构坐标。从 web/package.json 与 web/src/ 的目录结构可以确认:
| 层面 | 事实(以当前仓库为准) |
|---|---|
| 框架 | Next.js(当前依赖 next: 16.3.3)+ React 19(react/react-dom ^19.2.5),dev 模式走 Turbopack |
| 语言 | web/src/app 下的页面/路由为 TypeScript/TSX;可单测的纯逻辑沉淀为 .mjs(如 pdf-render.mjs、cv-envelope.mjs、origin-guard.mjs),因为测试直接用 node --test,无 TS loader |
| Node 版本门槛 | engines.node: ">=22.0.0"——README 说明:npm test 依赖 Node 22+ 才能展开 CLI glob |
| 数据入口 | Web 读取其所在的 career-ops checkout(父目录);也可在 web/.env.local 设 CAREER_OPS_ROOT=/path/to/checkout 指向别的目录 |
| API 表面 | web/src/app/api/ 下按功能划分路由:apply/、assistant/、clis/、cv/、cv-pdf/、doctor/、explore/、followups/、pipeline/、portals/、profile/、report/、run/、runs/、status/、tracker/、usage/、version/、whats-new/ 等 |
| UI 页面 | 根路由是"Today"首页(components/home/today-dashboard.tsx 等),另有 pipeline/、explore/、apply/、followups/、analytics/、cv/、config/、portals/、jobs/[id]、report 视图 |
快速启动(来自 web/README.md):
cd web
npm ci
npm run dev
# 打开 http://localhost:3000,现有 CV、pipeline 与报告原样可见
npm test # node --test 全局发现 tests/**/*.test.mjs
npx tsc --noEmit # 类型检查
npm run build # 生产构建
每次 Web 触发的运行(扫描、评估、CV 定制)本质是:后端 API 用 spawn-cli.mjs 拉起某个已安装的 AI CLI,把核心的 mode prompt 交给它,再解析其 stdout/stderr 与 JSONL 结果写回同一批文件。理解了这个闭环,后面所有版本条目就有了统一的"为什么"。
三、版本时间线总览
| 版本 | 日期 | 核心主题 |
|---|---|---|
| v0.2.0 | 2026-07-05 | 本地优先 Web UI 首次落地(opt-in alpha),收件箱三阶段分流、报告渐进披露、Bug 上报套件 |
| v0.3.0 | 2026-07-07 | Via(中介)渠道进入 tracker 与 patterns 分析;44px 触控目标、AA 对比度全面达标 |
| v0.4.0 | 2026-07-28 | 新增 VDAB 零鉴权 provider;Next/PostCSS/sharp 安全依赖升级 |
| v0.5.0 | 2026-07-30 | 合规检查表(司法辖区)落地;Via 渠道上线;Hired 终态全面板传播 |
| v0.6.0 | 2026-08-04 | Follow-up Tracker 页面;PDF 渲染从"Agent 侧"迁移到"后端侧" |
| v0.6.1 | 2026-08-10 | PDF 模式回收 Write/Edit;公司匹配键与 states.yml 热重读修正 |
| v0.7.0 | 2026-08-18 | Unicode 公司键、状态折叠与锁问题集中修复;Codex/Grok/OpenCode 运行器支持 |
| v0.7.1 | 2026-08-20 | CLI 持久化、followups/状态并发锁、states.yml 27 个别名补全 |
| v0.8.0 | 2026-08-25 | 关键安全特性:dashboard API 仅限同源 + loopback |
| v0.8.1 | 2026-08-27 | 运行超时误杀、徽标 Unicode 键、截断 JSON 抢救等修复 |
| v0.9.0 | 2026-08-31 | 全局"回到顶部";语言配置生效;失败原因级提示 |
| v0.10.0 | 2026-09-03 | 新 provider(Feishu Jobs/MokaHR);CV 解析前缀收紧;字体自托管;字体离线可用 |
四、逐版本要点全量回顾
这一节完整覆盖原 CHANGELOG 的全部条目(按版本与 scope 忠实归类),每个条目一句话点明动机或影响;技术纵深请见第五节"源码级拆解"。
4.1 v0.2.0(2026-07-05):实验性 UI 落地
- web:experimental local-first web UI(opt-in alpha,Issue #1451)——本子包的起点。
- web:收件箱分诊引入四阶段漏斗 Abundance → Triage → Shortlist → Opt-in Score(#1569),把"职位多到看不过来"拆成可决策步骤。
- web:报告渐进披露(P0.3 + P1.8,#1535),长报告不一次性压给用户。
- web:空状态提供 free-scan 按钮(P0.1,#1534),对应"反向 ATS 免费扫描"入口。
- web:Bug 上报在写入时去重——稳定指纹 + 点击触发的相似 issue 搜索(#1473);并补齐更丰富的诊断信息(数据形态指纹、核心版本、API 错误,#1469);同时修复了"report-a-bug kit 在 RC 分支与 main 之间丢失"的问题(#1456)。
- web:把
cleanChips抽成可测模块,并支持 Tab/CR 作为粘贴分隔符(#1516)。 - web:成本提示 CostBadge 降噪(P1.6,#1536);品牌橙层级:Mark-applied 软化 + 收件箱成本图例(P1.4,#1537);Config 文案人性化(P1.5,#1538)。
- web:移动端触控目标 ≥44px + FAB 避让(#1542),并把 FOLLOW-UPS DUE 条目在移动端抬到 44px(#1568)。
- pipeline:writer 支持每条 offer 可选 note(#1483)。
- web:把
turbopack.root固定下来,规避 Windows 上 PostCSS OOM(#1530);"Get one free" 链接指向免费 AI 引擎指南(#1540)。 - 一揽子正确性清扫:tracker、providers 与 eval 报告的 correctness sweep(#1528,关闭 #1527)。
4.2 v0.3.0(2026-07-07):渠道与可访问性
- tracker/patterns:新增 Via 渠道字段,区分"终端雇主"与"招聘方/中介"(#1599);并基于 Via 渠道给出分中介的 advance-rate 分析(b6ce551)。
- tracker:把剩余的位置式读取器统一迁移到共享的 header-aware 解析器上(#1598)。
- web:组件级 44px 触控目标(#1629);深浅两套主题对比度达到 AA(#1627);portals 文案与 analytics 语义修正(#1628);UX 审计收尾:CostBadge 全局 CSS + 剩余小于 44px 的"漏网"元素(#1648)。
- deps:npm 依赖例行更新(#1593)。
4.3 v0.4.0(2026-07-28):零鉴权 provider 与依赖安全
- providers:新增 VDAB 零鉴权(zero-auth)provider(#2084)——对应仓库内 providers/vdab.mjs。
- deps:Next.js 安全升级到 v16.2.11(#2198);用 overrides 解决嵌套的 PostCSS 与 sharp 安全公告(#2216)。
4.4 v0.5.0(2026-07-30):合规表与 Via 渠道全量上线
- compliance:新增
check-table-freshness.mjs——司法辖区表的"过期校验器"(#2036,对应根目录 check-table-freshness.mjs)。 - oferta/apply:移民身份要求的越界防护——司法辖区表 + posting 信号 + 表单警告(2a681d1)。
- oferta/apply:司法辖区禁止内容信号——表 + Block G + 申请表单警告(d8dac75)。
- oferta:中介发帖的 agency licensing 检查——司法辖区表 + registry 指针(#2037)。
- web:把 Hired 加入 states.ts 的 FALLBACK,让降级路径也能接受该状态(#2282);Hired 作为终态成功状态在整个 dashboard 传播(#2250)。
- web:label-aware 的 pipeline.md 读取器——
posted:/trust:/note:前缀永不会被误读为列(6c75d9a)。
4.5 v0.6.0(2026-08-04):Follow-up Tracker 与后端渲染 PDF
- web:新增 Follow-up Tracker 页面,支持日志、历史与 cadence 设置(#1422)——对应
app/followups/page.tsx、components/followups/(cadence-settings.tsx、log-dialog.tsx、next-date-dialog.tsx)与api/followups/。 - web:PDF 改为后端渲染而非交给被拉起的 Agent(#2182);Agent 只用无写工具模式产出 HTML,后端负责落盘与转 PDF。
- dashboard:本地化 hired 状态标签、缓冲拆分流的打开者(#2295)。
- deps:npm 依赖更新 + Dockerfile playwright 版本固定 + web lockfile 同步(f154f59)。
4.6 v0.6.1(2026-08-10):写权限回收与状态文件热读
- web:从 dashboard 的 pdf 模式拿掉 Write/Edit(#2508)。
- web:公司匹配键从"核心模块"派生,而非一份 ASCII-only 副本(#2667)。
- web:states.yml 变更后重新读取,而不是进程生命周期内缓存一份(#2590)。
- web:Today 的 "See all N" 链接改指向 fresh-matches 视图(#1790)。
4.7 v0.7.0(2026-08-18):Unicode、锁与多 CLI 运行器大修
- web:Apply 页面新增"返回"与"记录已投递"两个能力(#2735)。
- keys:修掉 dotted-I 修复把波兰语/立陶宛语/马耳他语错误折叠的问题;修掉土耳其语带点大写字母把同一雇主拆成两个的问题(5df43e7、462d276)。
- web:explore + registry 全面启用 Unicode 公司键(#2668);状态键折叠土耳其语带点大写,并停止手抄状态列表(#2786)。
- web:任何 runtime 都不得自授"全量写权限"(#2875);PDF 模式等场景由此得到更细的授权边界。
- web/scan:
POST /api/status取核心 tracker 锁(#2903);scan-history.tsv 追加也走共享锁(#2639)。 - web:把
posted:段带进 web 发起的评估,使其真正落到 POSTED 列(#2899);在 tracker-additions TSV 中输出 posting URL,让 web 运行纳入统一去重(#2833)。 - web:运行器运行时 picker 增加 Grok Build CLI(#2688);Codex 运行防 stdin 等待(#1973);Codex 的 JSONL + exit code 作为 web 打分的权威依据(#2102)。
- web:fallback stderr 分类器加锚定,避免一个单词导致整次运行失败(#2882);"run 出错先问授权"改为"按原因给提示"的前置修整;pipeline 表格窄屏可横向滚动而非裁切(#2363);tracker 读取器与
parseTrackerRow对齐行形态(#2565);按实际写入形态读取target_roles(#2750);运行时扫描错误显示 retry 卡片而不是 update-checkout 面板(#1904);查报告时跳过{n}-RESERVED.md哨兵文件(#1967);16-chip 上限不再截断用户自己的 portals.yml(#2749);从任意带字母的 block 剥离作者字母,而不只 A–G(#2420);analytics 对"持 offer 却显示 0 面试"的用户给出提示与更努力尝试的建议(#2410)。 - web:检测 OpenCode 的默认安装目录(#1794)。
- deps:js-yaml 4.x/5.x 导入兼容(#2656);修复 web 的两个 HIGH 公告(js-yaml 4.3.1、nanoid 3.3.18,a094ec9);js-yaml 下限抬到 ^4.3.1 并加守卫(#2767)。
4.8 v0.7.1(2026-08-20):CLI 发现与并发锁补漏
- web:持久化"唯一已安装的 CLI",保证任务能真正启动(#2966)。
- web:安全执行 Codex AI-search——带 mtime 键控的能力缓存(#2361)。
- web:
block_hard在 Explore 往返中存活——类型、URL 参数、seed 与序列化器四者一致(#3102)。 - web:公司徽标按名称解析,而非猜一个域名(#2942)。
- web:first-run 不再谎称"无需配置"(#2965);Today 主操作直接打开报告(#2967)。
- web:核心 followups 锁被 web 侧接管,写操作不与 seeder 竞争(#3034)。
- web:解决 27 个未映射的 states.yml 别名,并修正土耳其语状态折叠(#2918)。
4.9 v0.8.0(2026-08-25):API 同源防护(安全里程碑)
- web:把本地 dashboard 的 API 限制为 same-origin + loopback(b3974e6,见 web/src/lib/origin-guard.mjs 与第五节 T1)。
- web:methodology 链接有真实可点区域与新标签提示(#3023);crash 路径上子进程 stderr 不进 status 响应(#3022);Explore 去重键保留 query string,避免不同帖子塌缩成一个(#3082);静默 Agent 阶段保持 run 流不中断(#3026);周匹配数上报不做上限而渲染保持有界(#2662);pipeline 的
first_seen用本地日而非 UTC 打戳(#3081)。 - deps:web npm 依赖主版本更新(1207eae)。
4.10 v0.8.1(2026-08-27):运行时鲁棒性
- web:tracker-lock 的文件系统失败被归类为它自身,而不是"竞争"(#3138)。
- web:公司徽标改用 Unicode 感知的规范化键,非拉丁名称不再冲突(#3134)。
- web:截断 JSON 在每个前缀自己的深度抢救,而不是一个全局补齐(#3142)。
- web:一次评估后不再把整个雇主的 board 藏起来(b56cde5);不再在 285s 杀死评估运行并误报"被杀"(#3124)。
4.11 v0.9.0(2026-08-31):语言配置与体验细节
- web:全局"Back to Top"按钮(#2821,对应 web/src/components/back-to-top.tsx)。
- web:web 触发的运行遵循
language.modes_dir与language.output(#3253)。 - web:干净退出时忽略非致命 CLI stderr(#1974);只展示真正到期的 follow-up(#2157);confidential 雇主显示 Via 归属(f749939);评估表格不再塌缩成每行一词(#3254);停止追踪运行时数据路径(0a9d71f);Tab 标签间距与报告表格断词修正(#3160);decision-card 的报告链接改用 next/link(#1931)。
- deps:Next 升级后刷新 web lockfile;PostCSS bump 后重新生成嵌套 web lockfile。
4.12 v0.10.0(2026-09-03):离线与 CV 解析收紧
- providers:新增 Feishu Jobs 与 MokaHR 扫描 provider(#3491)——对应仓库内 providers/feishu-jobs.mjs 与 providers/mokahr.mjs。
- oferta:Block B 加入证据分级的"需求重要性"(#3596)。
- web:要求
cv-前缀,让 tailored-CV 解析器不再返回 cover letter(#2156);解析"本次申请"的 tailored CV,而不是该公司最新一份(ca11627);无可用 slug 的公司不得匹配每一份定制 CV(#3214);可从生成的 pdf index 直接发起 apply(ac761fa)。 - web:自托管生产字体,无 Google 网络依赖即可启动(a2e46c3,见 web/src/assets/fonts/README.md)。
- web:Today 动作队列先排序再截断(3a067ee);损坏的 portals.yml 予以保留而非被 example 覆盖(07ed4f3);"Awaiting your decision"按每个已 EVALUATED 的别名排序,而非英文前缀(d7573e1);beta 上报者在重复搜索无法运行时得到明确告知(77bf490)。
- web,dashboard:两处面向用户的引用指向仓库新家。
五、源码级拆解:CHANGELOG 背后真正值得学会的工程点
逐版本条目是"发生了什么",这一节回答"为什么会这样、代码怎么保证"。以下每个主题都能在仓库里找到对应实现,是可独立引用与继续深挖的单元。
T1. 本地 API 的同源与 loopback 双层防护(v0.8.0)
这是 CHANGELOG 里最重要的单一安全特性,实现是 web/src/lib/origin-guard.mjs,被 web/src/proxy.ts 消费。文件头注释把威胁模型写得极清楚:
- F1 drive-by:任意网页可在后台 POST 到
http://localhost:3000/api/...(经典 CSRF); - F2 LAN:若 dev server 绑到 0.0.0.0,同网段任何机器都能直接打这些路由。
由于 /api 会拉起子进程并读写用户文件,一个未鉴权的 API 等价于"远程代码执行原语",因此防护必须是双层的(two independent layers, both must pass):
- Host 层(防 F2):默认只应答 loopback(
localhost、整个127.0.0.0/8、IPv6::1,见isLoopbackHost),其他 Host 必须显式出现在CAREER_OPS_WEB_ALLOWED_HOSTS白名单; - Origin 层(防 F1):优先信任浏览器发出的
Sec-Fetch-Site(same-origin/none放行,same-site/cross-site拒绝);旧浏览器无该头时回退到 Origin 与 Host 比对;而显式 allowlist 的 origin 直接放行——因为浏览器自己设置 Origin,页面无法伪造,这正是本地伴生客户端(如chrome-extension://来源的浏览器扩展,对 Fetch Metadata 永远是 cross-site)所需的逃生门。
两个 opt-in 环境变量默认都不设(语义即"不信任额外来源"):CAREER_OPS_ALLOWED_ORIGINS(允许从 app 外部调用 API 的 origin,逗号/空格分隔、不带尾斜杠)与 CAREER_OPS_WEB_ALLOWED_HOSTS(额外非 loopback 主机)。从 web/src/lib/origin-guard.mjs 还能看到规范化细节:Host 要去端口、解 IPv6 方括号;origin 要去默认端口与尾斜杠并转小写——否则手写白名单里 http://localhost:80 这种拼法永远匹配不上浏览器发的 http://localhost。这个模块刻意零 Node/Next 依赖,所以能在 edge runtime 运行并被 node --test 单独单测。
T2. "Agent 不许写文件"的 PDF 模式闭环(v0.6.0 / v0.6.1 / v0.10.0)
v0.6.1 "take Write/Edit away from the dashboard's pdf mode" 与 v0.6.0 "render PDFs from the backend instead of the spawned agent" 连起来是一套完整的信任边界设计。为什么宁可让 Agent 吐出 15–25KB 内联 HTML 也不给它写工具?web/src/lib/cv-envelope.mjs 的注释给出理由:工具授权是按工具名、不是按路径,而职位描述与评估报告(都是不可信输入)会进入该 Agent 的上下文,一次 prompt injection 就可能把写入重定向到 cv.md、data/applications.md 甚至 .env。于是:
- pdf 模式不再授予写工具,Agent 把定制好的完整 HTML 夹在
<<cv-html format="a4|letter">>与<</cv-html>>两个行锚定标记之间(cv-envelope.mjs 导出OPEN_MARK/CLOSE_MARK),由后端解析、落盘、渲染 PDF; - 解析规则全部 fail-closed:标记必须独占一行(否则 Agent 只是"提到"语法或
codex exec回显 prompt 就会误开/误关信封)、第一个关闭符胜出(注入的关闭符只会截断而不会逃逸)、出现多个信封直接拒绝而不是猜(parseCvEnvelope,cv-envelope.mjs); createCvEnvelopeFilter处理流式场景:必须见每一字节才能重建 CV,但 15–25KB 原始 HTML 不能刷进用户的 run 日志,所以 display buffer 与 parse buffer 分离(cv-envelope.mjs);- 收尾判定"必须是完整文档":解析器要求 HTML 含
</html>,杜绝 Agent 中途断流的截断产出。
web/README.md 的 Safety 一节还补充了现实差异:Claude Code 下 pdf 模式禁用一切可写工具(Write/Edit/MultiEdit/NotebookEdit/Bash);其他 CLI 用裸 prompt 调用、保留各自默认工具,此时保证的是"被渲染的 CV 是后端从信封解析出来的那份,绝不会是 Agent 在背后写下的某个文件"。
v0.10.0 的三个 CV 修复是这个边界的收尾:要求 cv- 前缀避免 tailored-CV 解析器误返回 cover letter(#2156)、解析本次申请而非该公司最新一份 CV(ca11627)、无可用 slug 的公司不匹配任何定制 CV(#3214)。
T3. 运行器与 CLI 编排的可靠性(v0.7.x–v0.10.0 反复打磨)
Web 每次运行都要拉起外部 AI CLI,因此 CHANGELOG 里有大量关于"进程 IO 与状态判定"的条目,它们集中在 web/src/lib/ 的 spawn-cli.mjs、run-cli-support.mjs、run-prompts.mjs、clis.ts、saved-cli.ts、job-error-hint.mjs 等模块。可以归纳成四类:
- stdout/stderr 语义:干净退出时忽略非致命 stderr(v0.9.0,#1974),但 crash 路径上 stderr 又不进 status 响应(v0.8.0,#3022);fallback stderr 分类器要"锚定"整段文本,防止某个单词误杀整次运行(v0.7.0,#2882)。一句话:stderr 是诊断信号,不是运行结果。
- 退出码与结构化输出的权威性:Codex 打分以 JSONL + exit code 为权威(v0.7.0,#2102);Codex 运行必须防 stdin 等待(#1973);Codex AI-search 用 mtime 键控的能力缓存避免反复探测(v0.7.1,#2361)。
- CLI 发现与选择:把"唯一已安装的 CLI"持久化(v0.7.1,#2966);OpenCode 要能在默认安装目录被找到(#1794);Grok 要能在默认安装目录(而非仅 PATH)被找到(v0.10.0,77657dc);runtime picker 支持 Grok Build(v0.7.0,#2688)。
- 运行反馈:静默 Agent 阶段保持 run 流存活(v0.8.0,#3026);运行失败按具体原因给提示,而不是千篇一律的"请先授权"(v0.9.0,#2158);运行时扫描错误展示 retry 卡片而非 update-checkout 面板(#1904);不再 285s 硬杀并误报(v0.8.1,#3124);web 触发的运行遵循
language.modes_dir/language.output(v0.9.0,#3253)。
T4. 文件即数据库:锁、并发与解析一致性(v0.5.0–v0.8.1)
因为没有独立数据库,所有状态都写在同一批文本文件上,锁与解析一致性就成了高频主题:
- 锁的获取点补全:
POST /api/status取核心 tracker 锁(#2903);web 侧接管核心 followups 锁避免与 seeder 竞争(#3034);scan-history.tsv 追加走共享锁(#2639)。而 v0.8.1 的"tracker-lock 文件系统失败要归类为它自身、而非竞争"(#3138)说明:锁报错也要诚实,错误的归因会让用户在错误方向上排查。 - 文件行文协议的一致性:tracker 读取器与
parseTrackerRow对齐行形态(#2565);pipeline.md 采用 label-aware 读取,posted:/trust:/note:前缀永不被误读为列(v0.5.0);残余的位置式读取器统一迁移到共享 header-aware 解析器(v0.3.0,#1598);查报告跳过{n}-RESERVED.md哨兵(#1967)。 - 时间与分段语义:
first_seen用本地日打戳而非 UTC(v0.8.0,#3081);web 运行携带posted:段落到 POSTED 列(#2899);tracker-additions TSV 输出 posting URL 以汇入统一去重(#2833);Explore 去重键保留 query string(#3082)。 - 损坏输入的抢救:截断 JSON 按各自前缀深度抢救,而非一个全局 pad(v0.8.1,#3142);损坏的 portals.yml 予以保留、绝不拿 example 覆盖用户的文件(v0.10.0)——"用户的配置优先于默认值"。
T5. Unicode、多语言与状态机正确性(v0.7.0–v0.10.0 的一条暗线)
一批条目在修同一个根因:名称与状态必须先做 Unicode 感知的规范化,再作为键去比较。
- 公司键:从"ASCII-only 副本"改为从核心派生(v0.6.1,#2667);explore + registry 全面 Unicode 键(#2668);公司徽标键用 Unicode-aware 规范化,非拉丁名称不再碰撞(v0.8.1,#3134);徽标按名称而非猜域名解析(v0.7.1,#2942)。当前实现可看 web/src/lib/normalize-text-key.mjs 与 web/src/lib/logo-cache-key.mjs。
- 两个"修过头"的折叠修复尤其值得记取:dotted-I 的修复不能顺带把波兰语/立陶宛语/马耳他语也折叠(v0.7.0,5df43e7);土耳其语带点大写(如 İ)不能把同一雇主拆成两家(462d276)。Unicode 折叠必须懂语言,不能靠粗暴小写。
- 状态别名:状态键折叠土耳其语带点大写,并停止手抄状态列表(#2786);随后补齐 27 个未映射的 states.yml 别名(v0.7.1,#2918);states.yml 变更后热重读而非缓存到进程退出(v0.6.1,#2590)。底层状态清单在 templates/states.yml,web 侧映射见 web/src/lib/status-alias.mjs。
- 排序语义:Today 的 "Awaiting your decision" 按每个已 EVALUATED 的别名排名,而不是用英文前缀硬匹配(v0.10.0)——因为状态列表本身是多语言的。
T6. 求职者功能面:页面与动作闭环(v0.2.0–v0.10.0)
- Today/首页:动作队列"先排序再截断"(v0.10.0),保证优先级决定谁被保留;主操作直接打开报告(v0.7.1);"See all N"去 fresh-matches(v0.6.1);first-run 诚实展示配置缺口(v0.7.1);只显示真正到期的 follow-up(v0.9.0)。
- Follow-up Tracker(v0.6.0,#1422):带日志、历史与 cadence 设置的独立页面——源码在
app/followups/page.tsx+components/followups/+api/followups/;follow-up 到期逻辑见 web/src/lib/followups.ts。 - Apply:起草-预填但永不自动提交(README 硬规则);v0.7.0 起提供"返回"与"记录已投递"。
- 报告阅读:渐进披露(v0.2.0);decision-card 的报告链接走 next/link(v0.9.0);methodology 链接有真实触控目标与新标签提示(v0.8.0);评估表格不再塌缩成每行一词(#3254)。
- 收件箱/分析:四阶段分诊(v0.2.0);analytics 对"持 offer 却 0 面试"的提示(v0.7.0)。
T7. 可访问性、视觉与离线化(v0.2.0–v0.10.0)
- 触控目标 ≥44px(含组件级清扫与 FAB 避让、CostBadge 全局样式收尾),两套主题对比度 AA——这是 v0.2.0–v0.3.0 的 UX 审计主线。
- 全局 Back to Top(v0.9.0):web/src/components/back-to-top.tsx 展示了细节品味——监听 window scroll(main 无独立 overflow)、尊重
prefers-reduced-motion、隐藏时同时移出 Tab 顺序与辅助技术树、与 assistant launcher 用bottom-20错开避免重叠。 - 字体自托管(v0.10.0):只 vendor 用到的 latin 字重,
next build不再请求 Google Fonts;来源与 OFL 许可记录在 web/src/assets/fonts/README.md。
T8. 合规表、渠道语义与终态(v0.5.0)
v0.5.0 是"报告可信度"的一次集中增强:司法辖区维度上引入三类表驱动的检查——移民身份要求越界、辖区禁止内容、agency licensing,并提供 check-table-freshness.mjs 过期校验器,防止这些"讲法律"的表静默过时;表数据可在 templates/immigration-status-requirements.yml、templates/jurisdiction-prohibited-content.yml、templates/agency-licensing.yml 看到。同时 Via 渠道区分终端雇主与中介、支持分中介 advance-rate 分析,并把 Hired 作为终态成功在 dashboard 全局传播。
T9. 依赖与供应链安全(v0.2.0–v0.9.0 持续)
- Next.js 安全升级(v0.4.0 提到 v16.2.11,当前 web/package.json 已是 16.3.3);web HIGH 公告修复(js-yaml 4.3.1、nanoid 3.3.18)并把 js-yaml 下限抬到 ^4.3.1 且同时兼容 4.x/5.x 导入(当前依赖已是
js-yaml: ^5.0.0,导入兼容性由专门的 js-yaml 测试守护,如根目录 tests/js-yaml-import-form.test.mjs 与 tests/js-yaml-version-floor.test.mjs 所示); - 嵌套 PostCSS/sharp 公告用 overrides 解决(v0.4.0),并保留在 package.json 的
overrides字段里;turbopack.root固定防 Windows OOM(v0.2.0);Dockerfile playwright 版本固定(v0.6.0);lockfile 与依赖 bump 同步刷新(v0.8.0/v0.9.0 多次)。
T10. 工程约束与测试布局(架构性的隐形贡献)
web 团队把"可测性"写成了结构约束,web/README.md 的 Development/Tests 一节给出了完整规则,也解释了为何 CHANGELOG 反复出现 .mjs 抽取:
- 测试放在
web/tests/,路径镜像web/src/下被测文件({module}.test.mjs); npm test用 glob 发现,新套件无需注册(但要求 Node ≥ 22 支持 glob 展开);- 测试不得进
src/(会被next build文件追踪与tsc污染);必须用.mjs而非.ts(没有 TS loader);web 套件用node:test,而根目录核心套件刻意不用任何框架(连node:test都不用),两种风格互不跨越——由根目录 tests/web-test-layout.test.mjs 在 CI 上强制执行。
这正是 v0.2.0 "extract cleanChips to a tested module"(#1516)以及 origin-guard.mjs、cv-envelope.mjs 等纯逻辑模块反复出现的原因:难测的逻辑先抽成无框架依赖的 .mjs,再谈别的。
六、把 CHANGELOG 当索引:主题 → 源码路线图
读这份 CHANGELOG 最有效的方式,是按主题跳到对应的实现文件,再回来看条目:
| 想深入的主题 | 建议入口 |
|---|---|
| 本地优先架构、启动、环境变量 | web/README.md |
| API 同源/loopback 防护 | web/src/lib/origin-guard.mjs + web/src/proxy.ts |
| PDF 模式信封与流式过滤 | web/src/lib/cv-envelope.mjs、web/src/lib/pdf-render.mjs、web/src/lib/pdf-paths.mjs |
| CLI 编排与运行状态 | web/src/lib/spawn-cli.mjs、web/src/lib/run-cli-support.mjs、web/src/lib/clis.ts、web/src/lib/saved-cli.ts |
| Unicode 键与状态别名 | web/src/lib/normalize-text-key.mjs、web/src/lib/logo-cache-key.mjs、web/src/lib/status-alias.mjs、templates/states.yml |
| Follow-up 与 Today 逻辑 | web/src/lib/followups.ts、web/src/components/followups/、web/src/components/home/ |
| UI 与可访问性 | web/src/components/back-to-top.tsx、web/src/components/、web/src/assets/fonts/README.md |
| provider 扩展(本文件新增) | providers/feishu-jobs.mjs、providers/mokahr.mjs、providers/vdab.mjs、providers/README.md |
| 合规表与数据可信 | templates/agency-licensing.yml、templates/immigration-status-requirements.yml、templates/jurisdiction-prohibited-content.yml |
| 依赖与安全基线 | web/package.json(engines、overrides) |
| 测试约定 | web/README.md、tests/web-test-layout.test.mjs |
需要提醒的是,这份 CHANGELOG 覆盖的 web UI 仍处于 alpha 阶段(README 明示 "Expect rough edges"),其中部分条目描述的行为可能已被后续条目修正或重构;若要引用某个具体行为,应以当前源码为准,把 CHANGELOG 当作"演化时间线 + 问题清单",而不是最新的行为规范。整体看,v0.2.0 到 v0.10.0 的演进主线非常清晰:先让"本地优先视图"能跑起来,再在不可信输入 + 有写权限的 Agent + 无独立数据库这三个约束的夹缝里,用同源防护、写权限回收、行级协议、Unicode 键、细粒度锁和诚实的失败报告,把它打磨成一个可以放心日常使用的前端。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00