Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199rbQDTsUbCVw84wc8BTFe
189 lines
22 KiB
Markdown
189 lines
22 KiB
Markdown
# 任务书 · 成本记账补漏与缓存分价(2026-09-27)
|
||
|
||
基线:`origin/staging` @ `0c95a7ec`("docs(bugs): BUG-1061 deployed to staging d9c9e714")。
|
||
|
||
**行号只是线索,一律按符号名定位。** 本单是对 `TASK-billing-pricing-20260830.md`、`TASK-round2-cost-and-delivery-20260831.md` 的续单:前者把账本、功能定价、聚合端点、定价测算页都做完了;后者的任务 0(采真实单位成本)**从未执行**,没有 PROGRESS 文件。本单不重做已完成部分,只补让「成本算出来是假的」的那几处漏洞。
|
||
|
||
## 为什么要做
|
||
|
||
产品负责人 2026-09-27 提问「项目怎么做成本计算 / 后台有没有 token 统计 / 缓存开了没有」。按代码逐条核对后的结论:
|
||
|
||
1. **骨架完整**:`usage_reservations → usage_events → usage_ledger` 三表;`authorize_usage / complete_usage / release_usage` 三个 SQL 函数;`feature_pricing` 按功能定价;后台 `/admin/usage`(每行有 Token、缓存读 tokens、成本列)、`/admin/usage/aggregate`(按功能 30 天 runs / avg / p50 / p95 / max)、`/admin/pricing-simulator`(售价 vs 成本 vs 毛利率 + 缓存命中表)、`/admin/models`(输入 / 输出单价)。
|
||
2. **算出来是 0**:`model_config_versions.input_cost_microusd_per_million / output_cost_microusd_per_million` 默认 0,staging 从未填过;账本里没有一条非零真实成本行。**而且后台没有地方填**:`components/admin/model-management.tsx` 的模型草稿弹窗只有「供应商 / 模型 / 单次点数 / 启用 / 默认模型」五个字段,`saveDraft` 请求体里的 `inputCostMicrousdPerMillion / outputCostMicrousdPerMillion / modelTier / contextWindow` 全部原样回填旧值(新建时为 0 / standard / null)。列表页「成本/百万 Token」一列永远显示 `$0.0000 / $0.0000`。API(`api/admin/models/route.ts` 的 zod schema)是收这几个字段的,缺的只是表单。见 E0。
|
||
3. **即使填了单价,账本也系统性低估**,原因就是下面「事故实证」的五条。这才是本单要修的。
|
||
|
||
## 事故实证(按符号定位,已逐条读代码确认)
|
||
|
||
### E0 · 模型单价在后台无处可填
|
||
|
||
`model-management.tsx` → 模型弹窗(`title` 为「新增模型草稿」/「编辑 <modelId>」)内 `Form.Item` 的 `name` 只有 `providerId / providerModel / creditCost / enabled / isDefault`。`modelTier`(影响 `feature_pricing` 匹配哪一档)、`contextWindow`、两个单价都不可编辑。产品负责人 2026-09-27 在 staging 后台实际打开过测算页,「单位成本」列全部是「无实测数据 / 模型估算 0」,根源之一就是这里。
|
||
|
||
### E1 · 七处模型调用不进账本
|
||
|
||
| # | 调用点 | 符号 | 现状 |
|
||
| --- | --- | --- | --- |
|
||
| 1 | 会话标题 | `frontend/src/lib/session-title-agent.ts` → `generateSessionTitleText` | `agent.generate` 结果只取文本,usage 丢弃 |
|
||
| 2 | 上下文摘要 | `frontend/src/lib/session-context-summary.ts` → `generateSessionContextSummaryText` | 同上,默认模型,输入是整段历史,**不便宜** |
|
||
| 3 | 校正轮意图分类 | `frontend/src/lib/rectification-agentic/v9/turn-intent-classifier.ts` → `classifyTurnIntentWithRetry` | 只 `console.info` 诊断,每一轮都调 |
|
||
| 4 | 采用旁白 | `frontend/src/lib/rectification-agentic/v9/adopt-narration-agent.ts` → `generateAdoptNarrationText` | usage 丢弃 |
|
||
| 5 | 校正非 `message` 动作(opening 等) | `frontend/src/lib/rectification-agentic/v9/agent-route-billing.ts` → `createRectificationRunBilling` 的 `reserve / complete` 开头 `if (action !== "message") return` | 免费是产品决定,**但 agent run 的 token 一并被丢**:开场轮要加载整段 Skill,是校正里最贵的单轮之一 |
|
||
| 6 | 生时引导 | `frontend/src/lib/birth-time-guide-service.ts` → `generator.generate(prompt)` 那行(`createBirthTimeGuideService` 上方) | 不计费、不记账 |
|
||
| 7 | 每日星语 | `frontend/src/app/api/daily-starlanguage/route.ts` → `getDailyStarlanguageAgent(model).generate` | 只限流,不记账 |
|
||
|
||
对照:聊天主链已经把小聊分类器的 usage 合进去了(`consult/route.ts` → `usagePayload` 里的 `classificationUsage`),说明「附属调用并入主账」这个模式仓库里已有,不是新发明。
|
||
|
||
### E2 · 缓存命中的输入按全价算
|
||
|
||
四个成本计算点是同一段公式复制四份,都只有 `inputTokens × 输入单价 + outputTokens × 输出单价`:
|
||
|
||
- `frontend/src/app/api/consult/route.ts` → `usagePayload`
|
||
- `frontend/src/app/api/reports/route.ts` → 报告同步路径的 `costMicrousd` 计算
|
||
- `frontend/src/lib/personal-report-worker.ts` → worker 路径的 `costMicrousd` 计算
|
||
- `frontend/src/lib/rectification-agentic/v9/agent-route-billing.ts` → `complete(usage)`
|
||
|
||
`metadata.cache.readTokens` 已经被解析并写入账本(`promptCacheUsage` 覆盖 AI SDK、Mastra、OpenAI 原始三种形状;Mastra 1.50.1 会把 OpenAI 兼容返回的 `prompt_tokens_details.cached_tokens` 映射进 usage),**但它只用于展示命中率,不参与算钱**。`model_config_versions` 表也没有缓存读单价列。
|
||
|
||
供应商事实(2026-09-27 查官方文档,执行时须再核一次):DeepSeek 上下文缓存**默认对所有用户开启、不需要任何请求参数**,命中与否只看请求前缀是否与已缓存前缀完全一致;`usage` 返回 `prompt_cache_hit_tokens / prompt_cache_miss_tokens`,且 `prompt_tokens_details.cached_tokens` 与 hit 数相同;命中价约为未命中价的 1/30(V4-Pro 峰时 0.044 vs 1.32 美元 / 百万 token;非峰时半价)。所以「咱们是否开缓存了」的答案是:**开着,但账本把命中的那部分按 30 倍价格记**。本仓 `cachedSystemMessage / cachedHistoryMessage` 的 Anthropic 标记对 DeepSeek 无作用也无害。
|
||
|
||
### E3 · 多事件账本的 `metadata.cache` 被覆盖而不是累加
|
||
|
||
`complete_usage` 的 upsert 分支 `metadata = ledger.metadata || excluded.metadata`(`frontend/supabase/migrations/20260806030000_settle_order_usage_authorization.sql`)。校正一个 case 多轮、报告多章节都是多事件合一行;jsonb `||` 是浅合并,`cache` 键被最后一个事件整个替换,聚合端点与测算页的命中率因此失真。token 与成本列是累加的,只有 `metadata` 不是。
|
||
|
||
### E4 · 失败 / 释放的运行不留任何成本痕迹
|
||
|
||
`release_usage(p_user_id, p_request_id, p_reason)` 没有 usage 参数,只退积分、改状态。模型流到一半失败、用户中止、`empty_answer` 整轮失败(BUG-633 / 634 / 1049 那类)都已经花了供应商的钱,账本看不见。以校正为例,`runV9AgentTurn` 失败时走 `billing.release()`,此时 `result.totalUsage` 是有的,只是没人接。
|
||
|
||
### E5 · 后台聚合只展示成本,不展示 token 分位数
|
||
|
||
`/api/admin/usage/aggregate` 已返回 `inputTokens / outputTokens` 的 p50 / p95,但 `components/admin/pricing-simulator.tsx` 的「单功能表」只渲染售价、单位成本、毛利率。产品要看「一次校正到底吃多少 token」目前只能翻每行明细。
|
||
|
||
### 非缺陷、本单不做
|
||
|
||
- 推理 token:DeepSeek 把推理 token 计入 `completion_tokens` 一起按输出价收费,`outputTokens` 已经包含,**不需要单独定价**;只需把 `completion_tokens_details.reasoning_tokens` 存进 `metadata` 供观察(并入 T4)。
|
||
- 非模型算力(VedAstro 外部调用、Swiss Ephemeris 重计算):只有限流没有计量。另立单,本单不碰。
|
||
- 峰 / 非峰半价:不按时段分价,单价表填**峰时价**做保守估计。决策记录 D3。
|
||
|
||
## 根因
|
||
|
||
计费闭环那一轮的目标是「钱不出错」,账本是围绕**收费动作**建的:有预留才有账,免费的就没账。成本核算需要的是围绕**模型调用**建账。两者的差集就是 E1、E4;E2、E3 是缓存观测在 8 月 31 日那轮只做到「可见」没做到「计价」的遗留(PROGRESS-billing-pricing 的「任务 4 的缺口」已经写明)。
|
||
|
||
## 决策记录
|
||
|
||
- **D1** 产品负责人 2026-09-27 授权开本单:补记账、缓存分价、后台可看 token。**不动任何售价、积分价、会员公平使用参数**(沿用 0830 单红线 1)。
|
||
- **D2** 免费调用**照旧免费**,只记账不扣积分。E1 七处不得因此开始收费。
|
||
- **D3** 单价表按峰时价填;不实现按时段分价。
|
||
- **D4** `usage_reservations.feature_key` 与 `usage_ledger.source` 的 check 约束需要**加宽枚举**以容纳辅助调用(新 feature_key)与系统来源(新 source `system`)。这是 schema 扩展,不是放宽计费校验;`authorize_usage / complete_usage / release_usage` 的公平使用、配额、`model_not_included` 判定一条不得放宽(0830 单红线 4 继续有效)。辅助调用**不得**通过 `authorize_usage` 走一遍预留再释放来「借道」记账,那会污染公平使用计数。
|
||
- **D5** 供应商契约(缓存字段名、命中价)以执行当日官方文档为准,任务书里的数字只是线索。
|
||
- **D6** 8 月 31 日第二轮任务 0(真实成本采集)的**执行主体是产品负责人**,在本单部署后做;执行方只负责把清单写进 `docs/testing/`,并把 `TASK-round2-cost-and-delivery-20260831.md` 状态板行改为「任务 0 并入本单,其余未执行」。
|
||
|
||
## 硬红线
|
||
|
||
1. **钱不出错**:每条现有计费路径的 `reserve → complete / release` 三态不得改语义;本单只加「记」,不加「扣」。任何改动后 `application-billing-contract`、`consultation-billing`、`consult-reports-billing`、`high-risk-billing-routes-contract`、`feature-pricing-contract` 五个测试文件必须全绿且断言不改。
|
||
2. **成本公式只保留一份**:新建 `frontend/src/lib/usage-cost.ts`(名字可调),导出一个纯函数 `computeUsageCostMicrousd(model, usage)`;四个调用点全部改成调它,`git grep 'inputCostMicrousdPerMillion ?? 0'` 在 `src/` 下只剩这一处。
|
||
3. **辅助调用记账失败不得影响主流程**:写账的 promise 用 `void`/`catch` 隔离,且失败要 `console.warn` 带 feature_key,不带用户内容。
|
||
4. **动表必须真跑 `npm run test:db`**(需要 Docker)。没有 Docker 就把 migration 与 SQL 测试写好、`BLOCKED.md` 记环境缺口,**不得推 staging**。
|
||
5. 不得修改既有测试断言;确需改的,断言上方写「原值 / 新值 / 原因」三栏并在 PROGRESS 单列。测试总数不低于开工时 `origin/staging` 实测。
|
||
6. 账本、日志、测试 fixture 不得出现用户内容、姓名、出生资料、模型原文;`metadata` 只放数字与枚举。
|
||
7. `scripts/jyotish_api_server.py` 与 `frontend/src/app/page.tsx` 零增长;不改 `.gitea/workflows/**`;不动 DNS;不自行提升 `main`。
|
||
8. 不得顺手升级 `@mastra/core` 或任何依赖。
|
||
|
||
让步顺序:**钱不出错 > 数据不损坏 > 功能与测试不回归 > 成本数字诚实(宁缺勿假)> 后台好看 > 代码整洁**。
|
||
|
||
## 开工前置
|
||
|
||
```bash
|
||
git fetch origin --prune
|
||
git worktree add -b codex/cost-accounting-gaps-20260927 \
|
||
.worktrees/cost-accounting-gaps-20260927 origin/staging
|
||
```
|
||
|
||
读 `AGENTS.md` §1–§7、`frontend/AGENTS.md`、`docs/tasks/PROGRESS-billing-pricing-20260830.md` 全文(尤其「外部核对」一节)、`docs/tasks/TASK-billing-pricing-20260830.md` 的硬红线。在 `docs/BUG_HISTORY.md` 检索 `usage_ledger`、`cost_microusd`、`complete_usage`、`release_usage`、`cache`、`计费`。跑 `python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45`(本单动了 SQL 与运行链,按 §9 要求跑)。
|
||
|
||
开工时记录基线:`tsc`、`lint` warning 数、`npm test` 总数与无 Docker 失败清单(存文件,收工逐条 diff)。
|
||
|
||
## 任务分解
|
||
|
||
### T0(P0,门控)· 核实供应商契约与运行事实
|
||
|
||
1. 在 staging 后台 `/admin/models` 或只读查询确认当前发布模型的 `provider_type`、`provider_model`、`model_tier`,写进 PROGRESS(不写 key、不写 base_url 的凭据部分)。
|
||
2. 按当日 DeepSeek 官方文档(`api-docs.deepseek.com` 的 kv_cache 与 pricing 页)核对:缓存是否默认开启、`usage` 字段名、命中 / 未命中 / 输出三档价格。若 provider 不是 DeepSeek,按对应供应商文档核。
|
||
3. 写一条单测:用 DeepSeek 形状的原始 `usage`(含 `prompt_tokens_details.cached_tokens` 与 `completion_tokens_details.reasoning_tokens`)喂 `promptCacheUsage`,断言 `readTokens` 正确。若 Mastra 已把它映射成 `cachedInputTokens`,两种形状都要覆盖。
|
||
|
||
**验收**:PROGRESS 有「供应商 / 模型 / 缓存契约 / 三档价格 / 文档 URL / 核对日期」一张表;新单测绿。
|
||
|
||
### T1(P0)· 缓存分价 + 成本公式收敛
|
||
|
||
0. **先把表单补上**(E0,独立可交付,建议第一个 commit):模型草稿弹窗增加「模型档位」(下拉,沿用 `MODEL_TIER_OPTIONS` 标签)、「上下文窗口」、「输入价 $/百万 token」、「输出价 $/百万 token」,以及本任务新增的「缓存读价」「缓存写价」。表单以**美元 / 百万 token** 输入(小数,精度 4 位),提交时乘 1,000,000 取整成微美元;编辑既有版本时回显换算后的值。保存后仍走既有「草稿 → 连接测试 → 发布」流程,不得绕过。列表页「成本/百万 Token」一列加上缓存读价。组件测试断言这六个字段存在且换算正确。
|
||
1. migration:`model_config_versions` 新增 `cache_read_cost_microusd_per_million bigint not null default 0 check (>= 0)` 与 `cache_write_cost_microusd_per_million`(同型;DeepSeek 为 0,Anthropic 有写入溢价,留位)。`admin_save_model_config_draft`(或现名)与 `model_catalog.ts` 的读取、`/admin/models` 表单、返回 JSON 一并加字段;旧行默认 0,行为不变。
|
||
2. 新建 `computeUsageCostMicrousd(model, usage)`:
|
||
- `billableInput = inputTokens - cache.readTokens`(下限 0)按输入价;
|
||
- `cache.readTokens` 按缓存读价;`cache.writeTokens` 按缓存写价(为 0 则等于不计);
|
||
- `outputTokens` 按输出价;四舍五入到整数微美元。
|
||
- 缓存读价为 0 且 `readTokens > 0` 时**不得**静默按 0 算:返回值附带 `pricingComplete: false`,调用方写进 `metadata.pricing = "cache_price_missing"`,聚合端点把这类行单独计数。这是「宁缺勿假」。
|
||
3. 四个调用点改为调用该函数;删掉四份复制的公式。
|
||
4. 单测:纯函数覆盖「无缓存 / 有命中 / 缓存价缺失 / 全命中」四例;四个调用点各一条合同测试证明走了新函数(现有 `consult-reports-billing.test.ts` 等按需扩展,不改旧断言)。
|
||
|
||
**验收**:`git grep -n 'inputCostMicrousdPerMillion ?? 0' frontend/src` 只剩 `usage-cost.ts`;`npm run test:db` 含新列的读写用例;`tsc` 0 错。
|
||
|
||
### T2(P0)· 辅助调用进账本(不扣费)
|
||
|
||
1. 加宽枚举(D4):`usage_reservations.feature_key`、`usage_ledger.feature_key`、`feature_pricing.feature_key` **不动**(定价表不需要新键);只在 `usage_reservations` / `usage_ledger` / 聚合端点的 `FEATURE_KEYS` 加:`chat.title`、`chat.summary`、`rectification.opening`、`rectification.intent`、`rectification.narration`、`birth_time_guide`、`daily_starlanguage`。`source` 加 `system`。
|
||
2. 新 SQL 函数 `record_system_usage(p_user_id, p_request_id, p_feature_key, p_actual_usage jsonb)`:自建一条 `status='completed'、source='system'、credit_amount=0` 的预留并直接调 `complete_usage` 落账;幂等键仍是 `(user_id, request_id)`;**不触碰** `profiles.credits`、不写 `credit_transactions`、不参与公平使用计数(`authorize_usage` 的窗口查询若按 `feature_key in (...)` 或 `source` 过滤,要确认 `system` 行不会被算进去,并写测试钉死)。
|
||
3. TS 封装 `recordSystemUsage(accounting, ...)`,七处调用点接入;request_id 规则写进代码注释与 PROGRESS:
|
||
- 标题 / 摘要:`<consultRequestId>:title`、`<consultRequestId>:summary`
|
||
- 校正:`<turnRequestId>:opening`、`:intent`、`:narration`(同一轮可能有多次意图分类,用 `:intent:<n>`)
|
||
- 生时引导 / 每日星语:各自路由的 requestId
|
||
4. E1 #5(校正非 `message` 动作):`createRectificationRunBilling.complete` 在 `action !== "message"` 分支改为调 `recordSystemUsage(... "rectification.opening" ...)`,不再直接 `return true`;`reserve / release` 分支不变。
|
||
|
||
**验收**:七处各有一条测试证明 usage 被转发(mock accounting 断言 feature_key 与 token 数);`database-billing-*.test.ts` 新增 `record_system_usage` 幂等与「不动积分、不进公平使用窗口」两条 SQL 用例;聚合端点 JSON 出现七个新键且 `hasData` 语义不变。
|
||
|
||
### T3(P1)· 失败 / 释放的运行记成本
|
||
|
||
1. `release_usage` 增加可选参数 `p_actual_usage jsonb default null`;非空时向 `usage_events` 写一行(`metadata.outcome = "released"`,其余字段同 `complete_usage`),**不写 `usage_ledger`**(保持「ledger = 已完成交付」的语义),退款逻辑逐字不变。
|
||
2. `releaseUsage()` TS 封装加可选 usage;三条计费路径(聊天 `settleRun` 失败分支、报告 `completeReportFailed`、校正 `billing.release()`)在拿得到 `totalUsage` 的地方传进去,拿不到就照旧传空。
|
||
3. 聚合端点每个 feature 增加 `released: { runs, costMicrousd: { avg, p50, p95, max } }`,从 `usage_events where metadata->>'outcome' = 'released'` 聚合;测算页「单功能表」加一列「失败成本占比」= released 成本 / (completed + released 成本)。
|
||
|
||
**验收**:SQL 用例:释放带 usage 后 `usage_events` 有行、`usage_ledger` 无行、积分退回金额与之前测试一致;`admin-usage-aggregate-contract.test.ts` 扩展 released 结构。
|
||
|
||
### T4(P1)· `metadata.cache` 累加 + 推理 token 可见
|
||
|
||
1. `complete_usage` 的 upsert 分支:`cache.readTokens / writeTokens / noCacheTokens` 三个数按事件**累加**,`hit` = 累加后 `readTokens > 0`;`metadata` 其他键维持浅合并。写在 SQL 里,不靠调用方先读后写。
|
||
2. `ActualUsage.metadata` 加可选 `reasoningTokens`;`promptCacheUsage` 旁新增 `reasoningTokenUsage(raw)` 解析 `completion_tokens_details.reasoning_tokens`(及 AI SDK 的 `outputTokenDetails.reasoningTokens`),四个调用点带上。不参与算钱(见「非缺陷」)。
|
||
|
||
**验收**:SQL 用例:同一预留两次 `complete_usage` 各带 cache 后 ledger 的 `readTokens` 等于两次之和;`agent-generation-settings.test.ts` 加推理 token 解析用例。
|
||
|
||
### T5(P1)· 后台把 token 摆出来
|
||
|
||
1. `pricing-simulator.tsx` 「单功能表」增加列:输入 p50 / 输出 p50 / 缓存读 p50 / 命中率(复用聚合端点已有字段,命中率来自 `metadata.cache`);`source === "unavailable"` 时显示「无实测数据」不显示 0。
|
||
2. 表格上方一行说明文字:「成本 = (输入 − 缓存读) × 输入价 + 缓存读 × 缓存价 + 输出 × 输出价;单价在 `/admin/models` 配置;`cache_price_missing` 行表示有命中但未配缓存价」。
|
||
3. 写 `docs/testing/cost-accounting-20260927.md`:产品负责人部署后照做的清单——在 `/admin/models` 给当前发布模型新建一版草稿、填三档单价、连接测试、发布(T1.0 之前做不到这一步),在测算页填汇率 → 用可控账号每类功能跑 ≥5 次(聊天含多领域、校正跑到出卡、报告完整生成、另各触发一次标题 / 摘要 / 开场 / 每日星语)→ 在 `/admin/pricing-simulator` 读表 → 把「功能 → p50 成本 → p95 成本 → 命中率 → 失败成本占比」抄回 PROGRESS。
|
||
|
||
**验收**:`pricing-simulation.test.ts` / 组件测试覆盖新列的空态与有数态;`docs/testing/` 清单存在且每步可照做;`DESIGN.md` 不需要改(后台 antd 页不在 C 端设计合同内),若改了 C 端任何文件必须说明。
|
||
|
||
### T6(P2)· 状态板与文档收口
|
||
|
||
- `docs/tasks/README.md`:本单加行;`TASK-round2-cost-and-delivery-20260831.md` 行改为「任务 0 并入 TASK-cost-accounting-gaps-20260927;任务 1–3 未执行」。
|
||
- `CHANGELOG.md`:一条「后台成本统计:缓存分价、辅助调用与失败运行入账、token 分位数可见」;用户可感知行为无变化要写明。
|
||
- `docs/BUG_HISTORY.md`:见下节编号。
|
||
|
||
## BUG 编号起点
|
||
|
||
开工时核对 `docs/BUG_HISTORY.md` 当前最大号(2026-09-27 为 **BUG-1063**),从 **BUG-1064** 起连续编号。建议拆法(执行方可合并,但不得漏记):
|
||
|
||
| 建议编号 | 现象 | 对应 |
|
||
| --- | --- | --- |
|
||
| BUG-1064 | 缓存命中输入按全价计入 `cost_microusd`,账本高估 / 低估方向取决于单价配置 | E2 / T1 |
|
||
| BUG-1065 | 七处模型调用不进账本,成本系统性低估 | E1 / T2 |
|
||
| BUG-1066 | 校正开场轮 agent run 的 token 被 `action !== "message"` 分支丢弃 | E1 #5 / T2.4 |
|
||
| BUG-1067 | 失败 / 释放运行无成本记录 | E4 / T3 |
|
||
| BUG-1068 | 多事件账本 `metadata.cache` 被覆盖,命中率失真 | E3 / T4 |
|
||
| BUG-1069 | 后台模型草稿表单缺输入 / 输出单价、模型档位、上下文窗口字段,单价只能是 0 | E0 / T1.0 |
|
||
|
||
每条按 §5 格式写:状态、现象、触发条件、根因、修复、验证、防复发、关联记录(关联 `PROGRESS-billing-pricing-20260830.md` 任务 4 缺口)。**部署前状态写 `resolved(待部署)`**,部署核对后由验收方回填 SHA。
|
||
|
||
## 验收口径(验收方执行)
|
||
|
||
- 前端:`./node_modules/.bin/tsc --noEmit` 0 错;`npm run lint` 0 error 且 warning ≤ 开工基线;`npm test` 失败清单与基线逐条一致、总数不降;`next build` 后 `/` 仍 `○ Static`,首屏 gzip ±2%(本单不应触碰 C 端首屏,超出即异常)。
|
||
- 数据库:`npm run test:db` 全绿(Docker);无 Docker 视为**未通过**而非缺口,因为本单动表。
|
||
- 部署:`https://staging.jyotisha.chat/api/health` 的 `deployment.gitCommit` 等于合入 SHA;随后由产品负责人按 `docs/testing/cost-accounting-20260927.md` 采数,采到的表回填 PROGRESS。在采到非零真实成本之前,**本单不得被写成「成本已可信」**,只能写「记账链路已补全」。
|