Skip to content

Latest commit

 

History

History
1190 lines (798 loc) · 116 KB

decisions.md

File metadata and controls

1190 lines (798 loc) · 116 KB

决策记录(Decision Log)

记录各次决策的背景、裁决与理由,包括放行、否决和后续修订。条目中的「当前」「待办」是当时语境;同一主题有后续裁决时按后者理解,现行规范以 CLAUDE.md 为准,当前状态以 STATUS.md 为准。旧多模型协议已停用,其链接仅供追溯。


D1 · 版本回退 / 快照(暂不做)

评估日期:2026-06-30 · 状态:不落地

现状真相:

  • 数据只有单一状态 localStorage['timelog.v1'],没有历史/快照。
  • 导入是按 id 合并(src/storage.js mergeImportedEntries),不是替换。
  • 因此当前事实上没有「回退」能力:导入旧备份只能并回旧条目,无法移除「旧备份里没有、但之后新增/删除过」的条目。把「导入旧备份」当回退用会得到错误结果。

决策:满 28 天真实记录前不做。回退/快照属于状态历史管理,紧邻被锁死的「可扩展/同步」边界,收益不抵复杂度与跑题风险。

若将来真要做(不是现在):最小真回退原语=「导入时提供『替换而非合并』选项」(一次性整体替换当前 entries),而不是引入多版本快照栈或云端历史。先有这个原语,配合现有「下载 JSON」就能手动回退,无需新数据模型。


D2 · 付费转型 / 多人维护(暂不设计)

评估日期:2026-06-30 · 状态:不落地

疑问:之后若转付费、且有其他开发者帮忙维护,该怎么处理?

回答(付费与维护是两件独立的事):

  • 付费:时间尺是本地优先、零后端、无账号的静态 PWA,没有可计费的服务端。任何直接计费都会撞「无后端 / 无账号 / 无云同步」铁律。可设想的最小侵入路径是「赞助 / 一次性买断 + 可选云备份增值」,但这要等 v2 之后、且求职有实质进展再议。
  • 多人维护:靠许可证 + CONTRIBUTING + 治理文档约束,而不是改运行时结构。许可证已于 v31 落地(见下「许可证:已落地 AGPL-3.0」);作为唯一著作权人保留商业双许可权,暂不需要 CLA。

决策:许可证部分已落地(AGPL-3.0,保护性、耐久、便宜);付费转型与云备份增值仍 gated——现阶段(pre-28 天、求职未见实质推进)不设计、不落地,仅记录该决策本身。重申最大风险=把「付费/治理规划」当成逃避面试推进的镀金。

许可证:已落地 AGPL-3.0(2026-07-01,v31)

选择:AGPL-3.0-or-later(非 MIT),配「作者保留商业双许可」姿态。LICENSE 全文 + 每个运行时文件 SPDX 头 + README「许可证与版权」节 + CONTRIBUTING.md 均已落地。

理由:

  • AGPL §13 堵住 SaaS 闭源克隆:客户端 JS 本就完全可读,MIT 等宽松许可等于放任他人闭源套壳做成付费服务。AGPL 的网络条款要求「对外提供服务即须开源」,是对纯前端项目为数不多有牙齿的威慑。
  • 唯一著作权人保留商业双许可:现在只有一个作者,无外部贡献,可无 CLA 地在 AGPL 之外另行授权;这为将来「一次性买断 / 可选云备份增值」留了法律口子,又不必现在就设计付费。
  • 法律威慑 + git 溯源即最优解:前端代码无法技术性防拷贝,务实止损=清晰许可(威慑)+ 公开 git 历史(作者身份与时间线的可追溯证据)。

未来可选项(备注,不做):软件著作权登记、商标注册——都属「求职有实质进展后再议」,此处仅登记备选,避免日后追溯。


D3 · 推广策略(gated,暂不主动执行)

评估日期:2026-07-01 · 状态:不落地(推迟主动推广)

背景:需求里含「写推广文案 / 选渠道」。主动社区推广正是 CLAUDE.md「别镀金」点名的 #1 风险——用运营/推广动作逃避面试推进的高发区。经评估重框为「求职证据工作」:本轮只做便宜、耐久、保护性的产出(许可证、博客草稿),主动推广推迟到博客产物就位或出现真实使用里程碑之后。

决策:现阶段不主动推广。先让「开发博客」作为可写进简历/面试的工程叙事产物落地;等有真实用户信号或求职实质推进后再谈渠道执行。

渠道建议(就位后再用,不是现在):

  • 中文社区先拿真实用户:少数派 / 即刻 / V2EX / 掘金等,受众与「本地优先、隐私、零依赖」调性契合,且应用界面是中文,落地摩擦最小。
  • 英文社区用「博客 dev-story」当内容源:应用 UI 是中文,不适合直接向英文用户推应用;但工程故事(零依赖无构建 PWA、红线治理、postmortem 根因文化)可作为英文 dev-story 内容源投递 —— 正是「英文版当内容源、而非推应用」的设想。

可核实的事实卖点(避免夸大,仅这两条已核对属实):

  • 不主动保持屏幕常亮:应用未使用 Wake Lock,不会强制屏幕常亮,符合「轻量、不打扰」定位。
  • 完整备份是 Markdown 摘要 + JSON 导出:摘要导出为 Markdown(可直接贴给 AI 复盘),完整备份为 JSON(复制/下载/分享),均在本地完成、不上传。

重申:以上属 gated 规划,登记以备日后,不构成本轮开工项。最大风险=把「推广规划」当成逃避面试推进的镀金。


D4 · 日视图交互调研(v36;提案 F 于 v45/v48 落地)

评估日期:2026-07-07 · 状态:提案 E 仍为原型;提案 F 经用户授权于 v45 初版、v48 完整落地

背景:v34/v35「直接操纵 rail」(拖边界改时间)SE2 真机验收后被判定观感不及预期,v36 已回退为 v33 卡片列表(见 CLAUDE.md CHANGELOG v36、docs/postmortems.md P17)。用户要求研究是否有更优雅的日视图交互方向。为避免重蹈 P17——「demo 数据必须覆盖真实使用会撞到的极值分布,均匀数据会系统性藏起钳制类缺陷」——两个原型的 demo 数据统一采用真实分布:近 8h 整块睡眠、44.5h 专注工作块、外加一批 2045min 碎片(洗漱/通勤/吃饭/刷手机),不用均匀数值。

调研输入:WebSearch 时间记录类 App 的日视图/列表交互模式(Toggl Track、Timery、ATracker 等)。确认 Toggl Track 官方模式为「Timer 屏幕的记录支持左滑删除/收藏」,是验证过的真实产品模式,而非臆造。

曾产出两个可交互原型(纯 demo 数据,非运行时资产;v53 已删除可执行副本,历史代码由 Git 保留):

  1. day-band.html(提案 E · 比例日程带):日视图顶部加一条纯只读、不可拖的比例日程带——每段高度严格按分钟数比例分配,不做钳制、不做像素:分钟实时映射,因此结构上不可能重演 P17 的拖拽回弹问题。点一段只弹出摘要 popover(时间范围 + 内容 + 「编辑这条」跳转按钮),真正编辑仍下沉到下方卡片列表(与已发布运行时同款交互,零改动)。极薄碎片(视觉高度 <22px)自动隐藏文字、只留可点色带,避免挤爆。
  2. day-swipe-cards.html(提案 F · 滑动卡片):卡片右侧不再常驻编辑/删除图标按钮,改成左滑露出(Toggl Track 验证过的模式),正文因此更干净、留白更多;保留一个常驻「⋯」按钮兜底键盘/无触屏可达性——纯手势对键盘用户不可达,这是滑动模式必须补的口子。

后续决策:提案 E 仍未合入。提案 F 经用户明确授权后分两步落地:v45 先做左滑即编辑;v48 改为仅触摸/触控笔启用的 2×72px 编辑/删除轨道,补齐水平轴锁定、跟手、吸附、一次只开一张,以及纵向滚动/点空白关闭。原型里的常驻「⋯」兜底没有照搬,运行时用“点整卡编辑 + 编辑页删除 + 键盘 Enter/Space”提供完整非手势路径。

剩余项:提案 E 继续保持 gated,不因 F 已落地而自动开工。提案 F 的模型与双引擎自动化已完成,但 touch-action、惯性和纵横轴竞争仍必须在 Safari 浏览器与主屏 PWA 真机验收;手势类交互不能只靠 headless 宣布完成。


D5 · 第三方产品审计意见的可行性分档(2026-07-08,gated)

背景:外部审计给出六个方向(原生 widget/灵动岛、半自动记录、界面冲击力、漏损预算、意义闭环、"三块空地"战略)。逐条对照本项目铁律(零运行时依赖静态 PWA、无后端/账号/云、数据只在 localStorage、v2 锁死)分档如下。本条只登记判断,不构成开工项。

档一 · 结构性做不了(web 平台无 API,需原生载体)

  • 原生 widget / 灵动岛一键记录:WidgetKit/ActivityKit 只对原生 App 开放,PWA 无任何入口。做它=转原生(Swift + App Store 分发)=产品形态转向,须另立决策,不是"加个功能"。
  • 屏幕使用时间自动填充:Apple 不向任何第三方(含原生 App)开放可导出的 Screen Time 原始数据(DeviceActivity 只给扩展内聚合报表),web 侧为零。结构性不可行,与是否转原生无关。
  • 睡眠数据自动填充:HealthKit 仅原生。web 不可行。

档二 · 可行但只有降级形态(28 天门后再议)

  • 快捷指令近似"一键记录":iOS 快捷指令(可放主屏/锁屏 widget)打开 ?quick=switch 之类带参 URL,app.js 启动时解析参数直接开表单。体验是"打开 PWA + 自动弹表单",非真·一键,但零依赖、零后端。改动小,属工具打磨,门后做。
  • 日历 .ics 本地导入(半自动消未记录):用户手动导出/分享 .ics,应用本地解析、按时间段提议成记录(复用 mergeImportedEntries 的确认式合并姿态)。无 OAuth、无 CalDAV、无云,隐私模型兼容。是"半自动记录"里唯一够得着的一块,但是真功能,门后做。

档三 · 纯本地可行、门后优先级最高(审计说的"结论力 + 意义闭环"恰好全在这档)

  1. 异常天标记(T13):月/年汇总行按阈值(漏损%/未记录% 超标)标色/标记。纯 stats.js 层小改动,账本数据现成。门后第一顺位。
  2. 漏损预算:config 加一个"本周漏损 ≤N h"字段,ruler/摘要标注进度与超标。同样是统计层小改动。门后第一轮可与异常天同批。
  3. 周回顾仪式 + 漏损账单:summarizeRange 机制现成;加时薪 config 字段(localStorage,本地敏感数据可接受)即可算"本周刷手机 14h ≈ 时薪×14 机会成本"。门后第二轮。
  4. 基于数据的下周建议:内建 AI 违反零后端铁律,不做;但项目已有"摘要贴给 AI"闭环——便宜的增强是摘要导出里加周环比 delta,把闭环的原料喂得更好。规则式建议(如"漏损集中在 22:00 后")可作为周回顾的一部分。

档四 · 观感/定位类(park)

  • 人生格子图(LifeWidget 式):情绪冲击便宜是事实,但它不消费账本数据(纯出生日期算术),且审计自己也说"别跟 LifeWidget 比皮肤"。park,不排期。
  • ADHD 人群定位:营销文案,归入 D3 推广 gate,随 D3 一起解冻。

对"三块空地"战略的修正

审计圈的三块空地里,"记录自动化"这块对 web PWA 是半封死的(widget/系统集成需原生;Screen Time 对谁都封死)——本项目真正够得着的是结论力(异常天、预算)和意义闭环(周回顾、账单、环比摘要),两者全是纯本地统计层改动,恰好也是四桶 taxonomy 这个核心资产的自然延伸。若某天判断"记录自动化"值得全力去占,那是「转原生 App」级别的产品决策,须另立条目评估,不在本仓库红线内。

重申 v2 锁死:以上全部 gated 在「28 天真实记录 + 求职实质进展」之后;本条审计意见本身就是最典型的镀金诱惑源——账本还没记满一个月,先别给账本装审计委员会。


D6 · v55 功能冻结后视觉收敛豁免

评估日期:2026-07-11 · 状态:明确批准并锁定范围

日视图已确诊为 card soup:tabs、日期导航、周期标签、hero 与每张记录卡共用 --card + shadow-1 + top-light,导致所有表面视觉重量相同,今日结论无法成为主角。参照 v33/v34 UI 重构的豁免先例,本轮在功能冻结后仅偿还这一项明确遗留视觉债;范围锁死为纯 CSS 的弱卡片化、强调色预算与密度微调,不做连续日志容器,不动 DOM、模板、业务 JS、左滑轨道、刷新快照或动画约束,也不由此解冻任何其它镀金事项。


D7 · 里程碑、支持作者与主线高级设置(gated,待调研)

评估日期:2026-07-13 · 状态:记录方向,不构成开工项(里程碑部分已由 D32 修订,放行最小版)

里程碑按累计有效记录日研究

未来若研究里程碑,采用“累计有效记录日”而非连续签到;中断不会把累计值清零。首阶段只考虑一次性、温和的庆祝反馈,徽章系统或独立进度页更后置;节点必须由真实使用证据决定,不照抄 3/7/21/100。

“有效记录日”目前尚未定义,后续调研必须明确回答:

  • 是否只按不同本地自然日计数;
  • 只有计划、没有已发生记录的日期是否计入;
  • 导入的历史记录是否计入;
  • 删除当天记录后累计值是否回退。

无论答案如何,这一方向继续受“累计 28 天真实记录 + 求职有实质进展”的总 gate 约束;本条不授权实现庆祝、徽章或进度页。

付费方向改为“支持作者”

若未来设置金钱入口,定位为“支持作者”,只放在“更多”里,且所有功能始终免费。支持不购买功能、服务、股权、优先权或产品路线影响力;款项由作者自由支配。月度支持与 pay-what-you-want 只作为备选形态,当前不选择、不实现。

