首页
/ career-ops 本地优先 Web 前端演进全解读:从 v0.2.0 实验性 UI 到 v0.10.0 的变更全景与源码级技术拆解

career-ops 本地优先 Web 前端演进全解读:从 v0.2.0 实验性 UI 到 v0.10.0 的变更全景与源码级技术拆解

2026-09-07 14:16:14作者:何将鹤

本文围绕 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.mddata/applications.mdreports/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)约两个月的版本记录。值得注意的是,发布区间内合入的提交涵盖了 webdashboardofertaapplyproviderstrackerscancompliancedepskeyspatterns 等众多 scope——也就是说这份文件实际是一份"以 web 版本号为界、横跨多个功能域的变更档案",读它可以看到一个本地优先 UI 在安全、并发、数据正确性与可访问性上被反复打磨的完整过程。

二、本地优先架构概览(阅读源码的起点)

在逐版本展开前,先建立架构坐标。从 web/package.jsonweb/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.mjscv-envelope.mjsorigin-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.localCAREER_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.tsxcomponents/followups/cadence-settings.tsxlog-dialog.tsxnext-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/scanPOST /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)。
  • webblock_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_dirlanguage.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.mjsproviders/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):

  1. Host 层(防 F2):默认只应答 loopback(localhost、整个 127.0.0.0/8、IPv6 ::1,见 isLoopbackHost),其他 Host 必须显式出现在 CAREER_OPS_WEB_ALLOWED_HOSTS 白名单;
  2. Origin 层(防 F1):优先信任浏览器发出的 Sec-Fetch-Sitesame-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.mddata/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 就会误开/误关信封)、第一个关闭符胜出(注入的关闭符只会截断而不会逃逸)、出现多个信封直接拒绝而不是猜(parseCvEnvelopecv-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.mjsrun-cli-support.mjsrun-prompts.mjsclis.tssaved-cli.tsjob-error-hint.mjs 等模块。可以归纳成四类:

  1. stdout/stderr 语义:干净退出时忽略非致命 stderr(v0.9.0,#1974),但 crash 路径上 stderr 又不进 status 响应(v0.8.0,#3022);fallback stderr 分类器要"锚定"整段文本,防止某个单词误杀整次运行(v0.7.0,#2882)。一句话:stderr 是诊断信号,不是运行结果
  2. 退出码与结构化输出的权威性:Codex 打分以 JSONL + exit code 为权威(v0.7.0,#2102);Codex 运行必须防 stdin 等待(#1973);Codex AI-search 用 mtime 键控的能力缓存避免反复探测(v0.7.1,#2361)。
  3. CLI 发现与选择:把"唯一已安装的 CLI"持久化(v0.7.1,#2966);OpenCode 要能在默认安装目录被找到(#1794);Grok 要能在默认安装目录(而非仅 PATH)被找到(v0.10.0,77657dc);runtime picker 支持 Grok Build(v0.7.0,#2688)。
  4. 运行反馈:静默 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.mjsweb/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.ymltemplates/jurisdiction-prohibited-content.ymltemplates/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.mjstests/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.mjscv-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.mjsweb/src/lib/pdf-render.mjsweb/src/lib/pdf-paths.mjs
CLI 编排与运行状态 web/src/lib/spawn-cli.mjsweb/src/lib/run-cli-support.mjsweb/src/lib/clis.tsweb/src/lib/saved-cli.ts
Unicode 键与状态别名 web/src/lib/normalize-text-key.mjsweb/src/lib/logo-cache-key.mjsweb/src/lib/status-alias.mjstemplates/states.yml
Follow-up 与 Today 逻辑 web/src/lib/followups.tsweb/src/components/followups/web/src/components/home/
UI 与可访问性 web/src/components/back-to-top.tsxweb/src/components/web/src/assets/fonts/README.md
provider 扩展(本文件新增) providers/feishu-jobs.mjsproviders/mokahr.mjsproviders/vdab.mjsproviders/README.md
合规表与数据可信 templates/agency-licensing.ymltemplates/immigration-status-requirements.ymltemplates/jurisdiction-prohibited-content.yml
依赖与安全基线 web/package.jsonenginesoverrides
测试约定 web/README.mdtests/web-test-layout.test.mjs

需要提醒的是,这份 CHANGELOG 覆盖的 web UI 仍处于 alpha 阶段(README 明示 "Expect rough edges"),其中部分条目描述的行为可能已被后续条目修正或重构;若要引用某个具体行为,应以当前源码为准,把 CHANGELOG 当作"演化时间线 + 问题清单",而不是最新的行为规范。整体看,v0.2.0 到 v0.10.0 的演进主线非常清晰:先让"本地优先视图"能跑起来,再在不可信输入 + 有写权限的 Agent + 无独立数据库这三个约束的夹缝里,用同源防护、写权限回收、行级协议、Unicode 键、细粒度锁和诚实的失败报告,把它打磨成一个可以放心日常使用的前端。

登录后查看全文
热门项目推荐
相关项目推荐