Skip to content

Feature the conditional baseline on the executive summary and index#74

Merged
johnjosephhorton merged 1 commit into
mainfrom
exec-summary-conditional-baseline
Jul 9, 2026
Merged

Feature the conditional baseline on the executive summary and index#74
johnjosephhorton merged 1 commit into
mainfrom
exec-summary-conditional-baseline

Conversation

@johnjosephhorton

Copy link
Copy Markdown
Contributor

Part A of the report cleanup (per our discussion — B, the structural de-dup, is still open).

Problem

The executive summary and the bundle index — the two pages a decision-maker reads — compared the twin only against uniform guessing. The decisive comparison (twin vs the deployable XGBoost conditional baseline) lived only on the technical validation page (after #73). So the headline pages told a weaker, different story than the detail page, and "leaned on uniform" exactly as flagged.

Change

build_executive_summary now accepts the baseline rows and computes a twin-vs-conditional-baseline comparison — aggregate baseline metrics plus a paired win rate and p(actual) delta over the (respondent, question) rows both scored. Then:

  • Executive summary → Main Evidence gains a "Conditional baseline (XGBoost)" column beside Twin/Uniform, and a lead sentence: "Versus the deployable bar: the twin beats/ties/trails the XGBoost conditional baseline … +X.X% p(actual), wins on Y% of predictions. This is the comparison that matters; uniform and the empirical oracle are context."
  • Bundle index gains a "Beats conditional baseline / p(actual) vs conditional baseline" row right under the twin's p(actual).
  • The comparison is returned as conditional_comparison for the index to consume.

All gated on a baseline being present — no-baseline reports are byte-for-byte unchanged. The bundle passes the baseline rows folded into scope by #73; the standalone twin-results executive-summary command is left unchanged for now (noted follow-up).

Testing

  • pytest — 256 passed (adds exec-summary tests: conditional column + lead + conditional_comparison present with a correct paired win rate; unchanged when no baseline).
  • ruff clean.

🤖 Generated with Claude Code

The two headline pages a decision-maker actually reads — the executive summary
and the bundle index — compared the twin only against uniform guessing, while
the one comparison that matters (twin vs the deployable XGBoost conditional
baseline) was buried on the technical validation page. So the headline story was
weaker and different from the detail page.

build_executive_summary now takes the baseline rows and computes a twin-vs-
conditional-baseline comparison (aggregate baseline metrics + a paired win rate
and p(actual) delta over the rows both scored). The executive summary's Main
Evidence table gains a "Conditional baseline (XGBoost)" column and a lead
sentence framing it as the deployable bar (uniform/empirical demoted to
context), and the comparison is returned so the bundle index shows a "Beats
conditional baseline / p(actual) vs baseline" row next to the twin's p(actual).

All gated on a baseline being present, so reports without one are byte-for-byte
unchanged. The report bundle passes the baseline rows folded in by #73; the
standalone `twin-results executive-summary` command is unchanged for now.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@johnjosephhorton johnjosephhorton merged commit e1054be into main Jul 9, 2026
1 of 3 checks passed
@johnjosephhorton johnjosephhorton deleted the exec-summary-conditional-baseline branch July 9, 2026 13:35
@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a twin-vs-conditional-baseline (XGBoost) comparison to the executive summary and bundle index, so decision-maker pages now show the deployable bar alongside uniform guessing. The change is gated on a baseline being present and passes the baseline rows through from the bundle build path.

  • executive_summary.py: adds conditional_baseline_comparison to compute paired win rate / mean p(actual) delta, and injects a new column plus a lead sentence into the Main Evidence table when a baseline is present.
  • report_bundle.py: reads conditional_comparison from the executive summary result and renders a "Beats conditional baseline / p(actual) vs conditional baseline" row in the index tile.
  • tests/test_report_conditional_baseline.py: adds two tests covering the executive summary path — conditional column present with a baseline, and unchanged without one.

Confidence Score: 4/5

Safe to merge for the common path; the zero-matched-rows edge case is the only current defect.

The new conditional_baseline_comparison function returns a non-None dict whenever baseline rows are present, even when no (respondent_id, heldout_question) pairs overlap between the twin and baseline datasets. Both render sites check if conditional: on that dict, so a zero-match situation renders a misleading ties verdict with +0.0% / 0% of predictions on exactly the pages this PR is intended to improve.

zwill/executive_summary.py — specifically the return value of conditional_baseline_comparison when matched_rows == 0, and the corresponding if conditional: guard in render_html; zwill/report_bundle.py carries the same guard pattern.

Important Files Changed

Filename Overview
zwill/executive_summary.py Adds conditional_baseline_comparison and threads its output through render_html and build_executive_summary; zero-match case when baseline keys don't align produces misleading rendered output
zwill/report_bundle.py Reads conditional_comparison from executive summary payload and injects a conditional row into the bundle index tile; inherits the zero-match rendering issue from executive_summary.py
tests/test_report_conditional_baseline.py Adds two new tests covering the executive summary path — conditional column present with a baseline, and unchanged without one; both use fully matched datasets

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[build_report_bundle] -->|baseline_rows| B[build_executive_summary]
    B --> C[conditional_baseline_comparison]
    C -->|baseline_rows empty| D[return None]
    C -->|baseline_rows present| E{matched_rows > 0?}
    E -->|yes| F[return dict with metrics + win rate + delta]
    E -->|no — keys don't align| G[return dict with matched_rows=0\nmean_p_delta=0.0\nshare_twin_better=0.0]
    F --> H[render_html\nconditional column + lead sentence]
    G -->|if conditional: is truthy| H
    H --> I[Executive Summary HTML]
    F --> J[render_report_bundle_index\nconditional_row]
    G -->|if conditional: is truthy| J
    J --> K[Bundle Index HTML]
    D -->|no conditional block rendered| I
    D -->|no conditional_row rendered| K
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[build_report_bundle] -->|baseline_rows| B[build_executive_summary]
    B --> C[conditional_baseline_comparison]
    C -->|baseline_rows empty| D[return None]
    C -->|baseline_rows present| E{matched_rows > 0?}
    E -->|yes| F[return dict with metrics + win rate + delta]
    E -->|no — keys don't align| G[return dict with matched_rows=0\nmean_p_delta=0.0\nshare_twin_better=0.0]
    F --> H[render_html\nconditional column + lead sentence]
    G -->|if conditional: is truthy| H
    H --> I[Executive Summary HTML]
    F --> J[render_report_bundle_index\nconditional_row]
    G -->|if conditional: is truthy| J
    J --> K[Bundle Index HTML]
    D -->|no conditional block rendered| I
    D -->|no conditional_row rendered| K
Loading

Reviews (1): Last reviewed commit: "Feature the conditional baseline on the ..." | Re-trigger Greptile

Comment on lines +351 to +356
return {
"baseline_metrics": baseline_metrics,
"matched_rows": len(deltas),
"mean_p_delta": (sum(deltas) / len(deltas)) if deltas else 0.0,
"share_twin_better": (better / len(deltas)) if deltas else 0.0,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Zero-match baseline silently renders misleading stats

When baseline_rows is non-empty but no (respondent_id, heldout_question) key pairs match the twin rows (e.g. ID mismatch in the data pipeline), conditional_baseline_comparison returns a valid dict with matched_rows=0, mean_p_delta=0.0, share_twin_better=0.0. Both render_html and render_report_bundle_index then check if conditional: — which is truthy for any non-empty dict — and render the block on the decision-maker pages with a "ties" verdict, "+0.0% probability", and "wins on 0% of matched predictions", even though zero predictions were actually compared. The fix is to either return None when matched_rows == 0, or gate both render sites with conditional.get("matched_rows", 0) > 0.

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.

1 participant