本条明确 supersede D2 中“一次性买断 + 可选云备份增值”的付费方向。D2 已落地的事实仍保留:仓库使用准确的许可证标识 AGPL-3.0-or-later,公开源码,作者保留商业双许可的可能性。仓库继续公开;把仓库改私有不能保护已经部署、浏览器可读取的前端源码。官方渠道、项目名称和图标的防冒充规则只列为后续评估,不在本轮实施。

主线高级设置交由 Fable5 后续调研

主线高级设置本轮不实现,交由 Fable5 后续调研。四类动作必须分别评估,不得当作同一种“改配置”:

  • 改名:必须迁移历史标签或建立别名,否则旧记录会成为孤儿标签;
  • 改桶:会追溯性重算全部历史统计;
  • 停用:只应阻止新记录选用,不能破坏历史记录的归类;
  • longOk:会追溯改变长段待确认口径。

决策:以上三组方向只登记边界和待研究问题,不进入 v57 运行时。最大风险仍是用产品化、商业化或设置扩张逃避面试推进。


D8 · 发布存续、原生载体与 WebDAV 备份(gated,当前不实现)

评估日期:2026-07-14 · 状态:记录边界与候选路径,不构成开工项

背景:用户信心其实包含三个不同问题

本条源于对“仓库转私有后 GitHub Pages 是否仍可使用”“PWA 缓存被系统回收后还能否启动”“是否需要原生 App 才能给用户长期信心”的连续讨论。三个问题不能混为一谈:

  1. 源码可见性:仓库是否公开,只控制 Git 历史、issue、文档和未发布文件的访问权限。已经发到网页端的 HTML/CSS/JS 必须交给浏览器执行,不能靠私有仓库隐藏;已经被 clone、下载或形成公开 fork 的副本也收不回来。
  2. 运行时可获得性:用户关闭页面后,是否还能取得启动 App 所需的静态文件。当前 PWA 靠 GitHub Pages 在线源站和 Service Worker 缓存共同保证;缓存是浏览器管理的 best-effort 存储,不是永久安装包。
  3. 数据可恢复性:设备丢失、卸载、清站点数据或浏览器回收存储后,记录能否恢复。原生安装包只改善运行文件的持久可获得性,不会自动备份用户数据;WebDAV/文件备份解决的是数据恢复,也不会让已经下线且缓存已失的 PWA 重新启动。

当前事实与承诺边界

  • GitHub Free 个人/组织账号把发布中的仓库改为私有时,GitHub 会自动下线其 Pages;GitHub Pro/Team/Enterprise 可从私有仓库发布 Pages,但站点默认仍可向公网开放。私有仓库不等于私有网站。参见 GitHub 仓库可见性说明 与 GitHub Pages 建站说明。
  • 当前 sw.js 会预缓存全部运行时资产,并采用 cache-first 读取。已经访问或安装到主屏的设备在源站暂时不可用时通常仍能启动;但若 Cache Storage/Service Worker 被系统回收,且源站同时下线,刷新或重启后就不能重新取得 App。
  • 页面仍开着时,已载入内存的代码可能暂时继续工作;这不构成关闭、刷新或重启后的可用性保证。
  • 数据权威仍是当前 origin 下的 localStorage['timelog.v1']。仅运行缓存被回收时,记录可能仍在;若用户清除整个站点数据、卸载导致容器被清除、设备损坏或系统一并回收 origin 存储,记录也会消失。
  • 当前可诚实承诺的是“本地优先、完整导出、稳定公开地址下可离线使用”,不能承诺“安装一次后,即使作者永久停服也一定能启动”或“本机数据永不丢失”。

候选路径 A:继续稳定托管 PWA(当前基线)

保持固定公开 URL 和静态源站,是成本最低且符合本项目身份的方案。用户清掉运行缓存后,只要源站仍在,浏览器就能重新下载 App;已有 localStorage 未被清除时,记录会重新出现。

这条路径的可信度来自:长期稳定的访问地址、清晰的本地数据说明、完整备份/导入能力、可验证的版本发布和停服前充分通知。它不要求原生 App,但也不提供脱离源站的永久启动保证。

仓库当前继续公开;若将来只想隐藏 Git 历史而保持公网 App,应先确认付费套餐和 Pages 设置,并认识到浏览器拿到的前端源码仍然可读。不要把“仓库转私有”当作代码保护方案。

候选路径 B:可独立启动的安装包(未来须另立项目)

若未来要作出更强承诺——“用户取得某个版本后,即使源站关闭,仍能从设备上的完整运行文件启动”——就需要把运行时资产随安装包交付。可以是:

  • 用 Swift/Kotlin 重写的原生 App;
  • 内嵌现有 HTML/CSS/JS 的原生壳;
  • 桌面端打包载体或其它能把静态资源真正随安装包分发的方案。

不必为了离线启动保证重写全部 UI;关键是运行文件是否属于安装产物,而不是界面是否使用原生控件。但无论采用哪一种,签名、构建、商店/侧载、升级、平台审核和原生存储都会引入新的工程边界,直接违反本仓库“无构建 / 无运行时依赖 / 单页静态 PWA”的身份。因此必须放在独立仓库/独立产品载体中评估,不能把包装工具链塞进本仓库。

原生载体仍不能替代备份:卸载、清除 App 数据、设备丢失或换机仍会造成记录不可用。原生的额外价值是可进一步接入系统文件、后台任务、WidgetKit/ActivityKit 等平台能力;是否值得承担长期维护成本,要由真实用户需求证明。

候选路径 C:WebDAV 作为用户自有的备份通道(不是同步)

WebDAV 值得保留为门后候选,因为它可以让用户把备份写到自己选择的服务(例如支持 WebDAV 的网盘、NAS 或自建存储),不要求时间尺运营账号或自建业务后端。但若评估,第一版只能定义为备份传输层:

  • 上传时间戳命名、不可变的完整 JSON 快照,避免静默覆盖唯一副本;
  • 明确显示最近一次成功时间、文件名、大小与失败状态,网络失败不能伪装成已备份;
  • 恢复必须由用户显式选择备份并预览,复用现有安全导入;若需要真正回到某一时点,应先实现 D1 所述“整体替换”原语,不能把按 ID 合并冒充恢复;
  • 不做后台双向同步、不做最后写入者静默覆盖、不做多设备实时合并,也不引入时间尺账号;
  • 默认保持端到端的本地隐私边界。若远端文件需要加密,应在客户端上传前使用 Web Crypto 加密,并先设计恢复密钥、忘记密钥和跨设备解密流程;“加密了但用户无法恢复”不算可靠备份。

纯浏览器 PWA 落地 WebDAV 有现实限制,评估时必须逐项验证:

  • 远端必须使用 HTTPS,并正确允许浏览器 CORS;很多 WebDAV 服务默认不允许任意网页 origin 直接请求;
  • 用户名、密码或 token 如何保存是安全问题,不能把长期明文凭据直接塞进 localStorage;
  • iOS/PWA 后台执行不可靠,不能承诺关掉 App 后定时自动上传;
  • endpoint、认证方式、PROPFIND/PUT 支持、配额、证书和代理兼容性差异很大,需要真实服务矩阵测试;
  • 若用原生网络桥绕开 CORS/后台限制,就已进入候选路径 B 的独立原生载体,不再是本仓库的零依赖纯 Web 实现。

因此 WebDAV 的合理首版姿态应是“用户主动连接并手动备份/恢复”,不是“无感云同步”。在此之前,现有“存储备份”写入系统文件选择器(用户可自行选 iCloud Drive/其它文件提供方)仍是更简单、可理解、无需保存远端密码的基线。

解冻前必须回答的问题

即使跨过“28 天真实记录 + 求职有实质进展”的总 gate,也不能直接开工。须先完成一份小型设计评审,至少回答:

  1. 要提升的具体承诺是哪一个:源站下线后可启动、换机可恢复、自动备份,还是多设备同步?
  2. 有多少真实用户遇到过缓存回收或数据丢失,现有手动备份为什么不足?
  3. 备份是追加快照还是覆盖单文件;保留多少版本;如何校验完整性;如何做一次真实恢复演练?
  4. WebDAV 凭据、远端文件和本机数据各自的威胁模型是什么?忘记密码/密钥时如何诚实失败?
  5. 浏览器/iOS 主屏 PWA/未来原生载体分别能保证什么,哪些能力只能 best-effort?
  6. 独立安装包的发布、签名、更新和停止维护成本,是否真的小于“稳定托管 PWA + 文件/WebDAV 备份”的组合?

当前决策

  • **现在不转原生、不接 WebDAV、不做同步,也不改变现有运行时。**本条仅把讨论和边界落盘。
  • 当前继续以稳定公开 PWA + 完整手动备份作为基线;用户信心文案必须使用可兑现的承诺,不把 Service Worker 缓存说成永久安装。
  • WebDAV 保留为未来“用户自有远端的手动/可验证备份”候选,优先级低于把现有导出—恢复流程验证扎实;它不得绕过 v2 对云端/跨设备同步的锁定。
  • 若未来决定保证脱离源站仍可启动,另立原生/打包载体项目;若未来决定保证数据可恢复,单独设计备份与恢复。两项可以组合,但不能互相冒充。

重申最大风险:这类“长期产品化基础设施”很容易成为新的镀金方向。没有真实使用证据和求职实质进展时,正确动作是记录,而不是开工。


D9 · 外部验证延期,进入 14 天自用冻结(2026-07-15)

冻结周期:2026-07-16 00:00 → 2026-07-29 23:59(GMT+8);复盘 2026-07-30。 完整执行边界见 docs/dogfood-freeze-handoff.md。

背景与事实

docs/product-direction-validation-handoff.md 规划的外部招募、12 次访谈和 8 人 7 天试用全部延期。

必须准确陈述原因:

  • 延期不是「验证通过」,也不是「验证失败」——外部验证尚未开始,没有产生任何外部证据;
  • 真实原因是维护者当前没有候选用户,并明确选择先把产品作为个人工具跑通闭环;
  • 任何后续文档、作品集或对外表述都不得声称市场需求已被验证。

决策

  • 当前 V1 核心 = 以尽可能低的管理成本留下可信行动记录,让自己第二天仍能知道昨天做过什么。 这是当前唯一有个人证据支持的价值层。
  • 「识别目标偏差」「AI 复盘导致第二天改变行为」降为候选增值假设,不再是 V1 验收条件。「内建 AI / Agent 集成」冻结不实施。
  • 14 天内功能冻结:只允许处理数据损坏、无法启动、无法记录/编辑/导出/恢复、离线不可用等阻断问题,且仍需复现证据 + 最小改动 + 回退方法 + 自动化测试 + 维护者明确批准。
  • 求职外部结果是本阶段的硬约束:10 次高质量岗位投递 + 发布 1 个作品集案例。这条存在的意义是防止工具打磨替代现实行动——它直接对应本文件开头的「最大风险=用打磨工具逃避面试推进」。
  • 作品集不是新增功能的理由。 案例只能基于已完成的工作和真实工程取舍整理。

与 28 天 gate 的关系

CLAUDE.md 的「28 天真实记录 + 求职实质进展」gate 保留,本冻结更严格:14 天内即使累计记录达到 28 天,也只表示可以重新评审,不自动解锁 roadmap。

退出条件

2026-07-30 复盘只允许三类结论:继续冻结并使用;只修排名第一的真实摩擦;停止主动开发、把时间转回求职和作品集。禁止因为攒了一批想法就整体解冻路线图。 只有真实使用反复暴露的损失,才有资格成为下一轮开发输入。


D10 · AI 协作窗口关闭,候选定向提前处置(2026-07-18)

背景与事实

  • 维护者 2026-07-18 告知:对当前协作模型的访问窗口即将结束。D9 制定时未考虑「AI 执行杠杆是限时资源」这一变量。
  • 维护者明确分工:求职硬约束改由其它模型支持(硬约束本身与优先级不变);本模型剩余窗口专注本项目。
  • 触发方式:维护者在逐项批准式询问中明确选择「整体解冻分析,推进」。

决策(定向提前处置,不是整体解冻)

提前执行(一个开发期工具批次 + 一次 v67 发版):

  • C11(bump_version 脚本 + 模型层 ts-check):纯开发期、零运行时字节。「没有 AI 后最容易犯错的两件事」优先工具化——这是本决定里唯一因窗口关闭而价值上升的类别。
  • C8 + 发版顺手项(两处说明文案 + recordedDayKeys 去 export):行为零变化。
  • C7 方案 A(过夜写入即确认):本批唯一的行为改动;已有复现、改动极小、有回归测试;修的是「用户以为记了、统计静默少算」的真实缺陷。同日早前「等 07-30 与 C5 并案」的决定被本决定修订;方案 A 与未来任何阈值策略兼容(只把「显式双端断言」标为已确认,不动阈值语义)。
  • v67 发版本身顺带为 C1/C6(更新通道)再积累一个真机观察点。

继续冻结(不因窗口关闭而动):

  • C1/C2/C6 的语义改动:需要 v64 之后的真机数据先说话;近期可做的只有「诊断 v2」(补 SW 注册状态字段),列为后续批次,视窗口余量决定。
  • C3/C4/C5/C9/C10:产品方向判断,依赖真实使用数据或独立决策;AI 窗口关闭不改变其评估条件——这些项的难点是设计,不是代码。

边界(防止本决定成为坏先例)

  • D9 的「禁止因为攒了一批想法就整体解冻」仍然成立:本次判据不是「想法攒够了」,而是「执行杠杆到期,且所选项几乎全部零行为」。
  • 28 天 gate、别镀金条款、外部验证「未开始≠通过」的表述边界全部不变。
  • 2026-07-30 复盘照常举行:评审 14 天记录数据与剩余候选;本决定不预支复盘对「记录可持续性」的结论。

模型路由(窗口关闭后的维护指引)

  • 可交常规模型(如 Sonnet 5):发版仪式(有 bump 脚本护栏后)、文案、测试维护、文档整理。
  • 建议留给强模型:planner/确认语义等模型层改动、SW 更新链路、WebKit 布局怪癖类问题。

执行记(同日追补)

