Files
Jyotisha/docs/tasks/PROGRESS-report-writer-failure-20260902.md
T
Jesse_Chen 8db71aaf81 docs: product-level README, AGENTS.md split into code/reading parts, add CLAUDE.md, move task briefs to docs/tasks
- README.md is now the product/repo front door (architecture, repo map,
  local dev, test tiers, delivery flow, doc map). Engine positioning,
  VedAstro/Codex setup and the oracle/benchmark command reference move
  verbatim to docs/engine/README.md, docs/engine/vedastro-gateway.md and
  docs/benchmark/README.md. Capability badges realigned with the registry
  (91/78/8/0); tests/test_readme_badges.py was red on staging.
- AGENTS.md: Part A (environment truth, delivery, worktrees, record
  placement, bug workflow, growth freeze, frontend red lines, privacy,
  pre-work check, test tiers) and Part B (reading-rigor constraints).
  GitHub issue-tracker/triage boilerplate removed: GitHub is a read-only
  mirror. All strings locked by tests/ are preserved.
- CLAUDE.md added: roles, three working modes, task-brief sections,
  acceptance criteria, session discipline; imports AGENTS.md.
- 50 tracked TASK-*/PROGRESS-* files and 3 never-committed briefs move to
  docs/tasks/ with an index; REPO_LAYOUT.md merged into README.

Docs-only change (no gated path touched).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
2026-09-03 06:56:06 +00:00

13 KiB
Raw Blame History

PROGRESS · 个人报告写作阶段 report_schema_invalid2026-09-02

任务书:TASK-report-section-writer-failure-20260902.md838d9e76 分支:codex/report-writer-failure-20260902 基线:origin/staging @ 838d9e76

开工前置:已读 docs/research/pre_work_error_ledger.mdfrontend/AGENTS.mddocs/BUG_HISTORY.md 检索 BUG-352 / 451 / 486489 / 495。按任务书从 origin/staging 建独立 worktree,不碰原仓脏树。

隐私:下文只有错误码、token 计数、finishReason、时间戳与 request UUID(任务书已公开)。无 prompt、bundle、模型原文、姓名、出生资料或账号标识。


任务 0 · Staging 实证(门控)

取证主机:staging 118.26.111.127,路径 /opt/jyotisha-staging。Postgres 容器 jyotisha-staging-postgres-1

日志窗口丢失

取证时公开 staging health 仍指向 35688015838d9e76 的父提交)。Web 容器在 2026-09-02T14:00:21Z22:00 +08)被 recreate,与任务书提交 838d9e7614:00:43Z)几乎同时。事故窗口(13:19–13:21 UTC)的 docker logs 已不在。[personal-report-agent][personal-report] generation_failed 无法回收。usage_ledger 对该 requestId 为 0 行。

因此 不能 用本事故的 finishReason / outputTokens 分布来确认「主嫌 · 截断」。截断判定只能写 blocked

事故行(requestId 77b79067-bd70-465e-ac23-adc4500d4738

结果
personal_reports failed / report_schema_invalid / created 13:19:23 UTC / updated 13:21:38 UTC
personal_report_jobs failed / last_error_code=report_schema_invalid / phase failed / progress 43% / attempt 1 of 3 / finished 13:21:38 UTC
theme-career blockedattempts 2/2last_error_code=section_evidence_insufficientupdated 13:21:38.398 UTC
theme-marriage pending0 attempts
theme-timing pending0 attempts
theme-wealth pending0 attempts

43% = 30 + floor((1/4)*55)career 被 block 后第一次 onProgress。Job 在 career block 后约 39ms 结束,其余三章从未 start

历史 report_schema_invalid(08-30 起,与任务书 5 个小时一致):跨这些小时的 section 行里,本事故是唯一一条 blocked + section_evidence_insufficient。其余为 pending 或 ready。

定案

  1. 主嫌(CJK /2 截断)未证实。 落库码不是 section_output_truncated。截断在 writer 路径通常会变成 PersonalReportAgentOutputError("report_schema_invalid")section_output_invalid。本事故没有 telemetry,不得写成 finishReason=length
  2. 次嫌(evidenceRefs 回显)是 career 章的最佳解释。classifySectionErrorCode 把任何含 "evidence" 的消息打成 section_evidence_insufficientreport_writer_evidence_refs_mismatch 含子串 evidence,会被误分类。career 2/2 次、约 29s,符合 assert 失败 + 一次 repair,而不是四章长输出截断。
  3. 第三层:首章 blocked 后没有继续写其余章。 产品测试已要求其他章仍应交付。Job 以不可重试的 report_schema_invalid 失败(attempt 1/3),说明 worker 收到了 {status:"failed"}invalid_request/storage_invalid,而不是 retryable 抛错。无日志无法闭合「是 filter / onProgress / 其它未捕获抛错」。本轮仍把每章失败隔离开,避免一章 throw 中止整份。

all_sections_blocked 原先裸 return、不走 failSchema,因此即使日志还在,也可能看不到 generation_failed。本轮改为走 failSchema

任务 1 仍按任务书授权落地中文口径公式,校准用的是 08-30 任务 0 的整份调用观测(outputTokens=3069finishReason=stop),不是 本事故。research / deep 的 3072 cap 保持不变;若以后实测需要更高,另开任务。


任务 1 · CJK 输出预算

禁止 targetCharacters.max / 2

口径:1 中文字符 ≈ 1.3 tokenCJK_OUTPUT_TOKENS_PER_CHAR)。

