docs/design/2026-08-25-pr-evidence-oss-hosting.md
Automated PR verification currently publishes screenshots by committing them to
pr-assets/* branches in the main repository. A normal clone fetches objects
reachable from every advertised branch, so long-lived PNG-only branches add
hundreds of megabytes to clone traffic and repository storage even though the
images are unrelated to the product source tree.
/verify reports.Both trusted publisher jobs configure the existing ossutil client and invoke
scripts/upload-aliyun-oss-assets.js. The uploader keeps its existing
three-attempt retry policy and publishes objects with public-read ACL.
Web Shell previews use this immutable prefix:
pr-assets/web-shell-visuals/<pr>/<head-sha>/<run-id>/<run-attempt>/
An object key is never written twice. The head SHA binds a URL to the code it depicts; the run id keeps a re-run of that same head from writing back over the objects an already-posted comment references. The run id alone would not be enough: it is stable across re-run attempts (only the attempt number increments), so the run attempt segment keeps a maintainer re-run of the same workflow run off the previous attempt's keys too. That matters because GitHub serves comment images through a caching image proxy: reusing a key would leave reviewers looking at the previous run's screenshots at a URL whose bytes had changed. The Git-backed design got this property free from the per-run commit SHA in the raw URL.
Sandboxed /verify reports use this immutable prefix:
pr-assets/verify/pr<pr>-<run-id>-<run-attempt>/
The workflow continues to accept at most eight PNGs of at most 2 MiB each, checks PNG magic bytes, sanitizes names, and degrades to a text-only report if hosting fails.
The publisher jobs run in the base-repository context and never expose OSS credentials to the jobs that execute PR code. They consume only bounded, validated image artifacts.
The automated review workflow has a third, separate image path through
qwen review publish-assets. That CLI feature remains available for an
explicitly designated assets repository, but the workflow now rejects a
designation that points back to the repository under review. An unset or
self-targeting destination degrades to prose and downloadable run artifacts;
an external image-host repository remains supported.
Both publishers resolve the destination as
ALIYUN_OSS_PR_ASSETS_BUCKET → ALIYUN_OSS_BUCKET → qwen-code-assets, with
the public base URL derived from whichever bucket wins (or set explicitly via
ALIYUN_OSS_PR_ASSETS_PUBLIC_BASE_URL). Unset, this is exactly today's shared
bucket. The knob exists because PR evidence is untrusted, PR-derived content
while the shared bucket also serves release, desktop, and live-host downloads;
setting one variable separates them without a workflow change.
It does not narrow the credential: these jobs hold the same OSS key the release
sync uses. Scoping a dedicated RAM user to the pr-assets/ prefix is ops work
outside this PR, and is the reason the destination is a variable rather than a
constant.
Existing PR comments keep their Git-backed URLs. The PR-close cleanup workflow
continues deleting historical pr-assets/* refs, but no new refs are produced
by the migrated workflows.
OSS object retention is intentionally external to the workflow. The
pr-assets/ prefix can use a bucket lifecycle rule without changing repository
history or clone behavior.
/verify image-hosting harness against a fake OSS
uploader, including valid images, rejected images, duplicate names, the exact
size boundary, first publication, and upload failure.