批次一(v67:C7A/C8/死 export/ts-check + C11a 脚本)与批次二(v68:诊断 v2——SW 注册态早读 + FCP)均已于 2026-07-18 执行完毕。C1/C2/C6 语义改动继续等真机数据;同日已录得首个干净自动更新正例(见 freeze-candidates C1)。


D11 · 维护者批准的第三次冻结定向开口:底色冷移一档 + 阶段格言 v1(2026-07-20)

背景与事实

  • 维护者 2026-07-19 深夜提出两件事:① 评估亮色主题要不要回退到 v33 之前的冷白色;② 新功能想法「阶段格言展示区」(展示当前阶段对自己有用的一句格言,未设置用默认)。
  • AI 按惯例给出评估 + 冻结提醒 + 「最大风险=用打磨工具逃避面试推进」的明确风险提示(CLAUDE.md 要求在此类时刻必须指出),并把决定权交回维护者。
  • 维护者在一次性批量决策(随后离线休息,授权 AI 通宵执行完整发版)中明确选择:
    • 冷白问题:不整体回退,折中——只把亮色底色冷移一档;
    • 格言功能:实现并完整发版(合并 main + tag + GitHub Release),不等 07-30 复盘。

决策

  1. 亮色底色:--bg 从宣纸暖 #f7f5f1 改为近中性 #f6f6f5(两处主题块 + theme-color 两锚点 index.html/app.js;manifest theme_color 是暗色值,不动)。只动底色,--card/--input/--track/--border/阴影暖调全部保留。对比度重校(WCAG):全部文字令牌对新底比值持平或微升(正文 11.70:1、muted 5.27:1、最低的 danger 3.98:1),卡底分离 1.089→1.081,仍由阴影+发丝线承担。
  2. 阶段格言展示区 v1:完整规格与验收标准见 docs/freeze-candidates.md C13;随 v69 实现。
  3. 两项合并为一次 v69 发版,走完整版本仪式与全量自测。

诚实代价(不粉饰)

  • 这是冻结期第三次开口(D10 批次一/二之后),且判据不再是「零行为改动」——格言是新的用户可见表面。它不符合 D9「只修阻断」的字面边界,依据的是维护者明确批准这一唯一例外通道。
  • 风险提示已给出、维护者知情选择:本次开口发生在求职硬约束(10 投递 + 1 作品集)尚未见记录进展的情况下;07-30 复盘评审「记录可持续性」时,本次开口应作为「工具打磨引力」的一个数据点如实计入。
  • 底色冷移一档部分走回 v33「宣纸暖」的设计立场:v33 的亮色身份即暖底,这次只动 --bg 留下「中性底 + 暖调 input/track/边框」的轻微不对称——被有意接受(这些暖令牌几乎全部落在白卡面上,不与底色直接相邻)。若一档不够、还想要整套冷色板,那是 07-30 之后的另一次决策(完整回退需重校三桶彩色双主题,代价见 C12)。
  • 通宵执行意味着真机验收发生在合并与发布之后:v69 的双引擎测试与截图自验由 AI 完成,维护者真机核对是事后把关;若真机不符,回退路径=revert PR + v70 发版(SW cache-first 决定了任何修正都要走完整版本仪式)。

边界

  • D9 的 07-30 复盘照常举行,本决定不预支任何复盘结论;其余候选(C1–C6、C9、C10)继续冻结。
  • 格言 v1 不做的事也在此锁定:不做多条格言轮换、不做按日期/阶段自动切换、不做格言历史——这些都是「可扩展分类法」的近亲,撞「别镀金」条款。
  • (2026-09-30 注:「不做多条格言轮换」一项已由 D31 显式修订为「文字轮播,每天一句」;其余边界仍有效。)

执行记(同日追补)

同一批次追加第三项:C14 第三桶改名「漏损」→「偏航」(维护者原话「适时地放空是必要的」)。判据与前两项一致——维护者明确要求、改动面是显示名与文案、内部键 leak 不动因此零数据迁移。它比前两项更贴近 D9 的 V1 核心:道德化命名会抑制如实记录休息,改名直接服务「以尽可能低的管理成本留下可信记录」。但它同样是冻结开口,同样计入 07-30 复盘的「工具打磨引力」数据点。 明确没做的事见 docs/freeze-candidates.md C14「诚实边界」——改名不重新归桶,「发呆算不算必要消耗」仍是复盘的开放问题。

启动诊断的处置(同日判读):22 条样本已回答它被打开时的问题——SW 全程接管、常驻存储 22/22 为真、缓存恒为完整 21 文件、就绪 355–735ms,且长间隔样本(14h42m→355ms、12h34m→518ms、8h45m→635ms)不比短间隔慢。因此 P33/C6 的「iOS 长间隔回收 Cache Storage 导致起床首开特别慢」假说在页面侧被证伪:慢段确实落在页面计时零点之前(与 v66 录屏判决一致)。唯一尚未取证的是 v68 新增的 SW态 字段——本窗口 9 条全为 a:activated,但期间没有发生任何版本更新,该字段只在更新交接那一刻才有信息量。处置:留到 v69 更新落到真机、取得那一两条跨版本样本后即可关闭(更多 → 启动诊断 → 关,样本整键删除);不必开到 07-30。

2026-07-20 追补(v69 交接样本已取得,诊断可关闭):25 条样本收齐,两件事定案。① 冷 navigate 样本(n=18,排除更新后紧邻的热启动)间隔与就绪时间相关系数 +0.05,长间隔(≥4h)均值 516ms、短间隔(<1h)466ms,差值远小于组内波动(短间隔自身横跨 155–735ms),且全场最快的冷启动恰是间隔最长那次(14h42m→355ms)——「起床/久不开首开特别慢」在页面侧再无立足点,慢段确实在页面计时零点之前,本仪器原理上看不见它,已到测量上限。② v68→v69 交接首次取到 SW态 读数:a:activated,无 waiting 悬挂(C6 反证),但维护者确认期间完全退出过,故自动链路独立切版仍零正例。v69→v70 交接后样本升至 4/4:该次由前台恢复(visibilitychange → reg.update())而非冷启动触发,形态却完全一致,维护者同样确认完全退出过——说明问题不限于某条触发路径,而在 reload 动作本身拿不到新版本(详见 docs/freeze-candidates.md C1 盘点表与收窄后的问题定性)。结论:启动诊断的三个原始问题全部有答案,即可关闭,不必开到 07-30。


D12 · 域名迁移 time.eigentime.org(冻结期第四次定向开口:只做零运行时准备,发版序列待 07-30 复盘后)(2026-07-24)

背景与事实

  • 维护者 2026-07-24 带两个提案进入 grilling 评审:①「收敛/预计结束—实际结束—延期原因」结构化字段;② 迁移到个人域名 time.eigentime.org(双仓库拓扑,ADR 已先行写在 personal-blog 仓库 docs/adr/0001)。
  • AI 按惯例给出冻结提醒与**「最大风险=用打磨工具逃避面试推进」的明确风险提示**(并指出维护者自己委托的 ChatGPT 证据包在「暂缓」一节明确列有「仅为自定义域名或视觉效果进行开发」),把决定权交回维护者。
  • 维护者知情选择:申请第四次冻结开口(继 D10/D11),范围只含域名迁移;结构化字段功能不开工,归档为 docs/freeze-candidates.md C16。

决策链(grilling 逐项裁决)

  1. 拓扑:wowayou/time-logger 是唯一源码仓库与发布入口;另建公开部署镜像 wowayou/time-logger-site(只含生成产物,禁止手工维护业务代码),绑定 time.eigentime.org——/ 为产品主页,/app/ 为固定 PWA 入口。旧 origin wowayou.github.io/time-logger/ 按 ADR 三步处置:迁移横幅版 → 绑域名 → 只读冻结版。完整契约见 personal-blog 仓库 ADR 0001;本条是其在本仓库的同步与裁决记录,两处冲突时以本条与本仓库红线为准。
  2. 日历切分:冻结期内(→07-29)只做零运行时准备(本仓库文档修订、scripts/build_site.py、site/ 主页源码、publish workflow、部署仓库与 DNS 预置);三步发版序列(v71 横幅、绑域名上线、v72 只读冻结)07-30 复盘后启动——复盘若结论为「停止主动开发」,发版序列作废,已建的部署仓库与 DNS 记录无害闲置。
  3. 只读冻结页形态:完整应用 + 只读开关(隐藏 FAB、禁用新建/编辑/导入等写路径,保留导出/分享/时间轴浏览、常驻迁移横幅),不做独立精简导出页——后者是第二套 artifact,违背「双仓库不是两套代码」的自我约束。
  4. 发布机制:scripts/build_site.py(标准库、确定性组装、运行时清单直接解析 sw.js 的 FILES 数组为单一真源)+ 源仓库 Actions 薄层(vN tag push 触发,fine-grained PAT 仅授权部署仓库 contents:write,一年期);PAT 失效=workflow 红灯可见 + 本地同脚本手动兜底。选自动化而非纯手动的理由是 v59 学费:人肉多步仪式必漏,且「忘跑发布→新域名静默旧版」比红灯难发现。
  5. 备份格式:零改动(执行期修正——grilling 时计划「v71 加 meta.schemaVersion」,落地核对发现 exportData() 已写入顶层 version: 1,备份本就是版本化 JSON,不再重复加字段);ADR 的「校验值」要求删除——现有整批逐字段预检才是真完整性门闩,校验值会拒掉 dogfood 协议里合法的 GMT+8 分析副本,或逼出让它形同虚设的宽容路径。
  6. 技术事实(已验证):manifest scope:"./"、SW 注册与 FILES 全相对路径 → 应用搬到 /app/ 子路径零运行时代码改动;现有导入已满足 ADR「稳定 ID + 幂等合并 + 冲突预览」要求,迁移的导入侧零新开发。
  7. 权威设备迁移(07-30 后):完整备份 → 新 origin 添加主屏 PWA → 导入(C6 已验证流程,firstUsedDate 随备份);不依赖跨 origin 静默搬运。

诚实代价(不粉饰)

  • 这是冻结期第四次开口,且方向正是证据包「暂缓」清单点名的自定义域名。判据仍是维护者明确批准这一唯一例外通道;07-30 复盘评审「工具打磨引力」时本条如实计入。
  • 未来两次旧站发版(v71/v72)都要过 C1 那条更新通道——四次交接零正例、「完全退出」是唯一被证实可靠的路径;迁移横幅能否送达权威设备本身就是又一次 C1 观察点。
  • 求职硬约束(10 投递 + 1 作品集)仍未见记录进展,本次开口不改变其优先级;域名与主页对作品集有辅助价值,但「作品集不是新增功能的理由」条款不因此松动。
  • 发版序列后置的直接动机之一:权威设备正在跑 14 天 dogfood 观察窗,窗口内换 origin/重装 PWA/导出导入会污染「记录可持续性」的复盘证据。

附带协议修订(同次 grilling 裁决)

docs/dogfood-freeze-handoff.md §3「不把原始或派生 JSON 提交仓库、发给 AI 或上传第三方」中「发给 AI」一项与实际实践(ChatGPT 证据包、AI 复盘闭环)已事实性分裂,修订为:不进仓库/公开渠道的硬边界不变;发给 AI 服务属维护者知情选择,须当次有意识决定,且记录含他人可识别信息(公司、人名)时发送前须意识到这不只是本人隐私。死规则腐蚀整份协议的权威性,认可现实并划清边界优于留着一条已被常规违反的禁令。


D13 · 提前终止 D9 冻结,转入发版上线冲刺;确立「规格—执行—验收」多模型协作(2026-07-24)

背景与事实

  • 维护者 2026-07-24(D12 落盘同日晚些时候)明确决定:彻底抛开 07-30 复盘限制,集中力量快速完成基础发版和上线推广。
  • 这是维护者的知情选择:本会话内 AI 已多次给出「最大风险=用打磨工具逃避面试推进」的风险提示(见 D12),维护者仍明确选择提前终止冻结。按惯例如实记录,不粉饰。
  • 求职硬约束(10 投递 + 1 作品集)本身不因本决定取消,但其「冻结期内并行完成」的时间绑定随冻结终止而解除;28 天 gate 与「别镀金」条款继续约束功能扩张类 roadmap(发版上线不是功能扩张)。

决策

  1. D9 的 14 天冻结提前终止(原定 2026-07-29 止、07-30 复盘)。D12 的「三步发版序列待 07-30 后」时序约束同步解除,序列本身(v71 横幅 → 绑域名 → v72 只读冻结)与全部质量门(版本仪式、全量自测、真机验收前置)不变。
  2. 冲刺范围:完成 time.eigentime.org 迁移三步、更新定位文案、准备并执行首轮中文社区推广(D3 的推广 gate 随之解除;D3 的渠道建议与「可核实事实卖点」约束继续有效)。
  3. 多模型协作模式(D10「模型路由」升级):Fable 定方向/规格/验收,另一台机器上的 Sonnet 5(或 Opus 4.8 / GPT)执行,维护者做只有人能做的步骤(凭据、DNS、真机、发帖账号)。通讯通过本 GitHub 仓库:规格在 docs/specs/,执行走分支 + PR,验收=PR 评审合并。协议全文见 docs/collab-protocol.md(历史原文)。
  4. 产品硬约束固化(2026-07-24 分析定案,随本决定入 CLAUDE.md 产品红线):① 首次打开即用、无强制 onboarding;② 离线照常记录;③ AI/自动化不得悄悄修改原始时间线;④ 导出不依赖付费或云端账号。第五条「约 5 秒完成一次记录」为未达标军令状,是上线后第一轮产品迭代的裁决标尺(所有记录侧候选按「是否让中位记录耗时逼近 5 秒」取舍)。
  5. 核心定位(工作假设):「5 秒记下真实做了什么——本地、离线、可信的一天时间线」。人机并行记录与关闭循环审计为差异化候选层,等外部真实用户信号再晋级。对外表述继续禁止声称市场需求已验证。

边界

  • 隐私红线、零依赖铁律、版本仪式、提交推送前自测清单全部不变,对所有执行模型同等生效。
  • 推广材料不得使用真实数据/截图(demo PNG 白名单机制不变),不得夸大(沿用 D3「可核实事实卖点」纪律)。
  • 维护者手动步骤的唯一权威清单是 docs/launch-runbook.md。