章节结构余量 568narrative 之外的 title / actions / caveats / refs / JSON 外壳):

部分 估算
title ≈ 80 字 ceil(80 × 1.3) = 104
3 条 actions ≈ 120 字 156
caveats ≈ 80 字 104
evidenceRefs JSON 80
JSON 键与括号 124
合计 568

摘要结构余量 580 已经包含 headline / priorities / JSON,不再把 headline 算第二次:

ceil(2000 × 1.3) + 580 = 2600 + 580 = 3180,再与章节同一 cap 3072 取 min08-30 整份 outputTokens=3069 / stop)。落地 3072

章节:min(3072, max(1024, ceil(maxChars × 1.3) + 568))

深度 max 字 未封顶 落地预算
concise 500 1218 1218
standard 1200 2128 2128
deep 2000 3168 3072cap,缺口 96 token
research 2800 4208 3072cap;约合 narrative 2504 字,低于 2800 上限)
summary 2000 字段 3180 3072(与章节同一 cap

08-30 整份 writer 一次调用 outputTokens=3069stop,说明 3072 能装下一份大 JSON。本事故未证实截断,因此不把 cap 自行抬高。length 修复重试:同一 outputBudget(不得低于首次),提示词要求压到 targetCharacters.min 并闭合 JSON。


任务 2 · evidenceRefs 回显

  • sectionPrompt 增加:evidenceRefs 必须逐字复制 plan.evidenceRefs 数组,不得增删
  • assertWriterOutput 仍是 id/theme 全等 + refs 集合相等,语义未放宽。
  • Repair 只拼类别词:identity mismatch / refs mismatch / schema invalid / output truncated。refs 类再次重申逐字复制。不把 ref id、正文或 bundle 拼进 repair。
  • classifySectionErrorCode 先匹配 identity / refs,再匹配泛化 "evidence",避免再把 refs mismatch 写成 section_evidence_insufficient

任务 3 · 失败可观测

不改表。应用层聚合已有 personal_report_sections 行。

  • 详情 APIfailureSummaryinnerReasonall_sections_blockedsection_generation_incomplete)、sectionErrorCodes
  • 列表 API:失败行多查 request_id, status, last_error_code(无正文),写入 failureSummary
  • 报告中心:failureSummary ?? failureCode。详情页摘要为主文案,等宽错误码为辅。

本事故若用新摘要,应接近:「4 个主题中 1 个写作失败:证据引用未对齐,3 个未开始」。四章都写成但整份失败时,应接近:「4 个主题已写成,整份报告未完成装配」,不得再显示成空的「结构校验」。旧码仍会显示「证据不足」,因为历史行的 last_error_code 不会回写。


任务 4 · 真实验证

GET https://staging.jyotisha.chat/api/health.deployment.gitCommit = eda37c1523341898850e6d71d63c8dd40563973b。Web 容器 2026-09-02T17:31:48Z recreate。未提升 main。

Web docker logs 在 Ready 之后几乎不再收录 console.info。本轮拿不到 [personal-report-agent]finishReason / 每章 token。usage_ledger 对未 ready 的请求无 complete 行。2 倍线的真实 inputTokens 仍缺测;墙钟如下,不自行裁决。

Run A · requestId 54be49f1-a657-43fa-88ce-7d7e8d575831

standard personal_full,主题 career / marriage / wealth / timing。started 17:44:07 UTC,终态 17:54:30 UTC(约 10.4 分钟)。

status attempts narrative 字数 last_error_code
theme-career ready 1 1557 (空)
theme-marriage ready 2 1395 (空)
theme-timing ready 2 842 (空)
theme-wealth ready 1 470 (空)

事故那一章(career)这次 1 次成功,没有 section_output_truncated / section_refs_mismatch。四章都有正文。

Job 在写作过程中 lease_expired 两次(默认租约 120s,单章可超过 2 分钟;heartbeat 未能保住租约),第三次 attempt 写完 wealth 后约 39ms 整份 report_schema_invalid。进度停在 71%timing 完成后的值),wealth 的 85% onProgress 未落库。四个 section 行仍是 ready。摘要/装配没有产出 report_document。reservation released

默认 leaseSeconds=120 不够串行四章写作。心跳 setIntervalunref 后,Next.js instrumentation 在没有 HTTP 请求时会跳过定时器,单章 4 分钟写作期间租约无法续上。补丁:600s 租约、20s 心跳、心跳定时器保持 ref、章节 onProgressheartbeatLease 再写进度。摘要预算与章节同一 cap 3072(原公式 3180)。

