Reports¶
A decision about a harness change gets made where the change is reviewed: usually a pull request. flightdeck's report is a markdown comment there, not a dashboard.
hangar's canary lock-bump PRs are the typical place. Code: src/flightdeck/report/markdown.py.
flightdeck report experiment <id> [options]
flightdeck report post report.md --to owner/repo#n [--full-url URL | --gist]
report experiment reads only the data plane (--data, default ~/.flightdeck/data), so it renders with hangar
powered off. Its options:
--baseline control,--candidate candidate: the two arms. The verdict is always the candidate against the baseline.--decision decision.yaml: the pre-registered rule. Without it the report says No verdict.--out report.md: write the report as markdown and print the verdict. Without it the report prints as a terminal readout.--post owner/repo#n: post it as one comment, updated in place (GITHUB_TOKEN).--full-url URLor--gistsay where the full text lives when a comment is clipped.--details: per-task rows and trial links; keep it off for public repositories.--hangar URL: the trial links.
Anatomy¶
1. The verdict is the headline¶
In plain words from the pre-registered rule (below), with the numbers beside it: "candidate is non-inferior to control
on resolved: −2.0 pp (95% CI −4.0 to +1.0, margin −5.0)", or "No decision: underpowered". Without a valid
decision.yaml the headline is No verdict, and the numbers follow anyway.
2. One line on the experiment¶
The experiment's tail number first, FDK-EXP-0531, with the real ID, which the CLI takes, beside it. Then the arms, as hangar's
experiment described them; how many tasks and trials; the seed; and which pins differ between
the arms. Then the rule as pre-registered: metric, direction, margin, minimum tasks.
3. Outcome, Process and Cost¶
Three tables from flightdeck stats compare (Statistics): Outcome, Process (tool calls, wall time)
and Cost. Each has only the columns that apply, and every number carries its unit: 10 of 32 (31%), 3.0 calls,
174.9 s, $0.42.
4. Behavior specs¶
Pass rate per spec per variant, from spec_results (written by flightdeck check run,
Behavior specs) over the trials the Outcome table counts. not_applicable and unknown results
are left out, and a spec with none of either has no row.
5. Exclusions and drift¶
What every table left out and why, once; then the drifted trials.
6. Pins, collapsed¶
The rows that differ between the arms: harness and version, model, environment, config_digest, harness_digest,
environment_digest, the grader command, hangar's version. The ones that are the same follow in one line, with the
number of base SHAs. A missing pin says not recorded; a digest is never cut.
7. With --details¶
The per-task table (discordant tasks first, base SHA, resolved per arm) inside a collapsed <details>, and drift
messages per trial. With --hangar, links to each trial's trace on hangar REST.
Pre-registration¶
decision.yaml, written before the experiment runs (flat key: value lines):
metric: resolved # resolved | cost_usd | tool_calls | wall_s
direction: higher # higher or lower is better
margin: -0.05 # non-inferiority margin, in the metric's units (a proportion for resolved)
min_tasks: 10 # fewer paired tasks: no decision
A worked example is flightdeck/decision.yaml in the
team-harness repo: resolved, higher, margin
−0.05, at least 10 tasks. It sits next to flightdeck/suite.yaml and its specs; add a task before its --dry-run
works (flightdeck task new flightdeck/tasks/<id>).
Choose the margin with the power table in mind. With 50 tasks × 3 trials and no real change, a −0.05 margin gives a verdict only about one time in five.
When the rule counts¶
The report prints a verdict only when two things hold. The file is committed as it is, and the last commit that
changed it is older than the first finished trial. The git commit time counts, not the file's modification time, which any copy or
checkout resets. An uncommitted or locally edited decision.yaml gives No verdict.
The rule¶
Fewer than min_tasks paired tasks, or an underpowered interval, is No decision: underpowered. An interval wholly
above zero is better; wholly above the margin is non-inferior; wholly below it is worse. Anything else is
No decision.
Posting¶
--post owner/repo#n (on report experiment) or report post --to creates one comment on that PR or issue, or
updates it in place. The comment starts with a hidden marker, <!-- flightdeck:report:<experiment> -->.
The token comes from GITHUB_TOKEN: a fine-grained token with issues:write / pull_requests:write on the target
repo, kept in the operator's environment. Posting is flightdeck's only GitHub write.
Before posting, the text is re-scanned with the data plane's secret patterns; a match refuses the post.
Comments are clipped to GitHub's 65,536-character limit, with a link to the full report. --full-url takes an
https:// URL, such as the report committed to the private data repository. --gist makes a secret gist of it with
the same token, which then needs the gist scope.
Without either, the comment says the full report is not linked; it never points at a local path.
Reports on public repositories must not quote private team code. The default report has counts and intervals only;
per-task rows and trial links need --details.
See also¶
- Statistics: where the tables' numbers come from, and the rules they follow.
- Concepts: verdict, margin, pp, CI.
- Getting started: a report rendered from the fixture in six commands.
- Judges: a
decision.yamlthat names a rubric needs it calibrated.

