CarbClueCarbClue
Team BlogBuild In PublicDevelopment

开发日志 · 2026-06-26:24 小时内从零到五功能切片

CarbClue Team5 min read

这是 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_foodsget_foodget_diary_entrylist_diary_entrieslog_diary_entryget_gkilist_biomarker_readingslist_recipesget_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/mainfrontend/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.6strong_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 硬编码浅色,OnboardingStepViewsOnboardingPlanViewTextField 未设显式前景色,深色模式下白字白底几乎不可见。临时修复:在 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.

Keep reading