回溯抽查(只记是否对得上 ref,不摘正文)

# 叙事层(布尔) 对回的 evidenceRef
1 wealth 含 yoga 词 ev-tech-yoga
2 四章皆含 含 大运/主运/副运 ev-tech-vimshottari
3 marriage 含 yoga 词 + 大运词 ev-tech-d9 + ev-tech-dk
4 career 含 yoga 词 + 大运词 ev-tech-amk + ev-tech-vimshottari
5 wealth 含 yoga 词 + 大运词 ev-tech-d2 + ev-tech-d11

未在四章叙事中扫到强弱/shadbala 用词,不记为回溯命中。

Run B · requestId 8e75299b-58ac-43c2-8bf4-59f9bd394734

为区分租约与摘要失败另开一次。四次 POST /api/consultation_workflow 均为 200,随后 worker 在 buildReportEvidenceBundleV2 处非重试失败,calculation_unavailable,无 section 行。未再消耗第三次生成。

相对 2 倍线

无每章 inputTokens/outputTokens。Run A 墙钟约 10.4 分钟(含两次租约回收与 marriage/timing 的 section repair)。数据摆出,不裁决。

Run C · requestId 89c04491-81ca-457e-b09d-dc81c3af36b3

GET /api/health .deployment.gitCommit = 5c0bec0c9419e12e2c1fcab8e75fd35d535cb50e。Web 容器 2026-09-02T18:24:50Z。未提升 main。

standard personal_full,主题 career / marriage / wealth / timing。started 01:31:16 UTC,终态 01:45:53 UTC(约 14.6 分钟)。reservation released

status attempts narrative 字数 last_error_code
theme-career ready 1 170 (空)
theme-marriage ready 2 1338 (空)
theme-timing ready 1 926 (空)
theme-wealth ready 1 745 (空)

Telemetry[personal-report-agent],本轮 docker logs 可读):

调用 outcome elapsedMs inputTokens outputTokens finishReason repair
career resolved 5721 5215 560 stop false
marriage failed 9430 null null tripwire false
marriage failed 9447 null null tripwire true
marriage resolved 13618 5076 1240 stop false
timing resolved 8052 4474 816 stop false
wealth resolved 7446 5228 759 stop false
summary (无日志)

finishReason=length。四章 stop 合计 input 19993 / output 3375。无 summary telemetry。

Job attempt 1 在 marriage 写作中心跳停更(heartbeat 与 lease 同步老化,最长观测 584s),lease_expired 后 attempt 2。四章随后都写成 ready。wealth 落库 01:45:53.549,整份失败 01:45:53.583(约 34ms)。进度停在 57%marriage 完成后的 2/4),timing/wealth 的 onProgress 未落库。无 [personal-report] generation_failed 行 → 不是 failSchema 的摘要/装配返回,而是 onProgress/续租抛错被 worker 映射成 report_schema_invalid

Run C 回溯抽查(只记是否对得上 ref)

# 叙事层(布尔) 对回的 evidenceRef
1 四章皆含 yoga 词 true wealth 另有 ev-tech-yoga
2 marriage / timing / wealth 大运/主运/副运 truecareer false 四章皆有 ev-tech-vimshottari
3 marriage yoga + 大运 ev-tech-d9 + ev-tech-dk
4 career yoga;无大运用词 ev-tech-amk + ev-tech-vimshottari
5 wealth yoga + 大运 ev-tech-d2 + ev-tech-d11

未扫到强弱/shadbala 用词。

相对 2 倍线

四章 writer 墙钟合计约 35s 模型时间;整份 14.6 分钟含一次租约回收与 marriage tripwire。inputTokens 已测(上表)。数据摆出,不裁决。

任务 4 结论

Writer 修正在章节层成立。整份 ready 被两件事挡住:Next.js instrumentation 里章节写作期间心跳定时器仍不续租;最后一章 complete 之后 onProgress 抛错被标成 schema 失败,摘要从未开始。跟进补丁:进度投影失败不得中止已落库章节的摘要/装配。


质量门

结果
frontend/node_modules/.bin/tsc --noEmit 通过(exit 0,无输出)
eslint(改动的 TS/TSX 文件) exit 0,无 error 输出
聚焦 tsx --testplan / writer-failure / generation-v2 / api / view / agent-telemetry 104 pass / 0 fail
复跑 api + writer-failure 53 pass / 0 fail
personal-report-generation.test.ts + personal-report-worker.test.ts 51 pass / 0 fail

未跑全量 tests/*.test.ts,不声称全套绿。未改 .gitea/workflows/**,未提升 main。

任务 4 补丁(租约 600s、摘要 cap 3072、心跳保持 ref + 进度续租 + onProgress 不中止装配):tsc --noEmit 通过;generation-v2 / worker / writer-failure 聚焦测试通过。