feat(power): ingest and expose power audit provenance / 功率:摄取并公开功耗审计溯源字段(power_invalid_reasons、power_audit) - #929
Conversation
Turn the reserved power_invalid_reasons / power_audit contract fields live end-to-end: - migration 014 adds dedicated jsonb columns on benchmark_results and recreates latest_benchmarks with the migration-012 definition verbatim so br.* picks up the new columns - mapBenchmarkRow narrows both fields defensively (snake_case reason codes, fixed 8-key audit shape, empty -> undefined so the columns store SQL NULL) and both keys join NON_METRIC_KEYS so Number(['5']) === 5 can never mint a bogus numeric metric - bulkIngestBenchmarkRows persists both as jsonb lanes, NULL when absent, refreshed on conflict like workers - all four read paths select the columns via to_jsonb(...) -> 'col' (the PR #405/#407 deploy-order lesson: bare references fail at plan time until the next ingest applies the migration; the jsonb lookup degrades to NULL) - rowToAggDataEntry passes reasons through, and both chart tooltips render a bilingual "Measured power withheld" line with re-sanitized codes - unofficial-run overlay rows carry both fields; the calculator view strips them; OpenAPI wording moves from reserved to live The persistence input is built by spreading (ingest-ci-run.ts {...row, configId}; run-overrides.ts applyBenchmarkPointBackfill {...point}), so the new BenchmarkParams fields flow through with no changes there. 中文:将预留的 power_invalid_reasons / power_audit 契约字段全链路转正:迁移 014 为 benchmark_results 增加两个专用 jsonb 列并按迁移 012 的定义原样重建 latest_benchmarks;mapBenchmarkRow 对两个字段做防御性收窄(snake_case 原因码、 固定 8 键审计对象、空值映射为 undefined 以存储 SQL NULL);批量摄取以 jsonb 通道 持久化并在冲突时刷新;四条读取路径均用 to_jsonb(...) -> 'col' 容错读取(PR #405/#407 的部署顺序教训);图表提示框新增双语"实测功耗未采信"行;非官方叠加行 携带同样字段;计算器视图剥离;OpenAPI 文案由预留改为正式。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ies, and UI | 测试:覆盖功耗审计溯源在映射、摄取、查询与界面各层的行为 - extractPowerInvalidReasons: snake_case validation, dedupe, 32-code cap, 64-char boundary, empty/non-array/all-invalid -> undefined (never []) - extractPowerAudit: 8-field round-trip, Infinity/NaN/junk numerics omitted, negative and non-safe-integer counts rejected, sha trimming and null collapse, unknown keys dropped, empty husk -> undefined - mapBenchmarkRow lands the fields on BenchmarkParams for v1/v2/agentic rows, stores audits from valid rows too, and never mints a numeric metric from a malformed ['5'] reasons array (Number(['5']) === 5) - bulkIngestBenchmarkRows recording-mock: both columns in the INSERT list, two extra ::jsonb[] lanes, null lanes for absent fields, excluded.* refresh on conflict - all four read paths pin the tolerant to_jsonb(...) -> 'col' form and a negative regex guards against bare br./lb. references (the #407 lesson) - rowToAggDataEntry narrows null/[]/absent to undefined; tooltips render the bilingual withheld line only for sanitized codes (en + zh, overlay parity); calculator view strips both fields alongside workers 中文:为溯源字段新增全链路测试——提取函数的收窄规则(原因码校验、去重、上限、 空值处理;审计对象 8 字段往返、异常数值剔除、sha 归一化)、映射到 BenchmarkParams、批量摄取的 jsonb 通道与冲突刷新、四条读取路径的 to_jsonb 容错形式(并用反向正则钉死 #407 教训)、前端窄化与双语提示框行为、计算器 视图剥离。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @edwingao28's task in 3m 26s —— View job Review: PLAN-07 power audit provenance ingest🟢 LGTM — no blocking issues found. Reviewed the full stack against
What I verified
Nice attention to the stacked-PR merge order and the deploy-timing invariant throughout. |
…| ETL:补充数据摄取通道同样持久化功耗溯源字段 Review found ingest-supplemental.ts participates in the power publication contract (normalize + scrub, PLAN-03) but silently dropped power_invalid_reasons / power_audit: the persistence input carried no provenance fields, so a supplemental entry with them would persist NULL columns. Extract both via the shared narrowers — entry-level fields sibling to metrics (mirroring artifact rows), with a metrics-nested fallback since power_valid rides in metrics in this format — and delete the keys from metrics so the persisted jsonb stays a flat numeric record. Pin the call sequence next to the PLAN-03 supplemental tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @edwingao28's task in 1m 49s —— View job Re-review: PLAN-07 — new commit
|
|
Superseded by #939 (branch renamed to feat/power-provenance-ingest); identical diff. |
Important
STACKED PR — do not merge out of order. Base is
klaud/powerx-04-api-contract(#921), which is itself stacked on #909. Merge order: #909 → #921 → this PR. This PR contains only the PLAN-07 deltas relative to #921.Implements PLAN-07: ingest and expose the power audit provenance fields (
power_invalid_reasons,power_audit) that the producer (InferenceXaggregate_power.py, PLAN-06) emits alongside thepower_validverdict. PLAN-04 (#921) reserved the two fields in the OpenAPI schema; this PR turns them live end-to-end while remaining fully tolerant of their absence (legacy artifacts, pre-PLAN-06 runs, legacy DB rows).What changed
Storage — migration
014_power_provenance.sqljsonbcolumns onbenchmark_results(power_invalid_reasons,power_audit), mirroring the migration-006workersprecedent:metricsis a flatRecord<string, number>, so structured data gets its own columns.latest_benchmarkswith the migration-012 definition verbatim (recursive append-only-curve body plus both indexes) solely sobr.*picks up the new columns — not the obsolete 006 definition.ETL —
benchmark-mapper.tsNON_METRIC_KEYS:Number(['5']) === 5, so without the guard a malformed single-element reasons array would be auto-captured as a bogus numeric metric.extractPowerInvalidReasons: keeps snake_case codes (≤ 64 chars), dedupes preserving order, caps at 32; empty result →undefinedso the column stores SQL NULL, never[].extractPowerAudit: fixed 8-key shape (window_start_unix,window_end_unix,expected_gpu_count,observed_gpu_count,sample_count,max_sample_gap_s,producer_sha,exporter_image_sha256); finite-number/safe-integer narrowing, malformed numerics omitted (partial audit beats none), shas collapse tonullper the contract'sstring|null, unknown keys dropped, empty husk →undefined.power_valid— tolerance in both directions.ingest-ci-run.tsorrun-overrides.ts: the persistence input is built by spreading ({...row, configId}/applyBenchmarkPointBackfill's{...point}), so the newBenchmarkParamsfields flow through.Ingest —
benchmark-ingest.tsunnestlanes mirroringworkers; NULL when absent;excluded.*refresh on conflict (fresh artifact is authoritative, same asworkers).Reads —
queries/benchmarks.ts(deploy-order safety, the #405/#407 lesson)to_jsonb(br) -> 'power_invalid_reasons'/to_jsonb(lb) -> ...— never bare column references. Migrations run in the ingest workflows, not at Vercel deploy; a bare reference fails at query plan time until the next ingest applies migration 014, which is exactly how PR feat(power): measured-power multinode support (workers[] + per-stage joules) #405 produced a ~63% error rate. The jsonb key lookup degrades to NULL while the column is missing and is byte-identical once it exists, so merge order and deploy timing are irrelevant. A regression test pins the tolerant form with a negative regex.API & frontend
/api/v1/benchmarksand/historyreturn the fields verbatim when stored; calculator view strips them (payload-trimmed projection excludes measured-power data by design); unofficial-run overlay rows carry both fields via the shared mapper.api-route-catalog.tsdigests refreshed for the two changed contract sources.power_auditis API-only — no UI surface (follow-up).Testing
bun run --cwd packages/db test:unit— 650 passed (extractor suites, mapper integration incl. the['5']guard, ingest lane recording-mock, query-shape assertions with the bare-reference regex guard, supplemental-path provenance sequence)bun run --cwd packages/app test:unit— 4491 passed (transform narrowing, tooltip en/zh + injection filtering + overlay parity, unofficial-run overlay parity, calculator strip, OpenAPI docs invariants, catalog guard)bun run typecheck/bun run lint/bun run fmt— cleanpackages/mcpserver.test.tsfails identically on the base branch in this environment (z.enumundefined — zod resolution, unrelated to this change).Deploy-order proof: this PR's Vercel preview serves
/api/v1/benchmarksagainst the un-migrated production DB with the two keys null — the live demonstration of the tolerant-read design. The nextstage-results/ingest run applies migration 014 and new ingests populate the columns.Review
Two independent review passes ran over the implementation; one approved with no findings, the other approved with two nits, both addressed:
ingest-supplemental.tsparticipates in the power publication contract (PLAN-03's normalize + scrub) but built its persistence input withoutpowerInvalidReasons/powerAudit, so a supplemental entry carrying the fields would persist NULL columns silently. Fixed in the follow-up commit: the lane now extracts both via the shared narrowers (entry-level fields sibling tometrics, with a metrics-nested fallback sincepower_validrides inmetricsin that format) and deletes the keys frommetricsso the persisted jsonb stays a flat numeric record; the call sequence is pinned next to the PLAN-03 supplemental tests./api/v1/benchmarksdirectly. Indirect evidence is strong (green Vercel check,to_jsonbreads pinned by a negative-regex test, Postgres jsonb semantics), but a human with Vercel access should hit the preview once and confirm 200 withpower_invalid_reasons/power_auditnull before merging the stack.中文说明
实现 PLAN-07:摄取并公开生产端(InferenceX
aggregate_power.py,PLAN-06)随power_valid判定一同产出的功耗审计溯源字段(power_invalid_reasons、power_audit)。PLAN-04(#921)已在 OpenAPI 中预留这两个字段;本 PR 将其全链路转正,并对字段缺失(旧产物、早于 PLAN-06 的运行、历史数据行)保持完全兼容。benchmark_results上新增两个专用jsonb列(沿用迁移 006workers的先例),并按迁移 012 的定义原样重建latest_benchmarks,使br.*覆盖新列。mapBenchmarkRow防御性收窄两个字段(snake_case 原因码去重限长限量;审计对象固定 8 键、剔除非法数值、sha 归一化为string|null、丢弃未知键;空结果映射为undefined以存储 SQL NULL);两个键加入NON_METRIC_KEYS,避免Number(['5']) === 5生成伪数值指标。to_jsonb(...) -> 'col'容错形式,列尚未迁移时读取为 NULL,迁移后逐字节等价,合并与部署顺序均无关;回归测试用反向正则钉死该形式。测试:db 包 650 项、app 包 4491 项全部通过;typecheck / lint / fmt 干净。(
packages/mcp的server.test.ts在本地环境于基础分支上即失败,与本变更无关。)评审:两轮独立评审均通过,其中一轮提出两条次要意见——补充数据摄取通道(
ingest-supplemental.ts)此前未持久化溯源字段,已在后续提交中修复并以单测钉住调用序列;Vercel 预览部署受访问保护,评审无法直接探测,请有权限的同事在合并前访问预览的/api/v1/benchmarks确认返回 200 且两个新键为 null。🤖 Generated with Claude Code
Note
Medium Risk
Touches benchmark ingest, matview recreation, and all benchmark read SQL; deploy-order tolerance mitigates migration timing, but a bad matview rebuild or ingest regression could affect latest-curve queries and power tooltip HTML.
Overview
PLAN-07 turns
power_invalid_reasonsandpower_auditfrom reserved API placeholders into live end-to-end provenance for measured-power verdicts fromaggregate_power.py.Storage & ingest: Migration 014 adds dedicated JSONB columns on
benchmark_results(same pattern asworkers) and rebuildslatest_benchmarksso the matview includes them.mapBenchmarkRowextracts and narrows both fields (extractPowerInvalidReasons/extractPowerAudit), keeps them out of flatmetricsviaNON_METRIC_KEYS, and bulk ingest writes them with NULL lanes for legacy rows.Reads: All benchmark query paths select via
to_jsonb(...) -> 'power_*'so deploys stay safe before migration 014 is applied on ingest (avoids the #405/#407 plan-time failure mode).API & UI: Public benchmark rows and unofficial-run overlays surface the fields; the calculator trim strips them. Chart tooltips add a bilingual “Measured power withheld” line when reason codes exist, with snake_case re-validation before raw HTML. OpenAPI/docs drop “forthcoming” wording.
Reviewed by Cursor Bugbot for commit 8a6a0ee. Bugbot is set up for automated code reviews on this repo. Configure here.