Skip to content

fix(report): render fields on the axis and scope they were recorded on - #2043

Merged
xiaofei-zheng merged 6 commits into
mainfrom
fix/report-render-field-mismatch
Oct 21, 2026
Merged

xiaofei-zheng merged 6 commits into
mainfrom
fix/report-render-field-mismatch

Conversation

@lishuoshuo-amd

@lishuoshuo-amd lishuoshuo-amd commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator
  • Description:
    The session report (session_report.md and the LLM fact pack) printed several fields on a different axis or scope than the one they were recorded on. Each fix is in the reporter layer only; no recorder, collector or schema change.
    1. Headline: on an interactivity-graded session it paired output tok/s (1056.82 -> 1119.17) with the p50 validated gain (+26.55%). When outcome.final.graded_on is an interactivity axis, the headline now reads that axis, with before and after taken from the settled validation row of the last stack event (validations.settled.baseline_tput -> validated_tput, used only when its graded_objective is that axis and the pair reproduces the gain within 0.5%): e2e_norm_intvty_p50 63.03 -> 79.76 = +26.55% validated gain. When no consistent pair exists it states only the gain and the axis (+26.55% validated gain on e2e_norm_intvty_p50). A re-anchored baseline therefore no longer yields a before/after that contradicts the gain. Sessions not graded on interactivity keep the tok/s line. The Final section's Validated cumulative gain names the axis the same way.
    2. Elapsed: the summary showed this leg's elapsed_minutes (1602) as the session time. It now prefers total_elapsed_minutes (2742) and labels it elapsed(total); the Session section lists both under their own names.
    3. Objective: the summary printed the stop target dict ({'kind': 'gain_pct', 'value': 500.0}) as the objective. It now prints graded_on (outcome.final.graded_on, unset when none was recorded) and the stop target as gain_pct>=500 (all targets of a multi-target run, baseline=<dir> for a baseline target, time_only without a value). GlobalFacts.objective is replaced by graded_on and stop_target.
    4. TTFT: a scriptable workload records ttft_mean_ms = 0, which was printed as a reading. A non-positive TTFT is now treated as unmeasured in the Baseline and Final sections (no line, and no null warning).
    5. Final unit: the Final kv paired the raw throughput with the display unit (10.25 ms on a scriptable run). It now uses framework_registry.throughput_unit, the same unit the Baseline section prints.
    6. Validation warnings: a session with no validation got both "validation predates the final adoptions" and "no validation run completed". The first is now emitted only when a validation exists.
    7. Kernel speedup: an unmeasured attempt's speedup = 0.0 rendered as 0.00x ... [KEEP]. A non-positive speedup now renders as —.
    8. Roofline: three failed roofline runs read as [roofline] skipped: no data recorded this session. The skipped section now states 3 run(s), all failed: trace_analyze_failed×2, profile_failed×1.
    9. LLM prompt: it told the model to call any attribution_method other than "validated" not validated, a value never emitted. It now names stack_ledger as the validated method and lists the real other values.
  • Linked issue(s): Refs Serving throughput is labelled tok/s/GPU but is the aggregate over all TP GPUs #1932 (field audit of the report files). Builds on fix(breakdown): rename throughput_tok_s_per_gpu to output_throughput (schema 6.1) and record per-GPU output on baseline and validation rows #2023 (the output_throughput rename, schema 6.1), which is merged; the base branch is main.
  • Tests: added 29 tests to test_reporters_smoke.py, through render_session_report / build_global_facts / SYSTEM_PROMPT; the ones pinning a changed line fail on the base branch. Removals are asserted with exact lines or lists. Ran test_reporters_smoke.py, test_roofline_renderer_unit.py, test_breakdown_renderers.py, test_breakdown_report_integrity.py, test_reporters_llm_client.py, test_phase_timeline_renderer.py, test_critic_renderer_unit.py, test_coverage_margin_unit.py, test_coverage_margin3_unit.py, test_param_search_renderer_units.py individually; ruff and scripts/code_metrics.py pass. Re-rendered the seven audited real sessions to check each line before and after.
  • Size/complexity triggers crossed: 10 source files, but each change is a few lines in the renderer that prints the field; the tests are 357 lines in one file. scripts/code_metrics_baseline.txt lowers three entries (cognitive complexity and function length of final.py::render, function length of session.py::render) that the extracted helpers shortened.
  • If this simplifies or refactors: n/a.
  • Observable effect: the report lines listed above read as described; the LLM fact pack carries graded_on and stop_target instead of objective.
  • Breaking changes: no persisted field changes. GlobalFacts.objective (an in-process fact pack passed to the LLM) is replaced by graded_on and stop_target.
  • PR addresses single concern: yes, report lines that did not match the recorded field. Known limits left for separate changes: metadata.grading.objective (p90) and graded_on (p50) disagree in the collector; a duplicate warm_replay event id is a recorder issue; old sessions' stack ledger stays mixed-axis (fixed for new sessions by fix(stack): attribute AgentX adoptions on the graded axis #2022).
  • Root cause is upstream: no.

