feat(loop): GitHub-native gist artifact-hosting fallback for ui_review report#1332
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements a GitHub-native hosting fallback for the ui_review report by publishing the self-contained HTML as a secret GitHub Gist off-Claude (while keeping Claude Artifacts as the on-Claude enhancement), and adds Stage-5 teardown support to prune the created gist.
Changes:
- Core hosting selection now defaults to a portable
github-giststrategy off-Claude (Claude Artifacts unchanged on-Claude). - The report CLI publishes the HTML via
gh gist create, records gist metadata, and fails closed with a stated reason on publish failure. - The teardown core/CLI can consume a Stage-4 report result and (when confirmed) delete the recorded gist via
gh gist delete, with ledger coverage + tests.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
packages/core/src/loop/ui-review-report.mjs |
Switches off-Claude hosting to github-gist and updates review-body messaging for gist-hosted artifacts. |
scripts/loop/ui-review-report.mjs |
Adds publishGist() and wires gist publishing into the report CLI with fail-closed behavior. |
packages/core/src/loop/ui-review-teardown.mjs |
Adds gist-prune step, status enum, and ledger reporting to teardown orchestration. |
scripts/loop/ui-review-teardown.mjs |
Adds --report-result handling, gist extraction, and gh gist delete seam wiring. |
test/loop/ui-review-report.test.mjs |
Adds tests for gist publishing + updated hosting semantics and fail-closed behavior (mocked, no live API). |
test/loop/ui-review-teardown.test.mjs |
Adds teardown tests for gist-pruning outcomes and CLI deletion wiring (mocked, no live API). |
docs/ui-review-recipe-contract.md |
Documents gist fallback mechanics, required gh auth scope, and teardown pruning behavior. |
Off-Claude, the Stage-4 report now publishes the self-contained HTML as a secret GitHub Gist (gh gist create) — a real per-run URL with zero repo pollution — instead of failing closed. decideHosting selects the portable github-gist strategy in the pure core; the thin CLI performs the publish IO and fails closed with a stated reason (never a fabricated link) when gist creation yields no URL. The review body links the gist and states it renders as source (open raw to view/download). Stage-5 teardown prunes the accreting gist: the report result carries the gist id, teardown consumes it via --report-result and deletes it (gh gist delete) under confirmation, recording the outcome in the always-emitted ledger. Recipe doc documents the gh gist scope a consuming repo must provide and the source-vs-rendered tradeoff. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177JAWNT67FY6d48qQJeDwo
Gate review:
|
Gate review:
|
Summary
Implements the GitHub-native artifact-hosting fallback for the ui_review report, so a non-Claude harness (Pi, or any run where Claude Code Artifacts are unavailable) gets a real working link instead of a fail-closed stated reason. The Claude Artifacts path (shipped in #1120) is unchanged and stays the enhancement; the GitHub-native path is the portable default off-Claude.
Mechanism (explicitly chosen + documented)
gh gist create— a secret gist of the self-contained HTML. A real per-run URL with zero repo pollution, no Pages setup, and deletable by Stage-5 teardown. Tradeoff documented honestly: a gist renders HTML as source (not a live page), so the review body links the gist page and appends the raw file URL (… : <url> (raw: <rawUrl>)) so the reader can open the plain file directly. Fail-closed: ifgh gist createthrows / exits non-zero / returns no URL, the publisher emits a stated reason — never a fake link. The "renders as source" caveat is gated on an actually-published gist, so an explicit--hosted-urlself-hosted override falls through to the neutral artifact line rather than being mislabeled.Scope and context
packages/core/src/loop/ui-review-report.mjs:decideHostingreturns the portablegithub-giststrategy off-Claude — pure core, no harness-specific publisher; the core report flow does not depend on any harness's publisher.scripts/loop/ui-review-report.mjs: the thin CLI publishes the HTML viagh gist create(reusing the existingghrunChild seam — no new dependency), links it in the review body, and fails closed with a stated reason if no URL comes back.packages/core/src/loop/ui-review-teardown.mjs+scripts/loop/ui-review-teardown.mjs: Stage-5 teardown consumes the recorded gist id (new--report-resultinput) and prunes the gist (gh gist delete) under confirmation, recording the outcome in the always-emitted ledger (NONE/SKIPPED_UNCONFIRMED/DELETED/DELETE_FAILED).docs/ui-review-recipe-contract.md: documents thegistgh scope a consuming repo must provide and the source-vs-rendered tradeoff.Acceptance criteria
Satisfies #1285's ACs (checked off at merge): a documented GitHub-native fallback yielding a working link or a fail-closed stated reason; the core report flow is publisher-agnostic (Claude Artifacts stays an enhancement, gist is the portable default); the accreting gist is pruned by Stage-5 teardown via the ledger; the recipe doc documents the fallback hosting setup; a test asserts the fallback produces a link or fails closed without hitting the live API;
npm run verifygreen.Non-goals
Changing the Claude Artifacts path (shipped in #1120). Production/non-dev hosting targets.
Validation
node --test test/loop/ui-review-report.test.mjs test/loop/ui-review-teardown.test.mjs— 54 pass viamakeGhMock(no live API): publish success / fail-closed-on-nonzero / fail-closed-on-no-URL; two reportrunCliintegration tests (off-Claude gist happy-path body links the gist + raw URL; non-zero gh exit → unhosted stated-reason body, no fabricated link); the--hosted-urlself-hosted line is neutral; teardown gistNONE/SKIPPED_UNCONFIRMED/DELETED/DELETE_FAILED/ throwing-seam, plus a runCli test assertinggh gist delete <id>through an injected run mock.npm run verify— green (test:assets 191, test:scripts 2706, test:core 2019, test:extension 81, test:docs, test:pack, test:dev-loop 32);generate-claude-assets --checkclean.Closes #1285