Files
tanzhenxinandClaude Opus 5 be1dc925dc fix(chrome-extension): make the store release fail where it can be read (#12392)
* fix(chrome-extension): make the store release fail where it can be read

Follow-ups from the review of #12329, none of which changes a passing run.

Packaging refuses a missing source before it clears the staging directory:
redirecting the build with EXTENSION_OUT_DIR used to delete the previous
build on the way to an ENOENT, and now names the path instead. The key guard
tests for the field rather than its value, so a hand-edited `"key": ""`
cannot reach the store. Store calls retry like the other release workflows,
because a transient 5xx on publish would otherwise fail a release whose
upload had already landed, and a refusal now names the endpoint that issued
it — an expired refresh token fails at Google's OAuth service, not at the
dashboard the old message pointed to.

The managed-Chrome harness registers every trusted extension id, so a
store-keyed build loaded into it reaches the Host instead of being refused
as an unknown origin.

Refs #12240

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JHhPsocuEvo8UAQkrjtiAD

* fix(chrome-extension): refuse a source that is not a build, and bound the retries

Review of the previous commit found the packaging guard checked the wrong
thing: an empty directory, a file, or staging a build onto itself all passed
it and then deleted the staged copy on the way to an unnamed ENOENT — the
failure the guard was added to prevent. It now requires a manifest and
refuses aliasing, with a case per shape.

The retry budget was also wrong way round: one attempt could hang for the
whole retry window, so a slow store got no retries at all. Publishing drops
--retry-all-errors, because a reset can arrive after the store accepted the
submission and asking it to publish an item already in review would fail a
release that worked. A transport failure now names the endpoint instead of
leaving only curl's stderr.

Refs #12240

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JHhPsocuEvo8UAQkrjtiAD

* fix(browser-use): accept any trusted extension id in the managed preflight

The preflight still asserted the side-loading id, so a store-keyed build
stopped at the identity check — the one place left that could not verify the
store path before publication. It now accepts any id the bridge trusts, which
matches the registration the harness writes, and passes against a store-keyed
build reporting hdhmmjclhibojdddmancfgbkleahfaph.

Also corrects the retry comment: --retry 5 allows six attempts, and
--retry-max-time bounds when a new attempt may start rather than the total.

Refs #12240

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JHhPsocuEvo8UAQkrjtiAD

* fix(chrome-extension): keep the access token out of the release log

The transport-failure branch added two commits ago printed the response body
verbatim. On the token endpoint that body is the access token, and a transfer
that dies after it has landed reaches that branch before anything has masked
it: reproduced against a local server that answers with a token and then cuts
the connection, six attempts, token in the log every time.

Each call now says whether its body may be printed. The store's may, because
its refusals explain themselves there. The token endpoint's may not: a
refusal is reported as its error and description alone, so an expired refresh
token still names itself without the rest of the body following it.

Refs #12240

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JHhPsocuEvo8UAQkrjtiAD

* fix(chrome-extension): stop the token surviving in the shared response file

curl leaves --output untouched when a transfer dies before the first byte,
and every call in the step writes the same file, so an upload that failed at
the transport layer printed whatever the token call had left there. Each call
now clears it first; reproduced both ways.

The withheld-body arm also emitted nothing when the body was not Google's
error object, leaving the endpoint named and then silence; it now says the
body is withheld and why. Publishing is no longer retried at all: every
status plain --retry treats as transient is one where the store may already
have taken the submission, so the previous comment claimed a property the
flags never delivered. Its transport failure now says the package is
uploaded and a second dispatch will submit it.

Packaging reads EXTENSION_OUT_DIR like sync-extension, the esbuild config,
dev-watch and artifact-scan already do, which closes the asymmetry the guard
was compensating for rather than only naming it.

Refs #12240

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JHhPsocuEvo8UAQkrjtiAD

* fix(chrome-extension): tie the staging path to the source in play

Derived from the source actually passed rather than the environment, so a
caller giving an explicit source no longer gets a staging directory pointing
somewhere else. Packaging reads EXTENSION_ZIP alongside EXTENSION_OUT_DIR,
the pair artifact-scan already reads, and a spawned run pins both defaults —
the previous commit changed them and nothing executed them.

The withheld-body message no longer asserts a token was present, which is
unknowable for an HTML 5xx or an empty body; it reports the shape and the
byte count it did not print.

Refs #12240

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JHhPsocuEvo8UAQkrjtiAD

* revert(chrome-extension): keep packaging on its fixed default paths

Reverts the packaging half of the last two commits, which made
package-extension.js follow EXTENSION_OUT_DIR and EXTENSION_ZIP. That change
came from a review suggestion to close the asymmetry the guard compensates
for, and it should have been declined: the guard already refuses a source
that is not a build before anything destructive happens, the release
workflow never relied on packaging reading either variable, and each fix to
the change produced the next finding. It also silently moved
`npm run package`'s output for anyone with those variables set.

The guard, its cases, and every workflow fix from those commits stay: the
per-call response file, the unretried publish, and the withheld-body
message that no longer claims a token was present.

Refs #12240

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JHhPsocuEvo8UAQkrjtiAD

---------

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-09-23 03:13:13 +00:00
..