D1/D2 实测定案(本次亲自跑,非转述): - api 测试 = **381**:surefire 分模块 auth 40 + common 3 + community 107 + pet 100 + user 131 = 381,零失败零错误零跳过。iteration-4/04 给的 分解精确吻合;iteration-4/01「379 是 surefire 数、381 是注解数」两头 都不对——注解(@Test + @ParameterizedTest)实为 389。故 379 单纯是 releases.md 与 feature-checklist.md 的过期数字。 - 白名单 = **41**:EventDictionary.java `:42` 单个 WHITELIST map 内恰 41 个 Map.entry((另两处 WHITELIST 为读取用法)。原「community 域 19 + experiment_exposed = 42」把 experiment_exposed 重复计了一次。 - E2E 断言机械可数 = **226**:M2 42 + M3 84 + M3.5 84 = 210 个 check(, 加 M1 16 个 ✗。原记 234 无可复核来源,停止引用;已查到循环体内确有 断言(M2 3 处 / M3 14 处 / M3.5 3 处),故运行时数可能更高,要精确值 需给脚本加计数器。 常设页纠错:releases.md(api 381、flutter 标注 +2 skipped、断言口径改 226 并说明 234 的去向)、feature-checklist.md(381、41 并注明 EventDictionaryTest 无条数断言故此类漂移无门禁可拦,补断言列为 M4 前置)。 iteration-3/06(迭代存档,原文不改写)追加修正注记三条:09-21 时限失效 (本文 5 处)、A/B 前置条件 2 判绿依据失效并重置为 5 绿 3 半、字典 v3 community 域计数偏大 1。 验证:secret-scan exit 0;mkdocs build --strict exit 0。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
11 KiB
发布记录(常设)
定位:跨迭代常设文档——每次
dev → main发布在此追加一条记录:版本号、三仓 tag 与哈希、门禁证据、已知遗留。 维护约定:按发布 checklist(§3.3) 执行,完成后在此登记。最新版本在最上。 发布分支为main(ADR-011 原写 master,ADR-021 更正);日常开发直推dev。
v0.4.0 — M3.5 体验补齐(2026-09-14)
首次经 PR 流程发布(v0.3.0 为直推 main,分支保护启用前)。
三仓 tag
| 仓库 | tag | 提交 | 内容 |
|---|---|---|---|
| patbond-api | v0.4.0 |
3cd8005 |
用户资料读写 + 宠物头像 + 社区统计;381 测试;契约 v1.4.0 快照 |
| patbond-flutter | v0.4.0 |
fbcd734 |
中文本地化 + 日期录入收口 + 资料页/编辑页 + 头像接线;597 测试;四份 E2E 脚本 |
| patbond-doc | v0.4.0 |
本记录所在提交 | 契约 v1.4.0 + M3.5 报告 01~08 + 安全事件复盘 + 服务器暴露面清单 |
版本内容(相对 v0.3.0 的增量)
- 用户资料:
GET /me补nickname/avatarUrl;新增PATCH /me(三态:键缺省=不改 / 显式 null=清空 / 给值=设置) - 头像:用户与宠物头像上传(复用 M3 的 MinIO 两步上传),
purpose白名单 +user_avatar/+pet_avatar - 社区统计:
GET /me/community-stats(获赞总数 + 作品数,读侧实时聚合,无新冗余列) - 客户端体验:中文本地化(此前从未配置
flutter_localizations,Flutter 静默回退英文)、日期录入收口共享层(7 处调用点 + 手输 + 「今天」快捷)、资料页 demo 退役、花费卡显示实际月份 - 契约:openapi.yaml v1.4.0(32 路径/45 操作/75 schema),对 v1.3.0 纯增量(无字段删改/类型变更/必填收紧,已写入
info.description) - 零 Flyway 迁移(ADR-022):所需列 V1/V3/V5 早已存在,
purpose白名单为配置项 - 决策:ADR-022
发布门禁证据
| 门禁项 | 结果 | 证据 |
|---|---|---|
| 全量测试 | ✅ | api 381 / flutter 597(+2 skipped),双绿 |
| E2E 回归(四份,同环境串行) | ✅ | M1 7/7 + M2 11/11 + M3 14/14 + M3.5 10/10 = 42/42 场景零失败;断言数机械可数 226(210 check( + M1 16 ✗),原记「234」无可复核来源、已停止引用(脚本无计数器,循环内断言使运行时数可能更高——补计数器见 iteration-4/04) — 08 号报告 |
| 契约偏差 | ✅ 0 | 逐场景对照 v1.4.0;契约计数机械复核 32/45/75 |
| 契约向后兼容 | ✅ | v1.3.0→v1.4.0 纯增量,v1.3.0 客户端无需改动 |
| 契约一致性矩阵 | ✅ | 173→181 格,11 格「未声明字段即漂移」的红全部转绿,mutation 三处注毒自证 |
| 零迁移在存量库上实证 | ✅ | compose 起于既有 pgdata volume,Flyway 日志 Current version: 5 / No migration necessary. |
| CI(push + pull_request) | ✅ | api 5m31s / 6m42s;flutter 3m46s / 3m44s;doc 2m58s |
| 凭证防泄漏 | ✅ | check-secrets.sh --all exit 0 |
发布操作记录
- 首次走 PR 流程(分支保护生效后):两仓各建
dev → mainPR(标题v0.4.0 M3.5 体验补齐),等pull_request状态检查转绿后以 Fast-forward only 合并 → main == dev、零合并提交、历史保持线性。 - 流程本身也被验证了:v0.3.0 是直推 main,「PR + 状态检查」这套机制从未真实走过。本次确认:
pull_request触发器会跑、分支保护确实要求该检查通过、PR 会自动跟随 dev 新提交重新检查(发布途中 E2E 脚本提交推入 dev,PR 随即重跑)。 - CI 从安全事件恢复后的首次完整验证:见下方「期间事件」。
期间事件:Gitea gitconfig 注入(已闭环)
本迭代期间发生一次安全事件,CI 全面中断约一天。完整复盘见 07 号报告,纪律固化见新建的服务器暴露面清单。要点:攻击未达成代码执行、三仓代码经核对未被篡改、无系统层入侵;处置后外部验证四项通过。
checklist 变更(下次发布适用)
- 回归清单由两份改为四份:M1/M2/M3/M3.5。原则是「每个引入对外端点的迭代都应有对应的 E2E 脚本,并在此后每次发布回归」。
- 发布前若 dev 仍有提交推入,PR 检查会重跑——以最终 HEAD 的检查结果为准,不要用旧的绿色状态放行。
已知遗留
真机验证:M2 两项 + M3 四项 + M3.5 新增(头像上传弱网、头像缓存)——步骤全部备齐在真机验证清单,待设备到位。
功能遗留:「我的收藏与草稿」列表页(后端就绪,缺 2 页面)、月份网格选择器、SegmentedButton 主题债、widthPx/heightPx 恒 null、eventVersion 口径未定型、uploading 超时清理任务、429 限流、话题/关注列表/作者主页(ADR-018 剪出)。
跨迭代技术债:access token 黑名单、/internal 改 mTLS。
待评估:文档站公开可访问是否加访问控制(见服务器暴露面清单)。
v0.3.0 — M3 社区(2026-09-10)
首次正式发布,发布流程首次演练。
三仓 tag
| 仓库 | tag | 提交 | 内容 |
|---|---|---|---|
| patbond-api | v0.3.0 |
8089c06 |
五模块(common/auth:8081/user:8082/pet:8083/community:8084),334 测试 |
| patbond-flutter | v0.3.0 |
0e87413 |
502 测试 + 三份 E2E 烟囱脚本(M1/M2/M3) |
| patbond-doc | v0.3.0 |
本记录所在提交 | 契约 v1.3.0 + 三迭代全部报告(20+30+30 份) |
版本内容
- M1 认证纵切:JWT RS256、refresh 轮换、多设备会话、登录锁定
- M2 宠物健康档案:宠物 CRUD + 三角色权限 + 体重/疫苗/健康事件/提醒 + 档案聚合(18 操作)
- M3 社区:图片媒体上传闭环(自托管 MinIO,ADR-016)+ 帖子草稿/发布/删除 + 公共 Feed 游标分页 + 单层评论 + 点赞收藏幂等 + 关注(13 路径/19 操作)
- 契约:openapi.yaml v1.3.0 冻结,31 路径/43 操作/72 schema;契约一致性测试矩阵 173 格、43/43 操作零漂移
- 部署形态:docker compose 六容器(postgres:18 + MinIO + auth + user + pet + community),应用容器无状态(ADR-007)
- 数据库:Flyway V1~V5(identity/media、platform 埋点、pet_health、字典种子、community)
- 决策:ADR-001~021
发布门禁证据
| 门禁项 | 结果 | 证据 |
|---|---|---|
| 三仓 CI 绿 | ✅ | Gitea commit status API 直查 success |
| 全量测试 | ✅ | api 334 / flutter 502,mvnw clean test 与 flutter test 双绿 |
| E2E 回归(M2+M3 同环境) | ✅ | M2 11/11 + M3 14/14,各连跑 3 轮零 flake,契约偏差 0 — 30 号报告 |
| M3 验收标准逐条取证 | ✅ | 四条全过 — 28 号报告 |
| 契约向后兼容 | ✅ | v1.2.0→v1.3.0 结构化比对:paths/schemas removed 与 changed 均为 NONE(严格增量) |
| 共享代码面回归分析 | ✅ | M3 全区间 patbond-pet/src/main/ 0 文件变更;patbond-common 仅 ErrorCode +9 行纯新增 |
| 凭证防泄漏 | ✅ | check-secrets.sh --all 三仓 exit 0(9 规则两层检查,ADR-021) |
发布操作记录(首次一次性项)
-
命名统一:ADR-011 的
master更正为main(ADR-021);api 本地孤儿 master 已删。 -
api main 重建(方案 A,用户拍板):远端 main 原为建仓自动生成的单提交
ff876bc "Add README",与 dev 无共同祖先,无法 ff 也不宜缝合孤儿历史。操作:Gitea 默认分支临时切 dev → 删除远端 main →git push origin dev:refs/heads/main重建 → 默认分支切回 main。结果:main 41 提交、与 dev 同点位、零 force push。原孤儿提交保留本地备份 refrefs/backup/old-main-ff876bc。 -
flutter main 快进:main 本就是 dev 祖先,用
git push origin dev:main完成——较 checklist 第 4 步的checkout main && merge --ff-only改进:不切换工作区(当时有 E2E 脚本正在该工作区运行),且非快进推送会被 git 自动拒绝,等于内建 ff-only 保护。 -
分支保护启用(checklist 第 6 步,Gitea 平台):api 与 flutter 的
main均已启用,经 Gitea APIGET /repos/{owner}/{repo}/branches/main核实:仓库 protected 推送 状态检查上下文 所需批准 patbond-api true禁用直接推送 CI / backend-test (push)0 patbond-flutter true禁用直接推送 CI / flutter-gates (push)0 patbond-doc 未启用 —— —— —— 两仓
dev均保持protected=false(直推流,ADR-021 分层策略);doc 仓 main 即日常分支、不参与发布分支语义,按规划不设保护。状态检查上下文显式填写而非留空:留空时 Gitea 语义为「所有上报的检查都通过」,若某次工作流未触发则空集为真反而放行;写死检查名消除该歧义。
已知遗留(不阻塞发布)
真机验证四项挂起(步骤已备齐在真机验证清单):媒体上传弱网、乐观更新手感、Feed 图片加载、社区事件落库;另有 M2 两项(Android 事件落库、SessionTracker 30min)。桌面/脚本不可替代——platform=linux 埋点整批 400 属契约内行为。
功能遗留:完整草稿列表与自动保存、大图下滑关闭手势、widthPx/heightPx 恒 null(单图帖回落 4:3)、eventVersion 口径未定型、uploading 超时清理任务、429 限流(连带客户端 Retry-After 分支)、话题/关注列表/作者主页(ADR-018 剪出)。
跨迭代技术债:access token 黑名单(退出后已签发 access 在剩余 ≤15 分钟内仍有效)、/internal 改 mTLS。
发布后生效的纪律
-
main已禁止直接推送——影响main的一切变更(含发布本身与 hotfix)一律走 PR,CI 状态检查通过方可合并(ADR-021 强制情形之二正式生效)。dev保持直推流不变。 -
⚠️ 下次发布的姿势与本次不同:本次首发用
git push origin dev:main直推(当时 main 尚未保护);保护启用后该命令会被拒绝。此后发布流程为:- 完成 checklist 第 1~2 步(三仓 CI 绿 + E2E 双份回归 PASS);
- 在 Gitea 上创建 PR:
dev→main(标题写版本号,正文贴门禁证据链接); - 等 PR 的 CI 状态检查转绿(即上表的
status_check_contexts); - 在 Gitea 上合并 PR——因 dev 与 main 无分叉,合并应为快进;
- 继续 checklist 第 5、7 步(打 tag、写本页记录)。
故 checklist 第 4 步的本地
merge --ff-only && push写法仅适用于首次发布(保护启用前),后续版本以上述 PR 流程替代;第 3、6 步为一次性项,不再重复。 -
若某次 PR 显示
dev与main有分叉(无法快进),说明 main 被绕过 dev 改动过,先查明原因再合并,不要用合并提交掩盖。