# PROGRESS · 个人报告内容对齐 skill 解读深度(2026-09-01) 任务书:`TASK-report-skill-parity-20260901.md` 分支:`codex/report-skill-parity-20260901`(基线 `8b7eda9f`,未 push) 提交:`7714b5e5`(任务 0+1+2)、`deabcd8b`(任务 3+4 离线部分)、本文件所在提交(收尾) 开工前置:`docs/research/pre_work_error_ledger.md` 已读;`scripts/pre_work_check.py` 全绿(`python_runtime_ok/fragment_scan_ok/external_engine_adapters_ok/focused_tests_ok` 均为 true,`remote_visibility_status=verified`);`frontend/AGENTS.md` 已读;`docs/BUG_HISTORY.md` 已检索 bundle / claim card / evidence 同类记录(最近相关为 BUG-457 报告分章生成)。 **偏差说明(经委托方授权)**:未建 worktree,直接在主检出上从 HEAD 建分支(node_modules / .venv 都在主检出)。任务 4 中依赖 staging 部署的部分跳过,只做不依赖部署的 telemetry 补记。 --- ## 任务 0 · 引擎响应契约快照 ### 做法 用虚构 smoke 出生数据(1990-05-15 10:30,lat 31.23 / lon 121.47 / tz +8,city "Smoke City",非真人)通过 `frontend/src/mastra/consultation-workflow.ts` 的 `runConsultationWorkflow` 真实调用本地 `scripts/jyotish_api_server.py`(`--port 5200`)的 `/api/consultation_workflow`,career / marriage / wealth / education / migration / family / health / timing / general 各一次,9 次全部 `OK`。只记录 key 路径与类型,不记录值。 ### 字段存在性清单(9 个主题响应一致) | 任务书假定的路径 | 实际 | 类型 / 说明 | |---|---|---| | `chart.ai_prompt_pack.evidence_snapshot.functional_benefic_malefic` | **存在** | object,键 `status/ascendant/functional_benefics/functional_malefics/functional_neutrals/yogakarakas/owned_houses/effect_on_confidence/source`;本盘 `status="used"` | | `chart.ai_prompt_pack.evidence_snapshot.strength.shadbala_ranking` | **存在** | array(7),元素 `{planet: string, rupas: number, level: string}`——**不是**任务书写的 `{planet, rank, total_rupas, min_required, strength_level}` | | `chart.ai_prompt_pack.evidence_snapshot.strength.sav_scores` | **缺失** | 该路径不存在 | | `chart.ai_prompt_pack.evidence_snapshot.timing.vimshottari` | **缺失** | `timing` 只有 `{current_mahadasha, remaining_years, start_date}`,没有 `vimshottari` 子对象,也没有 antardasha | | `chart.ai_prompt_pack.evidence_snapshot.timing.convergence_top_domains` | **缺失** | 该路径不存在 | | `chart.ai_prompt_pack.evidence_snapshot.career_narrative` | **缺失** | — | | `chart.ai_prompt_pack.evidence_snapshot.relationship_narrative` | **缺失** | — | | `chart.ai_prompt_pack.evidence_snapshot.finance_narrative` | **缺失** | — | | `chart.modules.yoga.yogas` | **缺失** | 键名是 `yogas` 不是 `yoga` | | `chart.modules.guided_topics` | **存在** | array(4),元素键 `id/title/reality_value/why_worth_exploring/evidence/confidence/vedastro/strict_adjudication_bundle/strict_audit_gate/monthly_adjudication_summary/official_day_signal_summary/suggested_questions/answer_mode/priority`;id 为 `birth_time_rectification`、`relationship_partnership`、`career_direction`、`wealth_risk` | **为什么缺**:`/api/consultation_workflow` 返回的 `chart.ai_prompt_pack` 来自 `scripts/jyotish_api_server.py:6975` 的 `_build_chart_prompt_pack`,**不是**任务书引用的 `scripts/jyotish_engine.py:1592` 的 `_build_ai_prompt_pack`。前者是精简版,没有叙事种子、没有 dasa convergence、没有 SAV。这不是本轮能改的(任务书把"引擎侧新增计算模块或改动既有模块输出"列为不在范围内,止损分支只允许新增字段,本轮未走该分支)。 ### 同一响应里实际可用、任务书未列出的等价路径(已改用) | 路径 | 类型 | 用途 | |---|---|---| | `chart.modules.ashtakavarga.sav.scores` | object,按**星座名**计分(12 键) | SAV:按 D1 上升整宫投影成 1–12 宫 | | `chart.modules.ashtakavarga.sav.total` | number(本盘 337) | `savTotal` | | `chart.modules.dasha_sub_periods.current.mahadasha` / `.antardasha` | `{lord, start, end}` | `currentDasha`(含 antardasha) | | `chart.yogas` | array,元素 `{name, planets, category\|cat, desc?}` | 已成盘 yoga | | `chart.modules.yogas.yogas` | array,元素 `{name, rule_id, hit: bool, ...}` | **候选规则表**,本盘 `hit=true` 数为 0 | **重要发现(防复发已写进 BUG-467)**:`chart.modules.yogas.yogas` 是规则候选表,绝大多数 `hit=false`,把它当成"本盘的 yoga 名单"就是编造。提取层只收 `chart.yogas` 与 `hit === true` 的行。 ### 缺失字段的处置 按验收条款"缺失即标记不可提取并跳过,不得伪造": - `convergenceDomains`:本响应无来源 → 恒为空数组(schema 允许),代码保留 `evidence_snapshot.timing.convergence_top_domains` 与 `modules.dasa_convergence.top_convergent_domains` 两条读取路径,引擎哪天补上就自动生效。 - 三个 `*_narrative`:本响应无来源 → 叙事种子改由 `guided_topics` 文案 + 服务器确定性投影组成;`readEngineNarratives` 仍保留 `evidence_snapshot.*_narrative` 与 `modules.*_strict_evidence.user_narrative` 两条读取路径,且优先级高于 guided topic。 - `strength.sav_scores`:改读 `modules.ashtakavarga.sav.scores`,两条路径都在代码里。 - `timing.vimshottari`:改读 `modules.dasha_sub_periods.current`,两条路径都在代码里。 ### 契约 fixture 测试 `frontend/tests/report-interpretive-facts.test.ts`(合成数据,文件头部把上表逐条写成注释)。10 条用例,锁住上述每条路径的读取行为、缺失路径的空值行为、以及 `hit=false` 不算成盘。 --- ## 任务 1 · Bundle 承载解读性事实 ### schema(`frontend/src/lib/report-evidence-bundle-v2.ts`) `schemaVersion` 仍是 `report_evidence_bundle.v2`,两组新字段必填、允许空、`.strict()` 保持: ``` interpretiveFacts: { yogas: { name(≤80, 净化 pattern), category(17 项闭合枚举), planets(celestial 枚举 ≤9), evidenceRef }[] // ≤40 functionalRoles: { planet(celestial), role(benefic|malefic|neutral|yogakaraka), ownedHouses(1-12, ≤12), evidenceRef }[] // ≤12 shadbalaRanking: { planet(celestial), rank(int 1-9), rupa(0-100|null) }[] // ≤9 savScores: { house(1-12), score(0-100) }[] // ≤12 savTotal: number(0-1000) | null currentDasha: { mahadasha(celestial), antardasha(celestial|null), start, end } | null // 沿用既有 dashaPeriod 日期校验 convergenceDomains: string[] // ≤6 × ≤80,净化 } themeNarrativeSeeds: { theme, headline(≤300), strengths(≤8×≤400), risks(≤8×≤400), boundaries(≤8×≤400), evidenceRefs(≤24) }[] // ≤12 ``` `ownedHouses` 是相对任务书的**加法**(宫主分工是功能吉凶最有解读价值的一半,且是闭合的 1–12 整数数组),不是放宽。 ### 确定性与 hash `sortedBundleContent` 扩展:yogas 按 `name:category` 排序且 planets 去重排序;functionalRoles 按 planet 排序、ownedHouses 去重升序;shadbalaRanking 按 `rank, planet`;savScores 按 house;convergenceDomains 去重排序;seeds 按 theme 排序、evidenceRefs 去重排序。`computeReportEvidenceBundleHash` 因此覆盖新字段。测试 `bundle hash is stable under interpretive field ordering` 把五个数组全部 `reverse()` 后 hash 不变。 `shadbalaRanking.rank` **不采信引擎给的 rank**,而是按 `rupa` 降序、同分按行星名字典序重新推导,保证同一份输入永远得到同一份排名(本盘 Sun 与 Moon 都是 6.42 rupa,固定为 Moon(5) → Sun(6))。 ### 净化与 fail closed - 行星走既有 `safeCelestialName`;yoga 分类走 17 项闭合枚举,未知归 `other`;yoga 名走 `^[\p{L}\p{N} ()/·,.'’++-]{1,80}$`。 - `safeSeedText` 逐行清洗:控制字符 → 空格、折叠空白、超长截断,并对 21 个违禁子串(`vedastro` / `mevg` / `workflow` / `references/` / `scripts/` / `modules.` / `skill` / `prompt` / `oracle` / `engine` / `fallback` / `blocked` / `not_found` / `not_available` / `api` …)**整行丢弃**。这是相对任务书的收紧:引擎自产叙事里确实含外部供应商名与内部路由词,写作端红线不允许它们进 prompt。 - `validateReportEvidenceBundleV2` 新增 fail-closed:`report_bundle_invalid_interpretive_ref`、`report_bundle_duplicate_functional_role`、`report_bundle_duplicate_shadbala_rank` / `_planet`、`report_bundle_duplicate_sav_house`、`report_bundle_current_dasha_invalid`、`report_bundle_duplicate_seed_theme`、`report_bundle_seed_theme_not_requested`、`report_bundle_invalid_seed_ref`。consensus ≥2 verified 防升级校验一字未改。 ### 提取结果(真实 smoke 盘) | 层 | 条数 | 样例(值不敏感,为虚构盘) | |---|---:|---| | yogas | 1 | `Amala Yoga / extended`(`chart.yogas` 两行同名合并为一行) | | functionalRoles | 7 | Mars=yogakaraka(5,10);Jupiter/Moon=benefic;Mercury/Saturn/Venus=malefic;Sun=neutral | | shadbalaRanking | 7 | Venus(1,8.35) → Saturn(2) → Mercury(3) → Jupiter(4) → Moon(5) → Sun(6) → Mars(7) | | savScores | 12 | 上升 Cancer → 第 1 宫 24、第 10 宫 30、第 2 宫 27、第 11 宫 28 … | | savTotal | 1 | 337 | | currentDasha | 1 | Rahu / Sun,2011-11-11 – 2029-11-11 | | convergenceDomains | 0 | 源缺失,空 | | themeNarrativeSeeds | 6 | 6 个非 blocked 主题各一条 | --- ## 任务 2 · Claim card 从回执升级为结论 ### 改前 / 改后对照(同一份真实 smoke 盘,9 主题请求;career / marriage / timing 因缺 AmK / Transit / DK 仍是 blocked section,逻辑未动) **改前(`8b7eda9f`)——所有主题只有主题名不同:** ``` [education] parameter_sensitive conclusion : 服务器已闭合教育学习所需的最低证据组;本节只能在所列事实与确定性级别内解释。 supporting : D1=D1 上升为 Cancer,本次基础盘已建立 D24=D24 已执行并纳入本主题证据计划 Vimshottari=Vimshottari 已执行并纳入本主题证据计划 counterFacts: [] [wealth] parameter_sensitive conclusion : 服务器已闭合财富结构所需的最低证据组;本节只能在所列事实与确定性级别内解释。 supporting : D1=… / D2=D2 已执行并纳入本主题证据计划 / D11=… / Yoga=… / Ashtakavarga=… / Vimshottari=… ``` **改后:** ``` [education] parameter_sensitive ← 级别未变 conclusion : 本命上升为 Cancer;按宫主判定的功能吉星为 Jupiter、Moon,功能凶星为 Mercury、Saturn、Venus。 本主题相关宫位的八分力总分:第 4 宫 25 分,第 5 宫 35 分(全盘合计 337 分)。 当前处于 Rahu 主运、副运 Sun,主运区间 2011-11-11 至 2029-11-11。 supporting : D1=本命上升 Cancer;功能吉星 Jupiter、Moon,功能凶星 Mercury、Saturn、Venus D24=D24 已执行并纳入本主题证据计划 Vimshottari=当前 Rahu 主运、副运 Sun(2011-11-11 至 2029-11-11) Ashtakavarga=八分力:第 4 宫 25 分,第 5 宫 35 分,全盘合计 337 分 Functional Benefic/Malefic=功能吉凶已判定,Mars 为 yogakaraka Yoga=已成立组合:Amala Yoga [wealth] parameter_sensitive ← 级别未变 conclusion : 本命上升为 Cancer;按宫主判定的功能吉星为 …。 本主题相关宫位的八分力总分:第 2 宫 27 分,第 11 宫 28 分(全盘合计 337 分)。 当前处于 Rahu 主运、副运 Sun,主运区间 2011-11-11 至 2029-11-11。 [general] single_system_inference ← 级别未变 conclusion : …本主题相关宫位的八分力总分:第 1 宫 24 分,第 10 宫 30 分(全盘合计 337 分)。… ``` 六个可写主题的 conclusion 现在**逐主题不同**(主题相关宫位的 SAV 行不同),且不含任何模板回执句。 ### 拼装规则 - `conclusion = seed.headline + 最多 2 条 seed.strengths`,按句号补全后拼接,截断 1200。 - seed 的 headline 优先取引擎叙事 headline(结论式);无叙事时取第一条服务器确定性投影行;再无则取 guided topic 标题。主题相关行(SAV 宫位)排在前面,保证每章 conclusion 不同。 - `supportingFacts`:每条 matched receipt 一条,值改为该技法的真实解读语句;此外把通用解读层(Functional Benefic/Malefic、Yoga、Ashtakavarga、Vimshottari)作为额外事实条目挂在**它们自己的 receipt** 上,不冒名给别的技法。 - `counterFacts` ← seed.risks(本盘引擎叙事缺失,为空;合成夹具用例 `engine risk lines become counter facts on the matching claim card` 覆盖了有 risks 的路径)。 - `boundaries` 留在种子里由 writer 引用(任务书允许的第二种处置),`verificationQuestions` 未动。 - **`assertionLevel` 推导一字未改**;只有"完全没有种子"时把 `multi_system_consensus` 压到 `single_system_inference`(用例 `a theme without any narrative seed keeps the receipt wording and loses consensus` 覆盖)。 ### 端到端抽查(叙事内容可回溯到 bundle,3 处) 无模型凭据、无数据库,本地无法真跑 writer(见 BLOCKED.md)。改为对服务器确定性产出的 conclusion 做逐句回溯,这是 writer 唯一被允许复述的结论层: | # | conclusion 中的表述 | 回溯到 bundle 的哪一行 | |---|---|---| | 1 | "本命上升为 Cancer" | `charts[D1].ascendant.sign = "Cancer"`;同时 `supportingFacts` 该条 `evidenceRef = ev-tech-d1`,`executionLedger[ev-tech-d1].status = verified` | | 2 | "Mars 同时主管第 5、10 宫,在本盘属 yogakaraka" | `interpretiveFacts.functionalRoles[{planet:"Mars", role:"yogakaraka", ownedHouses:[5,10], evidenceRef:"ev-tech-functional_benefic_malefic"}]`;该 ref 在 `evidenceRefs` 中 status=partial,事实条目 status 同为 partial(未升级) | | 3 | "当前处于 Rahu 主运、副运 Sun,主运区间 2011-11-11 至 2029-11-11" | `interpretiveFacts.currentDasha = {mahadasha:"Rahu", antardasha:"Sun", start:"2011-11-11", end:"2029-11-11"}`,绑 `ev-tech-vimshottari` | | 4 | "本盘已成立的组合:Amala Yoga" | `interpretiveFacts.yogas[0] = {name:"Amala Yoga", category:"extended", evidenceRef:"ev-tech-yoga"}`;`modules.yogas.yogas` 里 6 条 `hit=false` 的候选规则**没有**出现在这里 | | 5 | "第 2 宫 27 分,第 11 宫 28 分(全盘合计 337 分)" | `interpretiveFacts.savScores[{house:2,score:27},{house:11,score:28}]` + `savTotal=337`,绑 `ev-tech-ashtakavarga` | --- ## 每章 prompt 增量(红线 6:必须摆数据) 同一份真实 smoke 盘、`depth=standard`、6 个可写主题。测量对象是 `sectionPrompt` 的 JSON 载荷 `{bundle, plan, completedSectionTitles}`(`filterReportEvidenceBundleForSection` 之后)。改前数据是把三个改动文件 checkout 回 `8b7eda9f` 后用同一份 workflow 快照重跑得到的,不是估算。 **token 数是离线估算**(无 tokenizer:CJK 字符按 1 token、其余按 1/4 token)。真实 `inputTokens` 必须来自部署后的一次真实生成。 | 章节 | 改前 chars | 改后 chars | 改前 est.tok | 改后 est.tok | 倍数 | |---|---:|---:|---:|---:|---:| | theme-general | 3459 | 6993 | 928 | 1999 | 2.15 | | theme-family | 3774 | 7298 | 1044 | 2112 | 2.02 | | theme-health_pressure | 3845 | 7354 | 1065 | 2121 | 1.99 | | theme-education | 4169 | 7361 | 1162 | 2127 | 1.83 | | theme-migration_home | 4511 | 7726 | 1270 | 2240 | 1.76 | | theme-wealth | 5129 | 7711 | 1465 | 2262 | 1.54 | | **p50** | **4004** | **7357** | **1113.5** | **2124** | **1.91** | 改后载荷构成(以 general 章为例,chars):`interpretiveFacts` 1365、`themeNarrativeSeeds` 500、`claimCards` 1262(改前约 500)、`executionLedger` 576、`evidenceRefs` 372、`charts` 1731(未变)、`calculationProfile` 232(未变)。 再加上任务 3 的静态系统内容(改前只有 24 字符的缓存边界句): | 项 | 改前 est.tok | 改后 est.tok | 是否吃 prompt cache | |---|---:|---:|---| | 缓存前缀内的静态内容(通用包 + 边界句) | 22 | 877 | **是**(`cachedSystemMessage`,前缀逐字节稳定,有测试锁) | | 本章主题包(缓存边界之后) | 0 | 203–313(p50 214) | 否 | | bundle 载荷 | 1113.5 | 2124 | 否 | | **每章合计(原始 token)** | **1135.5** | **3215** | — | - **原始 token p50 倍数 = 2.83×,超过改前 2 倍。** - 计费加权后(缓存写 1.25×、缓存读 0.1×,6 章摊到每章约 256 token):改后 ≈ 2594 token,**约 2.28×**,仍超 2 倍。 **按红线 6 与任务 3 止损条款处置**:`cachedSystemMessage` 已确认生效且缓存前缀逐字节稳定(`writer system content caches the general pack and appends only the chapter pack` 用例锁定),不属于"cache 未命中"的止损分支;止损条款明确禁止"砍证据来凑成本",因此**未削减任何证据**,数据原样摆出,是否接受这个成本请委托方裁决。可选的降本项(未做,逐条列出以便决策): 1. 主题包不下发,只留通用包:每章省约 214 token(→ 约 2.64×)。代价是丢掉主题级措辞纪律。 2. `themeNarrativeSeeds` 与 conclusion 前两句重复,去重可省约 150 token(→ 约 2.70×)。代价是 writer 看不到种子原始层与 claim 的差别。 3. `shadbalaRanking` 每章只留前 3 + 最弱 1:省约 90 token。代价是丢掉完整发力顺序。 4. `charts`(1731 chars ≈ 450 token)是改前就有的开销,与本轮无关;真要压成本,这里比砍新证据划算。 --- ## 任务 3 · 写作端静态解读知识包 ### 目录与清单 `frontend/src/lib/report-interpretation-packs/`(`general.ts` / `themes.ts` / `index.ts`),纯静态 TS 常量。蒸馏来源:`references/modern-language-guide.md`、`references/common-misconceptions.md`、`references/house-domain-planet-mapping.md` 及各主题判读要点。 | 包 | 字符数 | est.tok | 内容 | |---|---:|---:|---| | general_method(通用) | 961 | 855 | 表达顺序、术语现代化映射、强弱与分值怎么说、多层验证、必须避免的推理错误、语气与禁语 | | career | 366 | 313 | 主指标、10/6 宫怎么说、yogakaraka 怎么落地、禁止承诺升职/薪资/时点 | | marriage | 330 | 296 | 7 宫=一切长期对等关系、D9 与本命的分工、土星不写成"晚婚"、禁止预测结婚年份与忠诚判断 | | wealth | 298 | 261 | 赚到 vs 留住必须分开、低分不写成"破财"、禁止推荐任何标的/收益率/借贷建议 | | education | 248 | 214 | 学得快 vs 学得深、困难写成方法不匹配、禁止预测通过与录取 | | migration_home | 244 | 208 | 12 宫不写成"损失"、安定与离开并存、禁止指定国家城市方位与日期 | | family | 231 | 203 | 关系模式而非人品、5 宫不是生育能力、禁止预测生育与亲属健康 | | health_pressure | 243 | 214 | 只谈负荷与恢复、禁止任何诊断/点名疾病/用药建议 | | timing | 281 | 256 | 时间层只能激活本命已有结构、主运/副运分工、未获准时不写年月日 | | general(综合基础章) | 273 | 243 | 建框架不抢主题章结论、功能吉凶是角色分工不是性格好坏 | 单包上限 3000 字符(`PACK_CHARACTER_LIMIT`),最大的通用包用了 32%。 ### 注入方式 `buildWriterSystemContents(theme)`(`frontend/src/mastra/personal-report.ts`)返回: - `[0]` = 通用包 + `【上下文缓存边界】…`,作为 `cachedSystemMessage` 的内容 → **在缓存断点之内**,且逐字节稳定,第 2 章起是 cache read; - `[1]` = 本章主题包,放在缓存边界之后(每章不同,不能进前缀)。 - 摘要调用 `themePack=null`,只带通用包。 - 非 Anthropic 供应商 `cachedSystemMessage` 返回 null,此时降级为普通 system message(内容不变,只是不缓存)。 ### 合同同步 - writer 指令新增:interpretiveFacts / themeNarrativeSeeds 是"可引用的事实层,但不授权 claimCard 之外的结论";知识包"只管怎么解释和怎么措辞,不是事实来源,不能提高确定性"。`Use its claimCards exclusively for narrative conclusions` 原句保留。 - `skills/jyotish-personal-report/SKILL.md` 升到 **1.1.0**,`skills/jyotish-personal-report/versions/1.1.0/` 为不可变副本,registry 中 1.0.0 转 `deprecated`、1.1.0 为 `active`(sha256 `6be2279b…10e9`)。 ### 端到端改前改后同盘同主题章节全文对照 **未做,无法在本地做**:执行环境没有任何模型凭据(`ANTHROPIC_*` / `OPENAI_*` 等全部未设置,仓内只有 `.env.*.example`),也没有 Postgres(Docker 不可用),`generateSectionedPersonalReport` 需要 `PersonalReportSectionService`。见 `BLOCKED.md`。可离线给出的替代物是上面的"每章 prompt 增量"表与 claim card 全文对照。 --- ## 任务 4 · 观测(只做不依赖部署的部分) `PersonalReportAgentTelemetry` 新增两个数值字段,沿用既有隐私边界(只记指标名与数值): - `interpretiveFactCount`:本次调用收到的 bundle 里解读事实的条数(yogas + functionalRoles + shadbalaRanking + savScores + convergenceDomains + currentDasha + savTotal)。本盘各章为 **21**(1 + 7 + 7 + 2~3 + 0 + 1 + 1,随该章 SAV 宫位裁剪略有差异)。 - `knowledgePackCharacters`:本次调用随 prompt 下发的静态知识包字符数 = 缓存前缀内容 + 本章主题包。章节调用为 **1218–1353**(987 + 231~366),摘要调用为 **987**。 `countInterpretiveFacts` 对缺字段/畸形输入返回 0 而不抛错——telemetry 不得让生成失败。测试 `telemetry carries interpretive and knowledge-pack sizes as numbers only` 断言两个字段是数字,且序列化后不含任何事实文本。 **部署后测量(章节数、每章 in/out tokens、总墙钟、与 `PROGRESS-report-sectioned-20260830.md` 对照)按授权跳过,由委托方在 staging 部署后补。** --- ## 修改既有测试断言的逐条说明(红线 9) | 文件 | 断言 | 原值 | 原因 | |---|---|---|---| | `frontend/tests/personal-report-generation.test.ts` | `bundleV2Fixture()` | 无 `interpretiveFacts` / `themeNarrativeSeeds` | 两个字段按任务书要求为必填;夹具没有引擎快照,声明为 `emptyReportInterpretiveFacts()` + `[]`。断言语义未变。 | | `frontend/tests/personal-report-generation-v2.test.ts` | 同上 | 同上 | 同上 | | `frontend/tests/personal-report-plan.test.ts` | 同上 | 同上 | 同上 | | `frontend/tests/personal-report-generation.test.ts:814` | `snapshot.version` | `"1.0.0"` | Skill 合同变更,版本 +1;已在断言上方注明原值与原因 | | `frontend/tests/skill-registry.test.ts:92` | jyotish-personal-report 的 version / sha256 | `1.0.0` / `23149b9e…f982` | 同上;已在断言上方注明原值与原因 | | `tests/test_jyotish_personal_report_skill_package.py` | `VERSION_ROOT` 与 registry 期望行 | `versions/1.0.0` | 同上;改为 `ACTIVE_VERSION` 常量并注明原值与原因,同时**新增**四条断言锁住 1.1.0 的新合同措辞(只加不减) | 没有任何断言被放松:`report-evidence-bundle-v2` 的 `.strict()`、consensus ≥2 verified 防升级、`assertionLevel` 推导、章节串行、writer 输出 schema 全部原样。 --- ## 质量门实际结果 ``` $ ./node_modules/.bin/tsc --noEmit # frontend (无输出,通过) $ ./node_modules/.bin/eslint src tests # frontend ✖ 28 problems (0 errors, 28 warnings) (28 条全部是既有的 no-unused-vars warning,与本轮无关) $ npm run build # frontend ✓ Compiled successfully in 5.6s ✓ Generating static pages using 7 workers (62/62) in 447ms Turbopack build encountered 5 warnings: Warning: Dynamic filesystem access causes tracing of the whole project ×5 (该 5 条为既有告警,已记录在 BLOCKED.md 2026-08-17 条目) $ ./node_modules/.bin/tsx --test tests/*.test.ts # 基线 8b7eda9f # tests 2427 # pass 2393 # fail 24 # skipped 10 # todo 0 $ ./node_modules/.bin/tsx --test tests/*.test.ts # 本分支 # tests 2444 # pass 2410 # fail 24 # skipped 10 # todo 0 失败集逐条 diff:完全一致(SAME_FAILURE_SET),24 条全部是本机缺 Docker / PostgreSQL 的 数据库与部署夹具,以及 staging workflow YAML 校验,与 BLOCKED.md 既有记录一致: admin code functions … / admin customer reset … / service and restricted admin database identities … Owner recovery … / staging backups … / billing order adjustments … / billing, subscriptions, … saving a published product … / database env validator … / migration runner … / foundation grants … local PostgreSQL … / personal report job migration … / personal report sections enforce owner-read RLS … redeem security … / self-hosted identity migration … / database roles have no cluster privileges server compose defaults … / staging env validator … / Better Auth supports shared user OTP/password … database drops secret refs … / changed staging workflows are syntactically valid YAML live staging sync preserves env … / live staging sync repairs nested deploy-tree drift … 新增 17 个通过用例,0 个新增失败。 $ .venv/bin/python -m pytest tests/test_jyotish_personal_report_skill_package.py -q .. [100%] $ .venv/bin/python scripts/pre_work_check.py checks 全 true,remote_visibility_status = verified ``` `npm run test:db` 未跑:Docker 不可用,且本轮红线禁止数据库结构改动与新增迁移,本轮确实没有改任何库结构或迁移。 --- ## 交付物清单核对 | # | 交付物 | 状态 | |---|---|---| | 1 | 任务 0 契约快照(字段存在性清单 + fixture 测试) | 完成 | | 2 | bundle 新字段 schema + 提取 + 净化 + hash 覆盖 + fail-closed 测试 | 完成 | | 3 | `filterReportEvidenceBundleForSection` 主题裁剪 + 每章 prompt 增量数据 | 完成(增量数据超 2×,已摆出待裁决) | | 4 | 实质化 claim cards + 回退与级别压制 + consensus 回归测试 | 完成 | | 5 | 端到端抽查记录:叙事可回溯到 bundle(≥3 处) | 部分完成:对服务器确定性 conclusion 做了 5 处逐行回溯;真实 writer 输出因无模型凭据未跑(BLOCKED.md) | | 6 | 知识包目录 + 违禁词扫描测试 + SKILL.md 更新 | 完成 | | 7 | 改前改后同盘同主题章节全文对照 + tokens/墙钟对照 | 未做:无模型凭据与数据库(BLOCKED.md);已给出 prompt 载荷与 claim card 全文对照作为离线替代 | | 8 | `docs/BUG_HISTORY.md` 条目 | 完成:BUG-467(远端 `origin/staging` 最大号为 BUG-466,已确认) | | 9 | `BLOCKED.md` | 完成 | | 10 | `PROGRESS-report-skill-parity-20260901.md` | 本文件 | | 11 | tsc / eslint / tsx --test / next build 实际输出 | 完成(见上) |