Dashboard › publish › Distillation
7b48f901-5a5d-479c-92a3-4dd8a8135134["lore_tm_v1_2DblwPoGPgcFh04mVj434bEe5vS6mRGJzgtdicsCj8s","lore_tm_v1_CY6AobgpQmY8YXIY2njSk3ib0nC39ddwyZ4oJRkxNlY","lore_tm_v1_mj6wO9ARSAejBKieBOgjAgL_azcQEXpMqa2s-AXk6fI","lore_tm_v1_KN78SPr5VD8zOb0e8kmSIGHIUp4QfYT8cJRWz1-XuRw","lore_tm_v1_DoX3wFOXNCquow9oEIN6USa9UVrUHAXZGHAAFBRYHj0","lore_tm_v1_BdntJRp8Jg4c03pyILVvjkhmbh9SObXs94hMwBtT9tA","lore_tm_v1_1wBdj0kUr6O7eG-fsUMG2iAhyDS8gqQzVYruOEP1-Y4","lore_tm_v1_Yra9nNYkerV25YXyqSs5PXm811K9yHCYKzjerKxQG0E","lore_tm_v1_2EUDufIx_Rvu9CcOgJPy_DRWCT7v3tL_HL6K3_BPGgE","lore_tm_v1_4zxC6Fkui1WrNIwhEx7vZ4pNY6pgi4v5sYcAeN9kti0","lore_tm_v1_4ECE0UTWA_5lZnqoKxKHKqBIMQIbp5xHbeqYXEFjmMM","lore_tm_v1_bFhKaP8YX1EjNJgrttNePsTHQ2MABFZBF5wg9ZtXfXQ","lore_tm_v1_CCckau4OF629O7zCWOtdFz2Jm_78uINeJXTLK5Ghj6U","lore_tm_v1_it3_lyiiq7neNuET1qgVRgEex7eqBHrGZvNnHpXWRYc","lore_tm_v1_vLwy_7Cky-EiRhaOpZ-Vk_aJg8_yQsTFVWBsxljiBlg","lore_tm_v1_F-Ts5dlsZzMU0f7yoi603G_HWuoUP-bCIkt_Dx1FahM"]
Date: Sep 10, 2026
src/publish/__tests__/record-ci-ready-attestation.js:11-52 verifies recordCiReadyAttestation() binds the current approval to the app that will add ci-ready: for title publish: getsentry/sentry-javascript@10.0.0, accepted event ID 100 by contractor, and authenticated login sentry-internal-app[bot], the parsed attestation contains acceptedActor: "contractor", acceptedEventId: "100", ciReadyActor: "sentry-internal-app[bot]", the title, and a request digest.src/publish/__tests__/record-ci-ready-attestation.js:54-120 expects The approval changed before CI could be marked ready when the live issue title changes from publish: getsentry/sentry-javascript@10.0.0 to publish: getsentry/sentry-python@10.0.0, or when the poller observed accepted event ID 100 but the live accepted event is ID 200.src/publish/__tests__/record-ci-ready-attestation.js:123-156 rejects a valid live approval belonging to a different poller snapshot: expected body old revision with only accepted, versus current body new revision with accepted and dry-run; expected error is The approval changed before CI could be marked ready.src/publish/__tests__/validate-approval-attestation.js:354-412 verifies CI-ready proof is tied to the current accepted event and label actor: accepted event ID 100 by contractor, ci-ready event ID 200 by sentry-internal-app[bot], and matching approval/CI-ready attestations authored by github-actions[bot] make validateApprovalAttestation({ requireCiReadyAttestation: true }) return true.src/publish/__tests__/validate-approval-attestation.js:414-472 verifies a manually added ci-ready label is rejected: when event ID 200 was labeled by contractor but the attestation names sentry-internal-app[bot], validation returns false.yarn check:generated && vitest run src/modules/__tests__/ci-poller-workflow.js src/modules/__tests__/update-issue.js src/modules/__tests__/process-end-state.js src/modules/__tests__/ci-poller-input.js src/modules/__tests__/publish-workflow.js src/publish/__tests__/ci-poller-workflow.js src/publish/__tests__/publish-workflow.js src/publish/__tests__/record-ci-ready-attestation.js src/publish/__tests__/validate-approval-attestation.js --printConsoleTrace passed exactly 9 test files and 57 tests; Vitest v4.1.0 duration was 1.92s and total Yarn duration was 3.37s.src/modules/release-revision.js:3-55 defines lowercase 40-character SHA validation with /^[0-9a-f]{40}$/; getReleaseRevisionDetails() requires exactly one canonical View check runs link, parses ReleaseRevision, verifies details.repo === repo, and throws repository-specific errors otherwise; updateReleaseRevision() replaces only the parsed revision span and throws Release revision must be a lowercase 40-character SHA. for invalid replacements.src/modules/publish-location.js:1-49 makes resolvePublishLocation() reject invalid publish paths and invalid Craft workspace lists; path . remains { path: "." }, a matching ./workspace becomes { path: ".", workspace }, and a nonmatching safe path remains unchanged. Safe workspace segments match /^[A-Za-z0-9_.-]+$/, cannot equal ., .., or __proto__, and cannot start with -.src/publish/discover-location.js:9-49 pins CRAFT_IMAGE to getsentry/craft@sha256:9a4a5d5efa44a00c2215078ead39800d4aaa5a97908b94f45a64d7d506d6e14b; if .craft.yml exists, getWorkspaceNames() runs Docker with --rm, mounts the repository at /github/workspace/__repo__, sets that workdir, and executes workspace list; malformed JSON or a non-array result throws Craft workspace discovery returned an invalid workspace list.src/publish/discover-location.js:52-85 combines getWorkspaceNames() with resolvePublishLocation(); main() parses PUBLISH_ARGS || "{}", requires input.path or throws Publish input must define a path., defaults PUBLISH_REPOSITORY_DIRECTORY to __repo__, and writes JSON to the result action output.yarn check:generated && vitest run --printConsoleTrace passed exactly 26 test files and 171 tests; Vitest v4.1.0 duration was 4.24s and total Yarn duration was 5.58s.eslint src .github --ignore-pattern '!.github' completed successfully in 1.22s.git diff --check produced no output.src/publish/current-accepted-event.js:4-18 defines getCurrentAcceptedEvent(), which fetches issue events, passes them to currentAcceptedEvent(), returns the current event, or throws The issue has no current accepted label event.src/publish/current-accepted-event.js:20-48 requires APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, and APPROVAL_ISSUE_REPOSITORY; it fetches all events pages through getAllPages(), writes the accepted event as JSON to stdout, and catches main-process errors by logging them and setting process.exitCode = 1.src/publish/__tests__/current-accepted-event.js:5-37 verifies getCurrentAcceptedEvent() returns the latest accepted-label eventβevent ID 200 by contractor rather than earlier ID 100βand rejects an empty event list with The issue has no current accepted label event.MM) changes in .github/workflows/auto-approve.yml, .github/workflows/ci-poller.yml, .github/workflows/publish.yml, and src/modules/__tests__/details-from-context.js..github/workflows/cocoapods-keepalive.yml, .github/workflows/test.yml, AGENTS.md, README.md, docs/key_rotation_vault_changes.md, docs/rfc.md, src/libs/__tests__/github.js, src/libs/github.js, src/modules/__tests__/ci-poller-input.js, src/modules/__tests__/ci-poller-workflow.js, src/modules/__tests__/publish-location.js, src/modules/__tests__/publish-workflow.js, src/modules/__tests__/release-revision.js, src/modules/__tests__/update-issue.js, src/modules/ci-poller-input.js, src/modules/details-from-context.js, src/modules/post-workflow-details.js, src/modules/process-end-state.js, src/modules/publish-location.js, src/modules/release-revision.js, src/publish/__tests__/discover-location.js, src/publish/__tests__/resolve-release-revision.js, src/publish/discover-location.js, src/publish/inputs.js, src/publish/post-result.js, src/publish/post-workflow-details.js, src/publish/resolve-ci-poller-input.js, and src/publish/update-issue.js..github/workflows/ci-poller-dispatch.yml, .lore.md, src/modules/__tests__/approval-attestation.js, src/modules/__tests__/approval-authorizer.js, src/modules/approval-attestation.js, src/modules/approval-authorizer.js, src/publish/__tests__/authorize-approval.js, src/publish/__tests__/auto-approval-workflow.js, src/publish/__tests__/ci-poller-workflow.js, src/publish/__tests__/current-accepted-event.js, src/publish/__tests__/publish-workflow.js, src/publish/__tests__/record-auto-approval-attestation.js, src/publish/__tests__/record-ci-ready-attestation.js, src/publish/__tests__/request-digest-from-event.js, src/publish/__tests__/validate-approval-attestation.js, src/publish/__tests__/workflow-action-pinning.js, src/publish/authorize-approval.js, src/publish/current-accepted-event.js, src/publish/record-auto-approval-attestation.js, src/publish/record-ci-ready-attestation.js, src/publish/request-digest-from-event.js, and src/publish/validate-approval-attestation.js.MUST-FIX: poller rejection branches do not stop processing. Per-issue logic runs in a subshell at .github/workflows/ci-poller.yml:117-118, while rejection paths use continue at lines 150, 170, 300, 323, 335, 347, and 367; Bash rejects those continue statements outside an active parent-loop context and executes subsequent code. Concrete failure sequence: final approval validation fails; cleanup removes accepted and ci-pending; ineffective continue allows lines 370-378 to add ci-ready and claim publishing started. Recommended fix: replace each continue with exit 0, or remove the subshell and use a genuine loop-level continue; add executable tests for every rejection branch because src/modules/__tests__/ci-poller-workflow.js:103-113,213-228 only takes the branch-moved exit.MUST-FIX: successful publication can remain open and authorized when reporting fails. src/modules/process-end-state.js:24-35 posts its comment before closing the issue; the success handler at .github/workflows/publish.yml:449-454 runs after failure cleanup at lines 427-447 has been skipped. Concrete failure sequence: Craft publishes successfully; getWorkflowRun or createComment fails; the issue remains open with accepted and ci-ready; the run reports failure; rerunning the failed job can publish again. Recommended fix: close the issue and clear transition labels before non-critical comments/Sentry reporting; add a final always() reconciliation step after every notifier; test comment and workflow-run lookup failures because src/modules/__tests__/process-end-state.js:149-173 covers only success.MUST-FIX: failure cleanup depends on dependencies whose installation may itself have failed and has no retry. Approval validation occurs before Node setup/dependency installation at .github/workflows/publish.yml:203-245; cleanup at lines 427-433 invokes src/publish/update-issue.js, which requires @actions/github at lines 1-3. Concrete failure sequence: approval succeeds; setup-node or yarn install fails before @actions/github is available; cleanup and post-result.js cannot load; the open issue retains accepted and ci-ready. Additionally, src/modules/update-issue.js:69-81 couples one-shot revocation with optional target restoration using Promise.all, so one API failure has no reconciliation path. Recommended fix: revoke labels first through a dependency-free gh step, retry transient failures, separate target restoration from authorization cleanup, and add an independent final reconciler.MUST-FIX: a failed waiting-for-CI comment or variable update can strand an approved request. .github/workflows/publish.yml:127-135 first creates accepted + ci-pending, lines 137-148 post a blocking comment, lines 152-165 enable the poller, and lines 167-174 dispatch it. Concrete failure sequence: with CI_POLLER_HAS_PENDING=false, label mutation succeeds but the comment or variable update fails; later steps are skipped; no dispatch occurs; scheduled polling remains gated off by .github/workflows/ci-poller.yml:24-31. Recommended fix: make comments/variable synchronization best-effort, dispatch immediately after the label transition under a condition unaffected by unrelated failures, and remove accepted/ci-pending if neither activation path succeeds.MUST-FIX: malformed canonical issue input remains approved and is retried forever. Resolver failure exits without issue mutation at .github/workflows/ci-poller.yml:128-132; the remaining-state check at lines 413-425 keeps finding the issue, lines 440-450 keep the variable enabled, and lines 460-478 self-dispatch until cron takes over. src/modules/__tests__/ci-poller-workflow.js:175-181 explicitly expects no edit after resolver failure. Concrete failure sequence: a pending issue loses or corrupts its revision/body; every poll skips it; accepted + ci-pending remain indefinitely. Recommended fix: fail closed by removing both labels and posting a diagnostic for deterministic parse/validation failures, while reserving retries for classified transient API failures.MUST-FIX: a stale ci-ready race can suppress the required fresh publish event. Waiting-for-CI removes old ci-ready at .github/workflows/publish.yml:119-135, but another label event can add it while ci-pending remains; that event cannot publish because lines 186-192 reject ci-pending. The poller later removes only ci-pending and adds already-present ci-ready at .github/workflows/ci-poller.yml:370-373, producing no fresh labeled event and leaving accepted + ci-ready stranded. Recommended fix: after CI passes, the poller must perform and verify a distinct ci-ready removal/re-add cycle with approval revalidation between them, and must never omit the final re-add.CONCERN: missing PUBLISH_ARGS suppresses failure and cancellation reporting. src/publish/update-issue.js:8-18 tolerates absent/malformed input, but src/publish/post-result.js:7 unconditionally calls JSON.parse(process.env.PUBLISH_ARGS). Concrete failure sequence: input parsing fails or cancellation occurs before the output exists; label cleanup may succeed; the terminal handler throws before posting any status comment. Recommended fix: parse defensively, post a generic issue/run comment when repository or version details are unavailable, and derive one terminal status from the Craft step outcome instead of separate aggregate cancelled() and failure() predicates at .github/workflows/publish.yml:435-447, since both may become true when cancellation cleanup fails.PASS: normal CI terminal mapping and retry reset are correct. .github/workflows/ci-poller.yml:251-256 counts cancelled, timed-out, stale, and otherwise unsuccessful check runs; lines 380-392 transition them to ci-failed while removing accepted and ci-pending; failed commit statuses receive equivalent cleanup at lines 394-405. .github/workflows/publish.yml:119-135 makes a normal retry remove both ci-failed and stale ci-ready before adding ci-pending.57/57, full suite 171/171, yarn lint, and git diff --check all passing; the worktree status matched the initial snapshot after read-only inspection. Final disposition: DO-NOT-MERGE.