diff --git a/CHANGELOG.md b/CHANGELOG.md index 55aa5143..2d4612c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ ## [Unreleased] ### 变更(v1.7.0) +- [2026-07-18] 完成 ASC `1.6.0` 五语 iPhone App Preview poster frame 收口:从 v003 五语成片抽取候选帧并完成跨语言联系表目检,统一选择无转场、无价格信息且演示声明完整可见的 OCR 首屏 `1.4s`。扩展 App Preview 上传工具支持 `--poster-frame-time-code HH:MM:SS:FF`、dry-run、幂等写入和 `previewFrameImage=COMPLETE` 回读;按 30 fps 写入 `00:00:01:12` 后,美英、中简、中繁、日、韩五条 preview 的远端时间码和生成图状态均二次回读一致,原视频 checksum 继续匹配。本轮未替换或删除视频,未修改截图、App Privacy、Review Notes、构建绑定、提审或发布状态。 +- [2026-07-18] 完成 ASC `1.6.0` 五语 iPhone App Preview v003 本地制作:以一个 Hyperframes 模板生成美英、中简、中繁、日、韩五条 22 秒视频,按 OCR、语音、酒店水单、Apple Watch、月报和 Pro 六段展示当前产品能力;Pro 原始截图中的具体价格在成片内替换为五语“保存前由你确认”提示,并保留 Pro 权益披露。新增原创确定性配乐 `Quiet Control`,不使用第三方样本或 v002 音乐,和弦与轻提示音在五个画面转场点同步变化,母带为 `-20 LUFS`、48 kHz 立体声。五条最终 MP4 均为 `886x1920`、30 fps、H.264 High Profile Level 4.0、约 10.9 Mbps、AAC 256 kbps,时长 22.016 秒;五语 hero frame 目检、Hyperframes validate / inspect(0 issues)和新增离线 smoke 通过。生成资产与成片保持 ignored,未上传 ASC、未选择 poster frame、未提交审核,也未修改 App / Worker / schema / StoreKit / 签名 / entitlement / 构建标签。 +- [2026-07-18] 建立 ASC 版本元数据交接:从线上回读并归档 `1.5.0` 四平台、五语言 App Info / 版本文案及订阅元数据;扩展 metadata 工具支持精确 YAML 存档、按上一版本平台矩阵幂等创建新版本、优先选择 `PREPARE_FOR_SUBMISSION` App Info,以及按 locale 定点写入。已在 ASC 创建 iOS、macOS、tvOS、visionOS `1.6.0`,四平台均处于 `PREPARE_FOR_SUBMISSION`,并填充中简、繁中、美英、日、韩第一版版本文案;韩语 App 名称 / 副标题 / 隐私信息及月付 / 年付订阅本地化已补齐。线上回读确认 Primary Language 已由用户切换为 `English (U.S.) / en-US`。既有 ACTIVE 订阅本地化不可覆盖,继续保留 ASC 已审核值;未绑定构建、上传截图 / App Preview、修改 App Privacy、提交审核或发布。 - [2026-07-18] 补齐 `v1.7.0 / ASC 1.6.0` 韩语六平台截图管线:截图配置新增 `ko` / `ko_KR`,iPhone、iPad、Mac、Watch、tvOS、visionOS 共 30 个 shot 均有韩语营销标题与副标题;渲染器优先使用系统韩文字体并保留 Gothic fallback,导出脚本、预览页和 README 同步支持韩语。首轮视觉复核发现 iOS、Watch、tvOS、visionOS 截图 Host 的画面内文案仍回退中文,现仅在截图模式补齐韩语 copy 后重新编译导出。最终本地生成 30 张 raw 与 30 张商店尺寸成品,尺寸矩阵、预览索引、静态截图本地化 smoke 和六平台联系表视觉复核通过;新 smoke 已纳入完整离线回归。生成物继续忽略且未上传 ASC,App Preview、原生设备长文本检查、真实韩国样本与母语审校仍是发布门禁;未修改正常 App 启动路径、用户数据、Worker、schema、StoreKit、签名、entitlement、版本号、构建号或 Xcode Cloud 标签。 - [2026-07-17] 完成文档物理分类与后续路线规划:`docs/` 现按 `product / architecture / capabilities / platforms / operations / archive` 六组落盘,只有导航 `docs/README.md` 与核心 `docs/ROADMAP.md` 保留在根目录;全仓 Markdown 路径、四语 README、`AGENTS.md`、工具 README、历史版本计划和迭代记录同步更新。新增 `docs/product/I18N_ROADMAP.md`,固定从 `v1.7.0 / ASC 1.6.0` 起每个公开功能版本同时交付一组新语言,并规划韩语、西语 + 巴葡、法语 + 德语、印尼语 + 越南语及后续复杂脚本 cohort;新增 `v1.8.0` Review & Close 产品计划和西语 / 巴葡发布矩阵,明确当前均为 Draft,不扩大 `v1.7.0` 范围。当前版本计划与语言矩阵新增英语主语言发布门禁:工程 `developmentRegion = en` 已核验,ASC Primary Language 目标为 `English (U.S.) / en-US`,但本轮不修改 ASC 线上状态。文档真源 smoke 现递归检查生命周期、六类目录、根目录例外、索引一致性、核心路线与本地链接;`git diff HEAD --check`、脚本编译、文档 smoke 和完整离线回归均通过,仅保留既有 `AppFormatters` 编译警告。本轮未修改 App / Worker / 数据 schema / StoreKit / 签名 / entitlement / Xcode 配置,也未触发构建、部署、提交审核或线上 Primary Language 切换。 - [2026-07-17] 建立发布前项目文档真源:新增根目录 `PROJECT_STATUS.md` 作为当前阶段、产品代码基线、发布门禁、风险与下一动作的唯一状态文档,新增 `docs/ROADMAP.md` 作为跨版本 Now / Next / Later / Not Planned 的唯一产品路线图;重建 `docs/README.md` 完整索引并为全部 `docs/*.md` 标注 `Canonical / Active / Reference / Historical` 生命周期。四语 README、`AGENTS.md`、Pro 专题文档与 `v1.7.0` 计划改为派生摘要并链接真源,同时修正已过期的云端辅助口径,明确当前 hash-only 商户别名建议、服务端权益校验、本地确认与失败降级边界。新增文档真源 smoke 并接入完整离线回归;文档 smoke、`git diff --check` 和完整离线回归均通过。本轮不移动既有文档路径,不修改 App / Worker / 数据 schema / StoreKit / ASC / 签名 / Xcode 配置,也不触发构建或部署。 @@ -46,6 +49,8 @@ - [2026-07-05 20:05 +0800] 收口国家和地区展示与语言设置可见性:AutoLedger 酒店消费国家字段改为“国家和地区” / `Country/Region`,Common API 地点目录和 App 内置地点 fallback 将香港、澳门、台湾在国家和地区层级显示为“香港(中国)” / `Hong Kong (China)`、“澳门(中国)” / `Macau (China)`、“台湾(中国)” / `Taiwan (China)`,城市层级继续保留普通城市名。设置页将“区域与语言”从“外观”分组中独立为“语言与区域 / Language & Region”分组,便于用户发现 App 语言切换和默认消费币种设置。 ### 新增(v1.7.0) +- [2026-07-18] 完成 ASC `1.6.0` 发布材料与 build 绑定收口:显式 `en-GB` 已同步 `en-US` 元数据和六平台当前英文截图;截图上传器增加 processing 终态等待、nil checksum 防护与上传后 MD5 回读,避免处理中资产被误删重传;metadata-as-code 新增 iOS / macOS 主功能与 tvOS / visionOS 只读 Review Notes profile,四平台写入后长度 / SHA-256 回读一致且保留原审核联系人;新增 fail-closed build binder,验证 Xcode Cloud build `119` 成功、完整源码 SHA `9414b91694d405d3e4c91edbae99d547c1684564`、四平台有效 / 未过期 / App Store eligible 后完成 ASC 绑定和幂等回读。ASC App Privacy 三类 analytics 声明已人工核对与 `PrivacyInfo.xcprivacy` 一致,Privacy Policy URL 已保存并回读为 `https://getautoledger.app/privacy`,页面标记“已编辑”并将随下一版本发布;韩语母语审校按发布决定转为非阻断质量增强。用户确认最新 TestFlight 候选 iCloud 同步未发现问题;本轮不含 App runtime / CloudKit / entitlement 变化,不新建 build、不移动构建标签,也未自动提交审核。 +- [2026-07-18] 完成 ASC `1.6.0` planned 五语商店素材替换:中简、中繁、美英、日、韩在 iPhone、iPad、Mac、Watch、tvOS、visionOS 的 150 张目标截图经 API 回读均与本地 MD5 匹配;五语 iPhone App Preview v003 均为远端单条、MD5 匹配且 `videoDeliveryState=COMPLETE`,英文 / 简中旧片只在新片完成后删除。新增安全 App Preview 上传工具与 ASC 瞬时网络有界重试;未改 `en-GB` fallback、poster frame、App Privacy、构建绑定或审核状态。 - [2026-07-11 +0800] 完成统一待处理中心第一版:`AutoLedgerCore` 新增平台无关的待处理聚合模型,统一汇总本机 OCR 待确认账单、酒店水单待复核草稿、疑似重复、订阅异常和智能整理建议;iPhone 继续复用“记账”Tab,在导入入口下提供摘要卡和分类列表,iPad / Mac 工作台增加独立“待处理”侧栏入口。待处理中心本身免费可见,基础手动处理路径不加 Pro gate;订阅异常和智能整理等既有自动化分组继续标识 Pro。聚合计算使用快照并在后台执行,补齐中繁英日韩文案、离线回归和静态 UI smoke。本轮未接入云收件箱远端候选、批量邮箱候选或月结缺资料队列,未修改 SQLite / CloudKit schema、StoreKit Product ID、Worker API 合同、ASC metadata、entitlement、定价或构建 tag。 - [2026-07-08 12:26 +0800] 完成月结包 ZIP 化与月报交互性能第一轮优化:`writeMonthlyExportPackage` 现在仍生成交易 CSV、月报 PDF、酒店水单附件索引 CSV 和 manifest JSON,但外层通过系统 `NSFileCoordinator(.forUploading)` 打成单个 ZIP 后交给 Share Sheet,避免用户看到四个散文件;月结包文案同步改为“压缩包”。`LedgerStore` 新增当前账本 / 月份维度的 `MonthlySnapshot` 和异常提醒缓存,`ReportView` 不再每次重绘自行扫描交易;`MonthlySnapshot.build` 改为单次遍历汇总当前月和近 6 个月趋势,降低 Tab 切换、月份切换和月报交互时的同步重算压力。本轮未修改 Pro gate、基础 CSV / JSON 免费导出、SQLite / CloudKit schema、StoreKit、ASC metadata、截图 / App Preview 或构建 tag。 - [2026-07-08 11:03 +0800] 完成酒店旅程回忆文案第一版:酒店消费详情在历史天气可用时,会基于当前入住记录、地点、日期和天气摘要在端上生成一段轻量旅程回忆文案,并可一键带入“入住分享卡”作为默认评价。该文案不调用 LLM、不新增网络请求、不上传酒店名、金额、PDF、rawText 或个人备注,只复用已加载的 Common API 天气事实和本机酒店记录。新增 `HotelStayJourneyMemoryComposer` 与五语 `hotel_stay.detail.memory.*` 文案,`check_hotel_weather_ui_smoke.py` 同步检查旅程回忆卡片、分享卡联动和敏感字段隔离。 diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index 9d29f7ce..749ec9a7 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -13,11 +13,11 @@ |---|---| | 内部开发线 | `v1.7.0` | | App Store 对外版本 | `1.6.0` | -| 发布阶段 | TestFlight 反馈收口 -> Release Candidate 冻结前 | +| 发布阶段 | Release Candidate 冻结与发布证据收口 | | 已验证候选产品行为基线 | `9414b91694d4`;当前仓库后续 Swift 改动仅服务 `--screenshot-mode` / 截图 Host,不进入正式启动路径 | | Xcode Cloud 触发标签 | `xcbuild-v1.7.0`,基线指向 `9414b91694d4` | | 最近人工结论 | 当前 TestFlight 候选整体已接近发布;TS 117 起 Tab 体感明显改善 | -| 精确 TestFlight build | 属于 App Store Connect 外部证据;未从仓库推断或写死 | +| 精确 TestFlight build | Xcode Cloud / TestFlight build `119`;源码 `9414b91694d405d3e4c91edbae99d547c1684564`;ASC `1.6.0` 四平台已绑定并回读 | | 文档治理 | `PROJECT_STATUS.md`、根级 `docs/ROADMAP.md` 与 `docs/product/I18N_ROADMAP.md` 分别负责当前状态、核心产品路线和跨版本语言路线;其它 `docs` 已物理分类 | | 下一版本 | `v1.8.0 / ASC 1.7.0` 的 Review & Close 与西语 / 巴葡计划仅为 Draft,不扩大当前发布范围 | @@ -48,27 +48,33 @@ - 数据清洗的云端辅助已形成第一版可用闭环:用户显式开启且具备 Pro 权益时,App 发送商户键哈希和聚合特征,Worker 返回 hash-only 商户别名建议,用户确认前不会改写账本。 - 自动化规则指标已统一布局和字号,商户别名、分类修正和受影响账单说明不再截断。 - `docs/` 已按产品、架构、能力、平台、运维和归档物理分组;核心 `ROADMAP.md` 作为唯一专项例外保留在 `docs/` 根目录。 -- 已建立每版本一组新语言的跨版本路线和 `v1.8.0` Draft;工程 `developmentRegion = en` 已核验,但 ASC Primary Language 的线上切换尚未以仓库证据确认。 -- 韩语六平台截图管线已补齐:iPhone、iPad、Mac、Watch、tvOS、visionOS 本地导出 30 张 raw 与 30 张商店尺寸成品,逐平台视觉复核后修正截图 Host 的中文 fallback;生成物按规则忽略,不等同于 ASC 已上传或韩语已通过母语审校。 +- 已建立每版本一组新语言的跨版本路线和 `v1.8.0` Draft;工程 `developmentRegion = en` 已核验,ASC Primary Language 已在线回读确认为 `English (U.S.) / en-US`。 +- 五语六平台截图已完成 ASC `1.6.0` 上传与 API 回读:中简、中繁、美英、日、韩在 iPhone、iPad、Mac、Watch、tvOS、visionOS 的 150 张目标截图均与本地 MD5 矩阵匹配;显式存在的 `en-GB` 已同步 `en-US` 元数据和六平台当前英文截图,避免旧资产覆盖主语言 fallback。 +- ASC `1.5.0` 四平台元数据已从线上归档;ASC `1.6.0` 的 iOS、macOS、tvOS、visionOS 已创建并处于 `PREPARE_FOR_SUBMISSION`,planned 五语第一版版本文案已填充并回读一致。Primary Language 已确认为 `en-US`,韩语 App 名称、副标题、隐私链接与 Apple TV 隐私正文已定点写入并回读一致。 +- ASC `1.6.0` 五语 iPhone App Preview v003 已上传:五条视频均与本地 MD5 一致且 `videoDeliveryState=COMPLETE`;英文 / 简中旧片在新片完成后删除。五语 poster frame 已统一为 OCR 首屏的 `1.4s / 00:00:01:12`,API 回读时间码与生成图状态均为 `COMPLETE`。韩语母语审校按发布决定转为非阻断质量增强;最终 binary 逐镜一致性核验仍未完成。 +- 四平台 Review Notes 已从 repo profile 写入 ASC 并按长度 / SHA-256 回读一致;iOS / macOS 使用主功能说明,tvOS / visionOS 使用只读看板说明,均保持 `demoAccountRequired=false` 且未覆盖审核联系人字段。 +- Xcode Cloud build `119` 已确认运行成功且源码为 `9414b91694d405d3e4c91edbae99d547c1684564`;ASC `1.6.0` 四个平台版本均已绑定各自的有效、未过期、App Store eligible build,并完成幂等 API 回读。 +- ASC App Privacy 已人工查看:Crash Data、Performance Data、Product Interaction 均用于 Analytics,not linked,not tracking,与 `PrivacyInfo.xcprivacy` 一致;Privacy Policy URL 已保存为 `https://getautoledger.app/privacy`,页面回读新值并标记“已编辑”,该变更将随下一版本发布。 +- 用户已在最新 TestFlight 候选完成 iCloud 同步 smoke,未发现同步问题;2026-07-18 再次回读 Xcode Cloud 确认最新成功候选仍是 build `119` / `9414b91694d4`。当前待推送改动不含 App runtime、CloudKit schema、entitlement 或工程构建设置,因此不为本次文档 / ASC 工具收口另起 TestFlight build,也不移动 `xcbuild-v1.7.0`。 -详细证据见 [process/iteration-log.md](process/iteration-log.md) 的 `ITER-421` 至 `ITER-430`。 +详细证据见 [process/iteration-log.md](process/iteration-log.md) 的 `ITER-421` 至 `ITER-434`。 ## Release Gates ### P0 - Candidate Evidence -- 确认最终 Xcode Cloud 四平台构建成功,并记录 TestFlight build number 与源码 SHA 的对应关系。 +- 已确认最终 Xcode Cloud 四平台 build `119` 成功,源码 SHA 为 `9414b91694d405d3e4c91edbae99d547c1684564`,且四平台 ASC 版本绑定完成;设备 smoke 仍需使用该精确候选。 - iPhone、iPad、Mac 分别完成冷启动、快速切 Tab、月报月份切换、数据清洗和 OCR 固定路径 smoke;单设备结果不能替代其它平台。 -- 验证启动后立即交互时 iCloud 同步仍能最终完成,且不会重新阻塞 UI。 +- 最新 TestFlight 候选的 iCloud 同步 smoke 已由用户确认未发现问题;若后续修改 App runtime、CloudKit schema、entitlement 或同步实现,必须在新候选重新执行。 - 使用重庆 Moxy 记录复测“保留本机 -> 推送 -> 拉取”,确认并发改动冲突不再复活。 - 在最新候选中验证云端水单收件箱 `401` 自动续签不会更换既有专属地址,并验证云端商户别名建议成功与失败 fallback。 ### P0 - Release And Privacy -- 对照 [versions/v1.7.0-i18n-release-matrix.md](versions/v1.7.0-i18n-release-matrix.md) 完成五语状态复核;韩语虽已完成本地六平台截图导出,但母语审校、真实样本、ASC 上传与 App Preview 证据未齐前不得标记为 Ready。 -- 从 ASC `1.6.0` 起以 `English (U.S.) / en-US` 为 Primary Language;执行前确认当前 App 信息可编辑、英语此前已通过审核且全平台英语截图满足切换条件,执行后归档 ASC 线上证据。Xcode `developmentRegion = en` 不能替代该证据。 -- 确认 ASC metadata、订阅本地化、截图 / App Preview、Review Notes、隐私政策和 App Privacy 口径一致。 -- 确认 Crash Data、Performance Data 与 Product Interaction 只用于 Analytics,not linked,not tracking。 +- 对照 [versions/v1.7.0-i18n-release-matrix.md](versions/v1.7.0-i18n-release-matrix.md) 维护五语事实状态。韩语母语审校因暂无合适审校者,按发布决定不阻断 ASC `1.6.0`;真实样本与地区专项作为已知质量缺口继续积累,不得伪装为已经完成。 +- ASC `1.6.0` 的 Primary Language 已在线确认为 `English (U.S.) / en-US`;显式 `en-GB` 元数据和六平台截图已同步当前英语资产,提交前仅剩与最终 binary 的一致性复核。 +- ASC metadata、订阅本地化、截图 / App Preview、Review Notes 与 App Privacy URL 已完成线上回读;App Privacy 数据类型口径已核对一致。 +- Crash Data、Performance Data 与 Product Interaction 已在线确认只用于 Analytics,not linked,not tracking。 - 若最终候选包含 CloudKit record type、field 或 index 变化,必须先将 Development schema 部署到 Production 并记录证据。 - 冻结本次商店图标:明确沿用当前图标,或完成多层图标资产后再统一重导商店素材;不能在截图完成后继续改动。 @@ -82,16 +88,16 @@ - 云端商户辅助当前只覆盖首批可解释别名目录;成功响应缓存、持久化 cooldown / backoff、服务端配额和更广别名治理仍未完成。 - CloudKit 合并写入继续使用串行一致性路径;当前优化重点是让网络与解码不阻塞 UI,而不是把所有同步工作并行化。 -- TestFlight build number、ASC processing 和人工设备结果属于外部证据,仓库文档不得根据 tag 或 MARKETING_VERSION 猜测。 +- TestFlight build number、ASC processing 和人工设备结果属于外部证据,仓库文档不得根据 tag 或 MARKETING_VERSION 猜测;本次 build `119` / SHA / 四平台绑定来自实时 API 回读。 - Dashboard 历史数据包含旧版本和旧埋点语义,判断发布质量时必须按 source build 和新指标口径过滤。 -- 韩语截图生成物位于忽略目录,本地 30 / 30 导出与工程视觉复核只能证明素材管线可用,不能替代 ASC 上传状态、原生设备长文本检查或母语审校。 +- 截图与 App Preview 本地成品仍位于忽略目录;ASC MD5、视频处理状态、poster frame 时间码与生成图状态已经回读,但不会替代原生设备长文本检查或与最终提交 binary 的一致性复核。韩语母语审校是明确接受的非阻断缺口,后续反馈仍应修订文案。 - `versions/v1.7.0-plan.md` 保留逐阶段执行记录;其中带日期的“未完成”描述是当时事实,不能覆盖本文件的当前状态。 - `v1.8.0` 与后续语言组是规划事实,不代表实现、ASC locale、截图、识别样本或人工审校已经完成。 ## Next Actions 1. 完成当前候选的 iPhone / iPad / Mac 与 iCloud 最终 smoke,并把结果回填本文件和版本回归基线。 -2. 冻结五语商店资产、图标、ASC metadata、Review Notes 与隐私声明;上传并核对韩语六平台截图,完成 `en-US` Primary Language 的发布操作与留证。 +2. 复核已冻结商店资产、Review Notes、App Privacy 与最终 binary 一致性;Privacy Policy URL 已保存为当前官网地址。 3. 完成 release-readiness 审计后,再决定提交审核和移动不可变产品版本标签。 ## Source Of Truth Map diff --git a/process/iteration-log.md b/process/iteration-log.md index 99526030..125de39f 100644 --- a/process/iteration-log.md +++ b/process/iteration-log.md @@ -1,6 +1,6 @@ # 迭代日志 -更新日期:2026-07-18(ITER-430 韩语六平台截图管线) +更新日期:2026-07-18(ITER-435 ASC 1.6.0 发布材料与 build 绑定收口) ## 记录规则 @@ -44,6 +44,96 @@ ## 日志条目 +### ITER-435 ASC 1.6.0 发布材料与 build 绑定收口 +- 日期:2026-07-18 +- 所属版本:v1.7.0 / ASC 1.6.0 +- 所属阶段:Release Candidate / App Store Connect +- 类型:ASC 线上操作 / 发布工具 / 文档治理 / 隐私审计 / 测试 +- 目标:在不自动提审的边界内继续完成 ASC `1.6.0` 可执行发布门禁;将韩语母语审校改为有记录但不阻断的质量增强,并对主语言 fallback、Review Notes、App Privacy 和最终 build 绑定形成可回读证据。 +- 改动范围:同步显式 `en-GB` 元数据与六平台英文截图;增强截图上传终态校验;新增 Review Notes metadata profile、审计 /写入能力与 build 绑定工具;人工查看 ASC App Privacy;更新当前状态、语言矩阵、版本计划、审核说明、CHANGELOG、工具 README、smoke 和本日志;使用官方 App Store Connect API 写入 Review Notes 与四平台 build relationship。 +- 未改动范围:未提交审核、未修改发布时机、App Privacy 问卷答案、订阅已审核本地化、审核联系人、App / Worker 业务代码、用户数据、SQLite / CloudKit / D1 schema、StoreKit Product ID、signing、entitlement、版本号、构建号或 Git / Xcode Cloud tag;未在未确认时保存 ASC 网页表单。 +- 完成内容:显式 `en-GB` 的版本元数据已从 `en-US` 同步;iPhone、Watch、tvOS、visionOS 截图原已一致,旧 iPad 5 张替换为当前 6 张、旧 Mac 4 张替换为当前 5 张,最终六平台 dry-run 均与本地当前英文资产匹配。`en-GB` 不额外创建 App Preview,继续继承主英语预览。 +- 完成内容:`asc_screenshot_upload.rb` 现在遇到数量已完整但仍 processing 的截图集会等待终态,而不是删除并重传;nil `sourceFileChecksum` 不再触发排序崩溃;上传后必须轮询所有 checksum / state 为 `COMPLETE` 并逐文件验证 MD5。 +- 完成内容:`metadata.yml` 新增 `main` 与 `readonly` Review Notes profile;iOS / macOS 写入 2783 字符主说明,tvOS / visionOS 写入 1154 字符只读说明。四平台 API 回读长度与 SHA-256 均匹配,`demoAccountRequired=false`,审核联系人字段未被工具修改。 +- 完成内容:ASC App Privacy 页面显示 Crash Data、Performance Data、Product Interaction 均仅用于 Analytics,且为 not linked、not tracking,与工程隐私清单一致。取得用户对外部表单提交的明确确认后,Privacy Policy URL 已从旧地址更新并保存为 `https://getautoledger.app/privacy`;关闭弹窗后页面回读新值并显示“已编辑”,说明该变更将随下一版本发布。 +- 完成内容:新增 `asc_build_bind.rb`,只接受精确 build number 与完整 source commit;写入前验证 Xcode Cloud run 成功、源码 SHA 完全一致、版本为 `PREPARE_FOR_SUBMISSION`、每平台只有一个符合条件的 build、处理有效、未过期、`APP_STORE_ELIGIBLE` 且 `usesNonExemptEncryption=false`。build `119` 对应提交 `9414b91694d405d3e4c91edbae99d547c1684564`,iOS、macOS、tvOS、visionOS 已绑定并由独立 dry-run 确认 current relationship 等于目标 build ID。 +- 完成内容:韩语母语审校因当前找不到合适审校者,按版本负责人决定从硬门禁调整为非阻断质量证据;机器 key / placeholder 检查、离线 / golden 回归、六平台视觉复核、ASC MD5 / 处理终态和已知样本 / 地区缺口继续保留,不将缺少的母语结论标为 Ready。 +- 完成内容:用户确认最新 TestFlight 候选的 iCloud 同步 smoke 未发现问题;随后通过 Xcode Cloud API 再次确认最新成功候选仍为 build `119`、源码 `9414b91694d405d3e4c91edbae99d547c1684564`。本轮 Git 改动不含 Swift / App runtime、CloudKit schema、entitlement 或工程构建设置,因此本次推送不触发新 TestFlight build,也不移动 `xcbuild-v1.7.0`。 +- 未完成内容:未完成 iPhone / iPad / Mac 最终设备 smoke、iCloud 启动交互与重庆 Moxy 冲突复测、最终 binary 逐镜一致性检查、最终 release-readiness 审计或提审决策。 +- 测试情况:Review Notes 四平台线上长度 / SHA-256 回读通过;build binder 二次 dry-run 确认四平台 current build 均为 `119` 对应 build ID;`en-GB` 六平台截图 dry-run 全部匹配。四个 Ruby 工具语法、ASC metadata-as-code smoke(含 Review Notes 长度 / 隐私关键词 / secret marker)、App Preview v003 smoke、本地化发布 smoke、文档真源 smoke、`git diff --check` 与完整 `bash scripts/run_offline_regression.sh` 均 PASS;完整回归仅保留既有 `AppFormatters` `nonisolated(unsafe)` warning。 +- 风险与注意事项:App Privacy 页面显示“已编辑”表示新 URL 已保存并等待随下一版本发布,不代表当前线上已发布版本页面立即切换;build 已绑定不等于设备 smoke 或提审完成。韩语母语审校被明确接受为发布后质量风险,任何用户反馈仍应进入本地化修订,不得反向宣称已获母语认可。 +- 回滚方式:工具与文档可用 Git 回退;Review Notes 可由保留的 repo profile 再次写入旧文案;build relationship 可在 ASC 仍为可编辑状态时绑定回先前经验证 build。截图替换需使用上传工具和本地 MD5 清单重建;未发生提交审核或发布。 +- 结论:ASC `1.6.0` 主语言 fallback、Review Notes、App Privacy URL 与精确 build 绑定门禁已完成可审计收口;韩语母语审校不再阻断本版本。剩余重点是最终设备 / binary smoke 与提审决策。 +- 下一步建议:完成 build `119` 的最终设备 / iCloud smoke 与 release-readiness 审计,再单独决定是否提交审核。 + +### ITER-434 ASC 1.6.0 五语 App Preview poster frame +- 日期:2026-07-18 +- 所属版本:v1.7.0 / ASC 1.6.0 +- 所属阶段:Release Materials / App Store Connect +- 类型:ASC 线上操作 / 发布工具 / 视觉复核 / 测试 / 文档 +- 目标:为 ASC `1.6.0` 五语 iPhone App Preview v003 选择统一且稳定的 poster frame,写入后回读帧时间码和 Apple 生成图终态,不改动视频或进入提审。 +- 改动范围:从五语 v003 成片抽取候选帧并生成跨语言联系表;扩展 `asc_app_preview_upload.rb` 支持 poster frame dry-run、幂等 PATCH 与生成图轮询;更新工具 README、smoke、项目状态、语言矩阵、版本计划、CHANGELOG 和本日志;使用官方 App Store Connect API 写入五条 preview。 +- 未改动范围:未上传、替换或删除 App Preview 视频;未修改截图、`en-GB` fallback、App Privacy、Review Notes、构建绑定、提交审核、发布状态、App / Worker 业务代码、用户数据、schema、StoreKit、签名、entitlement、版本号、构建号或 Xcode Cloud tag。 +- 完成内容:在英文成片的 `0.8s` 至 `20.4s` 候选中选定 `1.4s` OCR 首屏;该帧已完全入位、演示数据声明可见、无转场残影,也不展示 Pro 价格或订阅权益。相同时间点在中简、中繁、日、韩成片均完成联系表目检,无截字或布局漂移。 +- 完成内容:ASC 回读确认 poster frame 使用 `HH:MM:SS:FF` 帧时间码,现有默认值为 `00:00:05:01`;v003 为 30 fps,因此 `1.4s` 对应 `00:00:01:12`。五次写入均只命中与本地 MD5 一致且 `videoDeliveryState=COMPLETE` 的唯一 preview。 +- 完成内容:美英、中简、中繁、日、韩五条 preview 已统一回读 `previewFrameTimeCode=00:00:01:12`,且 `previewFrameImage.state=COMPLETE`;再次 dry-run 二次确认五条视频 checksum、视频终态、时间码和生成图终态一致。 +- 未完成内容:未完成人工母语审校、Release artifact 归档、主语言 fallback 复核、与最终提交 binary 的逐镜一致性检查、App Privacy、Review Notes、最终 build 绑定或提审决策。 +- 测试情况:五语候选帧与统一时间码联系表目检通过;Ruby 语法、ASC metadata-as-code smoke、App Preview v003 smoke、文档真源 smoke、`git diff --check` 与完整 `bash scripts/run_offline_regression.sh` 均 PASS,仅保留既有 `AppFormatters` warning。ASC 写入前 dry-run、写入处理终态和写入后二次 dry-run 均通过。 +- 风险与注意事项:时间码是按当前 30 fps 交付规格计算的帧号;未来如果替换成不同帧率或不同时长的视频,必须重新选帧并计算,不能机械复用 `00:00:01:12`。远端生成图完成不替代合格母语审校或与最终 binary 的逐镜人工核验。 +- 回滚方式:工具与文档可用 Git 回退;ASC poster frame 若需回滚,应对同一 preview 显式写回经复核的旧时间码。未发生视频删除、提审或发布。 +- 结论:ASC `1.6.0` 五语 iPhone App Preview poster frame 已统一设置并完成双重 API 回读;该发布材料门禁关闭。 +- 下一步建议:完成母语 / fallback / 最终 binary 一致性复核,再进入 App Privacy、Review Notes、构建绑定和提审决策。 + +### ITER-433 ASC 1.6.0 五语截图与 App Preview 替换 +- 日期:2026-07-18 +- 所属版本:v1.7.0 / ASC 1.6.0 +- 所属阶段:Release Materials / App Store Connect +- 类型:ASC 线上操作 / 发布工具 / 可靠性 / 测试 / 文档 +- 目标:将当前五语六平台截图与五语 iPhone App Preview v003 替换到 ASC `1.6.0`,并以远端 MD5 与 Apple 处理终态完成回读,不绑定构建、不提审。 +- 改动范围:使用官方 App Store Connect API 替换 planned 五语截图;新增 `asc_app_preview_upload.rb`;为 ASC API 与二进制上传增加有界瞬时网络重试;更新 metadata 工具默认版本 / planned locale、README、smoke、项目状态、语言矩阵、版本计划、CHANGELOG 和本日志。 +- 未改动范围:未修改 `en-GB` fallback 素材、App Privacy、Review Notes、poster frame、构建绑定、提交审核、发布状态、App / Worker 业务代码、用户数据、SQLite / CloudKit / D1 schema、StoreKit Product ID、签名、entitlement、版本号、构建号或 Xcode Cloud tag。 +- 完成内容:ASC `1.6.0` 的中简、中繁、美英、日、韩五语在 iPhone、iPad、Mac、Watch、tvOS、visionOS 共 150 张目标截图均与本地 MD5 矩阵匹配;韩语六平台截图集由缺失推进为完整。`en-GB` 保持既有 fallback 状态。 +- 完成内容:五语 iPhone `IPHONE_65` App Preview 均为 1 条;五条远端 MD5 与 v003 本地成片一致且 `videoDeliveryState=COMPLETE`。英文和简中的旧视频仅在新视频完成处理后删除;繁中、日语和韩语新建 preview set。 +- 完成内容:App Preview 上传工具采用“先上传并等待 COMPLETE、再删除旧片”的安全替换顺序;遇到 `FAILED` 会保留旧片并清理失败的新资源。截图与视频上传共享 429 / 5xx / TLS / timeout 有界重试,支持按 MD5 幂等重跑。 +- 未完成内容:未选择 poster frame,未完成人工母语审校、Release artifact 归档或与最终提交 binary 的逐镜一致性复核;这些仍是发布门禁。 +- 测试情况:截图本地化 smoke、App Preview v003 smoke、三份 Ruby 脚本语法检查通过;上传前五语 dry-run 通过;上传后 ASC 全平台 audit 确认 planned 五语所有截图集合 `match`,五次 App Preview dry-run 均显示 checksum match / `COMPLETE`;文档真源 smoke、`git diff --check` 与完整 `bash scripts/run_offline_regression.sh` 均 PASS,仅保留既有 `AppFormatters` 四处 `nonisolated(unsafe)` warning。 +- 风险与注意事项:ASC 视频转码耗时约数分钟并受本机代理瞬时 TLS 抖动影响,本轮有界重试后全部完成;远端 MD5 / COMPLETE 只证明交付文件一致和 Apple 处理成功,不替代语言质量、poster frame 和最终 binary 一致性。 +- 回滚方式:仓库工具与文档可用 Git 回退;ASC 外部素材若需回滚,必须用对应历史资产重新执行上传,不能用 Git 回滚替代。未发生提审或发布。 +- 结论:ASC `1.6.0` planned 五语截图与五语 iPhone App Preview 已完成替换并回读确认;商店素材上传门禁关闭,剩余人工审校、poster frame、隐私与最终 build 门禁继续开放。 +- 下一步建议:选择五语 poster frame,完成母语 / fallback / 最终 binary 一致性复核,再进入 App Privacy、Review Notes、构建绑定和提审决策。 + +### ITER-432 ASC 1.6.0 五语 App Preview v003 +- 日期:2026-07-18 +- 所属版本:v1.7.0 / ASC 1.6.0 +- 所属阶段:Release Materials / App Preview +- 类型:本地化 / 视觉制作 / 原创音频 / 发布工具 / 测试 / 文档 +- 目标:用当前五语截图和统一产品叙事更新 iPhone App Preview,并补一条随六段画面切换变化、可重复生成且不依赖第三方样本的原创背景音乐。 +- 改动范围:新增 `tools/appstore-screenshots/app-preview/hyperframes-v003` 五语共享工程、manifest、模板、素材同步、语言选择、批量渲染、原创配乐生成和交付转码脚本;新增 `scripts/check_app_preview_v003_smoke.py` 并接入离线回归;更新 App Preview / 截图 README、i18n 发布矩阵、版本计划、项目状态、CHANGELOG 和本日志。 +- 完成内容:一个 22 秒时间轴依次展示 OCR、语音记账、酒店水单复核、Apple Watch、月报和 AutoLedger Pro;五语使用同一布局 / 转场顺序和各自 raw UI capture。Pro 截图中的具体价格由五语“保存前由你确认”卡片替换,同时保留“部分功能需要 AutoLedger Pro”披露。 +- 完成内容:新增原创确定性配乐 `Quiet Control`,由仓库脚本合成,不使用第三方样本、旁白或 v002 音乐;在 `3.15 / 6.52 / 9.88 / 13.24 / 16.62` 秒视觉转场点同步改变和弦、主音色与轻提示音,Watch 段稍明亮,Pro 段回到主和弦收束。母带综合响度 `-20.0 LUFS`,48 kHz 立体声,AAC 约 256 kbps。 +- 完成内容:本地生成 `en-US / zh-Hans / zh-Hant / ja / ko` 五条最终 MP4;全部为 `886x1920`、30 fps、H.264 High Profile Level 4.0、约 10.9 Mbps、AAC 256 kbps,时长 22.016 秒、约 30.7-30.8 MB。英文恢复为默认活动模板。 +- 未完成内容:生成素材与 MP4 按规则 ignored,尚未归档到 Release artifact,未上传 App Store Connect,未选择 poster frame,日语 / 韩语等正式商店文案仍需合格人工审校,也未与最终提交 binary 做人工逐镜一致性核验。 +- 测试情况:五语 Hyperframes `validate` 均无 console error 且文本通过 WCAG AA;五语 `inspect --samples 15` 均为 0 issues。日语第一次 validate 遇到一次 10 秒导航超时,单独重跑通过。五语六帧 contact sheet 目检通过;`ffprobe` 规格检查五条均 PASS;音频 `ebur128` 为 `-20.0 LUFS`;`python3 scripts/check_app_preview_v003_smoke.py`、文档真源 smoke 与完整 `bash scripts/run_offline_regression.sh` 均通过,Preview smoke 会在本地成片存在时额外校验五条交付规格。 +- 风险与注意事项:Hyperframes 静态 linter 对封装在函数中的动态 GSAP selector 继续给出 `__unresolved__` overlap / Studio edit blocked 提示,属于 v002 同类静态解析限制;浏览器 validate / inspect 与实际渲染均通过。App Preview 可静音自动播放,因此关键信息仍由本地化画面文字承担,音乐不作为理解前提。 +- 回滚方式:删除 `hyperframes-v003`、移除新增 smoke 与离线回归入口,并回退本轮发布文档即可;ASC 1.5.0 的 v002 中英文工程和历史视频不受影响。 +- 结论:ASC 1.6.0 五语 iPhone App Preview v003 已完成本地视觉、音频和交付规格门禁;下一步是人工语言审校、归档、ASC 上传与 poster frame 选择。 + +### ITER-431 ASC 1.5.0 元数据归档与 1.6.0 第一版填充 +- 日期:2026-07-18 +- 所属版本:v1.7.0 / ASC 1.6.0 +- 所属阶段:Release Metadata / Candidate Preparation +- 类型:发布自动化 / ASC 线上操作 / 文档 / 测试 +- 目标:从 ASC 读取并归档当前 `1.5.0` 元数据,建立 `1.6.0` 四平台可编辑版本并填充五语第一版元数据,不绑定构建、不提审。 +- 改动范围:扩展 `tools/asc-metadata/asc_metadata.rb` 与 README / smoke;新增 `asc-1.5.0.yml` 和 `asc-1.6.0-first-draft.yml` 线上快照;修正英文副标题长度;更新版本计划、项目状态、CHANGELOG 和本日志;在 ASC 创建及填充新版本。 +- 完成内容:实时归档确认 `1.5.0` 的 iOS、macOS、tvOS、visionOS 均为 `READY_FOR_SALE`,四平台均有 `en-GB / en-US / ja / zh-Hans / zh-Hant`;`1.6.0` 四平台均已创建为 `PREPARE_FOR_SUBMISSION`,并具有 `en-GB / en-US / ja / ko / zh-Hans / zh-Hant`,其中 planned 五语版本字段与 repo 配置一致;韩语订阅组、月付和年付本地化已存在。 +- 完成内容:用户在 ASC 将 Primary Language 切换为 `English (U.S.)` 后,API 回读确认 `primaryLocale=en-US`;工具修正为优先选择 `PREPARE_FOR_SUBMISSION` App Info,并增加 `--locale` 定点写入。韩语 App Info 已从中文继承值更新为 `AutoLedger - 빠른 가계부` / `스크린샷, 음성, 호텔 명세서 정리`,隐私链接与 Apple TV 隐私正文同步写入并回读一致。 +- 未完成内容:ACTIVE 订阅的既有本地化不可覆盖,因此其旧显示名 / 描述按 ASC 已审核现状保留。未绑定 build,未上传截图或 App Preview,未修改 App Privacy,未提交审核或发布。 +- 测试情况:`ruby -c tools/asc-metadata/asc_metadata.rb`、`python3 scripts/check_asc_metadata_as_code_smoke.py`、ASC 字段长度校验、三个商店链接 HTTP 200、create-version dry-run / apply、push-config dry-run / apply、Primary Language API 回读、韩语 App Info 精确对账、最终 live export 和完整 `bash scripts/run_offline_regression.sh` 均通过。 +- 风险与注意事项:新版本自动保留了 `en-GB` fallback;当前配置 planned locale 为五语,不应把 `en-GB` 误算成发布完成。五语截图与 App Preview、App Privacy 和最终候选构建仍是 P0 门禁。 +- 回滚方式:仓库工具与快照可回退;ASC 新版本与已写入本地化属于外部状态,若需撤销应在 ASC 按平台逐项处理,不能用 Git 回滚替代。未发生提审或发布。 +- 结论:`1.6.0` 第一版版本文案、`en-US` Primary Language 与韩语 App Info 已完成线上填充并回读确认;最终发布资产仍未完成。 +- 下一步建议:在候选冻结后上传五语截图与 App Preview,复核主语言 fallback、App Privacy 与 Review Notes,再绑定最终 build。 + ### ITER-430 韩语六平台截图管线 - 日期:2026-07-18 - 所属版本:v1.7.0 / ASC 1.6.0 @@ -7602,7 +7692,8 @@ - 类型:Bugfix / 规则 - 目标:修复滴滴出行"先乘车后付款"微信支付扣费凭证截图商户误识别问题——OCR 文本无"行程已"和"已支付"关键词,`parseDidiTrip` 两条已有分支均未命中,商户回退为"微信支付",分类为"其他";实际应为商户"滴滴出行"、分类"出行"。 - 触发调试记录(原始,未修正,完整内容): - ``` +``` + AutoLedger 单条测试记录 导出时间:2026-04-13 05:57:42 记录时间:2026-04-12 17:44:18 diff --git a/scripts/check_app_preview_v003_smoke.py b/scripts/check_app_preview_v003_smoke.py new file mode 100644 index 00000000..eab07033 --- /dev/null +++ b/scripts/check_app_preview_v003_smoke.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +"""Static contract checks for the ASC 1.6.0 five-locale App Preview project.""" + +from __future__ import annotations + +import json +import shutil +import subprocess +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PROJECT = ROOT / "tools/appstore-screenshots/app-preview/hyperframes-v003" +EXPECTED_LOCALES = ("en-US", "zh-Hans", "zh-Hant", "ja", "ko") +EXPECTED_SCENES = ("ocr", "voice", "hotel", "watch", "report", "pro") +EXPECTED_STARTS = (0.0, 3.15, 6.52, 9.88, 13.24, 16.62) + + +def require(condition: bool, message: str, failures: list[str]) -> None: + if not condition: + failures.append(message) + + +def inspect_optional_renders(failures: list[str]) -> None: + ffprobe = shutil.which("ffprobe") + final_dir = PROJECT / "renders/final" + renders = [ + final_dir / f"app_preview_iphone_{locale}_asc1.6.0_v003.mp4" + for locale in EXPECTED_LOCALES + ] + if not ffprobe or not all(path.exists() for path in renders): + return + + for path in renders: + result = subprocess.run( + [ + ffprobe, + "-v", + "error", + "-show_entries", + "format=duration,size:stream=codec_name,profile,level,width,height,r_frame_rate,sample_rate,channels,bit_rate", + "-of", + "json", + str(path), + ], + check=True, + capture_output=True, + text=True, + ) + payload = json.loads(result.stdout) + video = next((item for item in payload["streams"] if item.get("codec_name") == "h264"), None) + audio = next((item for item in payload["streams"] if item.get("codec_name") == "aac"), None) + duration = float(payload["format"]["duration"]) + require(15 <= duration <= 30, f"{path.name}: duration must be 15-30 seconds", failures) + require(video is not None, f"{path.name}: missing H.264 video", failures) + require(audio is not None, f"{path.name}: missing AAC audio", failures) + if video: + require((video.get("width"), video.get("height")) == (886, 1920), f"{path.name}: wrong dimensions", failures) + require(video.get("r_frame_rate") == "30/1", f"{path.name}: wrong frame rate", failures) + require(video.get("profile") == "High" and video.get("level") == 40, f"{path.name}: wrong H.264 profile", failures) + require(10_000_000 <= int(video.get("bit_rate", 0)) <= 12_000_000, f"{path.name}: video bitrate outside 10-12 Mbps", failures) + if audio: + require(audio.get("sample_rate") == "48000" and audio.get("channels") == 2, f"{path.name}: wrong audio layout", failures) + require(int(audio.get("bit_rate", 0)) >= 240_000, f"{path.name}: audio bitrate below delivery target", failures) + + +def main() -> None: + failures: list[str] = [] + manifest = json.loads((PROJECT / "preview-manifest.json").read_text(encoding="utf-8")) + score = json.loads((PROJECT / "score-manifest.json").read_text(encoding="utf-8")) + template = (PROJECT / "composition.template").read_text(encoding="utf-8") + selector = (PROJECT / "scripts/select-locale.mjs").read_text(encoding="utf-8") + sync_assets = (PROJECT / "scripts/sync-assets.mjs").read_text(encoding="utf-8") + renderer = (PROJECT / "scripts/render-locale.mjs").read_text(encoding="utf-8") + package = json.loads((PROJECT / "package.json").read_text(encoding="utf-8")) + active = json.loads((PROJECT / "active-locale.json").read_text(encoding="utf-8")) + + require(manifest.get("version") == "asc1.6.0_v003", "wrong Preview version", failures) + require(manifest.get("durationSeconds") == 22, "Preview duration must be 22 seconds", failures) + require(tuple(manifest.get("locales", {}).keys()) == EXPECTED_LOCALES, "five-locale order or set drifted", failures) + require(tuple(scene.get("key") for scene in manifest.get("scenes", [])) == EXPECTED_SCENES, "scene order drifted", failures) + for locale, values in manifest.get("locales", {}).items(): + require(bool(values.get("proMaskLabel")), f"{locale}: missing localized price replacement", failures) + require(bool(values.get("proDisclosure")), f"{locale}: missing Pro disclosure", failures) + + require(score.get("durationSeconds") == 22 and score.get("sampleRate") == 48000, "score delivery contract drifted", failures) + score_scenes = score.get("scenes", []) + require(tuple(scene.get("key") for scene in score_scenes) == EXPECTED_SCENES, "score scene order drifted", failures) + require(tuple(float(scene.get("start")) for scene in score_scenes) == EXPECTED_STARTS, "score cues no longer match visual transitions", failures) + for current, following in zip(score_scenes, score_scenes[1:]): + require(float(current["end"]) == float(following["start"]), f"score gap between {current['key']} and {following['key']}", failures) + require(float(score_scenes[-1]["end"]) == 22, "score must end at 22 seconds", failures) + + for start in EXPECTED_STARTS[1:]: + require(f", {start});" in template, f"missing visual transition at {start}s", failures) + require('src="assets/app_preview_bed_v003.m4a"' in template, "composition does not use the v003 score", failures) + require("{{PRO_MASK_LABEL}}" in template and "price-mask" in template, "localized price replacement is not rendered", failures) + require("PRO_MASK_LABEL: locale.proMaskLabel" in selector, "locale selector does not map the price replacement", failures) + require("generate-score.py" in sync_assets and "loudnorm=I=-20:TP=-1.5:LRA=7" in sync_assets, "score generation/mastering contract drifted", failures) + require("hyperframes-v002/assets/app_preview_bed_v002" not in sync_assets, "v003 must not reuse the v002 music bed", failures) + for required in ("-profile:v", "-level:v", "-minrate", "-maxrate", "-b:a", "-ar", "-ac"): + require(required in renderer, f"renderer missing {required}", failures) + require(package.get("scripts", {}).get("render-all") == "node scripts/render-all.mjs", "render-all command drifted", failures) + require(active.get("locale") == "en-US" and active.get("assetLocale") == "en", "default active Preview must be English", failures) + + inspect_optional_renders(failures) + if failures: + raise SystemExit("App Preview v003 smoke failed:\n- " + "\n- ".join(failures)) + print("App Preview v003 smoke passed (five locales, synchronized original score, delivery contract).") + + +if __name__ == "__main__": + main() diff --git a/scripts/check_asc_metadata_as_code_smoke.py b/scripts/check_asc_metadata_as_code_smoke.py index 8af8d62d..bbcd8770 100644 --- a/scripts/check_asc_metadata_as_code_smoke.py +++ b/scripts/check_asc_metadata_as_code_smoke.py @@ -14,9 +14,30 @@ def require(text: str, snippet: str, label: str, failures: list[str]) -> None: failures.append(f"{label} missing snippet: {snippet}") +def yaml_block_scalar(text: str, key: str) -> str: + marker = f" {key}: |-" + lines = text.splitlines() + try: + start = lines.index(marker) + 1 + except ValueError: + return "" + + value: list[str] = [] + for line in lines[start:]: + if line and not line.startswith(" "): + break + value.append(line[6:] if line.startswith(" ") else "") + return "\n".join(value).rstrip() + + def main() -> int: failures: list[str] = [] script = (TOOL / "asc_metadata.rb").read_text(encoding="utf-8") + screenshot_uploader = (TOOL / "asc_screenshot_upload.rb").read_text(encoding="utf-8") + preview_uploader_path = TOOL / "asc_app_preview_upload.rb" + preview_uploader = preview_uploader_path.read_text(encoding="utf-8") if preview_uploader_path.exists() else "" + build_binder_path = TOOL / "asc_build_bind.rb" + build_binder = build_binder_path.read_text(encoding="utf-8") if build_binder_path.exists() else "" readme = (TOOL / "README.md").read_text(encoding="utf-8") config_path = TOOL / "metadata.yml" @@ -28,22 +49,42 @@ def main() -> int: for snippet in [ 'when "push-config"', + 'when "export-config"', + 'when "create-version"', "def push_config", + "def export_config", + "def create_version", + 'attrs["appStoreState"] == "PREPARE_FOR_SUBMISSION"', "YAML.safe_load", "subscriptionGroupLocalizations", "subscriptionLocalizations", + "appStoreReviewDetails", + "push_review_notes_config", + "print_review_details", "appStoreVersionLocalizations", "appInfoLocalizations", "DRY-RUN", + 'DEFAULT_VERSION = "1.6.0"', + "zh-Hans zh-Hant en-US ja ko", ]: require(script, snippet, "asc_metadata.rb", failures) for snippet in [ "push-config", + "export-config", + "create-version", + "--source-version", + "--output", + "--skip-app-info", + "--shared-create-only", + "--locale", "metadata.yml", "--config", "dry-run", "subscription", + "asc_app_preview_upload.rb", + "videoDeliveryState", + "asc_build_bind.rb", ]: require(readme, snippet, "ASC metadata README", failures) @@ -55,6 +96,10 @@ def main() -> int: "version_localizations:", "subscription_group:", "subscriptions:", + "review_notes:", + "platform_profiles:", + "IOS: main", + "TV_OS: readonly", "top.darkrio326.AutoLedger.pro.monthly", "top.darkrio326.AutoLedger.pro.yearly", "ko:", @@ -64,12 +109,70 @@ def main() -> int: for locale in ["zh-Hans", "zh-Hant", "en-US", "ja", "ko"]: require(config, f"{locale}:", "metadata.yml", failures) + for snippet in [ + "TRANSIENT_HTTP_CODES", + "with_retry", + "upload_operations", + "wait_for_screenshot_set", + "screenshots_processing?", + "state=COMPLETE", + ]: + require(screenshot_uploader, snippet, "ASC screenshot uploader", failures) + + for snippet in [ + "appPreviewSets", + "appPreviews", + "sourceFileChecksum", + "videoDeliveryState", + "previewFrameTimeCode", + "--poster-frame-time-code", + "wait_for_poster_frame", + "wait_for_preview", + "delete_stale", + "COMPLETE", + ]: + require(preview_uploader, snippet, "ASC App Preview uploader", failures) + + for snippet in [ + "--build-number", + "--expected-source-commit", + "ciProduct", + "buildRuns", + "APP_STORE_ELIGIBLE", + "PREPARE_FOR_SUBMISSION", + "relationships/build", + "verified", + ]: + require(build_binder, snippet, "ASC build binder", failures) + subscription_block = config.split("\nsubscriptions:", 1)[1] if "\nsubscriptions:" in config else "" descriptions = re.findall(r'description:\s*"([^"]+)"', subscription_block) over_limit = [text for text in descriptions if len(text) > 55] if over_limit: failures.append("subscription descriptions must stay within ASC 55-character limit") + for profile in ["main", "readonly"]: + review_notes = yaml_block_scalar(config, profile) + if not review_notes: + failures.append(f"review note profile {profile} must not be empty") + continue + if len(review_notes) > 4000: + failures.append(f"review note profile {profile} exceeds ASC 4000-character limit") + for sensitive_term in ["ASC_ISSUER_ID", "ASC_PRIVATE_KEY", "BEGIN PRIVATE KEY"]: + if sensitive_term in review_notes: + failures.append(f"review note profile {profile} contains forbidden secret marker") + + main_review_notes = yaml_block_scalar(config, "main") + readonly_review_notes = yaml_block_scalar(config, "readonly") + for snippet in [ + "No demo account is required", + "Crash Data, Performance Data, and Product Interaction", + "not linked", + "not used for tracking", + ]: + require(main_review_notes, snippet, "main review notes", failures) + require(readonly_review_notes, snippet, "readonly review notes", failures) + if failures: print("ASC metadata-as-code smoke failed:") for failure in failures: diff --git a/scripts/run_offline_regression.sh b/scripts/run_offline_regression.sh index 924f3193..a9ad32bd 100755 --- a/scripts/run_offline_regression.sh +++ b/scripts/run_offline_regression.sh @@ -27,6 +27,7 @@ python3 "$ROOT/scripts/check_advanced_rule_automation_ui_smoke.py" python3 "$ROOT/scripts/check_pro_page_copy_smoke.py" python3 "$ROOT/scripts/check_asc_metadata_as_code_smoke.py" python3 "$ROOT/scripts/check_screenshot_localization_smoke.py" +python3 "$ROOT/scripts/check_app_preview_v003_smoke.py" python3 "$ROOT/scripts/check_hotel_weather_ui_smoke.py" python3 "$ROOT/scripts/check_documentation_truth_smoke.py" diff --git a/tools/appstore-screenshots/README.md b/tools/appstore-screenshots/README.md index 6b0ecb0b..d11eea04 100644 --- a/tools/appstore-screenshots/README.md +++ b/tools/appstore-screenshots/README.md @@ -2,7 +2,7 @@ This folder contains a repeatable local pipeline for App Store screenshots and App Preview keyframe preparation. It captures deterministic screenshot-mode screens from the iOS, iPad, Mac Catalyst, tvOS, and visionOS apps, and when available the Watch app, then renders store-ready PNGs and a local `preview.html`. -The same raw and store PNGs can be handed to Hyperframes or another video tool as keyframes for an App Store App Preview. This repository still does not upload assets to App Store Connect. +The same raw and store PNGs can be handed to Hyperframes or another video tool as keyframes for an App Store App Preview. This screenshot pipeline only generates assets; reviewed outputs can be uploaded with the separate helpers in `tools/asc-metadata/`. ## Supported Output @@ -13,7 +13,7 @@ The same raw and store PNGs can be handed to Hyperframes or another video tool a - Apple TV: zh-Hans, zh-Hant, en, and ja, default 4K landscape App Store size `3840x2160`. - visionOS: zh-Hans, zh-Hant, en, and ja, default landscape marketing size `3840x2160`. -The pipeline does not upload to App Store Connect or directly create official App Preview videos. App Preview / Hyperframes production material lives in `tools/appstore-screenshots/app-preview/`. +The pipeline does not itself upload to App Store Connect or directly create official App Preview videos. App Preview / Hyperframes production material lives in `tools/appstore-screenshots/app-preview/`, while upload and ASC state verification live in `tools/asc-metadata/`. ## Run @@ -89,11 +89,12 @@ open tools/appstore-screenshots/output/preview.html ## App Preview / Hyperframes -This repository does not directly generate the official App Preview video. It now provides production material for a 15-20 second Hyperframes workflow: +The ASC 1.6.0 v003 project now generates one 22-second iPhone App Preview for each current release language from a shared Hyperframes composition and an original deterministic score: ```text tools/appstore-screenshots/app-preview/ README.md + hyperframes-v003/ preview_script_zh-Hans.md hyperframes_brief_zh-Hans.md shotlist_zh-Hans.md @@ -106,8 +107,19 @@ Recommended source export: bash tools/appstore-screenshots/scripts/export.sh --ios-only --locale zh-Hans bash tools/appstore-screenshots/scripts/export.sh --watch-only --locale zh-Hans bash tools/appstore-screenshots/scripts/export.sh --ios-only --locale ja +bash tools/appstore-screenshots/scripts/export.sh --ios-only --locale ko +``` + +Current five-language render: + +```bash +cd tools/appstore-screenshots/app-preview/hyperframes-v003 +npm run sync-assets +npm run render-all ``` +`sync-assets` selects OCR, voice, hotel, Watch, monthly report, and Pro captures for `en-US`, `zh-Hans`, `zh-Hant`, `ja`, and `ko`, then generates the sample-free `Quiet Control` score. Final MP4 files are `886x1920`, 22 seconds, 30 fps, H.264 High Profile Level 4.0 with target 11 Mbps video and AAC 48 kHz stereo audio. Generated assets and renders remain ignored. The five v003 files were uploaded to ASC 1.6.0 on 2026-07-18 and verified by MD5 plus `videoDeliveryState=COMPLETE`; their poster frames use the reviewed OCR frame at `1.4s / 00:00:01:12`, and all generated frame states read back as `COMPLETE`. + Recommended folders to hand to Hyperframes: ```text diff --git a/tools/appstore-screenshots/app-preview/README.md b/tools/appstore-screenshots/app-preview/README.md index afc03f98..a56599d3 100644 --- a/tools/appstore-screenshots/app-preview/README.md +++ b/tools/appstore-screenshots/app-preview/README.md @@ -4,8 +4,12 @@ This directory contains planning material and Hyperframes source projects for Ap ## Current Material -- ASC 1.5.0 current preview: `hyperframes-v002/renders/app_preview_iphone_zh-Hans_asc1.5.0_v002.mp4` -- ASC 1.5.0 English preview: `hyperframes-v002-en/renders/app_preview_iphone_en_asc1.5.0_v002.mp4` +- ASC 1.6.0 current five-language source: `hyperframes-v003` +- ASC 1.6.0 local outputs: `hyperframes-v003/renders/final/app_preview_iphone_{en-US,zh-Hans,zh-Hant,ja,ko}_asc1.6.0_v003.mp4` +- ASC 1.6.0 audio: original deterministic 22-second score generated from `hyperframes-v003/score-manifest.json`; no third-party samples or reused v002 music +- ASC 1.6.0 live status (2026-07-18): all five `IPHONE_65` previews match local MD5 and report `videoDeliveryState=COMPLETE`; poster frames are set to the reviewed OCR frame at `1.4s / 00:00:01:12`, with `previewFrameImage.state=COMPLETE` +- ASC 1.5.0 previous Chinese preview: `hyperframes-v002/renders/app_preview_iphone_zh-Hans_asc1.5.0_v002.mp4` +- ASC 1.5.0 previous English preview: `hyperframes-v002-en/renders/app_preview_iphone_en_asc1.5.0_v002.mp4` - ASC 1.5.0 source projects: `hyperframes-v002`, `hyperframes-v002-en` - ASC 1.4.0 preserved preview: `archive/asc-1.4.0/renders/app_preview_iphone_zh-Hans_asc1.4.0_v001.mp4` - ASC 1.4.0 editable source remains: `hyperframes-v001` @@ -18,27 +22,27 @@ Generated screenshot outputs remain ignored under: tools/appstore-screenshots/output/ ``` -For ASC 1.5.0, the current `store/` PNG set is about 68 MB across 74 files, while `raw/` is about 142 MB. Do not commit the entire generated `output/` folder into normal Git history. +Do not commit the entire generated `output/` folder or v003's copied `assets/` / rendered videos into normal Git history. The v003 project regenerates selected captures and audio from repository scripts, screenshot output, and its checked-in manifests. Recommended retention: -1. Keep screenshot scripts, screenshot configs, App Preview source projects, rendered preview videos, keyframes, and contact sheets in Git. -2. Upload final ASC store screenshots as a GitHub Release artifact or versioned zip when release evidence needs to be preserved. +1. Keep screenshot scripts, screenshot configs, App Preview source projects, manifests, composition templates, and score-generation scripts in Git. +2. Upload final ASC screenshots and App Preview MP4 files as a GitHub Release artifact or versioned zip with checksums when release evidence needs to be preserved. 3. If screenshots must be committed to the repository, commit only curated `store/` PNGs for the exact ASC release, never `raw/` captures. ## Recommended Workflow -1. Export iPhone screenshots with the existing screenshot pipeline. -2. Copy the needed store PNGs into the active Hyperframes project `assets/`. -3. Update `index.html`, `DESIGN.md`, `preview_script_zh-Hans.md`, and `shotlist_zh-Hans.md` together. -4. Run Hyperframes checks. -5. Render the MP4 and extract keyframes / contact sheet for visual review. -6. Manually upload the finished video to App Store Connect. +1. Export all five iPhone screenshot locales with the existing screenshot pipeline. +2. Run v003 `npm run sync-assets`; it copies the six selected raw UI captures and deterministically generates the original AAC score. +3. Update `preview-manifest.json`, `score-manifest.json`, `composition.template`, and `DESIGN.md` together. +4. Run Hyperframes `validate` / `inspect` for every locale. +5. Render all five MP4 files, then extract hero frames / contact sheets for visual review. +6. Upload the finished video with the ASC helper, then set and verify the reviewed poster frame. Recommended export command: ```bash -bash tools/appstore-screenshots/scripts/export.sh --ios-only --locale zh-Hans +bash tools/appstore-screenshots/scripts/export.sh --ios-only ``` Recommended source folders: @@ -48,23 +52,24 @@ tools/appstore-screenshots/output/store/ios/zh-Hans tools/appstore-screenshots/output/raw/ios/zh-Hans ``` -Current render commands: +Current render command: ```bash -cd tools/appstore-screenshots/app-preview/hyperframes-v002 -npm run check -npm run render -- --output renders/app_preview_iphone_zh-Hans_asc1.5.0_v002.mp4 --fps 30 --quality standard - -cd ../hyperframes-v002-en -npm run check -npm run render -- --output renders/app_preview_iphone_en_asc1.5.0_v002.mp4 --fps 30 --quality standard +cd tools/appstore-screenshots/app-preview/hyperframes-v003 +npm run sync-assets +npm run render-all ``` Current outputs: ```text -tools/appstore-screenshots/app-preview/hyperframes-v002/renders/app_preview_iphone_zh-Hans_asc1.5.0_v002.mp4 -tools/appstore-screenshots/app-preview/hyperframes-v002-en/renders/app_preview_iphone_en_asc1.5.0_v002.mp4 +tools/appstore-screenshots/app-preview/hyperframes-v003/renders/final/app_preview_iphone_en-US_asc1.6.0_v003.mp4 +tools/appstore-screenshots/app-preview/hyperframes-v003/renders/final/app_preview_iphone_zh-Hans_asc1.6.0_v003.mp4 +tools/appstore-screenshots/app-preview/hyperframes-v003/renders/final/app_preview_iphone_zh-Hant_asc1.6.0_v003.mp4 +tools/appstore-screenshots/app-preview/hyperframes-v003/renders/final/app_preview_iphone_ja_asc1.6.0_v003.mp4 +tools/appstore-screenshots/app-preview/hyperframes-v003/renders/final/app_preview_iphone_ko_asc1.6.0_v003.mp4 ``` +The v003 final files are local release artifacts and remain ignored until they are archived outside normal Git history. ASC upload and poster-frame selection completed on 2026-07-18 through `tools/asc-metadata/asc_app_preview_upload.rb`. The shared `1.4s / 00:00:01:12` OCR frame was visually checked across all five languages before the remote timecode and generated-frame state were read back. Final binary consistency remains a manual gate. + Do not include real payment screenshots, receipts, account data, card numbers, order numbers, phone numbers, API keys, certificates, profiles, or private user data in App Preview assets. diff --git a/tools/appstore-screenshots/app-preview/hyperframes-v003/.gitignore b/tools/appstore-screenshots/app-preview/hyperframes-v003/.gitignore new file mode 100644 index 00000000..58a3876a --- /dev/null +++ b/tools/appstore-screenshots/app-preview/hyperframes-v003/.gitignore @@ -0,0 +1,5 @@ +renders/review/ +renders/final/ +.hyperframes/ +assets/ +scripts/__pycache__/ diff --git a/tools/appstore-screenshots/app-preview/hyperframes-v003/AGENTS.md b/tools/appstore-screenshots/app-preview/hyperframes-v003/AGENTS.md new file mode 100644 index 00000000..684ced94 --- /dev/null +++ b/tools/appstore-screenshots/app-preview/hyperframes-v003/AGENTS.md @@ -0,0 +1,87 @@ +# HyperFrames Composition Project + +## Skills — USE THESE FIRST + +**Always invoke the relevant skill before writing or modifying compositions.** Skills encode framework-specific patterns (e.g., `window.__timelines` registration, `data-*` attribute semantics, shader-compatible CSS rules) that are NOT in generic web docs. Skipping them produces broken compositions. + +**Doing anything with HyperFrames?** Start at `/hyperframes` — it tells you what HyperFrames can do and which skill or workflow handles your intent (make a video, TTS / BGM, prep footage, author / animate, render, install blocks), and routes every "make me a video" request to the right workflow. Read it first, especially when there's no project context to orient you. The video workflows it routes to: + +- `/product-launch-video` — a **product** URL or brief / script → 60-90s product launch / SaaS / promo video. +- `/website-to-video` — a **general** website / URL → a video _of_ the site (tour / showcase / social clip from captured visuals); a product **launch / promo** is `/product-launch-video`. +- `/faceless-explainer` — arbitrary text (topic / article / notes), **no URL, no website capture** → 60-90s faceless explainer. +- `/embedded-captions` — an existing talking-head video (MP4) → the same footage with captions / subtitles added (rail + embed, or pure-cinematic embed); the footage itself is untouched. +- `/graphic-overlays` — an existing talking-head / interview / podcast video (MP4) → the same footage **packaged with designed graphic overlays** (kinetic titles, lower-thirds, data callouts, pull-quotes, side panels, pip) synced to the transcript; the clip plays unchanged underneath. (Plain captions/subtitles → `/embedded-captions`.) +- `/pr-to-video` — a GitHub PR (URL / `owner/repo#N` / "this PR") → 30-90s code-change explainer (changelog / feature reveal / fix / refactor). +- `/motion-graphics` — a short (typically under 10s) design-led **motion graphic**, motion-is-the-message, no narration: kinetic type, a stat / number count-up, a chart, a logo sting, a lower-third / overlay, or an animated tweet / headline / captured-page highlight; rendered to MP4 or a transparent overlay. Longer / narrated / custom → `/general-video`. +- `/general-video` — fallback for any other video (title card, longer brand / sizzle reel, multi-scene montage, static loop, custom composition); the original hyperframes authoring flow, any length. + +**Porting an existing composition?** `/remotion-to-hyperframes` translates a Remotion (React) composition into HyperFrames HTML — a source migration, separate from the creation workflows above. + +The domain skills (`/hyperframes-core`, `/hyperframes-animation`, `/hyperframes-creative`, `/hyperframes-cli`, `/hyperframes-media`, `/hyperframes-registry`) and the full capability map live inside `/hyperframes` — it is the single source of truth for which skill handles which intent. + +> **Tailwind v4 projects** (`hyperframes init --tailwind`): see `/hyperframes-core` → `references/tailwind.md`. + +> **Skills not available?** Ask the user to run `npx hyperframes skills` and restart their +> agent session, or install manually: `npx skills add heygen-com/hyperframes`. + +## Commands + +```bash +npm run dev # start the preview server (long-running — keep it alive in background) +npm run check # lint + validate + inspect +npm run render # render to MP4 +npm run publish # publish and get a shareable link +npx hyperframes lint --verbose # include info-level findings +npx hyperframes lint --json # machine-readable output for CI +npx hyperframes docs # reference docs in terminal +``` + +> **`npm run dev` is a long-running server, not a one-shot command.** It blocks until stopped. +> In Claude Code, always run it with `run_in_background: true`. Never run it as a foreground +> command — it will time out and the server will die, breaking the browser preview. + +## Documentation + +**For quick reference**, use the local CLI docs command (no network required): + +```bash +npx hyperframes docs +``` + +Topics: `data-attributes`, `gsap`, `compositions`, `rendering`, `examples`, `troubleshooting` + +**For full documentation**, discover pages via the machine-readable index — do NOT guess URLs: + +``` +https://hyperframes.heygen.com/llms.txt +``` + +## Project Structure + +- `index.html` — main composition (root timeline) +- `compositions/` — sub-compositions referenced via `data-composition-src` +- `meta.json` — project metadata (id, name) +- `transcript.json` — whisper word-level transcript (if generated) + +## Linting — ALWAYS RUN AFTER CHANGES + +After creating or editing any `.html` composition, **always** run the full check before considering the task complete: + +```bash +npm run check +``` + +Fix all errors before presenting the result. Inspect warnings should be reviewed before rendering. + +## Key Rules + +1. Every timed element needs `data-start`, `data-duration`, and `data-track-index` +2. Elements with timing **MUST** have `class="clip"` — the framework uses this for visibility control +3. Timelines must be paused and registered on `window.__timelines`: + ```js + window.__timelines = window.__timelines || {}; + window.__timelines["composition-id"] = gsap.timeline({ paused: true }); + ``` +4. Videos use `muted` with a separate `