D13 追补(2026-07-24 同日)

  • 亮色整体回归冷白:维护者真机观感后决定「整体亮色风格回归原本的冷白色」,推翻 D11 的一档折中、执行 C12 评估过的整体回退选项(含 C12 列明的对比度重校代价)。规格见 docs/specs/SPEC-004-v72-cold-light-theme.md,占用 v72;SPEC-002(旧站只读冻结)顺延为 v73。
  • 宣传设计参考:维护者指定 https://otty.sh/ 为 landing 结构语言参考,落为 docs/specs/SPEC-005-landing-v2.md(短句诗式特性标题 + 真实画面 + 重复 CTA;像素不抄袭、诚实纪律不放松)。
  • 执行记:SPEC-001(v71 迁移横幅)与 SPEC-003(上线文案包)由 Sonnet 5 执行、Fable 验收合并(PR #25/#26),v71 已 tag + Release,publish-site workflow 首跑 10s 绿灯;time.eigentime.org 证书就绪、已强制 HTTPS,新旧两 origin 均运行 v71——多模型协作协议首个完整闭环成立。

D14 · ScreenCam 式盈利路径调研(登记不落地,2026-07-24)

背景

维护者提供参考案例:ScreenCam(macOS 录屏工具)「公测结束 → App Store 上架 → 订阅制 + 1 年兑换码赠送 → 官网开源」,问该路径对时间尺的适用性。

调研事实(2026-07-24 核实)

  • 「开源」是宣传话术:motion-space/thescreen.cam 仓库只是 landing page(MIT、10 stars、描述自称 "Landing page of ScreenCam");App 本体闭源。其路径实质是「闭源原生 App + 开源官网 + 公测养口碑 + 上架收订阅」。
  • 公测通道停更但可继续用——即「旧通道冻结、新通道收费」,与本项目「旧 origin 冻结只读、新 origin 继续」在结构上同形,但目的相反(它是付费墙的护城河,本项目是域名迁移的善后)。

对照本项目的四个硬约束

  1. 载体:上架 App Store 需要原生载体——这是 D8 候选路径 B,明确必须另立独立项目,不进本仓库。触发门槛(D8/ADR 0001 一致):Widget、锁屏快捷记录、Live Activity 等 Web 做不到的能力;仅为上架收费不构成触发。
  2. 许可:时间尺是真 AGPL 全源开放,与 ScreenCam 的「只开源官网」根本不同。AGPL 与 App Store 分发条款存在著名摩擦;可行出口是 v31 已保留的唯一著作权人商业双许可——自己给自己发商业授权上架。该出口在接受第一个外部代码贡献前有效,之后需 CLA 或重谈。
  3. 付费形态:订阅制与 D7 现行决策(「支持作者」:所有功能始终免费、支持不购买任何东西)直接冲突。若未来走「免费 Web + 付费原生壳(Widget 等原生增值)」,Web 端承诺可以不破——但那是显式推翻/修订 D7 的新决策,不能悄悄滑过去。
  4. 时机:ScreenCam 收费前有完整公测期养出真实用户与反馈。本项目当前外部用户≈0、外部验证未开始。任何盈利设计在拿到真实留存信号前都为时过早。

决策

登记不落地。可复用的顺序骨架:公开自用/分享期(现在)→ 真实外部用户与留存(先决条件)→ 原生壳独立项目(D8-B,若原生能力有真实需求)→ 届时再决定收费形态(修订 D7)。触发重估的条件:出现稳定外部用户群,或对 Widget/锁屏记录类原生能力的真实反复请求。届时另立决策,本条只存档调研结论。


D15 · 额度约束下的队列裁剪与固定成本治理(2026-07-25)

触发

维护者告知 AI 额度余量不多,要求对当前执行队列做裁剪并留档。裁剪前队列:SPEC-012 → 009 → 011 → 010 阶段一 → 008 → 007,五单四次发版仪式。

成本结构认定(裁剪依据)

单个规格的成本大头不是写代码,是每单的固定税:版本仪式六锚点 + CHANGELOG 行;264×2 双引擎回归全量复跑(执行方一次、Fable 复核一次);tag / Release / 镜像发布;以及三个模型各自把 CLAUDE.md 整读进上下文。

结论:合并发版是最省的杠杆——同样的代码量,四次仪式变一次,固定税砍掉约 3/4。

事实核查一则(改变了 SPEC-009 的优先级)

SPEC-009 的卡死实录是「v71 · 缓存 timelog-v72」。v71 是迁移横幅版本、只在旧 origin 有意义;新 origin 的 PWA 是 runbook Phase C 期间新装,起步版本只可能是 v72/v73。维护者确认:卡死的是旧 origin 图标——C2 第 6 步保留作对照、Phase D 收尾即删除的那个;新 origin 注册从未卡死。

决策

  1. v74 合并批次(一次仪式、一行 CHANGELOG):SPEC-012(导入 toast 回归)+ SPEC-011(状态栏遮条)+ SPEC-009-lite。
  2. SPEC-009 收敛为 lite:只保留「···」里常驻的「修复更新通道」cell(在线前置 + sw.js 探活 → unregister → reload),2 条测试;砍掉检测三条件、启动计数器、横幅第三态。自动检测方案登记为「出现外部用户后重启」,不销毁——外部用户≈0 时,检测的价值追不上其成本与误报风险。
  3. SPEC-010 阶段一收敛为单变体:只出变体 A,B/C 不做;排在 v74 之后,维护者提前拍板则并入 v74 同一次仪式。
  4. SPEC-007 / SPEC-008 park 到 2026-07-30 阶段复盘:007 是队列里最大、且唯一触及数据迁移路径(主线改名)的一单,触发它的原话只是「视觉可以优化」;008 是给外部验证尚未开始的产品做落地页配图。park ≠ 取消,规格正文与已作出的裁决全部保留。
  5. SPEC-002 不变(blocked):解锁后并入当时最近一次仪式,不为它单独发一版。
  6. CLAUDE.md 瘦身:CHANGELOG v1–v65 历史行移到 docs/CHANGELOG.md,本文件保留最近八版与全部红线条文;正文 40.9K → 21.7K 字(文件 80.4KB → 44.6KB)。project_audit.py 的 15 条 CLAUDE.md 短语断言全绿——其中「tooltip hover 延迟 800ms」此前只存在于 v12 历史行(红线原句写的是「hover 延迟 800ms」),已在 UI 红线原句内补足字面,这也说明历史行一直在替红线扛断言,外移时必须逐条验证而非整段搬走。
  7. 规格状态清账:SPEC-001/003/004/005/006 早已合并却仍挂 in-progress,统一改 done(附 PR 号),避免执行方按 status 认领时重复认领。

边界(本决策不做什么)

  • 不改任何红线条文,不降低验收标准:P35 红灯证明、双引擎全绿、隐私红线、版本仪式六锚点照旧。
  • 不改协作协议本身;执行方仍按 docs/specs/ 的 status: 认领,本决策通过改 status 生效。
  • park 的三单在 07-30 复盘按当时额度与求职进展重排,不在本条里预判结论。

一条诚实记录

被 park 或收敛的三单(007 设置重设计、008 landing mock、010 第三轮亮色)都是打磨类。本次裁剪由额度触发,但与「别镀金」条款「最大风险=用打磨工具逃避面试推进」同向。28 天 gate 与 07-30 复盘是下一个决策点,本条不预支那个决策。


D16 · 提前阶段复盘(2026-07-26,维护者要求不等 07-30)

事实盘点(可核实的)

  • 28 天 gate:已达成。权威设备 header 显示「已记录 29 天」——即 stats.js recordingMilestones 口径下有真实记录的自然日数。按 CLAUDE.md,达成只意味着可以重新评审,不自动解锁 roadmap。
  • 冻结状态:D9/D10 的 14 天冻结已由 D13 提前终止,当前无冻结;D15 的额度裁剪是现行排期约束。
  • 发版:v71–v75 五个版本已上线;time.eigentime.org 与镜像均已生效(runbook A1/A2/B 已按可验证事实勾选并注明证据)。
  • 外部验证:仍未开始。Phase E 首轮推广未发生,docs/promo/ 三份仍是 AI 底稿,外部用户 ≈ 0。
  • 队列:SPEC-001–012 中,除 002(已于本日解锁)外全部 done;007/008 由 D15 park。AI 侧无待办。
  • 额度:紧张,已是一等约束(执行方曾撞周上限中断)。

结论

  1. gate 达成不改变优先级。28 天 gate 证明的是「这个工具对作者本人成立」——它成立了。但下一道门不是功能,而是是否对作者以外的人成立,而那道门至今没有人敲过。
  2. 瓶颈是人肉步骤,不是 AI 产能。AI 侧两天内交付两个版本、队列已空;唯一能改变项目处境的动作(Phase E 首轮推广)是维护者动作,已挂起数日。再多 AI 产出不会移动这个瓶颈——这一条是本次复盘最重要的判断。
  3. SPEC-007 / SPEC-008 维持 park,理由比 D15 时更强:007 服务「已有用户的体验」,而用户数为 1;008(landing 活体 mock)服务「落地页的说服力」,而落地页访客未知。008 在推广发生、且确认落地页是转化瓶颈之后才值得做。
  4. 放行 v76 合并批次(一次仪式,全部是已发生事实的收尾,不是新功能):
    • SPEC-002 旧站只读冻结(浸泡已确认足够);
    • 控件表面分层修正(v75 真机反馈:亮色下 chrome 比 content 更重,层级倒挂;拆出 --chrome 令牌,壳比页面略亮、井保持凹陷);
    • SPEC-011 遮条底缘 8–12px 渐变(真机验收发现硬切边,规格已预留此退路)。
  5. 求职硬约束仍在。CLAUDE.md 写明本阶段并行硬约束是 10 次高质量投递 + 1 个作品集案例,且「作品集不是新增功能的理由」。本次复盘无法核实其进展(AI 不可见),需维护者自行对账;若落后,AI 侧任何扩张应继续冻结。

下一个复盘的触发条件

首轮推广发生后收到第一批外部反馈,或出现第一个非维护者用户。不再按日历排期——时间不是这个项目的瓶颈变量。


D17 · 英文化与 App Store 上架路径(分层裁决:Web 英文化放行,原生载体维持 gated,2026-07-31)

触发

维护者提出「计划上架苹果美区或日区应用商店,需要英文界面与英文宣传页」,并问在此之前还需要做什么。

核对结论(本条依据的事实,全部当日复核)

Apple 侧(官方文本,2026-07-31 取自 developer.apple.com)

  1. Guideline 4.2:「Your app should include features, content, and UI that elevate it beyond a repackaged website.」4.2.2 进一步排除「web clippings, content aggregators, or a collection of links」。纯 WKWebView 套壳是被拒第一大类,与本项目当前形态直接冲突。
  2. Guideline 2.5.2:「Apps should be self-contained in their bundles… nor may they download, install, or execute code which introduces or changes features or functionality of the app」。⇒ 原生壳不得远程加载 time.eigentime.org/app/,运行时文件必须随包分发。这与 D8 路径 B 对「运行文件属于安装产物」的定义一致。
  3. Guideline 5.1.1(i):「All apps must include a link to their privacy policy in the App Store Connect metadata field and within the app」。⇒ 隐私政策不仅要有公开 URL,还必须在应用内可达。本仓库目前两者都没有(site/index.html 的 #privacy 是一段「数据边界」说明,不是隐私政策)。
  4. 账号:Apple Developer Program 99 USD/年,一个账号即向 175 个国家/地区分发——不存在「美区账号」「日区账号」之分,美区/日区只是 Availability 勾选 + 本地化 + 税务表差异;个人(非公司)主体的法定姓名会作为 Seller Name 公开显示,公司名需 D-U-N-S。维护者原始设想里的「美区或日区」是一个不成立的前提,已在此更正。

许可侧

  1. AGPL × App Store 的冲突有先例:App Store Usage Rules(设备数限制等)被 FSF 认定为对 GPL 的「附加限制」,GNU Go 与 VLC 的 iOS 移植均因此被下架。出口仍是 v31 保留的唯一著作权人商业双许可。当日核实:全部提交作者仅维护者两个身份(cg/cgx/xcg/wowayou,同一人),全部已合并 PR 的 author 均为 wowayou(AI 执行方的 PR 也在此账号下)——双许可窗口目前仍开着,在接受第一个外部代码贡献后关闭。

本仓库侧(grep 实测,修正了初判)

  1. 「约 800 行含中文」是高估:含中文行里相当比例是注释(ui.js 196 行中 42 行是纯注释行)。真实翻译面是非注释行里的 433 条唯一中文字面量(src/*.js + index.html)。
  2. 真正的成本大头是测试:tests/ui_smoke.spec.js 有 606 行含中文定位符/断言。若直接把 UI 改成英文,等于重写整套回归。⇒ 本决策要求 zh 保持默认、Playwright 固定 zh locale,现有 272 条断言一字不改照常绿,英文另加薄 smoke。
  3. scripts/project_audit.py 硬编码了两条运行时中文断言(aria-label="标记计划为已发生"、>改</button> 的反向断言),i18n 重构必须同步改,否则 audit 会红。
  4. 静态壳在 app-ready 之前是可见的:启动门闩只压 #add-btn/.ruler/.tl-head/#timeline/.usage-day,header 站点名与 天/周/月/年 视图切换在模块到达前就画出来了。⇒ 英文用户首访会看到一帧中文 chrome,除非 locale 在同步内联脚本里就解析完。这是 SPEC-013 的一个硬性设计约束,不是可选项。
  5. site/ 不进 SW 缓存(CLAUDE.md 明载)⇒ 英文 landing 与隐私政策页不需要版本仪式;只有「应用内隐私政策入口」是运行时改动,需并入一次 bump。

裁决

分三层,第二层是 gate,不是排期。

第一层(放行,AI 侧现在可执行)· Web 英文化 time.eigentime.org 上的 PWA 加英文,与 App Store 完全解耦:零 $99、零原生壳、零许可冲突、零审核。规格 SPEC-013/014/015。这一层的产出在第三层里 100% 复用(原生壳装的就是同一套 Web 运行时),因此不存在「先做英文是浪费」的分支。

第二层(gate,不是待办)· 触发条件 以下全部成立前,不开原生载体项目:

  • 首轮推广(runbook Phase E)已发生,且英文渠道已投;
  • 出现非维护者的真实留存用户,且其中出现对 Widget / 锁屏或灵动岛快捷记录 / Shortcuts 这类 Web 做不到的能力的反复请求——这是 D8/ADR 0001 早已写死的触发门槛,D14 第 1 条重申「仅为上架收费不构成触发」;
  • 商业双许可已自签(见下)。

D16 的判断在此完全适用且更强:瓶颈是人肉推广步骤,不是 AI 产能。当前外部用户 ≈ 0 时启动原生壳,是把最贵的动作排在最不确定的前提之前。

第三层(gated)· 原生载体 一旦触发:必须另立独立仓库(本仓库铁律:单页静态 / 无构建 / 无运行时依赖)。届时的形态底线由 4.2 决定——不是「套壳 + 上架」,而是「原生能力 + 内嵌运行时」:运行文件随包(2.5.2),并至少提供一项真实的系统集成(Widget / Live Activity / Shortcuts / 分享扩展)。前置项见 docs/launch-runbook.md Phase F。

随本条一并落定的边界

  • 数据迁移断点必须写进上架文案:WKWebView 的 localStorage 与网页 origin 是两个容器,现有用户(含维护者权威设备)的记录不会自动进 App,必须走「导出备份 → App 内导入」。不写清楚,第一个用户就丢数据。
  • App Privacy 标签是本项目最强的一张牌:零采集、零网络、零账号——这是可核实事实,允许作为卖点;但 D3 纪律不变,英文文案同样不得出现 "trusted by" / "loved by" 一类未被验证的社会证明。
  • 英文 App 名= Eigentime(维护者当日拍板):与已有域名 eigentime.org 同源、可检索、无明显撞名。catalog 里是单个 key app.name。上架前仍需自行做 App Store 重名与商标检索(这一步 AI 做不了,已登记进 runbook Phase F4)。
  • 商业双许可当日自签:LICENSING.md 随本决策落地——记录唯一著作权人事实、明确保留以其它条款另行授权的权利、并把贡献者入站授权补齐到「可再许可」(CONTRIBUTING.md 许可证节同步指向)。这关掉了 runbook F1 的时效项:此后接受的外部贡献自带第 ③ 条授权,不必事后追人。
  • v2 锁死条款不被本条推翻:英文化是同一产品的语言层,不是功能扩张;SPEC-013/014/015 不得夹带任何新功能、新图表、新分类法。

模型路由(D15 成本治理的延续)

单 复杂度判据 执行 验收
SPEC-013 i18n 层 触及全部 9 个运行时模块 + 静态壳同步脚本 + v53 快照 locale 门 + 新增 audit 护栏;一处漏改即静默回退中文 Opus 5 Opus 5
SPEC-014 英文 catalog 术语表已在规格内定死,执行是机械填表 + 语言开关 UI Sonnet 5 Opus 5(逐条核术语)
SPEC-015 英文对外页面包 纯静态页翻译 + 隐私政策成文,文案底稿在规格里 Sonnet 5 Opus 5

成本控制三条:① 013 + 014 + 015 的应用内隐私入口合并为一次版本仪式(v78)(D15 已证明合并发版是最大杠杆);② 015 的 site/ 部分不进 SW 缓存,零版本仪式;③ zh 保持默认使既有 272 条回归零改动照常绿,避免整套测试重写。


D18 · SPEC-007 解 park:主线可编辑与标签设置重设计(2026-08-01)

触发

维护者 v78 真机使用后提问:「标签的高级设置还是不能改、或者把某个标签设为主线,为什么不做?是忘记了还是逻辑上有问题?」

答案是两者都不是:D7(2026-07-13)登记了边界并把四类动作(改名/改桶/停用/longOk)拆开评估,SPEC-007 已按此写出完整裁决;随后被 D15 额度裁剪 park、D16 复盘维持 park。

推翻 park 的证据

D15/D16 给的 park 理由是「007 服务『已有用户的体验』,而用户数是 1」。维护者当日截图正是这条理由的反例:那 1 个用户的主线历史已累积 9 个标签(含 杂 32 条、雖離但都是主線 18 条、試試 2 条等试验残留与口语化命名),且没有任何入口能改名或清理。

主线标签的新增一直是可行的——录入时把桶切到「主线」并输入自定义标签即隐式 unshift 进 config.mainline(storage.js addMainlineTag),维护者的 #app推進(57 条) 就是这么来的。缺的从来不是「加」,是改名/清理/显式切换当前主线。

D7 原话要求「节点必须由真实使用证据决定」。这就是那个证据:不是「想要更好的设置页」,而是用了 30 多天之后,隐式累积的数据没有出口。

决定

  1. SPEC-007 解 park,状态转 ready,执行模型 Opus 5(D15 认定它是队列里最大、且唯一触及数据迁移路径(主线改名)的一单)。规格正文的四类动作裁决全部维持不变(改名做、longOk 做、改桶不做、停用/删除不做,另加「设为当前主线」)。
  2. 补三条 v78 之后才存在的硬约束(写进规格 §0):新增文案必须走 i18n catalog 且 zh/en key 对等(三条 audit 护栏会直接拦);标签名是数据不是文案,不随语言翻译;原生 <select> 换分段控件这条在 v78 之后价值更高——原生弹层不跟随应用语言,英文界面下会弹中文选项。
  3. 新增一项范围:给存量 config 一个显式入口拿到本语言的默认标签。SPEC-014 §1.5(方案 B)「只在首次初始化种子、切语言不动数据」的规则不改,但它留下的真空是真实的——存量用户切英文后界面是英文、标签全中文,且无路可走。补法必须是只追加、不删除不改名不覆盖同名、追加前预览可取消,不得做成自动行为或切语言时弹窗。
  4. SPEC-008(landing 活体 mock)维持 park。D16 的理由对它依然成立且未被任何新证据推翻:它服务「落地页的说服力」,而落地页访客仍然未知。解 park 一个不等于解 park 全部。

边界

  • 28 天 gate 已于 D16 认定达成,本条不构成对「别镀金」条款的整体放宽。
  • CLAUDE.md 的并行硬约束(10 次高质量投递 + 1 个作品集案例)不变,且 AI 侧无法核实——若求职进展落后,本单应当继续等,这是维护者的判断,不是 AI 的。
  • 首轮推广(runbook Phase E)仍然未开始,仍然是这个项目的真实瓶颈(D16 的判断未被推翻)。本条解 park 的是一个服务既有用户的修复类工作,不是对外扩张。

D19 · 标签生命周期对称:零记录标签可删(2026-08-03)

触发

维护者真机反馈:「现在新加一个主线标签会被自动设置为当前主线,设计上是合理的;不过我为了测试新加的『測試主線』标签,即使这个标签下没有一条记录也无法删除,这有点不够优雅。」

认定

SPEC-007 的裁决表把「停用/删除」判为不做,理由是一句具体的话:「历史记录引用主线历史名,删除会产生孤儿标签」。这条理由是对的,但它只在有记录时成立。零记录的标签删掉之后没有任何东西会失去引用,此时的「不做」不是保护数据,只是把一个理由套用到了它不适用的场合——于是试错建出来的标签永远留在设置页里。

顺带查出同一件事的反面:清空名称一直等于悄悄删掉那一行(早于 SPEC-007 就存在)。它既不可发现(没有任何文案提到),又恰好在最危险的场合生效——chip 有记录时清空名称会静默把历史打成孤儿标签,统计当场变化。也就是说,删除在该被允许的地方被禁止,在该被禁止的地方却是隐式可达的。

同类排查的其余结论(一并记录,避免下次重查):localStorage 的其余键(视图、日期、主题、语言、记录模式、启动诊断、firstUsedDate)全部是可逆的开关或纯诊断值;记录本身有删除 + 8 秒撤销;格言可清空。标签是这个应用里唯一的单向门,本条修完即无。

决定

  1. 删除按「零记录」开放,判据 countEntriesWithTag(entries, name) === 0,主线与 chip 同一条判据、同一个入口。有记录的标签仍然不提供删除——改名(会迁移历史)是那种情况的正确动作。
  2. 判据必须在保存时按最新 load() 复算:sheet 打开期间另一个标签页给该标签记了一条,删除必须被拦下。渲染时的零记录判据会过期,这与 v48 区间事务「提交前基于最新数据重算」是同一条纪律。
  3. 删除是可撤销的待生效态(行变灰 + 按钮翻「撤销」,保存才生效),因此不加确认弹层:零记录=零数据损失,多一层确认只是噪声。
  4. 清空名称改为内联报错,删除只保留显式入口一条路。
  5. 放开 config.keepOneChip:删空一组不再被拦——录入时写自定义标签就能重新长出来,tag picker 早有空桶提示;空组显示空态提示而不是空的圆角灰盒。
  6. 删除生效时让还挂着的「撤销删除记录」失效(复用跨标签页冲突的同一机制):否则 8 秒窗口内一撤销就把引用该标签的记录放回来,正好从后门造出第 1 条要防的孤儿标签。

边界

  • 「停用」仍不做:保留历史归类但阻止新记录选用,是另一种语义(需要「隐藏但仍在」的第三态),本条不授权。
  • 改桶仍不做(SPEC-007 原裁决未被触碰)。
  • 本条是服务既有用户的修复类工作,不是功能扩张;首轮推广仍是这个项目的真实瓶颈(D16/D18 判断未被推翻)。

补充(同日,维护者拍板):新建标签也归位

维护者随即录屏问第二件事:标签高级设置里翻不到「新建标签」,是有意设计吗?

是有意的,但理由已经不成立了。原设计是「标签在记录时长出来」——录入时写自定义标签即按当时选的桶记住(rememberCustomTagForBucket),设置页被 SPEC-007 划定为「管理已有的」,依据是 D13 硬约束①「首次打开即用」(不要求先建分类体系)与「别镀金:不做可扩展分类法」。这条设计本身仍然对:标签应当从真实记录里长出来,而不是在一次规划会里被设计出来。

但 v82 之后,这张 sheet 能改名、改桶、设当前、删除,唯独不能建——这正是维护者上一条反馈的同一类不对称,只是镜像过来。而且页面上早就有个半吊子的「添加本语言的默认标签」,它只肯追加固定那一套。「不做上手期的分类法设计」与「有一个建标签的出口」并不冲突,前者靠不强制、不引导来实现,不需要靠把入口藏起来来实现。

决定:每组底部加「+ 新建标签」,插一行草稿,与已有行走同一套保存事务与校验(v83)。四条边界:空草稿视为没建过(与已有行空名报错相反,因为草稿从未落库);新建主线名追加到历史末尾、不顶掉当前主线;未知 是 unrecorded 桶的保留名,新建与改名同一处拦下;插入草稿不重渲染整张 sheet(否则会丢掉别的行未保存的编辑)。

仍然不做:桶的增删(四桶 taxonomy 是红线)、标签排序拖拽、批量操作、停用态。


D20 · v82/v83 真机验收反馈的裁决(2026-08-04)

触发

维护者在真机跑完 docs/device-acceptance.md 的验收单,结论「基本可以」,同时给出 8 条反馈。本条只记裁决与理由,逐条的实现细节在 CLAUDE.md v84 行。

做了(v84)

  1. 空名报错滚到页面最底部 —— 判据错误,不是参数问题:错误条挂在正文末尾,把它滚进视野就必然滚到底。改为定位出问题的那一行。
  2. 撤销横幅停留太长 —— 维护者选方案 b:8 秒上限不动,下一次交互即收起。理由是「你已经在做别的事了」比倒计时更准地表示撤销窗口该结束。代价如实记录:v82 的「删标签让待撤销失效」守卫在 UI 上因此不再可达,守卫保留为防御,用例改为断言当前事实。
  3. 「更多」面板太长 —— 维护者选 (a)+(b) 都做:备份四项 → 「备份与导入」,运维两项 → 「高级」。13 行压到 9 行。顺带把 v41 的返回栈从布尔升级为栈(两层下钻是这次才第一次出现的形态)。
  4. hero 的 ~ —— 维护者选 (a) 直接去掉。决定性事实是它只存在于中文路径:英文侧从来没有波浪号,所以它不承载任何信息。
  5. 退出动效白边 —— 维护者做了对照实验:暗色下白边仍在,排除「亮色页面 vs 深色 background_color」。改判候选为 color-scheme 从未声明(UA 因此按 light 画它自己的表面,含合成器基础背景色)。已按四处令牌块补齐声明,真机确认仍欠一次录制。

不做(附理由)

  1. 标签管理多选 —— 现在删多个标签本来就是「逐行点删除 + 一次保存」,多选省不了几步,却要引入选择态、批量动作条,并与草稿行/待删除态三态打架。真正的痛点是合并(下条),不是批量。
  2. 未知 的语义 —— 记一条 #未知 显示为未记录并给「补一下」,这是设计:它是「我在,但不想标」的显式标记,与系统留的空占位不同。v83 拦的只是把标签取名叫未知。
  3. joi.com 交互参考 —— 该域名解析到一个 NSFW AI 聊天站,与 PWA 交互参考对不上,判为记错域名。待维护者给出正确链接或具体交互点。

已受理、未排期(下一批的首选)

标签合并(睡觉 与 Sleep 是两个标签,且今天根本没法合并:改名撞到已有名字会被「重复了」拦下)。这是 v78「切语言不动数据」+ v80「添加本语言的默认标签」共同的可预见后果,出口被自己堵死了。做法定为:改名撞到已有名字时不再直接报错,而是问「把「Sleep」的 N 条记录并入「睡觉」吗?」→ 迁移记录 + 删源标签。大小写不敏感的重名判定(sleep/Sleep)必须与它同批上线——判定改严之后,已经同时存在两者的存量 config 会在每次保存时被拦下,没有合并出口就是死结。


D21 · 标签同一性与合并(2026-08-04)

触发

D20 文末受理项。维护者原话两句:「sleep 和 Sleep 应该算同一个吧」,以及中英默认标签并存那张截图(睡觉 与 Sleep 是同一件事却是两个标签)。

排查挖出的更严重一层

今天根本没法合并两个标签:把 Sleep 改名成 睡觉 会被「重复了」拦下。出口是 v80 自己堵死的;而并存本身是 v78(按 locale 种子)+ v80(「添加本语言的默认标签」)的可预见后果。所以这不是「加个便利功能」,是补一个被自己关掉的门。

决定

  1. 同一性判据改为 tagKey() = trim().toLowerCase(),一切「这两个名字是不是同一个」的比较走它。用 toLowerCase() 而非 toLocaleLowerCase():后者随运行环境语言变(土耳其语无点 i),同一份数据在不同设备上会得出不同答案,标签是数据。
  2. 判据改严必须同时给出口——否则存量同时存有两种拼写的 config 每次保存都被拦死。这就是「合并 + 大小写判定必须同批上线」的实质理由,不是工程便利。
  3. 数据层不做静默合并:normalizeConfig 的去重仍逐字比较。第一版顺手让它折叠,结果并存的两个在加载时就被丢掉一个(用户没同意;两者桶/longOk 不同还会悄悄改变历史归类),而且设置页因此看不见冲突、合并入口反而无从触发。只有显式动作才动数据。
  4. 合并的方向由数据说了算:一边被改名→被改名的是来源;两边都没动→按逐字计数留下用得多的那种拼写(同一性计数在这里会得出相同数字,分不出方向)。来源必须是已落库的行;草稿行撞名是「别建这个」,退化为普通重名错误。
  5. 合并是破坏性动作,因此要显式确认 + 复算:提示给确切后果(会改写多少条,已经是目标拼写的不计),带独立的「合并」按钮;签名按最新 load() 复算,确认期间数据变了就重新问一次(与 v48 区间事务同一条纪律)。
  6. 记录侧规范化拼写:敲 sleep 而 config 里是 Sleep 时,记录存权威拼写——否则时间轴显示 #sleep、设置页显示 Sleep,同一个标签两副面孔。

边界

  • 不做同义词/别名表(睡觉 ≡ Sleep 这种跨语言等价仍需用户手动合并一次)——那需要一张会随语言与个人习惯膨胀的映射表,且会把「标签是用户的词」变成「标签是我们的词」。
  • 不做全角/半角、空格折叠等更激进的归一化:本轮只处理大小写这一种,因为它是唯一有真实证据的。

D22 · 「在布局里」不等于「在视口里」(2026-08-04)

触发

v84 把标签设置里「保存被拦下」的滚动目标从错误条改成出问题的那一行,却把错误条留在正文末尾。真机第二轮验收里 A6/B5/B6/C1.1 四步全部表现为「拦下了,但没有任何提示」——用户看到的是应用没反应。

为什么用例没拦住

那条用例断言的是 await expect(error).toBeVisible()。Playwright 的 toBeVisible() 只检查元素有非空边界盒、且未被 display:none/visibility:hidden 隐藏——元素滚出视口照样通过。也就是说,v84 改的正是「元素在不在视口里」这件事,而用例检查的是另一件事。

这与 HANDOFF 里既有的那条(「反馈类断言一律 toBeVisible()」,用来取代 toContainText)不矛盾,是它的下一层:toContainText → toBeVisible() → 在视口里。凡是「用户能不能看见」的判据,都要走到第三层。

决定

  1. 修法是 position: sticky; bottom: 0(底色改不透明),出错行与提示条同屏——而不是把滚动目标改回错误条(那会把 v84 修好的定位又弄丢)。
  2. 新增测试写法约定:expectInViewport(locator) —— 比较边界盒与视口的相交关系。此类断言从此用它,不用 toBeVisible() 冒充。
  3. 记入 HANDOFF「已知坑」,与冻结时钟那条并列。

顺带两条判决

  • 白边(第三轮):录屏逐帧确认那条浅色带内部有滚动条滑块——是经典滚动条占的轨道,不是窗口底色。窄屏隐藏根滚动条,桌面保留。v84 的 color-scheme 保留:它管颜色,不管占位,两件事。
  • 状态栏那条线:取样显示它比两侧都暗(194 vs 236/238),而本项目在那一带只画 --bg 实色与 --bg→transparent 渐变,画不出比 --bg 更暗的东西。判为系统侧,不动代码,给维护者一个可证伪的暗色对照实验。

D23 · 退出动效白边结案:系统行为,不做(2026-08-05)

结论

Galaxy S23 上 PWA 退出动效里的白边,来自 Chrome/One UI 在窗口缩放期间用窗口底色填充未绘制区域,Web 侧改不掉。本条关闭,不排期。

定案依据(决定性的那一条)

维护者在其他 PWA 上做同样的退出手势,白边同样出现。同时确认:跑的是 v86、滚动时右缘已无滚动条(即 v86 的 CSS 确实生效了,但白边照旧)。

这条追查值多少钱:四轮,三个错误假设

轮次 假设 怎么死的
1 亮色页面 vs 深色 background_color 错配 维护者暗色对照实验证伪
2 color-scheme 从未声明 补了,白边照旧(但这条改动独立成立,保留)
3 经典滚动条占的轨道 静止态左右边缘亮度对称(220/220)、白边只在动效期间出现;「动效里看见滑块」只说明滚动条那一刻可见(退出手势自带一次滚动),不说明它恒定占位
4 —— 跨应用对照,20 秒,结案

方法论裁决:docs/HANDOFF.md 的验收纪律里早就写着「归因必须有对照组」。这条追查前三轮全在自己的 CSS 里找嫌疑人,直到第四轮才做对照——规则在、却没被执行,与「gitignore 规则在、却从不生效」是同一类失败。

写进纪律:现象只在某个系统动效/系统 UI 参与的场合出现时,第一步做跨应用对照,不是读自己的代码。这一步比任何逐帧分析都便宜。

两处遗留改动的去留

  • v84 的 color-scheme 四处声明:保留。理由换成独立的那条——暗色下 UA 自己画的表面(滚动条、原生控件、文本选中)本就该跟着主题走。
  • v86 的窄屏隐藏根滚动条:保留,但理由必须换——手机上恒定预留一条滚动条轨道本身就不对(iOS 与多数 Android 都是 overlay)。它不再是白边的解药;任何文档或提交信息都不得再那样引用它。维护者若认为丢了滚动位置指示更亏,回退那几行 CSS 即可。

D24 · 全量审计的四条裁决:没有任何写入路径可以代替用户确认(2026-08-10)

起因

维护者一个问题 + 一份真实备份(633 条):「时间尺 0808-0809 这两天这么大段的一个记录 为啥没让我确认呢?」,并要求「彻底审计下这个 app 还有哪些隐藏 bug」。

那两段为什么没问:C7A 的前提是假的

备份里 2026-08-08T06:48(17h12m)与 2026-08-09T00:00(20h41m)两条「整理」各带一枚 longConfirm,是 planOvernightContinuation 写入时盖的(v67/C7A,D10 定向解冻批次)。C7A 的理由写得很清楚:「过夜表单两端都是用户显式断言」。

这条前提经不起看一眼表单:过夜表单里用户能改的只有起点。两个终点都是程序给的——跨午夜段的终点是日历上的 00:00,今天段的终点是 now 或今天第一条已有记录。没有一段是双端断言。

而且这个碰撞是结构性的、不是偶发:过夜段的「昨天部分」只在很长的时候才会超过 3h 阈值——也就是说,自动确认唯一起作用的场合,恰恰是最该问一句的场合。典型的 23:00→24:00 只有 1h,本来就不会有徽章。

裁决:撤销 C7A,所有写入路径一律不自动确认。 豁免只剩一条:config 里显式设的 longOk(默认只有睡觉)。这也把规则收敛成一句能记住的话——没有任何写入路径可以代替用户确认。

比原提案更进一步:向维护者汇报时我提的是「只保留『起点→午夜』段的自动确认」。写代码时发现那个折中买不到任何东西——它唯一生效的场合就是 17h 那种,正是要修的病例。已如实说明并按更严的版本执行。

顺带查出的三条(同批修掉)

# 缺陷 为什么算数
② 起点晚于尾占位点时,[占位点, 新起点) 这段「确实没记」被静默改写成前一条记录的标签 本批最重。踩 D13 硬约束③;实测把两小时未记录变成主线时长,无预览、无提示
③ 空日继承前一天最后一个标签整整 24h,还各挂一颗「确认」按钮 与 CLAUDE.md 明文的「空日不继承前一天最后标签」正面冲突
④ 段内坐着一条计划时,那颗「确认」永远返回 stale、怎么点都确认不掉 渲染侧 loggedEntriesFrom / 确认侧 sortedEntriesFrom,两边右邻不同

②③ 的共同形状值得单独记一笔:两处都是「时间轴上的一段空白被无声地改了归属」,而且都不经过任何预览或确认。v48 给区间编辑/切分/删除建了完整的 planner + 预览 + 签名复算机制,唯独新增这条最热的路径没有——它靠的是「挪动占位条」这个隐式手势。

判据放哪里

③ 的上限放在 segmentBoundsForEntry 一处,因为 confirmSegmentInData 与 buildRangeSegmentsFromEntries 都调它——渲染、统计、rawMins、确认端点四者因此天然一致,不需要四处各写一遍再互相对齐。④ 则是同一条纪律的反面教材:同一个「右邻是谁」的问题,两处各答各的,答案不一致了一年多没人发现,因为它的症状是「按钮没反应」而不是报错。

性能:判据用结构性的,不用耗时的

同批修掉年视图的二次方开销(O(天数 × 条数) + 每片段三次 config 重解析;5475 条时一次 render 3055ms,且每分钟自动重算一次)。回归判据刻意选「一次年视图 render 读了几次 timelog.config」而不是毫秒数:stress.spec.js 的 boot ratio 判据常年 flaky,本批次前后各跑一次全量已确认那个 flake 与本改动无关——耗时判据在并行 CI 里天生抖,抖到最后没人再当真,那就等于没有护栏。

一条方法论

四条缺陷全部在真实备份上先复现、再落用例;②③④ 另外在真实页面上跑通了「修复前是错的」这一步。这批不是读代码读出来的——是拿 633 条真实数据把每一天的段重算一遍、和界面上应该出现什么逐条对照出来的。别人的真实数据是最好的模糊测试语料,尤其当它已经跑了 43 天。

后续(2026-08-10 同日)

D24 那份审计另外登记了四条「不是阻断级、登记不排期」的小缺陷。维护者当天回「bug 要修」,遂全部随 v88 交付:躲让越午夜、计划保存不过 normalizeEntries、计划与占位条同刻、更新通道探活被自己的 SW 缓存吞掉。逐条经过与红灯清单在 CLAUDE.md 的 v88 行。

一条边界记在这里:normalizeEntries 不补进导入路径。它里面的 coalesceRedundant 会删掉相邻同日同标签同文本的条目——对刚导入的一批数据跑它,等于可能静默丢弃备份内容。「所有写入路径都该走同一个归一化出口」这条一致性诉求在这里让位于「导入不得丢数据」。

D25 · 长段确认退出默认统计路径,保留为默认关闭的待核提醒(2026-08-11)

起因与证据

维护者要求复盘 v88 的长段确认,并明确倾向「直接砍掉」。取证仍使用 D24 的真实备份:633 条、43 个有记录日;长段不是极少数异常,至少 37 段超过 3h、分布在 30/43 天。里面既有疑似漏记,也有大量睡觉、整理、开发、出行等可以真实持续数小时的活动。确认动作并不稳定:一条 4h51m 段曾被确认,后来边界变化使标记按设计失效,用户没有察觉。旧规则会把整段从明确标签桶搬到未记录,单次即可让 hero 主线净时长跳变数小时;它防的是“可能误标”,代价却是制造确定的统计失真。

裁决

  1. 默认砍掉统计门槛。 config.longReview 缺失或非 true 时,明确标签段无论多长都按原桶统计,不显示待核,也不要求确认。
  2. 保留为本机可选提醒,默认关闭。 用户在高级设置显式开启后,超过 3h 的非 longOk 段附加“待核”状态;它不改变原桶分钟数,pending 是与四桶重叠的注记,不再等同于 unrecorded。
  3. 确认只清提醒。 longConfirm.startTs/endTs 的边界绑定、失效规则和 loggedEntriesFrom 同序列判据保留;确认不再决定这段时间算不算进原标签。
  4. 不迁移用户数据。 存量 longConfirm 与逐标签 longOk 原样保留。全局开关关闭时隐藏 longOk 控件但不删除值;再次开启即可继续使用。
  5. 导入不能替用户开功能。 longReview 是设备上的行为偏好,备份可以携带、校验,但合并时以本机值为准,避免导入后静默恢复确认流程。

明确不做

  • 不增加 1h/2h/3h 可调阈值;在默认关闭的提醒上继续扩设置,收益不足以支付复杂度。
  • 不增加“刚闭合长段就弹窗确认”。它会进入约 5 秒一次的最热记录路径,提醒价值不足以覆盖打断成本。
  • 不做跨日召回中心、未确认列表或徽标体系。可选提醒不影响统计,遗忘不再造成静默少算。

可逆性

这是可逆裁决:旧字段和确认逻辑都保留,未来若真实使用证据证明提醒有价值,只需开启本机开关;若长期无人开启,可再删除可选入口,而无需迁移记录数据。D24 关于“写入路径不得自动确认”的边界继续有效,但自 v89 起它只约束可选提醒,不再是统计正确性的前提。

D26 · 安卓原生载体开工:另立仓库、内嵌运行时、原生入口(2026-08-22)

背景:维护者要求「先做成安卓 apk 应用,要解决的核心问题是记录阻力」。这项此前是 gated 项——D8 / ADR 0001 / D14 / D17 把触发门槛写成「出现非维护者真实留存用户 + 对 Widget/锁屏记录类原生能力的反复请求」,launch-runbook.md Phase F 亦然。

裁决:维护者以维护者身份提前放行,本条如实登记这一点:门槛未达成,是显式 决定而非条件成立。放行范围只到「原生载体存在并能降低记录阻力」,不含上架、不含 收费形态(D7/D14 不受影响)。

形态(与 4.2「不是套壳 + 上架」一致):

  1. 另立独立仓库 time-logger-android(本仓铁律:单页静态 / 无构建 / 无运行时依赖, Gradle 工程绝不进来)。本仓一个字节不改。
  2. 运行时随包内嵌:scripts/sync_runtime.py 解析本仓 sw.js 的 FILES 逐字节复制到 assets/app/,并记录来源 commit;离线不依赖 time.eigentime.org。 刻意跳过 sw.js——APK 里 cache-first 的 Service Worker 只会造成「升了包还是旧界面」。
  3. 原生持有存储:一个键一个文件(原子 tmp+rename),web 运行时通过注入的 localStorage 桥(@JavascriptInterface 是同步且能返回值的)读写同一份数据, 运行时代码零改动。因此小组件/磁贴/通知写的与界面读的是同一份。
  4. 原生侧不实现业务逻辑:起点、占位条复用、同刻唯一、归一化、CAS 全部由本仓的 真实模块在一个无界面 WebView 里执行。契约写在安卓仓 docs/DATA-CONTRACT.md, 配套 quick_write_smoke.py(41 条断言,真实模块 + 真实桥)与 redlight.py (9 处守卫逐条撤掉必须变红,且只红对应用例)。
  5. 不声明 INTERNET 权限,云备份与换机迁移关闭。

一键记录的语义由维护者当场纠正并据此实现:点一下=追认「上次记录点 → 现在」 这段在做什么(因为记录只存起点、时长由下一条推断,一次点击就是一条完整记录), 而不是「从此刻开始计时」——后者是 planned 分支,做成可选项。

新增的守卫(不在本仓,但属同一条产品底线 D13③):默认起点距现在超过 4 小时、 或当天一条记录都没有(起点会落在 00:00)时,一键不静默写入,改为打开表单让人 确认起点。一次点击给一大段空白盖标签,比漏记更糟。

如实登记的代价:APK 内是一份独立数据。手机上已装的 Chrome PWA 与 APK 不共享 存储,首次要用现成的「完整备份」导出→导入迁移一次,之后不能两边同时记。这是 「原生能力 + 内嵌运行时」这条路的固有代价(TWA 套壳能共享 Chrome 存储,但原生代码 碰不到那份 localStorage,小组件也就只能「打开表单」而非真一键)。

对本仓的影响:无运行时改动、无版本仪式。本仓新增的只有本条决策与 docs/HANDOFF.md 的一行指针;安卓仓的版本号从本仓 manifest 版本派生 (versionName = <web 版本>.<android revision>),避免两处各说一个版本。

D27 · 安卓壳的付费形态:$1 买分发,不买功能(显式修订 D7)(2026-08-22)

背景:维护者决定把安卓壳上 Google Play,临时定价 $1,并明确要求「一定要诚实说明, 有开源的可用」。D7 写的是「若未来设置金钱入口,定位为『支持作者』……且所有功能始终免费」; D14 第 3 条早已预留过这条路(「若未来走『免费 Web + 付费原生壳』,Web 端承诺可以不破—— 但那是显式推翻/修订 D7 的新决策,不能悄悄滑过去」)。本条就是那个显式修订。

裁决:

  1. 卖的是商店分发与自动更新这项便利,不是功能。 不得引入付费功能、订阅、内购、广告、 分析 SDK;不得把任何现有能力挪到付费侧。D7 的「所有功能始终免费」在功能这个层面 继续有效,本条只是把「金钱入口」从「更多菜单里的支持作者」扩展到「商店售价」。
  2. 免费获得的路径必须一直畅通:源码公开、可自行构建、GitHub Release 提供免费 APK、 网页版永久免费。AGPL 本身也允许任何人把买到的包免费再分发。
  3. $1 不是护城河,且不得试图变成护城河:不做许可校验、不做联网激活、不做设备绑定 (既违背 AGPL,也与「无 INTERNET 权限」这条产品事实冲突)。
  4. 诚实声明是硬性交付物,三处缺一不可:应用内(壳设置「关于本应用」)、README、 商店完整描述的第一段。措辞要点:开源(AGPL-3.0)、源码与免费构建的地址、网页版永久免费、 「付的是分发与自动更新,不是功能」。删除或弱化这三处任一处,等于推翻本条。
  5. 仓库公开(不是私有)。三条理由任一足够:分发 AGPL 作品需向接收者提供对应源码; D2/D7 已定「仓库继续公开」且理由仍成立(改私有保护不了浏览器里就能读到的前端源码); 第 4 条那句声明必须可被验证。
  6. 不合进本仓:Gradle 工程进来就破「单页静态 / 无构建 / 无运行时依赖」铁律, D8 / ADR 0001 / D26 都已写明另立独立仓库。
  7. 不上 App Store:Apple 的 Usage Rules 与 GPL 系冲突(LICENSING.md §3 已记 GNU Go 与 VLC 的先例)。LICENSING.md §3 保留的「另行授权」是给那种商店的逃生门,本次 Play 上架不使用它——Play 照 AGPL 分发即可。

如实登记的边界:

  • 定价是临时的,没有任何需求验证支撑。不得据此声称「市场愿意付费」——外部用户验证 至今尚未开始(D9 以来这条没变)。
  • 上架不构成 D17 那个「原生载体 gated」门槛的达成,D26 已登记那是维护者提前放行。
  • Play 的「免费 ↔ 付费」切换有平台限制,定价一旦公开就不是随手可改的;这一步比写代码难退。
  • 上架前仍缺三件与代码无关的东西:time.eigentime.org/privacy 增补一节「Android 应用」 (零收集、零联网、本地存储、卸载即删除)、release 上传密钥、商店资产(必须用合成 demo 数据,真实记录截图不得用)。清单见安卓仓 docs/release-checklist.md。

D28 · 收窄 D27:安卓壳免费上架,钱放在网站上的自愿支持(2026-08-22)

背景:维护者拿来 https://bondavi.jp/en/donate 问「参考这个开发者的方式可以吗」。 调研结论是那套模式就是 D7 原文:应用完全免费、无广告、无内购、无订阅;纯自愿的 一次性捐赠(PayPal 三档 $10/$30/$100);捐赠者不获得任何功能或特权;页面明说 「一次性,不是订阅」。他们的理由是「把用户放在前面导致收入很低」。(该页完全没有提 开源——这一点我们与他们不同,见下。)

也就是说:可参考,而且同日早些时候定下的 $1(D27)才是偏离。维护者据此裁定采用 免费 + 自愿支持。本条收窄 D27,不推翻 D26。

裁决:

  1. Play 免费上架。 不做付费上架、不做订阅、不做内购、不做「解锁高级功能」、不做广告、 不接分析 SDK。D7 的「所有功能始终免费」恢复为字面有效。
  2. 金钱入口只在网站:一次性支持页(渠道建议国内爱发电、海外 PayPal.me 或 GitHub Sponsors),必须写明不购买任何东西(不买功能、不买优先权、不买产品路线影响力)。 应用内不放任何支付界面,只在「关于本应用」里陈述事实并指向网站。
  3. 不为阻止免费流通做任何事:不做许可校验、不做联网激活、不做设备绑定。AGPL 允许 任何人免费再分发本应用,这是特性不是漏洞。
  4. 诚实声明三处仍是硬性交付物:应用内、README、商店描述。措辞从「付的是分发与自动 更新」改为「免费、无广告、无内购;支持作者完全自愿且不购买任何东西」。

放弃 $1 的理由(如实记账,不是道德判断):

  • 现阶段最缺的是用户(外部验证至今尚未开始);$1 恰好用装机量去换几乎为零的收入。
  • 源码公开 + AGPL ⇒ 付费墙在事实层面不存在,$1 挡不住任何想免费拿的人。
  • 付费应用要开收款/税务/银行那一整套,为零收入付出的手续成本不成比例。
  • Play 的「免费 → 付费」不可逆(一个已发布的免费应用不能改成付费)。这一条与直觉相反: 先免费才是保留选择权的那一边——将来若真要收费,是新建付费 SKU 或另立应用的问题, 而不是把这个应用改价。

待办与风险:

  • Google Play 对「非营利之外的开发者在应用内引导外部捐赠」的规则历年反复,Epic v. Google 之后美国区外链规则亦有变动。上架前必须重查一次现行政策;当前形态(应用内零支付界面) 正是为了不依赖那条规则。
  • 网站支持页尚不存在。在它上线之前,应用内不放链接(宁可少一行也不给死链)。
  • 本条不改变 D26 的登记:原生载体是维护者提前放行的 gated 项;也不构成「市场愿意付费」 的任何证据——那件事至今未被验证过。

D29 · 英文名统一为 Eigentime(口径对齐,非改名)(2026-08-22)

背景:维护者问「叫什么名字合适呢?eigentime 还是什么」。调研先查了自己的仓库, 结论是这个决定其实早就做过了——运行时的英文词典里 shell.brand 就是 Eigentime (英文界面的 header 一直显示它),域名 eigentime.org 也早已在手。真正的问题不是取名, 而是三处两个名字:

表面 之前
运行时英文界面 Eigentime
site/en/ 英文站点(title / h1 / meta / alt,13 处) Time Logger
安卓壳的英文应用标签与小组件标题 Time Logger

裁决:英文统一为 Eigentime,中文保持 时间尺。站点英文页与安卓标签改过来, 运行时不动(它本来就是对的)。这是口径对齐,不是改名。

为什么不留 "Time Logger":它是通用词。Apple 自己的命名指南明确劝退 ("Be distinctive. Avoid names that use generic terms or are too similar to existing app names"),Play 的元数据政策也要求标题「honest, relevant」且禁止塞关键词——通用词既排不上, 也不构成品牌。描述性文字的正确位置是简短描述,不是标题。

Eigentime 这个词本身:德语 Eigenzeit 是相对论里的「固有时」——沿着你自己的世界线 测得的时间。对一个只记录「你自己这一天」的账本来说,这个来历比任何通用词都贴。

可获得性(如实标注证据强度):Play 商店搜索 eigentime 没有同名应用(实测, 只有 EmanTime / EasyTimeManager 这类不相干的近似词)。商标未查——USPTO / EUIPO / CNIPA 的检索没做,搜索引擎只返回了通用的商标常识页。上架前应自行检索一次;本条不构成商标可用性结论。

包名 org.eigentime.timelogger 保持不变:反向域名、用户看不见、上架后不可改, 换它没有任何收益。仓库名 time-logger-android 也保持(与 time-logger 成对;GitHub 改名 随时可做且会保留跳转)。

Play 标题按语言分别设置:zh-CN = 时间尺,en-US = Eigentime(均在 30 字符上限内)。

D30 · 自愿支持入口落地:渠道定爱发电、入口走 eigentime.org 中转页(修订 D28)(2026-09-21)

背景:维护者要优先恢复现金流,拿来一份《Eigentime 个人赞助体系 V1》规格, 把六个项目(本仓 Web、安卓壳、site、SEO Operator Lab、博客、其余 GitHub 项目) 统一指向一个赞助入口,本仓承担其中的「Agent B:Time Logger Web」。

方向上没有新东西:D7 早已定「付费方向=支持作者,入口只放『更多』里,所有功能 始终免费」;D28 定「金钱入口只在网站,应用内不放任何支付界面」;docs/freeze-candidates.md 的 C3 更是把形态就写成「更多 sheet 加『支持作者』cell」,并已排除收款码方案 (个人收款码图片带姓名局部,撞隐私红线「公开仓库不得含具体个人线索」),点名 平台中转页为替代项。本条只是把 C3 那个「平台中转页」填上具体渠道并落地。

裁决:

  1. 渠道:爱发电(https://afdian.com/a/eigentime),账号身份是创作者本人 (Eigentime · CG-X)而不是本项目——博客、本仓、SEO Operator Lab 将来共用它。
  2. 应用内只放一条外链,指向 https://eigentime.org/support?from=time-logger, 不直连收款平台。中转页是刻意的解耦层:换平台或加平台时只改那一页,不用 为此发一版 PWA(本仓发版要走七锚点仪式 + tag + Release + publish-site,把平台 URL 写进运行时等于把平台变更绑上发版成本)。判据见 tests/v1_1_0_support_entry.spec.js ⑤(应用内全 DOM 不得出现平台名或直连链接)。
  3. 来源参数 allowlist 固定,本仓取值逐字为 time-logger。禁止自创变体 (timelogger / time_logger / timer)。URL 不按 locale 分流——中转页 自己决定语言,而 from 是归因字段,两种语言必须落进同一个来源桶。这与旁边 PRIVACY_URL 的分流写法相反,所以 spec ② 留了反向哨兵:照抄那个模式是最自然 的「顺手改进」,而后果(来源桶被拆两份)在界面上完全看不出来。
  4. 应用内零支付界面、零埋点。D28 第 2 条继续字面有效:没有金额档位、没有支付 控件、没有 iframe/WebView 嵌入收款页。规格 §8 要的 GA4 support_click 属于 中转页那一侧;本仓不接任何分析 SDK(D27 第 1 条、D28 第 1 条都禁止),也不 记录点击——「哪个项目带来赞助意愿」由 from 参数在网站侧回答。
  5. 诚实声明进运行时文案(D28 第 4 条的「应用内」那一处):完全自愿 / 所有功能 始终免费、无广告、无内购 / 支持不购买任何功能、优先权或产品方向。措辞纪律沿用 规格 §18:不制造义务感、不喊紧迫、不卖惨,不得出现「没人支持项目就无法继续」 这类说法,也不提作者个人处境——用户支持的是工具本身。spec ③ 守这三层意思。
  6. V1 边界(与规格 §9 一致,逐条对本仓有效):不接爱发电 API / Webhook / Stripe / PayPal / Ko-fi / GitHub Sponsors,不做登录、赞助者账号、付费墙、Pro、 Sponsor Badge、排行榜、订单系统、License 校验、权益解锁。D13 四条硬约束不受影响。

显式修订 D28:把链接放在了 /support 上线之前

D28 原文写的是「网站支持页尚不存在。在它上线之前,应用内不放链接(宁可少一行也 不给死链)」,规格 §11 与 §24 的执行顺序(P1 网站 → P2 认证 → P3 本仓)是同一个 意思。2026-09-21 实测:https://eigentime.org/support 与 /zh/support/ 均为 404,eigentime.org 首页 footer 也还没有 Support 入口。

AI 代理据此提出该冲突并给出「完整落地但不发版,等上线后再推 tag」的选项; 维护者裁定直接发布上线,接受 /support 上线前存在死链窗口。本条如实登记这次 推翻,不粉饰:

  • 这是一个已知的、自愿承担的回归窗口——在 Agent A 完成 /support 之前,点 「更多 → 支持作者」的用户会看到 404。窗口长度取决于网站侧何时上线,本仓无法控制。
  • 风险不对称:本仓发版重(tag + Release + publish-site + SW 缓存换代),网站侧 改一页轻。所以缩短窗口的正确动作在网站侧,不是在本仓回滚。
  • 若窗口拖长到不可接受,优先补 /support,而不是发一版把链接摘掉——摘链接要 再走一遍完整发版仪式,成本比补一个静态页高得多。
  • 附带的未定项:eigentime.org 是双语站(/zh/ /en/,隐私政策走 /zh/privacy/), 所以 /support 究竟是无语言前缀的单一路径,还是会变成 /zh/support/ 两页,取决 于 Agent A 的实现。本仓按规格写死无前缀的 /support(规格 §12 的验收项就是 「/support 可公开访问」)。若网站侧最终落成带语言前缀的形态,必须让 /support 保持可访问(重定向即可),否则本仓要再发一版改 URL。

英文文案不用规格里的 "Support Time Logger":规格 §13 那句与 D29 冲突(英文品牌 统一为 Eigentime,"Time Logger" 是被明确弃用的通用词)。改用中性的 "Support the author",与中文「支持作者」同义,也更贴 D7/D28 的「支持的是创作者, 不是购买项目」。这是口径对齐,不是偏离规格意图。

不构成任何市场验证:外部用户验证至今尚未开始(D9 以来没变),本条同样不是 「市场愿意付费」的证据。规格 §21 的 V2 gate(≥5 个陌生真实付费者,或连续约 3 个月 持续有收入)未达成前,不得提前建设 API / 账号 / 商品体系。D9 的「最大风险=用打磨 工具逃避面试推进」在这里尤其适用:赞助入口是一行链接,不是一条产品线;如果它 开始长出后台、报表或会员梯度,那就是这条风险正在发生。

D31 · 格言位置加「文字轮播」:维护者显式修订 D11(2026-09-30)

背景:维护者 2026-09-28 提出三项(同批 v1.5.0):① 切一刀的预览与时间选择增强; ② 录入时标签跨桶同名无提示(真机:主线已有「阅读」,在维持里新建「阅读」,记录静默 记进主线);③「时间尺差异化」:导入自己喜欢的书籍或文字,在原格言位置轮播,原格言 保留、由用户自由选择用哪种。①② 属于既有能力的可用性修复,不触任何锁;③ 与 D11 的 字面边界「格言 v1 不做多条格言轮换……这些都是『可扩展分类法』的近亲,撞『别镀金』 条款」直接冲突。

AI 按惯例给出的评估(写进本条,不粉饰):

  • 冲突点明确:D11 锁的正是「多条轮换」;同时它属于 CLAUDE.md「v2 锁死 & 别镀金」 评审范围——达到 28 天门槛只表示可以重新评审,不自动放行。
  • 必要性:它不让记录更快、也不让时间线更可信,对「5 秒记下真实做了什么」没有贡献; 它的价值是让维护者本人每天更愿意打开应用。「差异化」目前只是假设——外部用户验证 至今未开始,不能把它当成已验证的市场卖点。
  • 风险提示(CLAUDE.md 要求在此类时刻必须指出):最大风险仍是「用打磨工具逃避面试 推进」。
  • 建议:若做,只做最小版本,并把不做的事写死。

维护者裁定(时间线写清楚,便于跨会话核实):需求 2026-09-28 提出;2026-09-30 在实现本批的会话里,AI 先在正文说明冲突与风险,再用选项提问请维护者裁定,维护者选 「解锁,本轮实现」、节奏选「每天一句」,并在同一答复里追问「只能按行拆句吗?……这个 功能你觉得有必要吗?虽然我想要,但是我也想听一下你的看法」。AI 答:非核心所需、差异化 未经验证,但极简版可做(不限于按行,粘贴或导入 .txt/.md,长段按句末标点自动切开,保存 后文本框即每天会显示的句子);维护者未撤回,本轮实现。

决策:

  1. 格言 sheet 顶部二选一「单句格言 / 文字轮播」。单句格言(v69 三态)原样保留;切回 单句格言只关轮播、保留句库;文字轮播里清空并保存=删除句库。
  2. 每天一句,只由日期决定:起始日(句子上次变更那天)显示第一句,逐日往下,读完 回到开头。同一天刷新、启动快照、分钟 tick 都不换句;不新增定时器、不做轮播动效。
  3. 拆句规则幂等:一行一句;≤100 字的行原样保留(用户刻意放在一行的两句话不会被 拆开);>100 字按句末标点切;切完仍 >160 字硬切;空行、Markdown 标题/分隔线/代码 块跳过,引用与列表记号去掉;最多 1000 句。保存后文本框回填为每行一句,再存一次 不得再变——否则「用户看见的」与「将要轮播的」会不一致。
  4. 存储:独立键 localStorage['timelog.quotes'],不进 timelog.config(一本书的 摘录可达几十 KB,config 每次渲染会读多次,年视图有读取次数守卫)。启动快照增加 quotesRaw 门,换了句库的快照不得接回旧句子。
  5. 随完整备份导出(可选顶层字段 quotes;旧版本导入时忽略未知键,向后兼容), 导入与格言同一精神:本机已有句子就保留本机。形状不对整批拦下。写不下(配额)不 回滚已导入的记录,但在完成提示里明说。
  6. 文件导入完全本地:.txt/.md,上限 2 MB;UTF-8 严格解码失败时按 GB18030 读 (老中文电子书多为 GBK);导入是追加,不覆盖已有句子。不支持 EPUB/PDF——解析 它们需要第三方库,撞「零运行时依赖」铁律。

仍锁定不做(D11 其余边界继续有效):收藏、出处/书名管理、逐句编辑界面、历史、 按日期/阶段自动切换句库、定时轮播动效、在线书库或任何网络获取。文本框就是全部管理 界面;若它开始长出列表管理、标签或统计,那就是 D9「工具打磨引力」正在发生。

诚实代价:这是 D11 之后又一次对「别镀金」的显式例外,依据是维护者的明确裁定, 不是门槛自动放行;不外推为整体解锁。本条不构成任何市场验证。

2026-10-01 追补(v1.5.1):

  • 另一台机器验收 v1.5.0 时指出:本条的放行依据只能凭文字核实。维护者 10-01 把该验收 结论转到实现会话,没有对本条提出异议,并就本功能的轮播进度做了新裁定(见下)。上面的 时间线据此补写;若维护者的记忆与之不符,以维护者更正为准。
  • 验收发现拆句规则并不幂等(嵌套记号只剥一层、先判断代码块再剥记号),会在「打开 设置、什么都不改、点完成」时把句库整键删除。v1.5.1 改为记号剥到不动点、判断放在剥 之后、落单的代码块记号不吞后文、每一句产出前再过一遍同样的判断,并加两道保险:文本框 没动过就原样沿用已存句子;有字却拆不出任何一句时拦下说明,不再当作删除。第 3 条的 「幂等」从此由逻辑 smoke 的组合记号断言与固定种子模糊测试守。
  • 轮播进度规则由维护者裁定为「接着今天这一句」(取代第 3 条隐含的「任何改动都从 今天第一句重来」):今天这句还在就从它接着往下;被删改了但新旧仍有共同的句子就停在 同一个序号;新旧毫无交集(整本换掉)才从第一句开始。
  • 句库保存补上 CAS(与标签配置同一纪律):格言 sheet 打开期间另一标签页改过句库就拦下。

D32 · 记录里程碑放行最小版:维护者显式修订 D7(2026-10-05)

背景:维护者 2026-10-05 问「已记录 100 天、21 天此类的,能否做一些里程碑效果」。 D7(2026-07-13)只记录了研究方向,并写明「本条不授权实现庆祝、徽章或进度页」;它同时 属于 CLAUDE.md「v2 锁死 & 别镀金」的评审范围——28 天门槛达成只表示可以重新评审。

AI 给出的评估(写进本条,不粉饰):

  • 它不让一次记录更快,也不让时间线更可信,对「5 秒记下真实做了什么」没有直接贡献;价值 在于让人更愿意持续记录。这一点只是假设,没有外部验证。
  • 风险提示(CLAUDE.md 要求必须指出):v1.5.2–v1.5.6 连续五个版本都是界面打磨,首轮 推广尚未开始,STATUS.md 没有求职进展的新记录;最大风险仍是「用打磨工具逃避面试推进」。
  • 口径提醒:「21 天」通常指连续 21 天养成习惯,而 D7 已定按累计记录日、中断不清零。
  • 建议:若做,只做最小版——当天 header 高亮 + 跨过节点那一刻的一句轻提示;不做彩带、 动效、徽章、连续打卡、进度页,不新增存储键。

维护者裁定(2026-10-05,同一会话,AI 先在正文说明冲突与风险,再用选项提问): 「放行,做最小版」;计数口径「累计记录日」;节点「7 · 30 · 100 · 200 · 365,之后每满 100」。

决策:

  1. 口径:与 header「已记录 N 天」同一口径(stats.js 的 recordedDayKeys:有非计划、 非空占位记录的不同本地自然日),中断不清零。由此回答 D7 留下的四个问题:只按不同 本地自然日计数;只有计划的日子不计;导入的历史记录计入数字(它们就是同一份数据); 删除记录后数字随之回退(纯派生)。
  2. 节点:7、30、100、200、365,之后每满 100(400、500……)。300 不是节点。判定集中在 stats.js 的 isMilestoneCount。D7 的「节点由真实使用证据决定」由维护者本人的裁定 代替——目前没有外部使用证据可依。
  3. header 高亮:今天恰好是第 N 个记录日(N 为节点)时,「记录历程第 N 天 · 已记录 N 天」 整天换成 accent 浅底胶囊;可见文字不变(320px 下这行已贴着「···」),读屏多说一句。完全 从数据派生(recordingMilestones().milestoneDay 按「今天的名次」判定),刷新、启动快照、 恢复备份都一致,第二天自然消失,不需要任何存储。
  4. 当场提示:本机记录写入(新建、过夜续记、补记/切分、编辑、标记已发生)成功后, 若累计记录日跨过节点,用既有 #info-toast 亮一次「里程碑:已记录满 N 天」。按「跨过」 判定(milestoneCrossed):过夜续记一次可新增两个记录日,99 → 101 也算到了 100。 导入、撤销删除、配额回滚、跨标签页同步都不庆祝——它们不是「今天又记了一天」;所以 调用点是记录写入路径上的显式调用,不挂在 saveChecked 这类公共写入口上。删掉当天记录 再重新记,会再提示一次,可以接受。
  5. 不做:彩带/动效/图标、徽章或成就墙、连续打卡、进度页或历史里程碑列表、通知推送、 分享卡片、新增存储键。若它开始长出这些,那就是 D9「工具打磨引力」正在发生。

诚实代价:这是 D11/D31 之后又一次对「别镀金」的显式例外,依据是维护者的明确裁定, 不是门槛自动放行;不外推为整体解锁,也不构成任何市场验证。「已记录 N 天」仍不等于 docs/dogfood-freeze-handoff.md 需要人工判断的「有效记录日」。


D33 · 推广与发布材料不进公开仓库(2026-10-13)

触发:维护者原计划把推广录屏材料(docs/promo/video-*.md、演示数据生成器与其 Playwright 检查)提交进公开仓库,随即撤回,改为「推广资料不要混到公开仓库中」,并要求定下规则、更新对应文档。

裁决:推广与发布材料不在公开仓库维护,统一放在工作区内的本地私有目录 推广/,由 .gitignore 整目录忽略——与既有的 toolkit/(私有策划)、memory/(会话备忘)、外部/(含个人线索的输入)同类。新增这类材料只放 推广/,不放进 docs/、scripts/ 或 tests/。

本次迁移范围(内容一字未改,只修正目录内相对路径):

原位置 去向
docs/promo/ 10 份(checklist posted linuxdo v2ex jike shaoshupai en-show-hn en-lobsters en-reddit-selfhosted) 推广/
docs/promo/video-recording.md、video-scripts.md(当时未提交) 推广/
scripts/make_demo_backup.mjs、tests/demo_backup.spec.js(当时未提交) 推广/,并加 推广/playwright.promo.config.mjs 让录屏检查仍能单独运行

留在公开仓库的:docs/launch-runbook.md(上线操作单:凭据、DNS、真机迁移、发布流程,STATUS 的开放项依赖它)、docs/roadmap.md、docs/decisions.md 与各规格。它们只描述边界与流程,不复述推广文案;Phase E 改为指向本地 推广/。

为什么不只是「不新增」:规则若与仓库现状矛盾就等于没有规则。已入库的底稿同样是推广材料,因此一并迁出,让「公开仓库不含推广材料」这句话可机械核对(project_audit.py 的对外表面扫描与 doc_sync_check.py 的文案面前缀都已移除 docs/promo/;CLAUDE.md「隐私红线」落成条文)。

诚实代价(不粉饰):迁出只影响当前与后续版本。这些文件在历史提交、fork 和已构建的镜像里仍然存在,可被检索;要真正撤回必须重写公开历史并强制推送,本仓不做,也不声称做过。它们本身不含真实记录或个人线索(底稿是通用文案、演示数据是脚本生成的虚构内容),所以这不是数据泄露处置,而是仓库定位问题:公开仓库是产品与规范,不是营销工作台。

不进 docs/ 的边界如何判断:读的人需要它来完成发布/维护,且内容不含对外文案草稿 → 留仓库(如 launch-runbook.md);内容是「准备对外的说法或素材」→ 放 推广/。