upstart_reporter — HTML broadsheet report
Purpose
Renders one self-contained Plotly HTML “broadsheet” (newspaper front page) from REAL pipeline data: overlaid time-series figures, the pipeline’s reduction tables, and an agent-written narrative. The HTML successor to star_reporter (LaTeX, kept for other run-specs); replaced the
checkinPDF flow (built 2026-06-28).
Role in the system
- Sits beside star_reporter at the presentation end of the pipeline:
run → log → analyze → report. Driven by the log-driver skill (on-demand log selection, agent-written summary, second-agent verify). - Calls the three orchestrator stages directly —
orch.analyze(orch.run_or_load_logs(orch.build_run_contexts()))— instead oforch.run(), so the matplotlib-PNG (plotter) and LaTeX stages never execute and no side-effect artifacts land. - Never recomputes a metric. Every trace is the pipeline’s own series via
orch.data_summary.signal_analysis(item.view).metric_time_series(key, use_norm=True)(data_analyzer); tables areanalysis.table_framesverbatim; “Measured highlights” lines areanalysis.conclusions. - Imports from the homes (
analysis.orchestrator,utils.infra). The formervalidation/signal_measurement.pyfacade is retired (2026-07-01, file gone); this module never routed through it. - Python emits no prose: the narrative slots are filled by a summary markdown the driving agent writes (checkin discipline).
Inputs / Outputs
- In: a run-spec stem (the
comparison.overlayblock defines which metrics plot), optionally an agent-written summary md (#→ headline,>→ dek,##→ section), optionally an explicitoutpath. - Out: one
report.html(Plotly via CDN, loaded once on the hero figure). Default path is derived from the orchestrator:orch.report_dir / orch.run_stamp / "report.html"→logs/logs_<date>/<safe_stem>/reports/run_PMHHMM/report.html.
Key methods / functions
build(stem, summary, summary_md, out)— the entry point: pack + write —analysis/upstart_reporter.py:433Broadsheet.html/Broadsheet.write— assemble the page from the design-tokenTemplate; write it —:85/:103_pack— drive load+analyze and pack aBroadsheet; raises if the overlay block defines no metrics —:376_metrics/_variant— overlay keys (+secondary_keys) from the run-spec; per-variant series through the pipeline —:350/:366decimate_for_display— envelope-preserving min/max display decimation (figures only) —:129summary_from_md— parse the agent summary md intoSummary—:396demo— self-checking smoke on real logs (checkin_Jun25_26): page well-formed, summary parse, decimation budget + spike survival —:446
CLI: python -m analysis.upstart_reporter <stem> [summary.md]; no args runs demo().
Footguns
The data seam is
Variant(label, t, series)— fill it only from the pipelineThe reporter was thrown out twice for the same mistake: fabricating that shape with a synthetic
_synth(), then dodging theWrite(analysis/**/*.py)deny with a scratch file + staged-mv. The sanctioned door wasEdit(noEditdeny exists onanalysis/), and the only legitimate filler is_variantreading the orchestrator’s own overlay extraction. (notes/upstart_reporter_footguns.md)
Figures are decimated; tables and metrics are not
decimate_for_displaykeeps each bucket’s min and max sample plus the endpoints (budgetMAX_PLOT_POINTS = 2000per trace,:126) — plain striding aliases away exactly the spikes a check-in exists to show. Added 2026-07-04: full-resolution traces made the Jun21–23 six-run page 34 MiB against the Cloudflare Pages 25 MiB file cap. Series at or under2 * max_pointspass through untouched. Smoke:_heredocs/test_decimation.py+ thedemoasserts.
"Measured highlights" is silent for comparison reports — by design
analysis.conclusionsis empty for an A/B run: the orchestrator’s conclusion lines only emit for frames with a singlevaluecolumn (single-run tables), and comparison frames are pivoted with variants as columns. The reduction tables carry the numbers;_highlightsrenders nothing when there are no lines. (notes/upstart_reporter_footguns.md)
Ask the orchestrator where the report goes
The output path is derived (
orch.report_dir / orch.run_stamp), never hardcoded — that keeps the HTML co-located with the run’s figures/npz by construction. The stem is lower-cased bysafe_filenameon the way in.
Legend-click trace toggling is free
Plotly hides a trace on legend click by default — the “click a line to remove it” spec needs zero custom JS. Horizontal legend configured in
_layout(:113).
Pseudocode
build(stem, summary_md):
orch = Orchestrator(stem)
analysis = orch.analyze(orch.run_or_load_logs(orch.build_run_contexts())) # load+analyze ONLY
metrics = overlay keys from the run-spec comparison block
variants = per item: signal_analysis(item.view).metric_time_series(key, use_norm=True)
tables = [(title, frame) for frame in analysis.table_frames] # pipeline reductions
summary = summary_from_md(summary_md) # '# ' headline, '> ' dek, '## ' sections
each figure: decimate_for_display(t, y) -> one go.Scatter per variant, shared axis
write orch.report_dir / orch.run_stamp / "report.html"
Related
orchestrator · data_analyzer · star_reporter · plotter · logger · terminology