- approval-authorizer GitHub roles: Chose live target-repository collaborator permissions over private `security-as-code` because GitHub reflects effective access without exposing internal RBAC. Humans authorize only with `write`, `maintain`, or `admin`; `triage` never authorizes. `getsantry[bot]` and `sentry-release-bot[bot]` may authorize only through the explicit allowlisted, attested automation path, while `sentry-internal-app[bot]` and every other `[bot]` identity are rejected by normal collaborator authorization. Bot repository access alone does not prove approved automation provenance.
- production environment secret boundary: Chose the protected `production` environment over repository- or organization-scoped publishing secrets because its `main`-only policy and disabled admin bypass gate credential access. Exceptions: `SENTRY_INTERNAL_APP_PRIVATE_KEY` and `SENTRY_RELEASE_BOT_PRIVATE_KEY` are organization-wide and this repository cannot isolate them; workflow guards cannot narrow that exposure. Treat it as a separate Security-led risk and block only if the current change worsens it.
- PUBLISH_ATTESTATION_SECRET retry-shell isolation: Use `PUBLISH_ATTESTATION_SECRET` only for HMAC approval, auto-approval, and ci-ready attestations. Trap: exposing it across a retry shell looks convenient because retries share environment state, but it leaks the credential to `sleep`, GitHub API commands, checkout, token actions, and target-revision Craft code. Fix: inject it only into inline Node attestation commands, with no step- or shell-wide secret environment.
- publish-and-validate canonical path fence: Keep `src/publish/publish-and-validate.js` as the final in-container fence: validate the live request, exact revision, unique release branch, branch head, and canonical publish-path containment immediately before Craft. Lexical `path.resolve`/`path.relative` checks look sufficient but accept symlinks that escape `__repo__`; canonicalize the relevant paths and reject escapes before spawning Craft.
- publish.yml Craft state isolation: Chose `$GITHUB_WORKSPACE/.craft-state` with explicit `XDG_STATE_HOME` over state inside the target checkout because untrusted repository contents could pre-populate privileged Craft state. Derive collision-resistant state filenames from canonical publish identity, and set `CRAFT_PUBLISH_STATE_GITHUB_REPO` from the issue checkout repository—not an optional workspace release-repository override—so restoration and publication use the same identity.
- publish.yml packages write scope: Chose job-scoped `packages: write` for the publishing job over workflow-wide permissions because `waiting-for-ci` only observes release state and must not inherit registry write capability. Workflow-wide permissions look simpler and preserve shared defaults, but they enlarge the privilege boundary for a non-publishing job. Keep permission separation covered by workflow regression tests.
- approval attestation request digest: Trap: validating an attestation against current live state looks sufficient, but the workflow may consume older event payload inputs after the issue changes. Fix: SHA-256 hash exact `{title, body, dryRun}` event state, require `EXPECTED_REQUEST_DIGEST` at every executable and exported validation boundary, and compare it with live state before minting or consuming proofs. Exclude transient workflow labels; any title, body, or `dry-run` change requires renewed approval.
- approval-attestation unsafe event IDs: Trap: converting GitHub issue-event IDs to JavaScript numbers before string comparison looks harmless because API payloads serialize IDs as strings and numeric comparison is convenient, but IDs above Number.MAX_SAFE_INTEGER can round and make stale/current-event checks compare the wrong event. Fix: preserve IDs as decimal strings, validate their canonical decimal form, and fail closed on unsafe numeric inputs; do not use BigInt because this repository’s ESLint environment rejects it.
- approval-authorizer legacy titles: Trap: retaining `PUBLISH_TITLE_REGEX` beside the generated parser looks useful for authorization compatibility, but creates parser drift between approval, CI, and publication. Fix: use generated `parsePublishIssueTitle()` plus shared validators everywhere; `parsePublishTitle()` remains non-throwing and returns `null` for authorization failures. Edit `src/modules/publish-issue-title.peggy` and regenerate—never hand-edit or merge `publish-issue-title.js`.
- approval-authorizer unused autoApprovedRepositories: Trap: retaining `autoApprovedRepositories` in `authorizeApproval()` looks harmless after direct bot authorization is removed, because it was previously part of the automated-approval path. It becomes an unused parameter and fails this repository’s ESLint `no-unused-vars` check. Fix: remove the argument when the Internal App attestation path no longer needs it; do not suppress the lint rule.
- authorization fixture open-state: Trap: mutation or self-approval fixtures without `state: "open"` look adequate because the intended invalid condition is present, but entry points reject the issue at the earlier open-state guard. Fix: establish every prior precondition—including open state—so removing the self-approval, title-mutation, or accepted-event guard makes the regression fail.
- authorize-approval auto-approve-repos path: Trap: reading `auto-approve-repos.txt` with a process-relative path looks correct from the repository root, but the publish job executes checked-out code under `.__publish__`, so the policy file may be missing and automated authorization fails or behaves inconsistently. Fix: resolve the allowlist from the trusted checkout/module location, and test authorization from the actual publish working directory rather than only from the repository root.
- authorize-approval.js AUTO_APPROVERS direct-label bypass: Trap: allowing `getsantry[bot]` or `sentry-release-bot[bot]` through generic `AUTO_APPROVERS` authorization looks safe because both are approved automated requesters, but either could directly add `accepted` to the other’s request and bypass the Internal App’s request-bound proof. Fix: `authorizeApproval()` must fail closed for direct labels by both bots; only `sentry-internal-app[bot]` may represent automated approval, backed by the matching `github-actions[bot]` attestation. Cover both direct-bot paths with regressions.
- auto-approve accepted-label stranding: Trap: adding `accepted` before recording the automated attestation looks necessary because proof should bind to the live label event, but label success followed by command or output failure can strand authorization before normal cleanup or poller activation. Fix: establish an independent recovery marker/path before the label mutation, and make post-label attestation, output, poller, and dispatch failures revoke `accepted` without requiring `approved=true`; scheduled reconciliation remains the final recovery path.
- auto-approve sparse checkout: Trap: sparse-checking out only `auto-approve-repos.txt` looks sufficient because it is the workflow’s policy input, but `auto-approve.yml` also runs `src/publish/record-auto-approval-attestation.js`; the absent script fails with `MODULE_NOT_FOUND`. Fix: include the entry point and its local dependencies in sparse checkout, and test checkout contents—not only textual step ordering.
- auto-approve.yml accepted-event binding: Automated approval must bind the attestation to the exact current `accepted` label event, not merely the Internal App actor, title, and request digest. A newer acceptance by the same bot can otherwise reuse an older proof. Record and validate `acceptedActor` and `acceptedEventId`, require the allowlisted repository and trusted `github-actions[bot]` proof, and revoke `accepted` on malformed input or any snapshot change.
- ci-poller accepted-only pending scan: Trap: relying on `CI_POLLER_HAS_PENDING` or scanning only issues with a complete `ci-pending` plus `accepted` combination looks efficient, but failed cleanup and stale variables can strand `accepted`, `ci-ready`, or `ci-pending`. Fix: scheduled poller runs must always scan open non-PR issues carrying any authorization label (`accepted`, `ci-pending`, or `ci-ready`), reconcile stale state, and keep recovery enabled even when authorization-listing or cleanup steps fail.
- ci-poller branch-head reread: Trap: checking the release branch head once looks sufficient because CI was evaluated against that SHA, but the branch can move before promotion. Fix: read the authoritative branch head again immediately before adding `ci-ready`; if it differs from the checked revision, rewrite the issue with the new revision, revoke approval/pending state, and stop. Never promote the SHA that was checked after the branch moves.
- ci-poller cross-command ordering: Trap: comparing separate `gh` and `node` logs looks sufficient because each records the expected calls, but their indices cannot prove cross-command order. Fix: append both stubs to one event log and assert stale `ci-ready` removal occurs before final approval validation, which occurs before adding fresh `ci-ready`.
- ci-poller deauthorization recovery: Trap: leaving `Mark ci-pending` unguarded or swallowing final deauthorization failures looks fail-closed because the current job stops, but it can strand `accepted` without `ci-pending`, while the poller scans only recoverable label combinations. Fix: restore or retain recoverable state when pending-marking fails, and if `ci-ready` promotion plus pending restoration fails, retry or surface independent removal of `accepted`; recovery must not depend on a later labeled event.
- ci-poller immutable default-branch checkout: Trap: validating the default branch and then checking out its mutable name looks safe because both initially resolve to the same revision, but the branch can move before checkout and load changed privileged workflow code. Fix: checkout the immutable validated run SHA or trusted handoff commit, never `github.event.repository.default_branch`; add a regression proving the branch-move race cannot reach secret-bearing poller logic.
- ci-poller requires complete CI evidence: Trap: treating one successful commit status as complete CI looks reasonable because GitHub exposes a green combined status, but it can represent an arbitrary context while required checks are absent or check runs are incomplete. Fix: bind readiness to the authoritative required checks or branch-protection result, require complete expected evidence for the exact revision and release branch, and reject missing, partial, ambiguous, or zero-check results; retain a regression for one successful status with no check runs.
- ci-poller subshell continue: Trap: `continue` in `.github/workflows/ci-poller.yml` rejection branches looks right because issue processing resembles a loop, but each issue runs inside a piped-loop subshell where that `continue` is invalid and execution can still add `ci-ready`. Fix: use `exit 0` to terminate the rejected issue’s subshell; mutation tests must prove replacing it with `continue` advances stale state.
- ci-poller-dispatch repository secrets: Trap: `permissions: {}` on `.github/workflows/ci-poller-dispatch.yml` looks sufficient because it removes `GITHUB_TOKEN`, but repository secrets remain injectable into caller-selected workflow YAML. Fix: the relay must never use repository secrets; use only `production` environment secrets after migrating values there, with the environment restricted to `main`. This preserves required `workflow_dispatch` recovery without letting an arbitrary ref expose privileged credentials.
- ci-poller.yml attestation output sourcing: Trap: `source "$attestation_output"` looks convenient because GitHub output files use `key=value` lines, but it executes the attestation payload as shell code. Fix: read the known `ci_ready_attestation` value as literal data and validate its expected format; the proof is untrusted transport data until the validator checks its trusted comment author, title, event, and actor bindings.
- ci-ready attestation provenance: Trap: snapshotting the `accepted` event after initial validation looks equivalent because validation checks the live label, but a rename or re-approval can occur in that gap and become the event CI binds. Fix: snapshot the current accepted actor/event before validation, fail closed and clean up labels if the event read fails, and pass that exact binding through both validations and CI-ready attestation recording. Immediately before adding `ci-ready`, verify the same title and event again. A renamed or re-approved issue must never reach `ci-ready`; the proof and live label actor must also match.
- cocoapods-keepalive immutable checkout: Trap: pinning `cocoapods` and locking Bundler dependencies looks reproducible, but `.github/workflows/cocoapods-keepalive.yml` can still check out mutable `main` before using `COCOAPODS_TRUNK_TOKEN`; a branch move can inject unreviewed code. Fix: pin checkout to a reviewed immutable commit, retain `Gemfile.lock`, run `bundle exec pod trunk me`, and keep the job behind the protected `production` environment.
- cocoapods-keepalive.yml yml/plain-scalar: Trap: quoting the workflow `name` in `.github/workflows/cocoapods-keepalive.yml` looks harmless because YAML accepts either scalar style, but this repository's ESLint YAML rule requires a plain scalar and fails `yarn lint`. Fix: use `name: CocoaPods Token Keep-Alive`; run `eslint src .github --ignore-pattern '!.github'` after workflow edits because YAML style violations may be outside the changed release workflow.
- current-ci-ready-event null bypass: Trap: returning null when no current ci-ready event exists looks tolerant because the poller can treat it as an empty previous-event ID, but an inconsistent API read then skips stale-event comparison and weakens fresh-event binding. Fix: when fresh-event validation requires a prior event snapshot, distinguish 'no event' from an unreadable or inconsistent event response and fail closed; add a regression covering the null path through record-ci-ready-attestation.
- currentLabeledEvent latest transition: Trap: filtering to valid `labeled` events before choosing the newest event looks correct because callers want the current label application, but it ignores later `unlabeled` transitions and malformed relevant IDs; a singleton malformed ID also bypasses comparator validation. Fix: validate every relevant ID, select the newest `labeled` or `unlabeled` transition, and return null unless it is a valid `labeled` event with an actor.
- detailsFromContext missing issue guard: Trap: dereferencing `parsePublishTitle(title).groups` looks concise because workflow issue contexts normally include a valid publish title, but manual or unexpected contexts yield an opaque TypeError. Fix: `detailsFromContext()` must explicitly reject absent issue context and malformed titles before accessing groups, using actionable errors. Chose explicit validation over implicit dereference because callers need fail-closed, diagnosable rejection of invalid publish requests.
- pnpm-lock.yaml Prettier exclusion: Trap: running Prettier over every file looks consistent with Sentry Publish’s formatting checks, but it flags pnpm’s generated lockfile. Fix: exclude `pnpm-lock.yaml` in `.prettierignore` rather than rewriting generated dependency data; continue formatting source and checking the lockfile with a frozen pnpm install.
- processEndState close failure telemetry: Isolate successful issue closure from terminal reporting and telemetry. Closing the issue first looks orderly, but a transient `issues.update` failure can otherwise abort workflow lookup, result comments, and Sentry reporting; treat closure as retryable or nonfatal so the terminal outcome remains observable.
- processEndState telemetry after comment failure: Trap: awaiting issue-comment publication before telemetry looks orderly because the comment is part of terminal reporting, but a GitHub comment or workflow lookup failure can prevent Sentry telemetry entirely. Fix: isolate optional comment/reporting failures from `withScope`, `captureMessage`, session completion, and `Sentry.close()` so failure and cancellation telemetry still runs; test comment rejection explicitly.
- publish Consume approval TOCTOU: Trap: validating approval immediately before `Consume approval` looks safe because the fence checks live labels, title, events, digest, and CI proof, but consuming labels creates a mutable window before Craft starts. Fix: after every label mutation and immediately before Craft, revalidate the live request, accepted-event provenance, CI-ready proof, title, digest, branch revision, and open state; abort and revoke on any change. Earlier validation cannot authorize publication of a changed snapshot.
- publish Inform start gate: Trap: leaving `.github/workflows/publish.yml`’s `Inform start` step unguarded looks reasonable because publication status should be announced, but transient workflow-run lookup or comment failures then block checkout and Craft. Fix: mark this informational step `continue-on-error: true`; authorization fences and publication remain mandatory, while comments and telemetry are best-effort.
- publish reconciliation mutation anchoring: Trap: globally removing a repeated reconciliation command looks like a valid mutation because both `.github/workflows/publish.yml` blocks contain nearly identical label cleanup, but it may mutate the earlier best-effort block instead of the final authoritative block. Fix: anchor mutations to the named `Verify publish issue reconciliation` section and inspect the diff before testing so the regression proves the intended final-cleanup invariant.
- publish terminal reporting: Trap: parsing `PUBLISH_ARGS` at module load or using separate `always()` steps for cancelled, failure, and success looks straightforward, but missing inputs can suppress reporting and cancellation predicates can overlap. Fix: parse inside `main()` with a warning and fallback, then use one `always()` reporter that derives exactly one status from the Craft outcome. `update-issue.js` uses undefined to skip target restoration; `post-result.js` uses `{}` so reporting continues.
- publish.yml Craft latest image: Trap: `docker://getsentry/craft:latest` looks convenient, and rewriting the pinned image as `ghcr.io/getsentry/craft@…` looks like equivalent registry hardening, but `latest` is mutable and the GHCR image is unavailable. Fix: use the verified Docker Hub reference `docker://getsentry/craft@sha256:9a4a5d5efa44a00c2215078ead39800d4aaa5a97908b94f45a64d7d506d6e14b`; update it only through explicit review.
- publish.yml credential cleanup failure: Trap: deleting `.publish-credentials` without verifying removal looks sufficient because Craft has already run, but later `always()` steps can close the issue and report success while credentials remain. Fix: perform independently verifiable cleanup, make cleanup failure fail the publication result, and gate issue closure/success reporting on both Craft success and confirmed credential removal. Keep a regression that simulates deletion failure.
- publish.yml nested pnpm checkout: Trap: configuring pnpm only at the workspace root looks sufficient, but the publish job checks out Sentry Publish under `.__publish__`; default pnpm setup and cache paths can select the wrong manifest or lockfile. Fix: set `package_json_file` to `.__publish__/package.json`, point the cache at `.__publish__/pnpm-lock.yaml`, and install with `pnpm --dir .__publish__ install --frozen-lockfile`. The separate `waiting-for-ci` job also needs its own install before running repository Node modules because jobs do not share dependencies.
- publish.yml release-token scope: Trap: passing a broad release-bot installation token as GITHUB_TOKEN and GITHUB_API_TOKEN to Craft looks convenient because target lifecycle code may need GitHub access, but it can grant organization-wide write access to approved target-repository code. Fix: mint the Release Bot token for the exact target repository, keep GitHub API credentials separate from the GitHub Container Registry credential, and never expose an organization-wide token to Craft. The publish workflow’s target-repository token is still privileged and must remain behind final live-state validation.
- publish.yml title snapshot TOCTOU: Trap: checking the mutable issue title and current `accepted` event at each stage looks safe because each check validates live state, but it permits a re-approval during a CI poll to replace the approval that CI actually evaluated. Fix: bind authorization to exact title and accepted-event ID, capture that event before CI work, and pass it as `expectedAcceptedEvent` through final validation and CI-ready-proof recording. A title change, label removal, or newer `accepted` event must remove approval and never reach `ci-ready` or publication.
- publish.yml waiting-for-ci dependencies: Trap: invoking `src/publish/inputs.js` in `publish.yml`’s `waiting-for-ci` job looks valid because the publish job installs dependencies later, but GitHub Actions jobs have isolated runners. On a clean runner, `@actions/core` and `@actions/github` are unavailable and approval fails before publication. Fix: run a frozen pnpm install in every job that executes repository Node modules; an install in another job cannot supply them.
- release branch check-suite ambiguity: Trap: deriving the release branch from the first or unique-looking check-suite `head_branch` seems authoritative because the suite is tied to the approved SHA, but multiple suites can associate different branches with one revision and the request does not store branch identity. Fix: bind the requested branch explicitly to the release snapshot and require every poller and publication fence to validate that exact branch; otherwise reject rather than selecting metadata-derived state.
- runPoller GitHub expression substitution: Trap: executing the extracted poller shell fixture unchanged looks faithful because it mirrors the workflow text, but Bash interprets `${{ secrets.PUBLISH_ATTESTATION_SECRET }}` as invalid substitution outside GitHub Actions. Fix: `runPoller` must replace GitHub expression placeholders with test values before execution; otherwise scoped-secret workflow changes produce misleading failures unrelated to poller behavior.
- updateIssue independent cleanup failures: Trap: awaiting target restoration and `accepted` removal with `Promise.all()` looks efficient, and treating a missing label as harmless seems sufficient, but one rejection can hide the other operation’s result and non-404 label failures can strand authorization. Fix: settle target restoration and label removal independently; treat only duplicate-removal 404 as already clean, rethrow target failures, and surface non-404 label failures while preserving cleanup retryability.
- Verify publish issue reconciliation: Trap: best-effort cleanup and a single final label-removal attempt look sufficient, but GitHub API failures can leave accepted, ci-pending, or ci-ready attached with no future labeled event to trigger recovery. Fix: keep an always-running authoritative reconciliation step independent of optional reporting, retry it exactly three times, close the issue only after successful publication, and fail the job if final reconciliation still cannot remove labels or close the issue. Preserve the issue for retry after unsuccessful publication.
- workflow-action-pinning full commit SHAs: Trap: GitHub Action version tags such as `actions/checkout@v6` look maintained and reproducible, but tags are mutable and let secret-bearing workflows execute changed third-party code without review. Fix: pin every local GitHub Action reference to its complete reviewed 40-character commit SHA, including `checkout`, `setup-node`, `cache`, and `create-github-app-token`; retain static contracts in `workflow-action-pinning.js`. Chose immutable SHAs over tags because privileged workflows expose Internal App and release tokens.
- Always audit publish authorization end to end: When working on the publish or approval system, trace authorization across the Node.js code, GitHub workflows, issue labels/events, attestations, repository allowlists, and CI gates. Verify exact actor identities, title and digest matching, accepted/ci-ready sequencing, permission roles, protected production-environment secrets, immutable action references, and failure outputs. Treat automated approval provenance and final publication fencing as security-critical, and inspect both implementation and tests/workflows before making changes.
- Always audit release automation for security and workflow correctness: When examining release infrastructure, systematically trace the end-to-end workflow: document release inputs and approval rules, inspect GitHub Actions and token/secret usage, verify protected-environment and permission boundaries, check action pinning, and validate all user-controlled repository, path, and version inputs. Confirm security-sensitive behavior with focused tests, including authorization, self-approval prevention, attestation integrity, malformed input rejection, and failure handling. Record exact files, lines, identities, scopes, and API interactions so the implementation and its security assumptions are unambiguous.
- Always enable workflow_dispatch for manual recovery: Ensure CI polling retains a reliable manual recovery path triggered through `workflow_dispatch`. Do not remove or condition this capability on pending-work variables, cron state, or other automation. When the security design uses a protected relay, keep `workflow_dispatch` on that relay and route privileged execution only through the trusted path; when direct dispatch is explicitly required, retain it on `ci-poller.yml`. Treat this recovery mechanism as an enforced workflow invariant and cover it with tests.
- Always enforce explicit, immutable, independently verified release authorization: Treat every release as a security-sensitive request: require approval from a second person, keep attestation secrets separate from publishing credentials, and never reuse proof across requests or approval events. Bind authorization to the exact issue title, body, labels, requester, repository/workspace identity, approved revision, and current event IDs. Preserve exact user-supplied workspace paths and identities rather than normalizing them. Validate each state transition against live GitHub data, reject ambiguity or changes, and fail closed whenever credentials, approvals, revisions, or repository metadata cannot be verified.
- Always enforce fail-closed security and trusted recovery paths: The user consistently prioritizes security over merging: they require privileged workflows to run trusted, immutable code; protect against mutable refs, branch movement, stale approvals, and TOCTOU races; revoke authorization when state changes; and fail closed when validation, cleanup, or API operations are uncertain. They expect independent exact-tree security reviews to block merges until every MUST-FIX finding is addressed, with regression tests added before fixes and focused/full validation performed afterward. Preserve these trust boundaries, add fail-first coverage for discovered races, and do not declare readiness or merge while review findings remain unresolved.
- Always enforce provenance-bound approval gates: Bind release authorization, approval, CI evidence, and readiness proofs to the exact live request, trusted actors, label-event provenance, authoritative release branch, and current immutable revision. Automated approval requires request-bound github-actions[bot] proof before sentry-internal-app[bot] adds accepted. Revalidate these fences before and after CI, when recording proof, and immediately before ci-ready or publication. Remove stale ci-ready before final validation, then re-add it to emit a fresh event. Never promote a changed, renamed, closed, re-approved, or branch-moved request. On failure, revoke authorization and readiness while preserving recovery. Scheduled runs must scan every open non-PR issue with a release-authorization label, including stranded accepted, ci-pending, and ci-ready issues.
- Always enforce security boundaries with fail-first tests and independent review: Treat workflow, credential, approval, provenance, and race-condition findings as security-critical merge blockers. First add an executable regression that fails for the intended defect, then implement a fail-closed fix and run focused tests, the full suite, generated-file, lint, formatting, YAML, and whitespace checks. Obtain a fresh independent read-only review of the corrected exact tree, including untracked files; repeat for any new blocker. Do not merge, open a PR, or declare readiness while a MUST-FIX finding remains or the verdict is pending or DO-NOT-MERGE. Preserve precise file-level review evidence and verify the reviewed tree was not modified.
- Always enforce security invariants with regression tests: Treat release automation as a security boundary. Encode every stated invariant—identity, authorization, immutable revisions, environment isolation, attestation freshness, TOCTOU protection, cleanup, recovery, and exact workflow ordering—in focused source and workflow tests. Prefer fail-closed behavior, explicit validation, pinned dependencies, protected manual-recovery paths, and exact error/condition checks. Preserve independent recovery mechanisms and verify credential handling and mutation ordering. When reviewing or changing code, provide concrete file-level evidence and test the negative cases, especially malformed input, API failures, stale or forged labels/events, branch movement, and partial workflow failures.
- Always enforce trusted-code execution and strict security boundaries: The user consistently prioritizes supply-chain and release safety by requiring trusted, pinned workflow code; protected-environment and authorization fences; immutable request/revision checks; and rejection of malformed, ambiguous, unsafe, or changed inputs. They prefer exact, non-normalizing behavior where paths, workspace names, versions, repositories, event data, and workflow refs are validated against explicit allowlists or formats. When strengthening these boundaries, add focused regression tests that assert exact outputs, errors, operation ordering, cleanup, and refusal to proceed, then run the relevant test suites.
- Always fail closed on stale or untrusted publication state: Before approving, promoting, or publishing anything, bind the operation to an immutable request snapshot, current issue labels/events, authenticated actors, and the authoritative release-branch revision. Revalidate these conditions at every security boundary, especially immediately before CI-ready and Craft publication. Reject title, body, digest, label, approval-event, CI evidence, branch, credential, or workflow-source changes; remove authorization state and require reapproval when validation fails. Use trusted default-branch code, protected environments, immutable action/container references, least-privilege tokens, and independent recovery paths. Add adversarial regression tests that verify exact workflow ordering, fail-closed behavior, stale-state cleanup, and prevention of bypasses.
- Always fail closed when release requests or approvals change: Treat the canonical parsed publish request and its approval as immutable, event-bound security state. Use Publish’s shared title parser everywhere so CI and publishing resolve exactly the same target. Bind approval and CI-ready attestations to the live title, body, dry-run state, label actor, and label event. Revalidate after potentially mutable operations and immediately before adding `ci-ready` or publishing. If the issue is renamed, edited, re-approved, relabeled, or otherwise differs from the approved request, revoke `accepted` as appropriate and stop; never advance it to `ci-ready`. Automated approvals must use the trusted Internal App attestation path rather than direct bot labels. For manually dispatched workflows, check out trusted default-branch code before creating or exposing privileged credentials.
- Always harden and test workflows against untrusted input and failure paths: When modifying GitHub Actions or publication authorization logic, treat event fields, issue titles, labels, actors, and repository data as attacker-controlled. Bind approvals and attestations to immutable event/request snapshots, use strict parsing and authorization, fail closed on mismatches or API errors, and revoke labels or pending state on every failure path. Pin actions and avoid exposing secrets. Add regression tests for bypasses, stale or reused attestations, title mutation, actor mismatches, cleanup failures, and workflow ordering, then run targeted Vitest suites to validate both normal and adversarial behavior.
- Always preserve trusted-code and approval-integrity invariants: When modifying or merging publish workflows, prioritize security invariants over textual upstream behavior. Run executable workflow code only from the trusted default branch, pin actions and publishing images immutably, and validate repository, version, and path before privileged API calls. Bind approval to the exact issue title, body, labels, and accepted-label event; revalidate after long-running CI and immediately before applying `ci-ready` or publishing. If the issue, revision, or approval changes, never regenerate approval automatically or advance in the same cycle—rewrite if needed, remove `accepted` and `ci-pending`, and require fresh approval. Preserve manual recovery through a trusted `workflow_dispatch` path and ensure malformed input cannot block security cleanup such as removing `accepted`.
- Always require fail-closed lifecycle handling and regression tests before merge: The user consistently scrutinizes approval, CI, publishing, and cleanup workflows for races, partial failures, stale labels, API errors, and stranded states. They expect every rejection or failure path to terminate safely, revoke authorization when necessary, preserve recovery paths, and revalidate live state immediately before transitions or publication. They treat reviewer findings and explicit user assertions as merge-blocking until fixed, and require focused executable regressions for each discovered defect, often including mutation checks and full validation (tests, lint, generated files, and diff checks). Keep the tree or merge gate closed when evidence is incomplete or behavior is not demonstrably fail-closed.
- Always require fail-closed, request-bound release authorization: When changing publish approval flows, preserve strict separation between human and automated authorization. Human approvers must have live GitHub repository permission at write, maintain, or admin level and must not self-approve. Automated bot requests must never authorize through collaborator permissions or by trusting the Internal App label actor alone; require the exact parsed repository/release path in `auto-approve-repos.txt` and a trusted attestation bound to the live request. Revalidate the issue’s open state, title, requester, current accepted-label actor/event, and relevant attestations before producing authorization or publishing. Treat malformed, missing, stale, mismatched, or changed state as unauthorized and fail closed.
- Always resume background agents after server restarts: When a server restart interrupts ongoing work, ask the assistant to continue and nudge the background agents to resume using their existing context. Expect reviews to continue against the unchanged or frozen worktree, without duplicating work or overlapping files and topics. Keep the merge gate closed until the resumed agents provide substantive results, file-level evidence, or an explicit blocker.
- Always review release workflows as adversarial security boundaries: When assessing GitHub Actions or publishing automation, assume issue data, workflow inputs, GitHub contexts, repository content, refs, actors, labels, and mutable state may be attacker-controlled. Trace authorization and provenance end to end, including second-person approval, live request binding, role checks, token scope, secret exposure, trusted-code execution, immutable action references, event ordering, and cleanup on every failure or cancellation path. Require fail-closed behavior: malformed, missing, stale, changed, or unverified state must never authorize, publish, or reach `ci-ready`. Pay special attention to dumping the full `github` context because it may include sensitive values such as `github.token`. Validate these boundaries with adversarial regression tests and report precise, severity-ordered evidence.
- Always run comprehensive validation before declaring changes complete: Before declaring publishing or GitHub Actions changes complete, run focused regressions followed by the full test suite, generated-file and relevant dependency checks, lint, formatting and whitespace checks, and workflow/YAML validation. Verify trusted-code execution, protected secrets, trigger safety, immutable action and image pins, approval attestations, cleanup, and event semantics. Report exact commands, counts, and failures, distinguishing current-change failures from pre-existing issues; passing unit tests alone is insufficient. For security-sensitive changes, obtain an independent read-only review of the exact final tree and discard review results made stale by subsequent changes.
- Always security-review GitHub Actions against documented trigger semantics: Verify GitHub Actions changes against official trigger semantics and the checked-in workflow files. Treat contexts, refs, events, artifacts, caches, and inputs as attacker-controlled; check default-branch requirements, reruns, workflow-chain behavior, conclusion handling, secret and token boundaries, and privileged downstream triggers. Use stable workflow filenames where identifiers are accepted, pin third-party actions immutably, validate inputs before minting credentials, and run the relevant tests and lint checks.
- Always test approval authorization as fail-closed: When changing release approval logic, preserve comprehensive tests for authorization boundaries. Verify automated approver identities cannot directly approve or fall back to collaborator permissions, malformed publish titles return unauthorized with a null repository, and only target-repository roles `write`, `maintain`, or `admin` authorize humans. Cover qualified and unqualified repository titles, exact repository resolution, allowlist mismatches, and ensure rejected bot or malformed-title paths do not call GitHub permission APIs. Also verify permission lookup failures propagate rather than silently authorizing or degrading.
- Always validate untrusted inputs and add regression tests for security edge cases: When changing behavior, encode the required invariants in focused tests before or alongside the implementation. Cover success and terminal states, exact error messages, operation ordering, failure propagation or recovery, telemetry, security-sensitive workflow conditions, and side effects that must not occur after invalid input. Run the focused tests, use failures to expose missing error handling or incorrect sequencing, then update the implementation and rerun verification.
- Always verify approval attestations against current live state: When reviewing or modifying publish approval flows, ensure every attestation is request-bound and fail-closed. Verify the trusted comment author, exact issue title, digest of body and dry-run state, and the latest relevant label event’s actor and arbitrary-length event ID. Automated approvals must identify the expected auto-approver, and CI-ready proofs must bind both the accepted event and CI-ready actor. Treat missing, malformed, stale, wrongly typed, or mismatched attestations as invalid. Preserve tests showing that title/body/label changes, re-approval, newer label events, manual label application, or untrusted authors invalidate previous proofs.
- Always verify GitHub App behavior against exact API permissions and failure paths: When working with GitHub App tokens or protected workflows, verify behavior using authoritative GitHub API documentation, workflow definitions, and focused tests rather than relying on names or textual matches. Check whether the specific token type supports each endpoint, identify required permissions and token scope, preserve exact repository/workspace identities, and account for token creation and post-step revocation. Trace every success and failure path—including authorization, attestation, retries, and cleanup—and confirm actor, repository, role, and event semantics exactly match GitHub’s behavior. Prefer least-privilege permissions and explicitly document unsupported operations or approval requirements.
- Always verify security-sensitive behavior with exact tests and evidence: The user consistently prioritizes rigorous, security-focused validation of GitHub Actions and publication workflows. They expect immutable action and image references, trusted-code checkout before credentials, protected workflow boundaries, strict input validation, safe failure and recovery behavior, and prevention of privilege escalation or untrusted-code execution. They favor precise automated tests covering both successful and failure paths, including command ordering, state restoration, malformed inputs, and exact error messages. When reviewing changes, provide non-empty findings with exact file-and-line evidence, command results, and tree-integrity confirmation; do not edit files unless directed, and follow any required verdict format exactly.
- Always verify technical details precisely before acting: When investigating a coding or infrastructure question, consult authoritative documentation and inspect the relevant repository files or tool output directly. Capture exact supported options, defaults, constraints, event behavior, paths, counts, error messages, and security implications rather than relying on assumptions. When implementation code is involved, validate the findings with targeted tests and linting, preserving precise observed results.
- Always write tests alongside implementation: Behavioral pattern detected across 3 sessions (action: requested-tests). The user consistently demonstrates this behavior.
- ci-poller trusted workflow_run provenance: Preserve manual CI-poller recovery through a permissions-empty relay that loads no privileged logic and hands off only to trusted default-branch code pinned to an immutable commit. Before privileged checkout, validate the successful triggering ci-poller-dispatch.yml run, repository, event, workflow identity, default-branch head, and exact trusted workflow SHA. Check out that validated SHA with persist-credentials: false before requesting app tokens. A branch name, workflow name, or successful conclusion alone does not prove provenance. Never execute caller-selected refs with secrets; retain protected production environments, immutable action and container pins, and independent scheduled reconciliation.
- Complete and validate the pnpm migration before opening a PR: For package-manager migrations, update scripts, lockfile, CI jobs, tests, and contributor documentation together; partial replacement makes local and CI installs disagree. Validate with a frozen install, tests, lint, and formatting, then push a branch and open a PR rather than leaving the migration only on `main`. Burak Yigit Kaya also repeatedly requests code review before committing; preserve that review step where the workflow permits it.
- Enforce defense-in-depth in release workflows: For workflows handling release state, approvals, secrets, or publication, use trusted default-branch code, immutable action and image pins, least-privilege permissions, protected production environments, and an unprivileged relay for manual recovery; never run arbitrary dispatched refs with protected credentials. Bind authorization and attestations to the exact live request, actor, label event, digest, and revision. Reject missing, malformed, stale, changed, or ambiguous evidence; revalidate before sensitive transitions and publication. Isolate credentials, revoke invalid state, preserve scheduled and manual recovery and cleanup, and fail closed when those paths fail. Cover each trust boundary and adversarial failure case with focused regressions, then run full tests and lint.
- Enforce fail-closed release workflows with regression tests: Treat the user’s release-workflow requirements as strict invariants, not optional guidance. Preserve exact request identity and path semantics, invalidate authorization whenever issue, label, event, actor, branch, or revision state changes, and never promote changed or ambiguous requests to `ci-ready`. Scope short-lived tokens and secrets narrowly, revoke or clean them up on every failure path, pin actions and reusable workflows to immutable commits, and never expose protected credentials to arbitrary refs or untrusted target code. Maintain explicit recovery mechanisms such as scheduled scans and manual `workflow_dispatch`, while ensuring recovery cannot bypass approval, attestation, CI, or branch-head checks. Add tests for malformed, stale, missing, ambiguous, and partial-failure states, and reject rather than normalize or infer unsupported values.
- Enforce fail-closed security in release automation: Treat publishing and auto-approval workflows as security-sensitive. Always run trusted controller code, keep secrets behind protected environments, use least-privilege tokens, and never expose repository secrets to arbitrary dispatched refs. Strictly validate issue titles, repository names, versions, paths, allowlist membership, approval events, attestations, and current repository state. Revalidate immediately before security-critical label transitions or publishing so renamed, modified, re-approved, or stale requests never reach `ci-ready`. Use exact comparisons rather than permissive parsing, and do not assume GitHub collaborator roles prove a direct repository grant when permissions may be inherited. Fail closed by removing approval/readiness labels when verification cannot be completed.
- Follow the established git workflow (branch, PR, review): Behavioral pattern detected across 3 sessions (action: enforced-workflow). The user consistently demonstrates this behavior.
- GitHub workflow_id file names: Use the workflow file name as GitHub Actions REST API `workflow_id` (for example, `ci-poller.yml`) rather than a numeric workflow ID. Chose file names over numeric IDs because GitHub endpoints accept either, while the checked-in filename is stable, self-describing, and avoids discovering or hardcoding repository-specific numeric IDs.
- Independent reviewer retry and resumption: Keep the exact review tree frozen and the merge gate closed until every required independent reviewer returns substantive file-level evidence or an explicit blocker. Retry one empty result with the same reviewer; after a second empty result, replace only that reviewer and wait for the replacement report. Do not cancel, poll, query, duplicate, overlap, edit, or proceed while the replacement review is active: those actions can disturb its preserved scope or mistake missing evidence for approval. Resume interrupted reviews with preserved context, and require a substantive verdict before merge or activation.
- Inspect the repository before proposing changes: Begin by examining the repository tree, relevant source files, tests, workflows, and compiled dependencies. Use exact file paths, line ranges, call sites, and search-result counts where available. Trace behavior through implementations and tests rather than relying on assumptions, and base recommendations or edits on concrete evidence found in the codebase.
- Prefer evidence-based security reviews of GitHub release automation: When reviewing GitHub Actions or release tooling, inspect the actual workflows, implementation, tests, API behavior, permissions, and live-state checks before reaching a conclusion. Treat GitHub contexts, event payloads, issue metadata, actors, and checked-out code as potentially attacker-controlled. Verify trusted-code checkout, least-privilege credentials, exact actor and event provenance, allowlisted bot paths, self-approval prevention, and revalidation immediately before privileged transitions. Expect authorization to fail closed when permissions, attestations, current labels, titles, or event bindings cannot be verified. Support findings with concrete file/line or test evidence and avoid relying on generic bot identity or stale workflow state.
- Prefer fail-first regression tests for workflow hardening: Before changing GitHub Actions or release workflows, add fail-first executable regression tests for the intended security behavior. Exercise provenance, approval/request binding, branch-head and SHA races, manual recovery from a validated immutable default-branch SHA, scoped credentials, cleanup, and failure paths; ensure each test fails for the intended reason. YAML-text assertions alone cannot catch runtime parsing mistakes, SHA replacement races, or GitHub-expression leakage into shell fixtures. After fixing the behavior, run focused tests and broader generated-file, test, lint, formatting, and YAML checks, resolving fixture or indentation failures rather than ignoring them.
- Prioritize fail-closed, test-backed publishing security: When reviewing or changing the publishing system, preserve and verify security invariants end to end. Bind approvals and CI-ready state to the exact live issue title, body, relevant labels, actors, and latest label-event IDs; reject stale, malformed, manually forged, self-approved, or changed requests. Revalidate immediately before publishing. Keep automated approval allowlisted and proof-backed, isolate manual dispatches from repository secrets, use protected environments, and pin actions and containers to immutable revisions. Preserve workflow ordering that records attestations before labels and removes/re-adds `ci-ready` to generate a fresh event. Add or inspect focused tests that assert exact failure behavior, API calls, outputs, token separation, and step ordering.
- Prioritize strict security invariants in workflow changes: Treat GitHub Actions and approval-flow changes as security-sensitive: preserve manual recovery, trusted-code execution, immutable transitions, and live provenance checks before privileged actions. Never claim workflow or environment guards isolate organization-wide `SENTRY_INTERNAL_APP_PRIVATE_KEY` or `SENTRY_RELEASE_BOT_PRIVATE_KEY`. Record their existing exposure as a separate Security-led workstream; it is not a merge blocker unless the current change worsens it.
- publish CommonJS test ESM: Use CommonJS (`require`/`module.exports`) in Sentry Publish source files and ES Modules (`import`/`export`) in tests; prefer named CommonJS exports when exposing multiple values. Chose this established split over converting source to ESM because the repository’s Node 24 runtime, entry points, and test mocking conventions depend on it.
- publish naming import order: Always use kebab-case filenames, camelCase variables/functions, and UPPER_SNAKE_CASE constants and regexes. Order imports as Node.js built-ins, external packages, then relative modules; preserving this convention avoids noisy diffs and lint or review churn.
- Publish second-person approval: Always require an independent second person to approve a release; reject an issue requester approving their own request. Chose requester/approver separation over generic target-repository write access because a requester with label and repository permissions can otherwise self-approve. `authorize-approval.js` must fetch the live issue, fail closed if `issue.user.login` is missing, and reject a case-insensitive match with `APPROVAL_ACTOR`, with a regression test. Automatic approval is allowed only through an explicitly authenticated, request-bound automation path.
- Require independent evidence-based security reviews: Require an independent, substantive, read-only security review of the exact current worktree before merge or handoff. Inspect staged, unstaged, tracked, and relevant untracked files without mutating files or VCS state; trace authorization, provenance, CI, publication, trust boundaries, credentials, races, failure, cancellation, cleanup, and recovery. Report non-empty findings in severity order using only MUST-FIX, CONCERN, and PASS, with precise current file:line evidence and compact validation results. If blocked, begin with BLOCKED and the exact error. Keep the merge gate closed until a fresh review passes; restart if the tree or review becomes stale. End exactly MERGE or DO-NOT-MERGE.
- Review code before committing: Behavioral pattern detected across 3 sessions (action: requested-review). The user consistently demonstrates this behavior.
- Review implementation, tests, and workflows together: Before changing publish behavior, trace the relevant implementation end to end across CLI entry points, source modules, GitHub Actions workflows, tests, and documentation. Record exact environment handling, parsed outputs, validation and errors, label and attestation transitions, failure recovery, and security invariants. Treat tests as executable specifications for ordering, formatting, pins, and edge cases rather than analyzing one file in isolation. Verify GitHub API assumptions against authoritative documentation, including endpoint scope, pagination, and issue/PR semantics. Identify missing coverage for malformed input, unsafe values, races, and partial failures, then verify changes with unit and workflow-integration tests.
- Sentry Publish pnpm toolchain: Sentry Publish chose pnpm over Yarn Classic to keep local and CI dependency resolution consistent. Use the version pinned by `packageManager` in `package.json`, retain Volta’s Node pin, and run `pnpm install --frozen-lockfile` in CI. Set up pinned pnpm before `actions/setup-node` when using `cache: pnpm`; install separately in the test job and both isolated publish jobs. Keep the `undici` override in `pnpm-workspace.yaml`; do not restore Yarn commands, `yarn.lock`, or Yarn `resolutions`.
- Treat accepted as approval and publish only from a fresh ci-ready event: Preserve the two-phase release lifecycle: accepted starts CI polling but never directly triggers publishing. After CI passes, the poller must revalidate the open issue, exact request and approval event, checked SHA, and release-branch head; remove any existing ci-ready before final validation and re-add it on every valid promotion to emit a fresh labeled event. Publishing must require that fresh ci-ready event and valid label state, never an earlier event. Changed, closed, renamed, re-approved, unauthorized, failed-CI, or branch-moved requests must not advance; remove stale authorization or readiness and require re-approval. Keep trusted workflow_dispatch recovery available and document this lifecycle.
- Treat regression tests as exact security specifications: When modifying the publish and CI workflows, use the provided tests as the authoritative contract. Preserve exact step ordering, environment variables, output text, API calls, immutable action/image pins, state-file naming, and byte-sensitive body transformations. Maintain fail-closed validation for malformed or stale requests, changed issue snapshots, approval events, attestations, actors, labels, paths, versions, revisions, and dispatch inputs. Do not weaken trust boundaries or bypass request-digest and live-state revalidation. Add or update focused unit and workflow-integration tests for normal behavior, edge cases, race conditions, and failure paths, and verify that rejected inputs cause no privileged side effects.
- Use a branch-to-PR workflow for code changes: Use a dedicated branch-to-PR workflow for code changes; validate and review before merging. An explicit admin-merge authorization may bypass a required review only for the named PR, never incomplete or failing CI. Immediately before merging, recheck the PR head/base, diff fingerprint, local worktree, all checks, Seer Code Review, Semgrep findings, and unresolved threads; squash with `--match-head-commit`, then verify the commit on `main` and clean up the branch. Passing checks alone is not merge authorization.
- Use precise GitHub REST API documentation and preserve access nuances: When answering GitHub API questions, rely on authoritative documentation and preserve exact endpoint paths, required headers, token types, scopes or app permissions, supported actions and parameters, response fields, status codes, and access-control edge cases. Do not simplify distinctions involving public versus private responses, self-authenticated visibility, Enterprise Managed Users, webhook security, or URI-template behavior.
- Use the pinned package manager consistently throughout the repository: When changing the project’s package manager, update local scripts, lockfiles, CI workflows, and contributor documentation together. For the requested Yarn-to-pnpm migration, pin pnpm, replace Yarn commands and caching, use frozen-lockfile installs in CI—including both publish jobs—and verify the result with tests, lint, and formatting.
- Verify GitHub approval authorization against authoritative permission semantics: Before changing GitHub-integrated authorization logic, confirm the exact REST endpoint contract from current GitHub documentation. Record required headers and API version, supported token types, OAuth/classic PAT scopes, fine-grained or GitHub App permissions, caller privilege requirements, response status codes, and relevant edge cases. Distinguish installation tokens from user access tokens and do not assume similarly named endpoints share permissions. For collaborator checks, account for effective highest-role semantics, organization/team-derived access, invitation limits, permission-update behavior, and 204/404 responses. Prefer precise, cited API behavior over assumptions inferred only from existing code or tests.
- Verify the current update-issue implementation before changing it: Verify the current update-issue implementation before changing it. Inspect src/modules/update-issue.js directly and preserve its exact target-restoration semantics. Confirm that state discovery uses CRAFT_STATE_FILE_PATH or the intended version-derived path, and guard reads with fs.existsSync(CRAFT_STATE_FILE_PATH). A missing state file must be a no-op with no issue API calls while still permitting cleanup such as removal of accepted. When present, fetch the issue and parse state concurrently, update declared target checkboxes, append undeclared published targets in object-key order, update the issue body, and preserve concurrent accepted-label removal. Do not assume behavior from earlier revisions; add or review focused tests when these details change.