The session report mixed axes and scopes in several places: the headline
paired output tok/s with a p50 gain, elapsed showed one leg, the stop target
was printed as the objective, a zero TTFT or kernel speedup read as a
measurement, the final unit did not match its value, an unvalidated session
got a contradictory validation warning, failed roofline runs read as no data,
and the LLM prompt keyed not-validated on a value never emitted.
@lishuoshuo-amd
lishuoshuo-amd requested a review from a team as a code owner October 20, 2026 02:36
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Code metrics gate: PASSED

Judged: the 12 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.

Dimension Limit New Worse Baseline out of date Baseline entries vs base
Cyclomatic complexity fails > 20 0 0 0 55 +0
Cognitive complexity fails > 30 0 0 0 203 +0
Function length (lines) fails > 80 0 0 0 346 +0
Nested block depth fails > 5 0 0 0 20 +0
Parameter count fails > 5 0 0 0 95 +0
Module length (lines) warns > 800, fails > 1200 0 0 0 49 +0
Duplicated lines per file fails > 0 0 0 0 96 +0
Dead code (vulture) fails > 0 0 0 0 2 +0
Check Findings Effect
Comments 0
English only 0
Production code imports test code 0
Repeated literals 0
A1 Private name of another package 0
A8 Flag argument 0
A9 Tuple return 0
A10 Raw vocabulary comparison 0
A11 Hidden global write 0
A12 Pure forwarder 0
A12 Single-implementation abstraction 0
108 findings in files this change does not touch

Outside this change (informational): 108

Unit Dimension Baseline Now
src/hyperloom/orchestrator/state/shared_state.py resolve_graded_comparison Cognitive complexity absent 34
src/hyperloom_kb/collect/mapping.py _experience Cognitive complexity absent 31
src/hyperloom_kb/schema.py FieldDeclaration.__post_init__ Cognitive complexity absent 32
src/kernelforge/cli/forge_loop_command.py _prepare_measurement_driver Cognitive complexity absent 36
src/kernelforge/cli/forge_loop_command.py _read_kb_warm_start Cognitive complexity absent 40
src/kernelforge/cli/forge_loop_command.py forge_loop Cognitive complexity absent 31
src/kernelforge/loop/runner.py IterationLoop._collect_session_candidate Cognitive complexity absent 44
src/kernelforge/loop/runner.py IterationLoop._rule_on_attempt Cognitive complexity absent 38
src/hyperloom/inference_optimizer/cli/__init__.py _reexport_session_environment Function length (lines) absent 111
src/hyperloom/inference_optimizer/cli/__init__.py _resolve_agent_lineup Function length (lines) absent 89
src/hyperloom/inference_optimizer/cli/__init__.py _resolve_run_topology Function length (lines) absent 92
src/hyperloom/inference_optimizer/cli/__init__.py _resume_session Function length (lines) absent 170
src/hyperloom/inference_optimizer/cli/__init__.py _seed_fresh_session Function length (lines) absent 164
src/hyperloom/inference_optimizer/multi_node/cli.py _restart_multinode Function length (lines) absent 107
src/hyperloom/inference_optimizer/multi_node/scripts/launch_multinode.py _build_parser Function length (lines) absent 84
src/hyperloom/inference_optimizer/multi_node/scripts/launch_multinode.py _spawn_rank_actors Function length (lines) absent 84
src/hyperloom_kb/sync.py GlobalSync.pull Function length (lines) absent 84
src/kernelforge/cli/forge_loop_command.py _RunReporter.build_result Function length (lines) absent 92
src/kernelforge/cli/forge_loop_command.py _prepare_measurement_driver Function length (lines) absent 136
src/kernelforge/cli/forge_loop_command.py _read_kb_warm_start Function length (lines) absent 149
src/kernelforge/loop/runner.py IterationLoop._close_iteration Function length (lines) absent 117
src/kernelforge/loop/runner.py IterationLoop._collect_session_candidate Function length (lines) absent 185
src/kernelforge/loop/runner.py IterationLoop._implementer_history Function length (lines) absent 112
src/kernelforge/loop/runner.py IterationLoop._rule_on_attempt Function length (lines) absent 102
src/kernelforge/loop/runner.py IterationLoop._source_attempt Function length (lines) absent 175

