From 065e764f4b43ad42f99ec7cd55c1f1b121ae199a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 03:56:17 +0000 Subject: [PATCH 1/5] ci: add weekly dependency maintenance sweep Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01Pg4JdrMxy6q2hdvmuPnKyR --- .../weekly-dependency-maintenance.yml | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 .github/workflows/weekly-dependency-maintenance.yml diff --git a/.github/workflows/weekly-dependency-maintenance.yml b/.github/workflows/weekly-dependency-maintenance.yml new file mode 100644 index 00000000..953dd199 --- /dev/null +++ b/.github/workflows/weekly-dependency-maintenance.yml @@ -0,0 +1,111 @@ +name: Weekly Dependency Maintenance + +# Covers the surface Dependabot version updates do not: the npm workspace +# (.github/dependabot.yml runs npm and GitHub Actions in security-only mode, +# open-pull-requests-limit: 0). Dependabot security PRs stay as they are, and +# workflow files are out of scope here. The token is scoped to what the job +# does: push a branch and open a draft PR. + +on: + schedule: + # Mondays at 14:00 UTC, early Pacific business hours. + - cron: "0 14 * * 1" + workflow_dispatch: + +concurrency: + group: weekly-dependency-maintenance + cancel-in-progress: false + +permissions: + contents: read + +jobs: + maintenance: + name: Security and package updates + permissions: + contents: write + pull-requests: write + id-token: write # claude-code-action exchanges a GitHub OIDC token for its app token + runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} + timeout-minutes: 60 + steps: + - name: Checkout main + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + ref: main + fetch-depth: 0 + + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 + with: + node-version: 24 + cache: npm + cache-dependency-path: package-lock.json + + - name: Install workspace dependencies + run: npm ci + + # Bill API credits when ANTHROPIC_API_KEY is set, else the subscription + # OAuth token. The action forwards both to the CLI and does not choose + # between them, so pass only one. Secrets cannot be tested in `if:`. + - name: Choose Claude credential + id: claude-auth + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: | + if [ -n "$ANTHROPIC_API_KEY" ]; then + echo "use_api_key=true" >> "$GITHUB_OUTPUT" + else + echo "use_api_key=false" >> "$GITHUB_OUTPUT" + fi + + - name: Run Claude dependency maintenance + uses: anthropics/claude-code-action@a26b07d8c778b0c43f64618e5fd29fd0f8af72e8 # v1.0.250 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + claude_code_oauth_token: ${{ steps.claude-auth.outputs.use_api_key != 'true' && secrets.CLAUDE_CODE_OAUTH_TOKEN || '' }} + + additional_permissions: | + contents: write + pull-requests: write + + prompt: | + REPO: ${{ github.repository }} + BASE BRANCH: main + RUN ID: ${{ github.run_id }} + + You are running weekly dependency and security maintenance for the Terminal49 API monorepo (npm workspaces: MCP server, TypeScript SDK, CLI). This is a public repository: never put secrets, internal URLs, or customer data in commits or PR text. + + Goal: + - Create at most one draft pull request against `main` when there are safe dependency/security updates. + - If an open PR from a `claude/weekly-dependency-maintenance-*` branch already exists, do nothing except write a one-line summary to `$GITHUB_STEP_SUMMARY`. + - If there are no useful updates, write a concise no-op summary to `$GITHUB_STEP_SUMMARY` and do not create a PR. + + Dependency surface (only this; GitHub Actions and workflow files belong to Dependabot and must not be touched): + - npm workspace: root `package.json`, `packages/mcp/package.json`, `sdks/typescript-sdk/package.json`, `sdks/typescript-sdk-cli/package.json`, and the single root `package-lock.json`. + + Required inspection (dependencies are already installed with `npm ci`): + - `npm audit --audit-level=moderate || true` + - `npm outdated --workspaces --include-workspace-root || true` + + Update policy: + - Prioritize known vulnerabilities and low-risk patch/minor updates. + - Avoid major upgrades unless required to resolve a high/critical vulnerability. + - Move `vite-plus`, the `vite` override, the exact `vitest` pin, `@oxlint/plugins`, and each workspace's `@vitest/coverage-v8` together or not at all (see AGENTS.md). + - Do not change `engines`, `.tool-versions`, package `version` fields, the `@terminal49/sdk` pin in `packages/mcp`, or the Node 20/22/24 support the SDK and CLI have in CI. Published packages (SDK, CLI) must not gain a higher Node floor. + - Do not hand-edit generated files (`sdks/typescript-sdk/src/generated/**`, `packages/mcp/src/generated/**`, `docs/sdk/reference/**`, the Postman collection). If an update changes their regenerated output, drop that update and note it in the PR. + - Do not make unrelated code changes. Manifests, `package-lock.json`, and narrowly required compatibility fixes are allowed. Do not touch `docs/`, `.github/`, or `skills/`. + - Prefer targeted `npm update PACKAGE_NAME` or `npm install PACKAGE_NAME@VERSION --workspace WORKSPACE` over broad all-dependency updates. Keep the lockfile consistent with `npm ci`. + + If changes are made: + 1. Run validation: `npm ci && npm run agent-verify` (the repo's completion gate; it needs Node 24.11+). If a step cannot run in this environment, record what was skipped and why. + 2. Create branch `claude/weekly-dependency-maintenance-${{ github.run_id }}`. + 3. Commit with message `chore(deps): weekly dependency maintenance`. + 4. Open a draft PR against `main` with a summary of updates, security findings, validation, and any risk. + + If no changes are made: + - Write the audits run and a brief conclusion to `$GITHUB_STEP_SUMMARY`. + - Do not open a PR. + + claude_args: | + --model claude-sonnet-5-5 + --allowedTools Bash,Glob,Grep,Read,Write,Edit From aa90db1e1e77365b98d7ede7a93f8a0b090aa99a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 04:04:08 +0000 Subject: [PATCH 2/5] chore: address review findings in weekly sweep Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01Pg4JdrMxy6q2hdvmuPnKyR --- .../weekly-dependency-maintenance.yml | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/.github/workflows/weekly-dependency-maintenance.yml b/.github/workflows/weekly-dependency-maintenance.yml index 953dd199..fb80078d 100644 --- a/.github/workflows/weekly-dependency-maintenance.yml +++ b/.github/workflows/weekly-dependency-maintenance.yml @@ -3,8 +3,10 @@ name: Weekly Dependency Maintenance # Covers the surface Dependabot version updates do not: the npm workspace # (.github/dependabot.yml runs npm and GitHub Actions in security-only mode, # open-pull-requests-limit: 0). Dependabot security PRs stay as they are, and -# workflow files are out of scope here. The token is scoped to what the job -# does: push a branch and open a draft PR. +# workflow files are out of scope here. The job's GITHUB_TOKEN is read-only: +# the branch push and draft PR use the short-lived GitHub App token that +# claude-code-action exchanges for via OIDC (its default permissions are +# contents, pull_requests and issues write). on: schedule: @@ -23,8 +25,7 @@ jobs: maintenance: name: Security and package updates permissions: - contents: write - pull-requests: write + contents: read id-token: write # claude-code-action exchanges a GitHub OIDC token for its app token runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} timeout-minutes: 60 @@ -34,8 +35,10 @@ jobs: with: ref: main fetch-depth: 0 + persist-credentials: false - - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 + - name: Set up Node + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0 with: node-version: 24 cache: npm @@ -47,15 +50,20 @@ jobs: # Bill API credits when ANTHROPIC_API_KEY is set, else the subscription # OAuth token. The action forwards both to the CLI and does not choose # between them, so pass only one. Secrets cannot be tested in `if:`. + # Fail here, before any agent time is spent, when neither is configured. - name: Choose Claude credential id: claude-auth env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} run: | if [ -n "$ANTHROPIC_API_KEY" ]; then echo "use_api_key=true" >> "$GITHUB_OUTPUT" - else + elif [ -n "$CLAUDE_CODE_OAUTH_TOKEN" ]; then echo "use_api_key=false" >> "$GITHUB_OUTPUT" + else + echo "::error title=Missing Claude credential::Set the ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN repository secret; the weekly dependency sweep cannot run without one." + exit 1 fi - name: Run Claude dependency maintenance @@ -64,10 +72,6 @@ jobs: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} claude_code_oauth_token: ${{ steps.claude-auth.outputs.use_api_key != 'true' && secrets.CLAUDE_CODE_OAUTH_TOKEN || '' }} - additional_permissions: | - contents: write - pull-requests: write - prompt: | REPO: ${{ github.repository }} BASE BRANCH: main @@ -77,7 +81,7 @@ jobs: Goal: - Create at most one draft pull request against `main` when there are safe dependency/security updates. - - If an open PR from a `claude/weekly-dependency-maintenance-*` branch already exists, do nothing except write a one-line summary to `$GITHUB_STEP_SUMMARY`. + - If an open sweep PR already exists, do nothing except write a one-line summary to `$GITHUB_STEP_SUMMARY`. A sweep PR is one whose head branch starts with `claude/weekly-dependency-maintenance-`, whose head repository is `${{ github.repository }}` (not a fork), and whose author is `claude[bot]`. Check with REST, not GraphQL: `gh api "repos/${{ github.repository }}/pulls?state=open&per_page=100" --jq '.[] | select(.user.login == "claude[bot]" and .head.repo.full_name == "${{ github.repository }}" and (.head.ref | startswith("claude/weekly-dependency-maintenance-"))) | .html_url'`. A branch name match alone does not count. - If there are no useful updates, write a concise no-op summary to `$GITHUB_STEP_SUMMARY` and do not create a PR. Dependency surface (only this; GitHub Actions and workflow files belong to Dependabot and must not be touched): @@ -89,15 +93,19 @@ jobs: Update policy: - Prioritize known vulnerabilities and low-risk patch/minor updates. + - Do not bump to a version published fewer than 7 days ago (check with `npm view PACKAGE@VERSION time --json`), unless that version fixes a high or critical advisory. + - Preserve each dependency's existing range style or exact pin: keep `~4.3.6` as `~`, `^` as `^`, and exact pins exact (for example `zod` stays `~4.3.6`). Never loosen or tighten a range. + - release-please owns the `@terminal49/sdk` pin in `packages/mcp`, the CLI's pin on the SDK, and the workspace package versions recorded in `package-lock.json`. Do not edit them, including via `npm install` side effects: revert any such lockfile hunks. - Avoid major upgrades unless required to resolve a high/critical vulnerability. - Move `vite-plus`, the `vite` override, the exact `vitest` pin, `@oxlint/plugins`, and each workspace's `@vitest/coverage-v8` together or not at all (see AGENTS.md). - Do not change `engines`, `.tool-versions`, package `version` fields, the `@terminal49/sdk` pin in `packages/mcp`, or the Node 20/22/24 support the SDK and CLI have in CI. Published packages (SDK, CLI) must not gain a higher Node floor. - Do not hand-edit generated files (`sdks/typescript-sdk/src/generated/**`, `packages/mcp/src/generated/**`, `docs/sdk/reference/**`, the Postman collection). If an update changes their regenerated output, drop that update and note it in the PR. - Do not make unrelated code changes. Manifests, `package-lock.json`, and narrowly required compatibility fixes are allowed. Do not touch `docs/`, `.github/`, or `skills/`. - Prefer targeted `npm update PACKAGE_NAME` or `npm install PACKAGE_NAME@VERSION --workspace WORKSPACE` over broad all-dependency updates. Keep the lockfile consistent with `npm ci`. + - Run every `npm ci`, `npm install`, and `npm update` with `npm_config_ignore_scripts=true` (for example `npm_config_ignore_scripts=true npm install PACKAGE@VERSION --workspace WORKSPACE`) so lifecycle scripts of newly bumped packages never run. The repo does not need them: the only packages with install scripts are `esbuild` (its binary ships via optional dependencies) and macOS-only `fsevents`. If changes are made: - 1. Run validation: `npm ci && npm run agent-verify` (the repo's completion gate; it needs Node 24.11+). If a step cannot run in this environment, record what was skipped and why. + 1. Run validation: `npm_config_ignore_scripts=true npm ci && npm run agent-verify` (the repo's completion gate; it needs Node 24.11+), then the SDK docs drift check that `ci.yml` also runs: `npm run sdk:docs && git diff --exit-code -- docs/sdk/reference`. If it reports a diff, drop the update that caused it, restore `docs/sdk/reference` with `git checkout -- docs/sdk/reference`, and note it in the PR. Never hand-edit `docs/sdk/reference`. If a step cannot run in this environment, record what was skipped and why. 2. Create branch `claude/weekly-dependency-maintenance-${{ github.run_id }}`. 3. Commit with message `chore(deps): weekly dependency maintenance`. 4. Open a draft PR against `main` with a summary of updates, security findings, validation, and any risk. From 8ff66006c0cef87e5a9cf97b3f5d02fb0cb32d6e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 04:18:10 +0000 Subject: [PATCH 3/5] chore: ignore lifecycle scripts in the sweep's initial install Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01Pg4JdrMxy6q2hdvmuPnKyR --- .github/workflows/weekly-dependency-maintenance.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/weekly-dependency-maintenance.yml b/.github/workflows/weekly-dependency-maintenance.yml index fb80078d..d5aa7c4b 100644 --- a/.github/workflows/weekly-dependency-maintenance.yml +++ b/.github/workflows/weekly-dependency-maintenance.yml @@ -44,8 +44,9 @@ jobs: cache: npm cache-dependency-path: package-lock.json + # Lifecycle scripts stay off: this runs on merged main before the agent, and the repo needs none. - name: Install workspace dependencies - run: npm ci + run: npm ci --ignore-scripts # Bill API credits when ANTHROPIC_API_KEY is set, else the subscription # OAuth token. The action forwards both to the CLI and does not choose From c677b630c3bdf49d3eaec2c4da8d613d28135297 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 04:59:49 +0000 Subject: [PATCH 4/5] ci: split the weekly sweep into a read-only agent job and a publish job Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01Pg4JdrMxy6q2hdvmuPnKyR --- .../weekly-dependency-maintenance.yml | 498 ++++++++++++++++-- scripts/ci/verify-dependency-sweep-patch.sh | 319 +++++++++++ 2 files changed, 766 insertions(+), 51 deletions(-) create mode 100755 scripts/ci/verify-dependency-sweep-patch.sh diff --git a/.github/workflows/weekly-dependency-maintenance.yml b/.github/workflows/weekly-dependency-maintenance.yml index d5aa7c4b..0680dc6b 100644 --- a/.github/workflows/weekly-dependency-maintenance.yml +++ b/.github/workflows/weekly-dependency-maintenance.yml @@ -3,10 +3,51 @@ name: Weekly Dependency Maintenance # Covers the surface Dependabot version updates do not: the npm workspace # (.github/dependabot.yml runs npm and GitHub Actions in security-only mode, # open-pull-requests-limit: 0). Dependabot security PRs stay as they are, and -# workflow files are out of scope here. The job's GITHUB_TOKEN is read-only: -# the branch push and draft PR use the short-lived GitHub App token that -# claude-code-action exchanges for via OIDC (its default permissions are -# contents, pull_requests and issues write). +# workflow files are out of scope here. +# +# Two jobs, split so that the job that runs an AI agent over freshly published +# third-party code never holds a credential that can write to the repository. +# This is a public repository: nothing in this workflow or in the PR text it +# produces may carry secrets, internal URLs or customer data. +# +# agent permissions: contents: read, pull-requests: read. No id-token. +# Checks out main without persisting credentials, lets the agent +# edit package.json files and package-lock.json in the working +# tree, then a deterministic step exports the change as sweep.patch +# (plus the agent-written PR description) after +# scripts/ci/verify-dependency-sweep-patch.sh accepts it. The job +# restores and saves no caches (see the setup steps), so nothing the +# agent leaves behind can reach other workflows through the cache. +# +# publish permissions: contents: write, pull-requests: write, actions: write. +# Runs only when the agent job produced a patch. Installs nothing +# and runs no agent. It checks out the exact commit the agent worked +# on, re-runs the same verify script from that checkout (trusted +# repository content, never from the artifact), applies the patch, +# pushes a branch and opens a draft PR against main. The publish job +# is the trust boundary; the agent job's own check only fails fast. +# +# What the verify script enforces (one copy of the path allowlist, in the +# script): modifications of existing manifests and the lockfile only; and +# content checks per file type (package.json, package-lock.json) so a permitted +# path cannot carry scripts, new dependency names, non-registry sources, or a +# changed workspace `version` or workspace pin (release-please owns those). +# +# Tokens: both jobs use the workflow's own GITHUB_TOKEN. The agent step passes +# it to claude-code-action as `github_token`, which makes the action skip its +# OIDC app-token exchange, so the job needs no `id-token: write`. With a +# read-only token the action cannot push or comment; everything that writes +# happens in `publish`. +# +# Pull requests and pushes made with GITHUB_TOKEN do not trigger other +# workflows, and ci.yml has no workflow_dispatch trigger. The sweep PR +# therefore gets no CI run until a maintainer closes and reopens it or pushes +# to its branch. The publish job dispatches ci.yml only if ci.yml ever gains a +# workflow_dispatch trigger. +# +# The tool allowlist given to the agent is a guardrail, not a sandbox (see the +# comments on the agent step); the verify script and the job split are what +# actually bound the damage. on: schedule: @@ -22,99 +63,454 @@ permissions: contents: read jobs: - maintenance: - name: Security and package updates + agent: + name: Dependency agent (read-only) permissions: contents: read - id-token: write # claude-code-action exchanges a GitHub OIDC token for its app token + pull-requests: read runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} timeout-minutes: 60 + outputs: + has_changes: ${{ steps.collect.outputs.has_changes }} + # The main commit the agent worked on; publish checks out this exact sha + # so the patch is verified and applied against the same base. + base_sha: ${{ steps.base.outputs.sha }} steps: - name: Checkout main uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 with: ref: main - fetch-depth: 0 persist-credentials: false + - name: Record the base commit + id: base + run: | + set -euo pipefail + sha="$(git rev-parse HEAD)" + [[ "$sha" =~ ^[0-9a-f]{40}$ ]] + echo "sha=$sha" >> "$GITHUB_OUTPUT" + + # One sweep branch at a time. The sweep skips while any branch named + # claude/weekly-dependency-maintenance-* exists on origin, or while a + # same-repository PR from such a branch is open. A branch left behind + # without an open PR (PR closed without deleting the branch, or a PR that + # could not be created) therefore also blocks later runs on purpose: it + # may hold a pushed patch nobody has looked at. The summary says how to + # clear it. Fork PRs are ignored: only same-repository branches count. + # + # Everything goes through the REST API: this checkout keeps no + # credentials, so `git ls-remote` would fail on a private repository, and + # the sweep needs nothing from GraphQL. + - name: Skip when a sweep branch or PR is still open + id: skip + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + prefix="claude/weekly-dependency-maintenance-" + + open_prs="$(gh api --paginate "repos/$GITHUB_REPOSITORY/pulls?state=open&per_page=100" \ + --jq ".[] | select(.head.repo.full_name == \"$GITHUB_REPOSITORY\" and (.head.ref | startswith(\"$prefix\"))) | .head.ref" | + paste -sd, - | sed 's/,/, /g')" + remote_branches="$(gh api --paginate "repos/$GITHUB_REPOSITORY/git/matching-refs/heads/$prefix" \ + --jq '.[].ref | ltrimstr("refs/heads/")' | paste -sd, - | sed 's/,/, /g')" + + if [ -n "$open_prs" ] || [ -n "$remote_branches" ]; then + echo "skip=true" >> "$GITHUB_OUTPUT" + { + echo "## Weekly dependency maintenance skipped" + echo + echo "A previous sweep is still outstanding." + echo + echo "- Open sweep PR head branches: \`${open_prs:-none}\`" + echo "- Sweep branches on origin: \`${remote_branches:-none}\`" + echo + echo "To let the next run proceed: merge or close the PR, then delete the branch (for example \`git push origin --delete \`). Enabling \"Automatically delete head branches\" in the repository settings does this on merge." + } >> "$GITHUB_STEP_SUMMARY" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + # No cache: actions/setup-node would save the npm cache in its post step, + # after the agent has finished, and on a scheduled run that save lands in + # the default-branch cache scope, which every other workflow can restore. + # (`package-manager-cache: false` also stops its automatic npm caching.) - name: Set up Node + if: steps.skip.outputs.skip != 'true' uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0 with: node-version: 24 - cache: npm - cache-dependency-path: package-lock.json + package-manager-cache: false - # Lifecycle scripts stay off: this runs on merged main before the agent, and the repo needs none. + # Lifecycle scripts stay off: this runs on merged main before the agent, + # and the repo needs none (the only packages with install scripts are + # esbuild, whose binary ships via optional dependencies, and macOS-only + # fsevents). - name: Install workspace dependencies + if: steps.skip.outputs.skip != 'true' run: npm ci --ignore-scripts - # Bill API credits when ANTHROPIC_API_KEY is set, else the subscription - # OAuth token. The action forwards both to the CLI and does not choose - # between them, so pass only one. Secrets cannot be tested in `if:`. - # Fail here, before any agent time is spent, when neither is configured. - - name: Choose Claude credential - id: claude-auth - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - run: | - if [ -n "$ANTHROPIC_API_KEY" ]; then - echo "use_api_key=true" >> "$GITHUB_OUTPUT" - elif [ -n "$CLAUDE_CODE_OAUTH_TOKEN" ]; then - echo "use_api_key=false" >> "$GITHUB_OUTPUT" - else - echo "::error title=Missing Claude credential::Set the ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN repository secret; the weekly dependency sweep cannot run without one." - exit 1 - fi + # The only place outside the checkout the agent may write to. + - name: Create agent output directory + if: steps.skip.outputs.skip != 'true' + run: mkdir -p "$RUNNER_TEMP/agent-out" - name: Run Claude dependency maintenance + if: steps.skip.outputs.skip != 'true' uses: anthropics/claude-code-action@a26b07d8c778b0c43f64618e5fd29fd0f8af72e8 # v1.0.250 + env: + # Best-effort strip of Anthropic, cloud and GitHub Actions + # credentials from the environment of the commands the agent runs. + # It is not isolation: the action installs its bubblewrap/socat + # sandbox only when `allowed_non_write_users` is set (it is not, and + # must not be, here), so npm and whatever it executes still run as + # ordinary processes on the runner. That includes `npm run + # agent-verify`, which runs the repository's own build and test code + # against the updated dependencies; the job holds only a read-only + # token and its output is re-verified by the publish job. + CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: "1" + + # `npm install` and `npm update` would otherwise run the lifecycle + # scripts of newly bumped packages. Explicit `npm run