跳轉到

Chip 歷史重建工具(#1848)

本工具支援 #1829 修正部署後的 chip 歷史重建。正式資料寫入仍須先提交固定 DB、pin、範圍、備份、逐日 payload 及回滾方案取得核可。工具 PR、備份還原演練及測試通過不代表歷史資料已重建。

範圍與前置條件

  • evidence floor 固定為 2021-09-27。W = MAX(stock_ohlc.timestamp::date) - 7 天;只重建到 W - 1,保護窗口包含 W。
  • 用完整 TWSE 官方交易日及當日 resolver 凍結 eligible/excluded 鍵;缺必要輸入即拒絕。resolver 排除鍵的原有列(含 orphan evidence)保留,另交 #1831 追蹤。
  • producer 固定使用已部署的完整 Git commit。工具、正式來源、官方交易曆、seed 與備份 hash 不符時不得續跑。
  • snapshot/evidence 全部完成後才重播 chip SES。日期串行、當日 producer 股票併發為 2。
  • SES 重播前須確認 financial、technical、scheduler 等沒有其他 SES writer。source-state 的交易列鎖及 driver 的本地 state-file lock 不能代替跨 runner 的排他協調。
  • 開跑前另確認 watchdog、磁碟餘裕、允許時段、scheduler busy 狀態、C6 基準及 writer 排除。這些運維條件由核可的外層 runner 負責;以下 Python 工具不會暫停 scheduler。
  • 不使用 targeted/selective replay,不寫 cross_domain,不刷新策略統計或 latest cache。

以下命令在固定 checkout 的 investment-agent/backend 執行。使用 bash;Docker mount 須完整加引號。DATABASE_URL 由核可的執行環境注入,不寫入 log。

唯讀準備與備份

python -m scripts.chip_history_rebuild prepare \
  --output-dir "$PLAN_DIR" --producer-pin "$PIN"

python -m scripts.chip_history_backup export \
  --root "$BACKUP_DIR" --plan "$PLAN_DIR/plan.json" \
  --plan-sha256 "$PLAN_SHA256"

prepare 建立 canonical plan.json、逐日鍵檔、完整 chip-state seed 及六種正式 snapshot 輸入的 fingerprint;日期固定從 floor 到 W−1,沒有日期覆寫參數。先計算並保留 plan.json 的 SHA-256,再交給 export。備份以同一 READ ONLY REPEATABLE READ 交易核對 frozen plan 的最新 OHLC 與範圍;若 prepare 後 MAX 日期變動,須重新 prepare。SELECT COUNT 與 COPY text 列數、完整內容 SHA-256、欄位順序及 schema 都須一致。備份包含五組 chip 回滾列、五組保護窗口列、三組 cross 基準、source-state 與 latest chip/cross 基準。

在已另建的 chip1848_restore_* disposable DB 注入其 DATABASE_URL 後執行:

python -m scripts.chip_history_backup restore-check \
  --root "$BACKUP_DIR" > "$RESTORE_REPORT"

restore-check 拒絕正式 DB,核對每組原始 COPY 的還原列數及重新 COPY 的內容 hash。封存器另核對 zstd 壓縮/解壓的完整 bytes/hash;restore-check 的 PostgreSQL 演練使用原始 COPY 檔。不要把壓縮 roundtrip 宣稱為 PostgreSQL 壓縮檔直接還原演練。

封存前準備同一 $PIN 的 git archive、git ls-tree -r -l 與 commit object,使用 host 工具 scripts/seal_chip_history_backup.py --help 列出的參數,包含 --plan-sha256 "$PLAN_SHA256"。封存器會驗證 Git object、producer 檔案、工具實體檔、還原報告與各 role 的精確 table/predicate/args/cardinality,建立不可覆寫的 manifest 及 SHA-256。共享的 stdlib 模組 chip_history_plan.py 也封存為工具實體檔。失敗時只移除該次建立且身分未變的暫存產物,保留原始 COPY 與既存檔案,可重新執行。舊工具版本的封存 manifest 不可當作新版本 pin 的執行證明。

核可後逐日重建

python -m scripts.chip_history_rebuild apply \
  --plan "$PLAN_DIR/plan.json" --state "$STATE_FILE" \
  --date "$DAY" --producer-pin "$PIN" \
  --backup-manifest "$BACKUP_MANIFEST" \
  --backup-manifest-sha256 "$BACKUP_SHA256" --apply

只能執行 completed prefix 之後的下一個官方交易日。driver 檢查 immutable 檔案、正式來源、resolver 與第一個 eligible 日的 seed。若股票在 eligible 之後離開又重新進入,prepare 與 apply 全檔核對會拒絕,避免讀入未凍結的 retained state;此類範圍須另凍結每個邊界再設計處理。producer 之後核對統計、精確 snapshot/evidence 鍵集合、更新時間、同交易寫入及四種 cost-zone metadata;全部通過後,將每日開始/完成時間、eligible 數及僅含 eligible 列的 snapshot vintage hash 與 completed marker 同次寫入 state。當日 readback 仍涵蓋全部 snapshot,以拒絕意外更新的 retained 列;未更新的 retained 列不參與 receipt。

producer 每 ticker commit。中途失敗可能已有部分當日寫入;保留 log,從最早未完成日整天重跑。若已完成日需要重算,後續狀態鏈和 SES 都須按核可方案重播。不要手改 completed marker 跳過失敗日。每日前後全範圍來源 fingerprint 的成本須納入 pilot 時間估算。

此版本保留每日兩次完整來源 fingerprint。既有全範圍唯讀量測每次約 196 秒,1,215 日僅來源核對即約 132 小時,未含 producer 計算;工具驗證通過不代表執行時間已可接受。改用每日窗口前,prepare 須先產生且認證該日正式 loader 的完整窗口 digest,並加入最終全範圍核對憑證;目前沒有這項契約,不能只改查詢日期或使用 cache 省略漂移檢查。

state lock 使用 flock,同一時間只允許一個 runner。lock 檔保留原 inode;正常退出或程序被 SIGKILL 後由核心釋放鎖,無須刪除 lock 檔。禁止手動刪除正在使用的 lock 檔。

全部 snapshot 完成後重播 SES

snapshot 完成後,保留完整 state 並計算 STATE_SHA256。每個 frozen 日期先 dry-run,再用相同參數去掉 --dry-run:

python -m scripts.backfill_ses_story_daily \
  --market taiwan --date-from "$DAY" --date-to "$DAY" \
  --rebuild-domain chip --require-v2-inputs --fail-on-day-errors \
  --frozen-chip-plan "$PLAN_DIR/plan.json" \
  --frozen-chip-plan-sha256 "$PLAN_SHA256" \
  --frozen-chip-rebuild-state "$STATE_FILE" \
  --frozen-chip-rebuild-state-sha256 "$STATE_SHA256" \
  --timing-json "$LOG_DIR/ses_${DAY}_dry_run.json" --dry-run

frozen guard 限 chip full mode、精確日期與 eligible 鍵。必須先證明全官方日期的 snapshot phase 已完成,state 與 plan pin/config/範圍相符,且當日全部 eligible snapshot 的更新時間與 receipt 的 vintage hash 完全一致;缺 snapshot/evidence、舊 vintage、檔案被替換或修改、相同 stock_code 的 listing 身分不符都拒絕。讀取 scope、第一筆 append 前及 commit 前皆驗證檔案;失敗交易 rollback,該日不得標記完成。保留舊列不透過外加 ticker 清單擴大寫入。

禁止 --domain、--signal-code、--selective-replay、--force-recompute、--refresh-latest-cache。shared source-state revision/dirty marker 沿用正式 service,不執行 refresh。

驗收與回滾

python -m scripts.chip_history_validation \
  --plan-dir "$PLAN_DIR" --plan-sha256 "$PLAN_SHA256" \
  --date-from "$FLOOR" --date-to "$END" \
  --mismatches-jsonl "$LOG_DIR/chip_mismatches.jsonl" \
  --summary-json "$LOG_DIR/chip_validation.json"

validator 從認證的 frozen plan/day files 推導 rewritten 與 retained scope,檢查完整預期鍵及 C2 streak/margin、C3 state duration、C4 cost-zone metadata、C5 coverage vintage/state;rewritten coverage 的 listing_type 須與 frozen key 一致,包含 ^TWII 預期為 NULL 的情況。另枚舉 event/story,驗證其 coverage、rule version、listing 與 scope,合法空 event/story 不要求新增列。整組缺失、coverage-only key 及 orphan/unexpected ledger key 必須失敗。指定 --stock-code 的局部結果不可作全範圍 PASS;殘留 C3/C5 缺口仍阻止驗收。

另收集價格日期 probe、C1 部署證據、C6 cross 前後基準、C7 evidence floor/保護窗口與 C8 相同 API GET 的前後 JSON。validator 不會代替這些驗收。六種 snapshot 來源 fingerprint 也不宣稱涵蓋所有 SES 讀取依賴。

chip_history_backup.rollback_day_sql 只產生逐日、完整 frozen eligible 鍵的五表 upsert/delete SQL,保留 retained/其他 domain/保護窗口;不執行 SQL。將原始 COPY 還原並讀回 staging、取得精確回滾 payload 核可後才可執行。共用 source-state 不盲目還原舊 revision,應在單 writer 保護下使用正式 dirty marker。