...and 83 more; the job log lists every row.

Verdict computed by the gate scripts in /home/runner/work/_temp/code-metrics-gate, not by this tree's copy.

Thresholds and sources: [tool.hyperloom.code_metrics] in pyproject.toml. Tools: complexipy 8.0.1, jscpd 5.4.1, ruff 0.16.2, vulture 2.16.

A re-anchored baseline no longer matches the ledger baseline the gain is
measured against, so the pair is dropped unless before*(1+gain) equals
after within 0.5%. The graded axis no longer falls back to the configured
axis, which is recorded as p90 while the verdict is taken on p50.
Before and after now come from the last stack event settled validation
row, the one that produced the gain, and only when it is on the graded
axis and reproduces the gain. Otherwise the headline states the gain and
the axis without throughput figures.
@lishuoshuo-amd

Copy link
Copy Markdown
Collaborator Author

PR #2043 -- fix(report): render fields on the axis and scope they were recorded on

What it does: The session report and LLM fact pack printed several fields on the wrong axis or scope: a tok/s headline beside an interactivity gain, one leg's elapsed time as the session's, the stop-target dict as the objective, a zero TTFT/speedup as a reading, and failed roofline runs as "no data". This PR fixes each one in the reporter layer only. On an interactivity-graded session the headline now reads before → after from the settled row of the stack ledger that produced the gain, used only when it is on the same axis and reproduces the gain; otherwise it prints only the gain and the axis. GlobalFacts.objective is replaced by graded_on (the recorded verdict axis, otherwise unset) and stop_target.

Blocking issues: none

