# 股票估值端到端协调层 V2 编码方案 V003 创建人员:`dev.developer.project.secondary / infodev-2` 开发事项:`DEV-ANA-STOCK-VALUATION-PIPELINE-V2-20260801-001` 当前审核员:`dev.reviewer.project / inforev` 方案性质:重型编码方案返修;一次闭环 V002 独立审核的 F1—F8。 当前状态:`PENDING_FOCUSED_DESIGN_REREVIEW`;审核 PASS 前禁止创建 V2 代码、测试、fixture,禁止修改 `ana-dev`,禁止真实联网 smoke。 前序审计:`DEV-AUDIT-PROJECT-INFO-STOCK-VALUATION-PIPELINE-V2-DESIGN-20260801-001`=`HOLD/8`。 ## 1. 单一权属与替代关系(F1) 项目管理员已通过原生交接 `HANDOFF-INFOADMIN-INFODEV2-STOCK-VALUATION-V2-TAKEOVER-LEDGER-CLOSE-TERMINAL-20260801-001` 完成三本 append-only 根账闭环: - `dev-doc/开发事项总纲.md:709`:当前 owner=`dev.developer.project.secondary`,reviewer=`dev.reviewer.project`; - `dev-doc/开发事项计划.md:802`:V003 是唯一当前返修版本;V001、旧 ana 审核请求和 V002 审核快照均为历史; - `dev-doc/开发问题记录.md:362`:`DEV-ISSUE-ANA-STOCK-VALUATION-V2-REVIEW-SESSION-20260801-001` 已因有效 project 审核员改派而关闭; - 代码=`dev/project-dev/stock_valuation_pipeline_v2/`,测试=`dev/project-dev/test/stock_valuation_pipeline_v2/`,文档=`dev-doc/project-doc/`,审计=`dev-doc/开发审计报告.md`; - `dev/ana-dev/stock_valuation_pipeline/` 全部只读,不复制公式、校验、报告或缓存实现;旧 inbox/会话仅保留历史证据,不再形成活动阻塞。 管理员另作条件单文件授权:仅在最终实现独立审核 PASS 后,本事项开发者可同步 `outputs/20260730_stock_valuation_guide/股票价格合理性评估操作手册_v1.0.md`;其他 `outputs/` 不在写入范围。 ## 2. 目标、边界与非目标 目标是把 V1 的“人工填好 `valuation_snapshot.json` 后计算”升级为 ticker+as-of 端到端机械协调层:并发公开取数、可信信源停止、内容寻址缓存和公司增量基线、异常预算内降级、最小判断覆盖、固定 16 节报告、自动 QA、原子输出和从用户任务起点到提交完成的双墙钟。 必须保留 V1 `--input` 兼容;不输出交易指令、收益承诺或自动批准的投资结论;任何 `publish_date` 或 `data_date` 晚于 as-of 的信息都不得进入结果。 明确排除:V1 公式/schema/registry 修改,第二套估值计算核心,GUI、API、数据库、常驻服务、批处理、定时任务、付费或登录来源、验证码规避、自由搜索站点、未来信息、未披露业务事实推断。 ## 3. 代码、测试与文档落点 ```text dev/project-dev/stock_valuation_pipeline_v2/ __init__.py __main__.py cli.py telemetry.py http_client.py cache.py providers.py acquisition.py snapshot_builder.py judgment.py v1_bridge.py full_report.py report_qa.py workflow.py provider_registry.json judgment_overlay.schema.json README.md dev/project-dev/test/stock_valuation_pipeline_v2/ test_*.py fixtures/chengchang_20260801/ dev-doc/project-doc/ 股票估值端到端协调层V2.md 开发证据/DEV-ANA-STOCK-VALUATION-PIPELINE-V2-20260801-001/ ``` ## 4. CLI 与入口合同 V1 兼容模式: ```powershell python -m stock_valuation_pipeline_v2 --input SNAPSHOT --output-dir OUT [--force] ``` ticker 模式: ```powershell python -m stock_valuation_pipeline_v2 --ticker 001270.SZ --as-of 2026-08-01 ` --output-dir OUT --cache-dir CACHE [--judgment JUDGMENT.json] ` [--fixture-dir FIXTURES] [--task-start 2026-08-01T20:00:00+08:00] [--force] ``` `--input` 与 `--ticker` 互斥。ticker 模式必须同时给出 as-of、output-dir、cache-dir。ticker 只接受 `NNNNNN.SZ|SH|BJ`,别名规范化在网络前完成;as-of 不得晚于本地当前日期。fixture 模式把网络 transport 替换为显式拒绝对象,任何网络调用都使测试失败。 ## 5. V1 只读桥接完整合同(F2) ### 5.1 固定解析与指纹 以仓库根为锚点,只允许加载: - 包目录 `dev/ana-dev/stock_valuation_pipeline/`; - 默认 registry `dev/ana-dev/stock_valuation_pipeline/source_registry.json`; - `__version__ == "1.0.0"`; - `__init__.py`=`257 bytes / F39479C1A7B7F40A1A71D5EDE8A287F4E701B4F1473D674F04A2E1BA1B522D29`; - `core.py`=`53165 bytes / 6AB34917A8B136F684EF655995428ACB5936050E1DA36B7B2371941A3E5A5F8F`; - `cli.py`=`1811 bytes / 260692D62C8983B623483E0AA29C763869E30D40952700D46C636357A7376A1E`; - `source_registry.json`=`4958 bytes / 0895D4F0559A1353787059D506DF16572E6818CD847C21042FE1D1106AA854E0`。 桥接加载后核对 `module.__file__` 位于上述只读包,函数签名逐项等于冻结合同。运行 manifest 记录实际模块路径、版本、四个 SHA-256 和 registry hash。任一不匹配产生 `E_V1_CONTRACT`、退出 3,并在任何写正式输出前 fail-fast;绝不以内嵌公式或复制 CLI 补偿漂移。 ### 5.2 两种调用表 | 模式 | 精确调用 | 返回/用途 | |---|---|---| | `--input` | `run_pipeline(snapshot_path=Path, output_dir=Path, source_registry_path=REGISTRY, force=bool)` | 原样返回 V1 status;保留 `GENERATED/REUSED` 和 V1 三件套 | | judgment | `snapshot=load_snapshot(Path)`;`registry=json.load(REGISTRY)`;`results=compute_valuation(snapshot, registry)` | `results` 是 V2 所有估值数值章节的唯一来源;V2 不调用 V1 报告生成器 | 兼容模式复刻的仅是异常到进程语义,不复制计算:成功 stdout 为 V1 status JSON、exit 0;`InputError` stderr 为 `{"status":"INPUT_ERROR","errors":[...]}`、exit 2;`OSError`/`json.JSONDecodeError` stderr 为 `{"status":"RUNTIME_ERROR","error":"..."}`、exit 3;合同漂移也 exit 3,status=`RUNTIME_ERROR`、error_code=`E_V1_CONTRACT`。 测试必须同时运行直接 V1 CLI 与桥接:比较 stdout status、exit 0/2/3、`GENERATED/REUSED`、三件套相对文件名/bytes/SHA-256;judgment 模式将 V2 桥接的 results 对象与直接 `compute_valuation(snapshot, registry)` 做规范 JSON 字节相等比较。 ## 6. 冻结的公开 provider registry(F3) 首期不允许运行时自由换站。所有 URL 必须 HTTPS、host 精确命中 allowlist、重定向后的每一跳也重新校验。未知域名、未知 endpoint、未知方法或未登记回退在创建 socket 前拒绝。 | provider_id / version | 等级、核心性 | host、endpoint、方法 | 规范化参数、市场、日期 | 字段、单位、响应上限 | 顺序/停止 | |---|---|---|---|---|---| | `cninfo.announcement_index/1.0.0` | A1;核心回链 | `www.cninfo.com.cn/new/data/szse_stock.json` GET;`www.cninfo.com.cn/new/hisAnnouncement/query` POST;PDF `static.cninfo.com.cn/` GET | stock list 得 `orgId`;form 固定 `pageNum,pageSize=30,column,tabName=fulltext,plate,stock=code,orgId,category,seDate`;SZ/SH,BJ 不支持即显式 gap;`announcementTime<=as-of 23:59:59` | `announcementId,title,time,adjunctUrl,orgId,secCode`;时间毫秒;JSON/PDF,单响应 20 MiB | 唯一公告索引;找到覆盖报告期且未被后续修订替代的 A1 即停 | | `eastmoney.market/1.0.0` | B1;价格/股本核心 | `push2.eastmoney.com/api/qt/stock/get` GET;`push2his.eastmoney.com/api/qt/stock/kline/get` GET | `secid=0.code(SZ)|1.code(SH)`;quote fields=`f57,f58,f84,f116,f124`;kline `klt=101,fqt=1,beg=end=YYYYMMDD,fields1=f1..f6,fields2=f51..f61`;SZ/SH;历史价取不晚于 as-of 最近交易日 | quote `f84` 股、`f116` 元、`f124` epoch 秒;kline `f51`日、`f53`收盘元/股;JSON,5 MiB | 历史收盘以 kline 为权威;quote 只补 as-of 可用股本/市值,不能把当前股本回填旧 as-of | | `eastmoney.finance/1.0.0` | B1,须 A1 ID 回链;财务核心 | `datacenter-web.eastmoney.com/api/data/v1/get` GET;reportName 固定 `RPT_F10_FINANCE_MAINFINADATA`,`RPT_DMSK_FN_INCOME`,`RPT_DMSK_FN_BALANCE`,`RPT_DMSK_FN_CASHFLOW` | `columns=ALL,filter=(SECUCODE="code.SZ|SH"),pageNumber,pageSize=20,sortTypes=-1,sortColumns=REPORT_DATE`;记录须 `NOTICE_DATE<=as-of` 且 `REPORT_DATE<=as-of` | 收入 `TOTAL_OPERATE_INCOME`,归母 `PARENT_NETPROFIT`,扣非 `DEDUCT_PARENT_NETPROFIT`;现金 `MONETARYFUNDS`;资产/负债/权益;经营现金 `NETCASH_OPERATE`,资本开支 `CONSTRUCT_LONG_ASSET`;金额原始元、无缩放;JSON 20 MiB | 四张表均只取满足 as-of 的最新所需期间;字段必须链接同报告期 CNInfo A1,否则该字段 gap/核心阻断 | | `eastmoney.forecast_summary/1.0.0` | B2;非核心 | `datacenter-web.eastmoney.com/api/data/v1/get` GET,reportName=`RPT_WEB_RESPREDICT` | `filter=(SECURITY_CODE="code")`, page 1/50;记录 `REPORT_DATE/UPDATE_DATE<=as-of` | `RATING_ORG_NUM`、年度 EPS/利润汇总字段;原始元/股或元,按响应定义且不猜缩放;JSON 10 MiB | 预测第 1 个来源;取得可用汇总即不重复请求汇总 | | `eastmoney.forecast_detail/1.0.0` | B2;非核心 | `emweb.eastmoney.com/PC_HSF10/ProfitForecast/Index` GET | `code=SZ001270|SHxxxxxx,type=web`;只解析固定表头“机构/报告日期/年度/EPS或净利润”;明细发布日期不得晚于 as-of | UTF-8 HTML;机构名、报告日期、预测年度、EPS/净利润;5 MiB | 预测第 2 且最后来源;只为可见明细。解析不到即一个 coverage gap,不再搜索第三站 | 财务数值只在响应字段存在、可解析、币种/单位明确时采用;缺失不得填 0。公告索引仅提供 A1 身份与链接,不从标题猜财务值。市场 `f124` 为 0/缺失时不得作为日期,历史价日期以 kline `f51` 为准。所有 adapter 保存原始字段路径、解析版本、发布/数据日期和单位换算表达式。 ## 7. Provider fixture 合同(F3/F8) 实现后才可创建 `fixtures/chengchang_20260801/`。`fixture_manifest.json` 固定以下规范请求键到响应文件;测试不得由被测 V2 生成 expected: | data kind | request | fixture response | |---|---|---| | stock identity | CNInfo stock list GET | `cninfo_stock_list_success.json` | | A1 index | CNInfo announcements POST page 1 | `cninfo_announcements_success.json` | | market quote | Eastmoney quote GET | `eastmoney_market_quote_success.json` | | historical close | Eastmoney kline GET, beg/end=`20260731` | `eastmoney_market_kline_success.json` | | finance main/income/balance/cashflow | 四个固定 reportName GET | `eastmoney_finance_{main,income,balance,cashflow}_success.json` | | forecast summary | `RPT_WEB_RESPREDICT` GET | `eastmoney_forecast_summary_success.json` | | forecast detail | emweb fixed GET | `eastmoney_forecast_detail_success.html` | 每个 provider 另有最小 `empty`、`http_404`、`http_500`、`schema_drift`、`future_record` fixture;HTTP fixture 的状态和 headers 放在 manifest,不伪装为 200 body。forecast summary=4 家、可见 detail=3 家,expected 只能生成一个 `W_FORECAST_COVERAGE` gap。 ## 8. 唯一 90 秒网络截止(F4) 机械网络阶段开始时以 `time.monotonic()` 形成唯一 `network_deadline = network_started_mono + 90.0`。每个请求、DNS/连接、响应头、分块读取、退避、重试以及 forecast 二次来源都调用同一个 remaining 函数;连接/读/总超时分别取固定上限与剩余预算的较小值。remaining<=0 后不得创建请求或重试。 最多 4 worker。调用方不用 `with ThreadPoolExecutor(...)` 的隐含等待;显式 `shutdown(wait=False,cancel_futures=True)`,provider transport 必须协作检查 deadline,socket 超时也裁剪到 deadline。已在途任务须在同一 deadline+冻结调度容差内返回;不得再叠加 20 秒读超时。容差固定为 `max(0.5s, 测试环境调度分位上限)`,正式验收上限 91.0 秒。 只对连接重置、超时、5xx 最多重试 2 次;4xx、schema/解析、as-of 错误不重试。退避序列 0.25/0.75 秒,实际 sleep=`min(backoff,remaining)`。每次请求记录 provider、request_id、started/finished、remaining_before/after、attempt、bytes、HTTP、cache、semantic status、deadline cause;阶段记录 budget start/deadline/end。 离线桩覆盖四 worker 同启、截止前 50ms 启动、阻塞读取、5xx+退避、shutdown,断言不晚于冻结容差且截止后请求计数不增长。 ## 9. 内容寻址缓存与公司基线(F5) 布局: ```text CACHE/blobs/sha256//.bin CACHE/raw-index///.json CACHE/reusable//.json CACHE/companies//baselines/-.json CACHE/companies//current.json ``` fingerprint 前像为 UTF-8 RFC8785 等价规范 JSON(键排序、无多余空白、数字/布尔规范化),字段固定:`schema_version,provider_id,adapter_version,method,canonical_scheme_host_path,sorted_query,canonical_body_sha256,ticker,as_of,data_kind,fixture_identity|null`,再取 SHA-256。query 重复键保持值数组原序;body 先按 content-type 规范化。fixture_identity=`fixture_manifest_sha256:response_id`,绝不与真实请求碰撞。 raw blob 可归档成功或失败响应。raw-index 分别记录 transport_complete、HTTP、content_type、bytes、blob_hash、parse/schema/as_of 状态和 provider semantic status。`complete=true` 只表示归档写完整,不代表可复用。 reusable 索引必须同时满足:transport_complete、登记 HTTP 2xx、允许 content-type、bytes<=上限、blob hash 复核、adapter 版本相同、parse/schema PASS、as-of PASS、semantic status=`OK`。404/5xx/超时/空数据/schema drift/GAP/BLOCKED 只在本次运行去重;允许负缓存 TTL 固定 60 秒且不得跨 as-of/adapter,也不得写 reusable。篡改时保留 raw 证据、废弃 logical index 并重取。 baseline schema 固定包含 `schema_version=1,adapter_bundle_version,ticker,baseline_as_of,created_at,watermark{announcement_time,report_date,market_date,forecast_date},provider_results,field_lineage{field:[source_id,raw_hash]},source_hashes,data_hash`。data_hash 的前像排除自身和 created_at,包含其余规范 JSON。只以临时文件+`os.replace` 提交;同 ticker 加锁并在 replace 前重新读取 current,解决并发竞争。 选择规则:只复用 `baseline_as_of<=requested_as_of` 且 source hashes 可验证的最近基线;较新基线绝不回跑较旧 as-of。动态模块局部刷新失败时,可保留未过期、日期不晚于 requested as-of、来源哈希仍有效的旧字段并显式 gap;不得把新价格/股本回填旧日期。核心采集未完成不更新 current。并发较旧运行不得覆盖较新 current;相同 as-of 以确定的 data_hash 字典序作幂等选择并保留两个不可变 baseline。 ## 10. 数据、判断覆盖与 V1 单一数值源 无 judgment:完成 `data_snapshot.json`、`snapshot_build_report.json`、来源/原始哈希、基线、16 节数据就绪报告、QA、gaps、runtime 和 manifest;终态固定 `DATA_READY_NEEDS_JUDGMENT`。不创建 `valuation_snapshot.json` 或 `valuation_results.json`,所有需判断章节写“GAP:需要人工判断覆盖层”,不输出方向性价格区间或评价。 有 judgment:schema 只允许 `normalization.adjustments`、`valuation.scenarios/reverse_pe_multiples/exit_pe_multiples/holding_period`、`analysis` 的业务摘要/模型/交叉验证/风险/触发器/边界。禁止写价格、股本、法定收入/利润、报表日期、来源日期、A1 ID/URL/hash;递归发现保护键或覆盖后数据哈希改变即 `E_JUDGMENT_CONFLICT`。合并后由 V1 `load_snapshot` 与 `compute_valuation` 计算;报告数字只读取 V1 results,不重新算 PE/PB/PS/情景/反向 PE/持有期。 ## 11. as-of 与可信信源停止规则 任何记录必须同时满足 `publish_date<=as-of`、`data_date<=as-of`。无明确 publish_date 的财务/预测记录不得用于核心值;修订公告按 A1 ID/标题/日期建立替代链,as-of 前的最后有效版本才可用。发现未来记录产生 `E_ASOF_VIOLATION` 并阻断受影响核心字段,不得降成普通 warning。 同一数值取得 A1 原文或可核对 A1 ID 的结构化记录,且公司、期间、币种、单位、口径、修订状态明确,勾稽/数量级合理,即停止继续查询。forecast 只查上述两个登记来源;汇总 4/明细 3 形成一个 gap 后立即停止。 ## 12. 固定 16 节报告与自动 QA 章节顺序固定:1 结论摘要;2 市场和资本结构;3 信源优先级;4 业务及利润来源;5 历史盈利;6 TTM 与归一化;7 机构预期;8 利润质量与现金流;9 模型选择;10 三情景;11 反向隐含条件;12 交叉验证;13 持有期测试;14 风险与触发器;15 复评清单;16 数据局限和复算摘要。 QA 硬门禁:标题齐全且唯一、Markdown fence/表列完整、无 TODO/TBD/待计算/NaN/Infinity;本地产物链接存在;核心字段有 source_id/A1/raw hash 且日期安全;报告数字与 data snapshot 或 V1 results 规范值一致;judgment 不改保护字段;无 judgment 无方向结论;不含“买入、卖出、加仓、减仓”等交易指令或收益承诺;manifest 中每个正式文件的 bytes/hash 可复算。外部 URL 可达性仅 warning,不改写既有结果。 ## 13. 终态、退出码、stdout 与产物矩阵(F7) stdout 仅一行规范 JSON:`schema_version,status,run_id,output_dir,manifest_path,receipt_path,task_wall_seconds,process_wall_seconds,gap_count,error_code|null`;进度写 stderr/runtime.log。 | 状态 | exit | 正式目录必有 | 可有 | 禁止/manifest | |---|---:|---|---|---| | `COMPLETE` | 0 | data snapshot、build report、valuation snapshot/results、16节报告、QA、gaps、sources、runtime、manifest | provider diagnostics | manifest status 同名、complete=true | | `COMPLETE_WITH_GAPS` | 0 | 同 COMPLETE 且 gaps 非空 | 非核心缺口 | 核心缺失禁止;complete=true | | `DATA_READY_NEEDS_JUDGMENT` | 0 | data snapshot、build report、16节报告、QA、gaps、sources、runtime、manifest | provider diagnostics | 禁止 valuation snapshot/results 和方向结论;manifest status 同名、complete=true | | `BLOCKED` | 4 | 无正式目录;`OUT.failed-RUN/failure.json,runtime.log,runtime_metrics.json` | 已形成诊断/原始哈希引用 | 禁止 complete manifest/receipt;failure complete=false | | `FAILED` | 5 | 无正式目录;同上失败包 | 诊断 | 禁止 complete manifest/receipt;failure complete=false | 输入合同错误在网络前 exit 2,不创建正式或失败目录;V1 兼容模式严格使用第 5 节 0/2/3。ticker 模式内部合同错误归 FAILED/5,核心数据/A1不足归 BLOCKED/4。预期的 DATA_READY 不是伪 COMPLETE,但属于可消费成功数据终态。 ## 14. 输出预检与 BaseException 原子状态机(F6) 在网络前以 `lstat` 检查 output、staging、backup、receipt、所有 `.failed-*` 冲突;普通文件、符号链接、junction/reparse point 或不明类型一律拒绝。cache 可单独存在但也拒绝链接/reparse。 absent 路径:`ABSENT -> STAGING_OWNED -> STAGING_COMPLETE(manifest last) -> OUTPUT_INSTALLED(rename) -> RECEIPT_WRITTEN`。force 路径:先完整创建 staging;`OLD_OUTPUT -> BACKUP_OWNED(rename1) -> NEW_OUTPUT_INSTALLED(rename2) -> RECEIPT_WRITTEN -> BACKUP_REMOVED`。 提交边界捕获 `BaseException`。若旧目录已移到 backup 且新目录尚未成功就位,先把 backup 恢复到 output;恢复失败时保留 backup 并在 failure 证据记录双异常,绝不删除。若新目录已就位,正式真值是新目录;backup 清理失败只留 warning/backup,不撤销新结果。清理异常不得替换原 KeyboardInterrupt/SystemExit;以异常链保存。只有新目录和 receipt 均成功后才对外宣告成功;receipt 失败时 force 模式恢复旧目录,新 staging/新 output 改名为 failed。 注入点固定:mkdir、每个 write/replace、manifest write、rename1、rename2、restore、receipt、backup cleanup。逐点断言旧输出全树 hash、正式目录唯一、staging/backup/failed 归属、无伪 complete manifest、原异常类型。 ## 15. 双墙钟与提交回执(F7) `process_start_wall` 用带时区 `datetime.now().astimezone()`,duration/预算全部用 monotonic。`--task-start` 必须是带时区 ISO-8601,解析后不得晚于 process_start;保留原字符串、规范 UTC、source=`arg|process_start`,缺省等于 process_start。未来值/无时区在网络前 exit 2。 正式包内 `runtime_metrics.json` 在 manifest 前冻结,包含 `task_start,process_start,commit_ready_at,task_wall_seconds,process_wall_seconds,wall_scope=through_commit_ready`,以及阶段和 provider telemetry;两个 wall 字段均非负且 task>=process(允许 1ms 舍入容差)。manifest 哈希该文件。 目录 rename 完成后不改写包内文件,而在同级写 `OUT.commit-receipt.json`:`schema_version,run_id,output_path,manifest_sha256,commit_ready_at,commit_finished_at,task_wall_seconds,process_wall_seconds,wall_scope=through_commit_finished`。它是最终总墙钟权威记录;stdout 必须复制 receipt 的两个最终 wall 和 receipt path。receipt 以临时文件 fsync+replace 写入;其 hash/bytes 写 stdout,不回写 manifest。正常、data-ready、blocked、failed、force rollback 测试双墙钟、阶段之和上界、receipt/manifest 绑定;失败包 runtime 的 wall_scope=`through_failure_recorded`,无 receipt。 ## 16. 内容与安全边界 User-Agent 固定、禁止 cookie/auth、禁止跨 host 重定向、日志移除 query 中潜在敏感值;响应按 bytes 上限流式终止。报告明确“研究与复算材料,不构成交易指令”,但不以免责声明替代数据/结论 QA。不输出任何自动买卖动作。 ## 17. 铖昌 fixture 独立验收前像(F8) 只读基线固定: - `outputs/20260801_chengchang_technology_valuation/valuation_snapshot.json`=`10585 bytes / 4C27EE81A670BFA8BBD1E2DFB03A9730F6940D7A340D410D232585C57598A138`; - `outputs/20260801_chengchang_technology_valuation/generated/valuation_results.json`=`9766 bytes / 27671D42A75C82125B893B3374E27497E33B929BB1AFEFA61A5D3370BC446559`; - judgment overlay 只从该 snapshot 的 `normalization`、`valuation` 和允许的 `analysis` 字段人工摘取;剔除 `analysis.executive_conclusion` 中的既有价格/方向文字;文件 hash 在创建后单独登记,不由 V2 运行生成; - request map 固定为第 7 节,fixture manifest 自身 hash 在实现证据登记。 关键 expected 以 Decimal 字符串比较,绝对容差 `1e-8`(完整规范 results 仍要求字节等价): | 指标 | expected | |---|---:| | market cap | `19077995236.56` | | TTM revenue | `416511774.73` | | TTM attributable | `132018113.77` | | TTM deduct / normalized profit | `118174918.34` | | PB | `12.22659092842983667773303996` | | PS | `45.80421585662767464686795987` | | base price low/high | `32.40910757830167747066477256` / `41.96688331621399852114525189` | | optimistic high | `66.95294679349747740945716487` | | reverse PE 50 profit | `381559904.7312` | | required exit price | `152.48767265546425000` | forecast expected=`summary_count=4,detail_count=3,gap_count_by_id.W_FORECAST_COVERAGE=1`,禁止为了第四家无限取数。 ## 18. 分层测试与回归(F4—F8) L0:全包 compile/import、JSON/schema/registry、V1 指纹/函数签名。 L1:每个 provider 成功/空/4xx/5xx/schema drift/future;未知域名网络前拒绝;90 秒四 worker 阻塞桩;cache 404/5xx/负 TTL/篡改/adapter 变化;较新 baseline 回跑旧 as-of、局部失败、并发提交;judgment 禁写;16 节/交易词/数值 QA。 L2:铖昌有/无 judgment、冷/热缓存;有 judgment 与冻结 V1 results 比较,无 judgment 为 DATA_READY 且无伪结论;冷运行<5秒,热缓存<2秒。 L3:force 每个注入点、KeyboardInterrupt/SystemExit、恢复/cleanup 双失败;断言旧树 hash、唯一正式目录、manifest/receipt 真值。 L4:真实网络按第 20 节;不把外站故障伪装成功。 V1 10/10 固定入口:`python -m unittest dev/ana-dev/test/stock_valuation_pipeline/test_pipeline.py -v`;测试文件=`7231 bytes / C1ECD2A8C8C0C9131C3E93B1E384616508314CB2843571D169876CA155D171CD`。长城 V1 fixture=`dev/ana-dev/test/stock_valuation_pipeline/fixtures/greatwall_example.json`=`7142 bytes / 06AB6A7C45C2E43F71654C616F15A826B21E468C4BA72AFB0B253C9DD89FACD8`。直接 V1 与 V2 bridge 对该 fixture 分别运行无 force、复跑、force 和输入错误,按第 5 节比较。 ## 19. 性能与预算判定 fixture 冷运行以新 cache/OUT 计 `<5.0s`;热运行复用同 cache、新 OUT `<2.0s`;均报告 process/task wall。真实网络机械阶段硬门禁 `<=91.0s`,目标 `<90s`;报告每 provider/request 和 cache telemetry。性能测试只比较同机、同 fixture、同输出模式,不能以 V1 毫秒计算时间冒充端到端总墙钟。 ## 20. 冻结的真实网络 smoke(F8) PASS 后首选且固定 `001270.SZ / 2026-08-01`。使用独立目录: ```powershell python -m stock_valuation_pipeline_v2 --ticker 001270.SZ --as-of 2026-08-01 ` --cache-dir dev/tmp/valuation-v2-smoke/cache --output-dir dev/tmp/valuation-v2-smoke/cold ` --task-start 2026-08-01T00:00:00+08:00 python -m stock_valuation_pipeline_v2 --ticker 001270.SZ --as-of 2026-08-01 ` --cache-dir dev/tmp/valuation-v2-smoke/cache --output-dir dev/tmp/valuation-v2-smoke/warm ` --task-start 2026-08-01T00:00:00+08:00 ``` 唯一换股条件:登记的交易所/行情 provider 明确返回证券不存在,或 as-of 前后无历史收盘且可证明停牌;一般超时、5xx、解析漂移、财务/预测缺口不得换股。满足条件时唯一备用为 `000001.SZ / 2026-08-01`,保留原失败证据并用同样两条隔离命令。 证据必须保存:两次命令、exit、stdout/stderr、runtime/receipt、request indexes、raw hashes、provider/gap、A1 document IDs/links、价格/股本/市值/财务期间/预测日期、cold→warm cache 命中和 baseline diff、network deadline/end、是否<=91秒。真实数值可变化,但来源身份、日期、单位、预算、复用和缺口真值不可省略。本节只冻结验收,不在方案 PASS 前授权联网。 ## 21. 正式证据与文档收口 实现证据写入 project 文档路径,列代码/测试/registry/fixture bytes+SHA-256、全部命令和 exit、V1 10/10、fixture expected、冷/热性能、真实 smoke、失败注入、无关改动隔离。工具说明写 `dev-doc/project-doc/股票估值端到端协调层V2.md` 与包 README。实现审计 PASS 后,才依管理员条件授权同步原操作手册单文件;审核员在 `dev-doc/开发审计报告.md` 形成独立结论。 ## 22. F1—F8 闭环映射 | Finding | 本版闭环 | |---|---| | F1 | 第1节引用管理员三账 closure 和唯一 V003 权属 | | F2 | 第5节冻结 V1 路径/hash/签名/registry/异常/一致性测试 | | F3 | 第6—7节冻结五个 adapter、allowlist、字段/日期/单位/fixtures/停止 | | F4 | 第8、18—20节冻结单调 deadline、协作取消、容差和阻塞测试 | | F5 | 第9节冻结 fingerprint、raw/reusable、负 TTL、baseline 并发/as-of | | F6 | 第14节冻结 absent/force BaseException 状态机和全点注入 | | F7 | 第13、15节冻结状态/产物/exit/stdout、task-start、receipt 双墙钟 | | F8 | 第7、17—20节冻结基线 hash、request map、expected、V1/真实 smoke 命令和证据 | ## 23. 审核门禁 V003 只提交现有 `dev.reviewer.project` 做限定方案复审。只有审核明确 `PASS`,开发者才能创建 V2 代码、测试和 fixture;之后依次执行实现、离线测试、fixture 性能、真实 smoke、文档与实现独立审核。若复审仍 HOLD,只返修阻断项,不绕过门禁,不修改 `ana-dev`。