开发日志 · 2026-06-26:24 小时内从零到五功能切片
这是 CarbClue 的第一篇开发日志。我们选择 Build in Public——不只是发布功能截图,而是把 PR 编号、issue 引用、测试数量和未解决的阻碍项都写进来。这样的透明度对我们自己是约束,对关注我们的人是真实的进度窗口。
今日数字概览
| 指标 | 数值 |
|---|---|
| 后端合并 PR | 13 个 |
| iOS 合并 PR | 11 个 |
| 产品 / 契约 PR | 7 个 |
| 官网 PR | 1 个 |
| 后端测试(收盘) | 284 个 |
| iOS 测试(收盘) | 190+ 个 |
| 生产部署 | 1 次(Cloudflare Workers) |
| 跨端数据不一致修复 | 1 处(GKI 9.0 边界) |
里程碑完成度
| 阶段 | 完成率 | 说明 |
|---|---|---|
| 0 研究 / 设计 | 75% | VIS v0.1 已锁定;设计系统组件库待启动 |
| 1 基础设施 | 100% | 全部已上线 |
| 2 v1 核心功能 | 60% | 5 大切片完成;Auth OIDC、AI 识别、Insights 待做 |
| 3 预发布 | 71% | 候补名单已上线;TestFlight 基础设施已就绪 |
| 4 发布 | 0% | App Store Connect 配置为最近期阻碍项 |
第一章:创世——一天内从零到生产
后端架构 [backend#1]
技术选型:Cloudflare Workers + Hono + D1(SQLite)。架构严格遵循 DDD 四层分离:
domain/— 纯业务逻辑,无框架依赖application/— 用例编排 + 出站端口接口infrastructure/— D1 存储适配、UUID、时钟interfaces/http/— Hono 路由、Zod 校验、组合根
Day 1 就有 30 个测试。D1 绑定在测试环境里必须被 mock,这倒逼我们第一天就把测试框架建好了。
HTTP 错误全部遵循 RFC 9457 Problem Details 格式;所有营养相关响应均附带 safety_notice 字段(这是 v1 强制要求,不是可选的)。
CD 流水线 v1.0.0 [backend#3] — 首次生产部署
GitHub Actions 串联:lint → typecheck → test → wrangler deploy。
/healthz 端点从第一天起就返回结构化版本信息:
{
"version": "1.0.0",
"commit": "...",
"ref": "main",
"built_at": "2026-06-25T...",
"environment": "production"
}
concurrency: deploy-production 防止并发部署覆盖彼此。这是第一次生产部署。
状态页 [backend#7, #8, #11, #19]
域名:status.carbclue.com(Cloudflare Pages)。轮询 /healthz + /readyz,历史记录持久化在 D1 status_checks 表里;Worker cron 每分钟写入一条。页脚用品牌域名,不泄露 *.workers.dev。
MCP 服务器 [backend#13]
POST /mcp Streamable HTTP,JSON-RPC 2.0,无状态(兼容 Workers 无持久内存的限制)。
工具集:search_foods、get_food、get_diary_entry、list_diary_entries、log_diary_entry、get_gki、list_biomarker_readings、list_recipes、get_status。
tools/call 执行与 REST 相同的 JWT 作用域 + 同意检查——没有为 MCP 单独写一套业务逻辑。合并时 181 个测试通过。
创世收尾 v1.1.0 [backend#15]
版本升级,注入 ENVIRONMENT:production 变量,GitHub Actions 从废弃 Node 20 升级。
第二章:契约优先——跨仓库治理层锁定
在写功能代码之前,我们先把"事实来源"固定下来。
API 契约 §1–§8 + 决策日志 DL-001..019 + 11 个 fixture 预言机 [product#53]
product/main 现在是所有四个仓库的唯一权威:API 契约文档(§1–§8)、20 条架构决策记录、逻辑数据模型、合规差距记录,以及 11 个标准 JSON fixture 文件:
profile · goals · food · food-search · diary-entry · diary-summary · water-log · streak · biomarker-reading · GKI · error-validation
任何 product/main checkout 均可将实现响应与标准 JSON 对比——这是独立可验证的,不依赖口头约定。
生物标志物 §9 + DR-01 + DL-020 + GKI 边界锁定 [product#54, #56]
冻结生物标志物读数模型(marker_type{ketone,glucose} + method{blood,urine,breath})。
关键发现:GKI 9.0 边界在后端和 iOS 之间存在分歧。后端把 9.0 划进 non_ketosis,iOS 端把它划进 light_ketosis。product#56 用半开区间语义锁定了答案:
deep_ketosis: GKI < 1
strong_ketosis: 1 ≤ GKI < 3
light_ketosis: 3 ≤ GKI < 9
non_ketosis: GKI ≥ 9 ← 9.0 归属此处
fixture 预言机让这类漂移在到达用户之前可检测。
第三章:切片 1–5——完整 v1 用户旅程
所有五个切片于同一天依次落地 backend/main 和 frontend/main。
切片 1 — 引导 / 档案 / 宏量目标 [backend#16 · frontend#11 · frontend#15]
后端 [backend#16]:PUT/GET/PATCH /v1/profile + PUT/GET /v1/goals;Mifflin–St Jeor 目标引擎——BMR → TDEE → 目标乘数 → 宏量素分配 → CKD 蛋白质上限;ED 安全模式自动筛查(DL-007);低于 800 kcal 硬拦截(451 SAFETY_BLOCKED);迁移文件 0006。235 个测试。
Fixture 回路验证:女性/34岁/168cm/70kg/轻度活动/减重/生酮 → 1561 kcal,净碳 20g / 蛋白质 98g / 脂肪 121g。
iOS 数据/领域层 [frontend#11]:RemoteProfileRepository(离线优先 PUT 协调);RemoteGoalsRepository(服务端权威);本地引擎保留为离线回退。
iOS 引导 UI [frontend#15]——本期最重要的 PR,第一次有真实用户可以完整走通的 iOS 路径:
打开 App → S1 设定目标 → S2 填写基本信息 → S3 选择饮食方案
→ S4 情绪/习惯问卷(可跳过)→ S5 安全红旗问题
→ S6 计划展示(DL-011 透明计算:BMR→TDEE→目标→宏量素)
→ 进入 5 标签主界面
几个设计细节值得记录:
- S4 的"跳过"和"下一步"按钮权重完全相同——没有视觉上暗示"你应该继续"的层级差异
- S5 的 ED 病史问题触发
ed_safe_mode,并在页面内即时告知用户,不是在后台静默开启 - S6 的 ED 安全变体只显示宏量素比例,不显示绝对数字
- 引导完成后
RootView直接跳到 5 标签主界面;再次打开跳过引导
这个 PR 的 commit message 明确标注 "macOS CI green"。
切片 2 — 食物目录 + 日记记录 [backend#21 · frontend#3, #5, #17]
后端 [backend#21]:食物重构为每 100g 营养 + common_servings JSON;Diet Fit DL-006 评分公式:clamp(0,100, round(100×(1−nc/ceiling))),生酮上限 45g / 低碳水上限 90g;日记条目含锁定宏量素快照(防止食物数据变更影响历史记录);迁移文件 0007。243 个测试。
Diet Fit 示例:希腊酸奶净碳 3.6g/100g → round(100×(1−3.6/45)) = 92 → Friendly。
iOS 目标引擎 [frontend#3, #5]:本地 Mifflin–St Jeor 实现,91 个测试,与后端 fixture 对齐。
iOS 食物 UI [frontend#17]:FoodSearchView(≥2 字符防抖 300ms,4 标签)、FoodDetailView(份量实时重算)、DietFitBadge(图标 + 文字——绝不只靠颜色区分)。与 backend#21 同步合并(breaking change)。
切片 3 — AI 拍照识别 [frontend#7]
仅 iOS:FoodRecognitionRepository 端口 + 桩代码,PhotoLogView,生酮/低碳水切换实时重评分(DL-006)。后端 /v1/recognitions 尚未实现(backend issue #12 追踪,无 ETA)。104 个测试。
切片 4 — Today 看板 [backend#23 · frontend#20]
后端 [backend#23]:GET /v1/diary/summary?date=(7 天趋势、喝水、打卡、safety_notice);POST /v1/water-logs(累积杯数,下限 0);POST /v1/streaks(宽容间隔 ≤1 天,≥2 天重置,当天无记录则 409)。迁移文件 0008。280 个测试。
iOS Today UI [frontend#20]:宏量素环(超预算显示橙色——绝不红色,遵循诚信账单规则);喝水 +1 乐观更新;连续天数 🔥;7 天迷你趋势。ED 安全变体:环显示百分比,模糊标签("还有空间"/"接近目标"/"差不多了"),趋势仅箭头——无任何绝对数值。190+ 个测试。
切片 5 — 生物标志物 + GKI [backend#25 · frontend#22]
后端 [backend#25]:血酮/呼气/尿液定性/葡萄糖全类型支持;非阻塞安全标记(酮酸中毒警告、低血糖、高血糖);GKI ÷18.02,同日 ≤4h 配对,4 档级别;迁移文件 0009(DROP+重建,预生产无真实数据,这个操作在有数据后不可重复)。284 个测试。
iOS 生物标志物 UI [frontend#22]:方法选择芯片、葡萄糖单位切换(mmol/L ↔ mg/dL)、非阻塞危险横幅;GKI 4 档徽章(颜色+文字——绝不仅颜色),ED 安全隐藏绝对数值,空状态 CTA。多态 value(数字 | 定性字符串)以 Swift enum 解码。
GKI 验收基准:血糖 90 mg/dL → 5.0 mmol/L;血酮 1.9;GKI = 2.6;strong_ketosis——跨仓库一致确认。
第四章:设计基础
VIS v0.1 [product#39]
VI 规范 + 8 张设计图板 + 终版 Logo(彩色/单色/SVG)+ tokens/carbclue.vi.tokens.json。品牌方向 B 锁定:暖色系橘红(#FF6B4A / #F2A93B / #FFE7C6 / #2A2A30)。
DS 产品上下文桥接文档 [product#40, #41]
将完整页面规格 IA、屏幕清单、组件清单、6 态模型蒸馏进 04-design/ 上下文。零色值——颜色依据 VIS 方向 B + tokens,不硬编码。
第五章:Xcode Cloud + TestFlight 基础设施
这部分差点被我们自己漏掉——因为它埋在了一个标题叫"scaffold foundations"的 PR 里。
[frontend PR#1 commit 644d0f0]:project.yml(xcodegen 配置)+ ci_scripts/ci_post_clone.sh(Xcode Cloud 克隆后钩子)+ ios/docs/xcode-cloud-testflight.md(App Store Connect 完整操作手册)。
TestFlight 当前状态:
| 条目 | 状态 |
|---|---|
加密合规声明 ITSAppUsesNonExemptEncryption: NO |
✅ 已完成 [frontend#9] |
| Xcode Cloud 基础设施脚本 | ✅ 已完成 [frontend PR#1 commit 644d0f0] |
| TestFlight 操作手册 | ✅ 已就绪 [ios/docs/xcode-cloud-testflight.md] |
| 引导 S1–S6 SwiftUI 流程 | ✅ 已完成 [frontend#15] |
| 食物搜索 → 记录 → Today | ✅ 已完成 [frontend#17] |
| Today 看板(环 / 喝水 / 连续) | ✅ 已完成 [frontend#20] |
| 生物标志物 + GKI | ✅ 已完成 [frontend#22] |
| iOS CI | ✅ 已确认(切片 #15, #17, #20, #22 commit message 均标注 "macOS CI green") |
| App Store Connect 初始配置 | ❌ 唯一剩余阻碍(一次性手动操作) |
| 生产环境 Auth(OIDC RS256) | ❌ 当前为 dev-stub,对外 Beta 前必须完成 |
第六章:预发布官网
/early-access 候补名单 [official_website#14]
Next.js 路由 /early-access;getwaitlist 推荐插件 + UTM 归因 + 转化事件;路由级 OG 图片(构建时生成);已加入 sitemap.ts + llms.txt。
Hero 文案和 N=300 目标为待确认决策(尚未在产品层面锁定)。
当前阻碍项
TestFlight 上架路径
唯一剩余阻碍:App Store Connect 一次性 UI 配置。按 ios/docs/xcode-cloud-testflight.md 手册,在 App Store Connect UI 创建 App record、配置 Xcode Cloud 工作流、设置 TestFlight 内部测试组。技术侧全部就绪,只差这一步人工操作。
对外 Beta 开放前还需完成生产环境 Auth(JWT 颁发者验证当前为 dev-stub,接受任何格式正确的 Bearer Token)。
已知问题(不阻塞 TestFlight,建议上架前修)
深色模式引导页文字不可见 [frontend issue #18]:临时主题 CarbClueColors 硬编码浅色,OnboardingStepViews 和 OnboardingPlanView 的 TextField 未设显式前景色,深色模式下白字白底几乎不可见。临时修复:在 CarbClueApp.WindowGroup 加 .preferredColorScheme(.light) 全 App pin 浅色模式。正式修复等 Design System 落地时一并处理。
产品待确认
Diet Fit 公式确认挂起 [product issue #42, 状态 OPEN]:后端已按 DL-006 落地并通过 fixture 验证,但产品侧 issue 尚未关闭确认。若产品认可 DL-006,请关闭 issue #42。
其他待做
/v1/recognitions后端接口未实现 [backend issue #12],无 ETA- Android 已暂停 [DL-016],CI 任务禁用(
if: false),无恢复时间表
下一步
| 事项 | 里程碑 |
|---|---|
| App Store Connect 初始配置 | TestFlight (M4.1) |
| 生产环境 Auth(OIDC RS256) | M2.5 |
| AI 拍照识别后端接口 | M2.6 |
| 设计系统组件库 | M0.4 |
| Plans / Insights / Me 标签页 | M2.9–M2.11 |
如果你正在探索低碳水或生酮饮食,欢迎通过早期体验页面加入候补名单,我们会在 TestFlight 开放时第一时间通知你。
— CarbClue 团队
Track low-carb with CarbClue
Snap a meal for net carbs, see how it fits with Diet Fit, and get honest, guided support. Detect ketosis when you want it.