Checked: diff vs merge base and the two follow-up commits vs round-1 head f0a803c; cross_section.py (_settled_pair, _graded_axis_headline, _headline, build_global_facts, GlobalFacts), compose.py, _renderers/{final,baseline,session,workload,roofline,_kernels}.py, base.py (events_of, stop_target_text, measured_ms), llm_prompt.py. Confirmed the settled row is the row that produced the gain: the collector (collectors/v6.py _validation_from_timeline, last stack event; final.gain_pct = validated_total_gain_pct) and the recorder (recorder/stack_event.py assemble_stack_ext: validated_total_gain_pct = settled.validated_gain_pct; stack_event_id() fixed to stack:0:stack, so there is one ledger event across resume legs). Tree-wide git grep over src/ and docs/ for GlobalFacts(, as_prompt_dict, global_facts, _graded_on and objective readers. Earlier findings were re-verified at this head and are all fixed: the headline pair, the graded_on fallback, and the stale test numbers (the description now says 29 tests and 357 lines). The rest of the description was re-checked against the diff. SKIPPED: V4 -- #1032 #1154 #1276 #1869 #1907 #2004 compared on their first 100 files only, overlap there unchecked | Ran: test_reporters_smoke.py at head (74 passed) and against the base tree (22 of the 29 new tests fail); rendered 9 real sessions at head: vllm-dsv41flash-tp4 e2e_norm_intvty_p50 103.48 → 149.54 = +44.52% validated gain, ds-v4-pro 63.03 → 79.76 = +26.55%, glm52-v6-yueliu 133.93 → 192.47 = +43.71%, sglang-glm52-tp4ep4 graded on unset, output_throughput sessions keep the tok/s line | Base: c565c19 | Head: d6ff195

LGTM

@lishuoshuo-amd

Copy link
Copy Markdown
Collaborator Author

PR #2043 -- fix(report): render fields on the axis and scope they were recorded on

What it does: The session report and LLM fact pack printed several fields on the wrong axis or scope: a tok/s headline beside an interactivity gain, one leg's elapsed time as the session's, the stop-target dict as the objective, a zero TTFT/speedup as a reading, and failed roofline runs as "no data". This PR fixes each one in the reporter layer only. On an interactivity-graded session the headline now reads before → after from the settled row of the stack ledger that produced the gain, used only when it is on the same axis and reproduces the gain; otherwise it prints only the gain and the axis. GlobalFacts.objective is replaced by graded_on (the recorded verdict axis, otherwise unset) and stop_target.

Blocking issues: none

Checked: the diff against the new base main (958874f) is this PR's own change only: 12 files, and its added/removed lines are byte-identical to the previously reviewed d6ff195 vs c565c19 diff, so the merges of #2023 (6.1) and main added nothing to the PR's change set. The auto-merged compose.py carries main's log.warning in render_session_report's except (#2049) beside this PR's _UNSET/_elapsed_label edits in _deterministic_exec_summary/_render_global_facts_block; the two touch different functions and share no symbol. Inputs the reporter reads were checked for drift between c565c19 and main: collectors/v6.py, recorder/stack_event.py, session_facts.py, framework_registry.py and coerce.py are unchanged; perf_metric.py adds baseline_on_axis and leaves INTVTY_OBJECTIVES unchanged; manifest _objective_summary emits the same shapes; schema 6.1 keeps output_throughput on outcome.baseline/final. Tree-wide git grep over src/ and docs/ for GlobalFacts(, as_prompt_dict, global_facts and objective readers. The description matches the head: "Builds on #2023 ... merged; base is main", 29 tests, 357 test lines, 10 source files, three metric-baseline entries. SKIPPED: V4 -- #1869 and #2055 compared on their first 100 files only, and only the newest 100 of 103 open PRs were compared; overlap there unchecked | Ran: test_reporters_smoke.py at head a92a7f2 (74 passed) and against base main 958874f (22 of the 29 new tests fail); test_breakdown_renderers.py, test_reporters_llm_client.py, test_roofline_renderer_unit.py at head (pass); rendered 9 real sessions at head: vllm-dsv41flash-tp4 e2e_norm_intvty_p50 103.48 → 149.54 = +44.52% validated gain, ds-v4-pro 63.03 → 79.76 = +26.55%, glm52-v6-yueliu 133.93 → 192.47 = +43.71%, sglang-glm52-tp4ep4 graded on unset, output_throughput sessions keep the tok/s line | Base: 958874f | Head: a92a7f2

LGTM

@xiaofei-zheng xiaofei-zheng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What it does

The session report printed several fields on a different axis or scope than the one they were recorded on: the headline paired output tok/s with a gain measured on interactivity, the summary showed one leg's elapsed_minutes as the session time, the workload line printed the raw stop-target dict, a workload that measures no TTFT had its recorded 0 printed as a reading, the Final kv paired raw throughput with the display unit, a session with no validation got both "validation predates the final adoptions" and "no validation run completed", an unmeasured kernel speedup rendered as 0.00x, an all-failed roofline section read as "no data recorded", and the LLM prompt named an attribution_method value that is never emitted. Every fix is in the reporter layer — the recorded fields are correct, only the render-time pairing was not.

Blocking issues: 1

  1. The branch conflicts with main and cannot be merged as it stands. mergeable=CONFLICTING / mergeStateStatus=DIRTY at head a92a7f2, and git merge-tree origin/main a92a7f2 conflicts in scripts/code_metrics_baseline.txt. The conflict is textual and confined to that file: this PR lowers three entries (final.py::render 42→37 and 178→175, session.py::render 93→92) while main has since deleted neighbouring entries in both hunks (kernel_lifecycle.py::render, collectors/v6.py, recorder/framework_event.py, recorder/instrument.py), so the context around each edit no longer exists on main. Please merge main in and re-resolve that file — it is the input to the code-metrics gate, so the resolution decides whether the gate stays green.

No other blocking issue. The nine claimed fixes each do what the description says, and the description matches the diff at head.

Checked

  • cross_section.py _settled_pair / _graded_axis_headline: the before/after pair comes from the same settled row of the same (last) stack event that collectors/v6.py:483-528 reads validated_total_gain_pct and graded_on off, so the pair and the gain cannot be cross-wired. writeback.py:738-765 is the only caller of record_validation and passes graded.reference / graded.candidate / graded.objective with validated_gain_pct = (candidate-reference)/reference*100 of that exact pair, which makes the _PAIR_TOLERANCE check a float round-trip of an identity: it cannot admit a contradicting pair nor reject the true one. Axis coverage is complete — total_throughput is "reported, not graded" (schema.py:147), so the graded set is {interactivity axes, output_throughput}.
  • final.py _validation_gap_warnings: the new validated=val_stack_len is not None gate cannot silence a real stale-validation warning — recorder/stack_event.py:290 writes stack_len unconditionally on every settled row and to_int(0) returns 0, so the gate means "a settled row exists", which is exactly the discrimination the fix needs.
  • base.py measured_ms / _kernels.py _measured_speedup: applied at every render site — baseline.py:108/174 and final.py:259 for TTFT, and the single entry["speedup"] read at _kernels.py:205 for the kernel table (kernel_lifecycle._fmt_speedup(None) → —). baseline.py:147 still warns on an explicit null, so absent and zero stay distinguishable.
  • Final throughput unit: framework_registry.throughput_unit(fw) is the same string the Baseline section prints, because recorder/baseline_event.py:226 records the measurement unit from that same function; the old primary_metric_unit is "ms" on a scriptable framework, which is the mislabel being fixed, and it is still used for the delta line at final.py:168.
  • GlobalFacts.objective removal: the only readers were compose.py:212-218 and compose.py:278-288, both converted; as_prompt_dict is asdict(self), so the LLM payload follows the dataclass. No surviving reader in src/, docs/ or tests.
  • roofline.py _failed_runs_note: actions is one row per dispatch (roofline_event.py:889-929, empty failure dicts normalised to None), so the count and the "all failed" wording cannot overcount; a session with no roofline event keeps the old wording.
  • Description against diff at head: the two trailing commits are base merges, the nine numbered claims are all in the diff, the added test count (29) matches, and the three lowered code_metrics_baseline entries are the ones the body names.

Ran

gh pr view 2043; git merge-tree --write-tree origin/main a92a7f2; git diff 958874f origin/main -- scripts/code_metrics_baseline.txt; tree-wide greps for objective, elapsed_minutes, ttft_mean_ms, speedup, throughput_unit, validated_at_stack_len, GlobalFacts. Local pytest was not run (CI is green at a92a7f2: 12/12 test shards on 3.10 and 3.11, coverage, code-metrics, mypy, ruff, pr-hygiene); test behaviour was checked by reading the producer of each asserted field.

Base: 958874f20e98e5d6a80d0960024f6caf6e5580fb · Head: a92a7f2ac5154b6d4f001daf0fdfcdcde5665f20

@lishuoshuo-amd

Copy link
Copy Markdown
Collaborator Author

Addressed the blocking issue: main is merged in at c65b158a7. The conflict was confined to scripts/code_metrics_baseline.txt; I took main's version and re-applied only the three entries this PR lowers (final.py::render cognitive complexity 42→37 and function length 178→175, session.py::render function length 93→92), taken from the code-metrics report's "Baseline out of date" list. The gate passes and all checks are green at the new head. The rest of the diff is unchanged. Please take another look.

@xiaofei-zheng
xiaofei-zheng dismissed their stale review October 21, 2026 07:54

Resolved at head c65b158: the base conflict in scripts/code_metrics_baseline.txt is gone and the branch now merges cleanly.

@xiaofei-zheng

Copy link
Copy Markdown
Collaborator

Re-reviewed at head c65b158. The one blocking item from my previous review is resolved and my CHANGES_REQUESTED is dismissed.

Blocking issues: none

The base conflict is gone: mergeable=MERGEABLE, and git merge-tree origin/main c65b158 produces a clean tree. The resolution in scripts/code_metrics_baseline.txt is correct — it keeps main's deletions (kernel_lifecycle.py::render, collectors/v6.py, recorder/framework_event.py, recorder/instrument.py) and re-applies only this PR's three lowered entries at the same values: final.py::render cognitive 42→37, final.py::render function-lines 178→175, session.py::render 93→92.

Checked

  • The merge carried nothing else in: git diff <merge-base> <head> -- src/ at the new base e25cb2d is byte-identical to the same diff at the previous head a92a7f2 against base 958874f. Same 12 files, same +/- counts. So the code review below still stands unchanged.
  • main has advanced past this PR's merge base (e25cb2d → ba808c3), and none of those commits touch breakdown/reporters/, collectors/, recorder/, perf_metric.py, framework_registry.py, or the three baseline entries this PR edits — no new textual or semantic conflict.
  • Carried over from the previous pass, re-confirmed as still applying to this identical source diff: _settled_pair/_graded_axis_headline read the same settled row of the same last stack event the collector reads gain_pct/graded_on off, and writeback.py:738-765 (the only caller of record_validation) derives validated_gain_pct from that exact before/after pair, so the tolerance check cannot admit a contradicting pair; the graded axis set is fully covered (total_throughput is reported, not graded); the validated=val_stack_len is not None gate cannot silence a real stale-validation warning (stack_event.py:290 always writes stack_len); measured_ms/_measured_speedup cover every render site; the Final and Baseline throughput units now come from the same producer; no surviving reader of the removed GlobalFacts.objective.

Ran

gh pr view 2043; git merge-tree --write-tree origin/main c65b158; git diff e25cb2d c65b158 against git diff 958874f a92a7f2 (identical over src/); git diff e25cb2d ba808c3 --name-only for overlap with the reviewed modules. CI is green at c65b158: 12/12 test shards on 3.10 and 3.11, coverage, code-metrics, ruff, pylint, mypy, import-linter, CodeQL, bandit, gitleaks, reuse, pr-hygiene, docs. No check is failing or pending.

Base: e25cb2db2a572f3af70103f2b91aa3a0fa683d4c · Head: c65b158a7dedb45bf2d5eebfd689b5ecac344431

LGTM

@xiaofei-zheng xiaofei-zheng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at head c65b158. No blocking issues.

The base conflict from my earlier review is resolved: mergeable=MERGEABLE, the resolution in scripts/code_metrics_baseline.txt keeps main's deletions and re-applies only this PR's three lowered entries, and the src/ diff is byte-identical to the previously reviewed head. CI is green at this head (12/12 test shards on 3.10 and 3.11, coverage, code-metrics, ruff, pylint, mypy, import-linter, CodeQL, bandit, gitleaks, reuse, pr-hygiene, docs).

Details of what was checked are in my comment above.

@xiaofei-zheng
xiaofei-zheng merged commit 6dc1aef into main Oct 21, 2026
40 of 42 checks passed
@xiaofei-zheng
xiaofei-zheng deleted the fix/report-render-field-mismatch branch October 21, 2026 07:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants