From a486b96fee5666e1cafa5f69f90923dffff5b87d Mon Sep 17 00:00:00 2001 From: Andrea Bueide Date: Thu, 27 Aug 2026 11:24:39 -0500 Subject: [PATCH 1/5] ci: add API key length check and a push-triggered smoke test The previous real doc-sync run failed with "401 API key is invalid" - adds a step that prints ANTHROPIC_API_KEY's length (never its value) right before the real Claude step, to confirm the freshly-replaced secret is actually reaching the runner. Also adds a separate smoke-test job on push to abueide/** so this can be checked by just pushing a commit, rather than needing to merge a real, approved PR each time - push events have no PR to gate on or diff against, so it's a distinct job with its own minimal prompt rather than reusing doc-sync's PR-dependent steps. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/claude-doc-sync.yml | 43 ++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/.github/workflows/claude-doc-sync.yml b/.github/workflows/claude-doc-sync.yml index 2d33bd875..c823ac293 100644 --- a/.github/workflows/claude-doc-sync.yml +++ b/.github/workflows/claude-doc-sync.yml @@ -28,18 +28,48 @@ name: Sync docs on merged PRs # ourselves only needs actions/checkout and actions/setup-node, both # GitHub-owned and already allowed, so it sidesteps that policy without # requiring an org/repo policy change. +# The smoke-test job below runs on push to abueide/** so we can check +# ANTHROPIC_API_KEY/CLI auth without merging a real PR each time - it's a +# separate job because push events have no PR to gate on or diff against. +# Adjust the branch pattern if you're testing from a different branch prefix. on: pull_request_target: types: [closed] branches: [master] + push: + branches: ['abueide/**'] permissions: contents: write pull-requests: write jobs: + smoke-test: + if: github.event_name == 'push' + runs-on: ubuntu-latest-large + steps: + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: 22 + + - name: Install Claude Code CLI + run: npm install -g @anthropic-ai/claude-code + + - name: Check ANTHROPIC_API_KEY length + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: | + echo "ANTHROPIC_API_KEY length: ${#ANTHROPIC_API_KEY}" + + - name: Smoke test Claude auth + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: | + claude -p "Reply with exactly the word OK and nothing else." + doc-sync: - if: github.event.pull_request.merged == true + if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == true runs-on: ubuntu-latest-large steps: - name: Require an approved review @@ -96,6 +126,17 @@ jobs: 5. If you did update documentation, create a new branch off $BASE_REF named \`docs/sync-pr-$PR_NUMBER\`, commit the changes, push it, and open a pull request against $BASE_REF with \`gh pr create\`. Title it "docs: sync with #$PR_NUMBER" and explain in the body which doc(s) you updated, why, and link back to #$PR_NUMBER. EOF + # Temporary debugging step: confirms the secret is actually reaching the + # runner without ever printing its value. Safe to remove once we've + # confirmed a real Claude run succeeds - ANTHROPIC_API_KEY was recently + # replaced after the previous run failed with "401 API key is invalid". + - name: Check ANTHROPIC_API_KEY length + if: steps.gate.outputs.approved == 'true' + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: | + echo "ANTHROPIC_API_KEY length: ${#ANTHROPIC_API_KEY}" + - name: Sync docs with Claude if: steps.gate.outputs.approved == 'true' env: From 53845960a2f6a4f568920bde882efd2c86e95f22 Mon Sep 17 00:00:00 2001 From: Andrea Bueide Date: Thu, 27 Aug 2026 12:40:54 -0500 Subject: [PATCH 2/5] ci: scope doc-sync to existing docs only, record no-op decisions - Removes the temporary ANTHROPIC_API_KEY length check from the real doc-sync job now that the push-triggered smoke test has confirmed the replaced secret authenticates correctly. - Prompt now explicitly instructs Claude to only update EXISTING documentation and never create new docs/README sections that didn't already exist before the merged PR. - When no existing docs are relevant, Claude records that decision to doc-sync-decision.md instead of silently doing nothing - a new step uploads it as a job artifact (if-no-files-found: ignore covers the normal case where a PR was opened instead and no file was written). Co-Authored-By: Claude Sonnet 5 --- .github/workflows/claude-doc-sync.yml | 40 +++++++++++++++------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/.github/workflows/claude-doc-sync.yml b/.github/workflows/claude-doc-sync.yml index c823ac293..bade769d4 100644 --- a/.github/workflows/claude-doc-sync.yml +++ b/.github/workflows/claude-doc-sync.yml @@ -1,9 +1,11 @@ name: Sync docs on merged PRs # Runs after a PR merges into master and asks Claude to check whether any -# documentation (root README, package READMEs, /docs) needs updating to -# reflect the change, opening a follow-up PR only if it finds something to -# update. +# EXISTING documentation (root README, package READMEs, /docs) needs updating +# to reflect the change, opening a follow-up PR only if it finds something to +# update. Claude is instructed to only edit docs that already exist - never to +# create new documentation - and if nothing needs updating, to record that +# decision in doc-sync-decision.md, uploaded as a job artifact instead of a PR. # # Gated to keep run volume (and cost) down: only master-targeted merges # (branches: [master] below) that also carry an approving review @@ -119,24 +121,15 @@ jobs: cat > "$RUNNER_TEMP/claude-prompt.txt" < Date: Thu, 27 Aug 2026 13:28:48 -0500 Subject: [PATCH 3/5] ci: drop the push smoke test, add CLAUDE.md comment-length guidance Removes the smoke-test job and its push-to-abueide/** trigger now that it's served its purpose (confirmed the API key works) - doc-sync is back to its original single-job, pull_request_target-only shape. Adds CLAUDE.md instructing in-code comments stay to 1 line, so Claude follows it both for interactive use and for the doc-sync automation. Not AGENTS.md: this repo's .gitignore deliberately keeps AGENTS.md local-only (grouped with other "not for commit" entries), which would mean it never reaches doc-sync's CI checkout at all. CLAUDE.md isn't gitignored and Claude Code reads it the same way. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/claude-doc-sync.yml | 32 +-------------------------- CLAUDE.md | 3 +++ 2 files changed, 4 insertions(+), 31 deletions(-) create mode 100644 CLAUDE.md diff --git a/.github/workflows/claude-doc-sync.yml b/.github/workflows/claude-doc-sync.yml index bade769d4..568827f35 100644 --- a/.github/workflows/claude-doc-sync.yml +++ b/.github/workflows/claude-doc-sync.yml @@ -30,48 +30,18 @@ name: Sync docs on merged PRs # ourselves only needs actions/checkout and actions/setup-node, both # GitHub-owned and already allowed, so it sidesteps that policy without # requiring an org/repo policy change. -# The smoke-test job below runs on push to abueide/** so we can check -# ANTHROPIC_API_KEY/CLI auth without merging a real PR each time - it's a -# separate job because push events have no PR to gate on or diff against. -# Adjust the branch pattern if you're testing from a different branch prefix. on: pull_request_target: types: [closed] branches: [master] - push: - branches: ['abueide/**'] permissions: contents: write pull-requests: write jobs: - smoke-test: - if: github.event_name == 'push' - runs-on: ubuntu-latest-large - steps: - - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version: 22 - - - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code - - - name: Check ANTHROPIC_API_KEY length - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - run: | - echo "ANTHROPIC_API_KEY length: ${#ANTHROPIC_API_KEY}" - - - name: Smoke test Claude auth - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - run: | - claude -p "Reply with exactly the word OK and nothing else." - doc-sync: - if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == true + if: github.event.pull_request.merged == true runs-on: ubuntu-latest-large steps: - name: Require an approved review diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..a9a0ed10f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# Agent instructions + +- In-code comments must be no longer than 1 line. From 3c4347d77aa9f59531f86b70c356b3fe92cd31f9 Mon Sep 17 00:00:00 2001 From: Andrea Bueide Date: Thu, 27 Aug 2026 13:37:16 -0500 Subject: [PATCH 4/5] ci: condense workflow comments to 1 line each Follows the CLAUDE.md rule added in the previous commit - collapses the four multi-paragraph header comments and the artifact-upload comment down to a single line each, moving the dropped detail to PR descriptions instead. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/claude-doc-sync.yml | 38 ++++----------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/.github/workflows/claude-doc-sync.yml b/.github/workflows/claude-doc-sync.yml index 568827f35..3d1a213e0 100644 --- a/.github/workflows/claude-doc-sync.yml +++ b/.github/workflows/claude-doc-sync.yml @@ -1,35 +1,9 @@ name: Sync docs on merged PRs -# Runs after a PR merges into master and asks Claude to check whether any -# EXISTING documentation (root README, package READMEs, /docs) needs updating -# to reflect the change, opening a follow-up PR only if it finds something to -# update. Claude is instructed to only edit docs that already exist - never to -# create new documentation - and if nothing needs updating, to record that -# decision in doc-sync-decision.md, uploaded as a job artifact instead of a PR. -# -# Gated to keep run volume (and cost) down: only master-targeted merges -# (branches: [master] below) that also carry an approving review -# (reviewDecision == APPROVED, checked in the gate step) trigger a Claude run. -# master requires 1 approval by branch protection, but enforce_admins is off, -# so an admin can still merge without one - this gate closes that gap rather -# than assuming protection alone guarantees a review happened. -# -# Uses pull_request_target (not pull_request) so the workflow runs with this -# repo's own permissions even when the merged PR came from a fork - this repo -# takes plenty of external contributions and a plain `pull_request` trigger -# gets a read-only token for fork-authored PRs, which would make `gh pr -# create` fail below. This is safe here because we only ever check out -# base.ref (the already-reviewed, already-merged default branch), never the -# PR's own head ref - we never build or execute the contributor's code. -# -# Installs the Claude Code CLI directly via npm rather than using -# `uses: anthropics/claude-code-action` - this repo's Actions policy is -# allowed_actions: selected with an empty patterns_allowed list (only -# GitHub-owned actions are permitted), so a third-party `uses:` reference -# fails at startup before any job runs. Installing and invoking the CLI -# ourselves only needs actions/checkout and actions/setup-node, both -# GitHub-owned and already allowed, so it sidesteps that policy without -# requiring an org/repo policy change. +# On merge to master, has Claude update existing docs affected by the change (never creating new ones); records a no-op decision as an artifact if nothing needs updating. +# Requires an approving review before running, since master's approval requirement can be bypassed by admins. +# Uses pull_request_target (not pull_request) for write access on fork-authored PRs too; safe since we only check out the already-merged base branch, never the PR's own head. +# Installs the Claude Code CLI directly instead of using a third-party action, since this repo's Actions policy only allows GitHub-owned actions. on: pull_request_target: types: [closed] @@ -109,9 +83,7 @@ jobs: claude -p "$(cat "$RUNNER_TEMP/claude-prompt.txt")" \ --allowedTools "Edit,MultiEdit,Write,Read,Glob,Grep,Bash(git:*),Bash(gh:*)" - # Only exists when Claude decided nothing needed updating (step 4 above) - - # if-no-files-found: ignore covers the normal case where it opened a PR - # instead and never wrote this file. + # Only exists if Claude decided nothing needed updating; ignored otherwise. - name: Upload no-op decision artifact if: always() && steps.gate.outputs.approved == 'true' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 From 39f13e03eda4ee2e6af4619853819f7fd6b1486e Mon Sep 17 00:00:00 2001 From: Andrea Bueide Date: Thu, 27 Aug 2026 13:42:22 -0500 Subject: [PATCH 5/5] ci: stop stacking one-line comments, clarify the rule in CLAUDE.md Four single-line comments in a row at the file header read as one 4-line comment in practice. Distributes each to sit directly next to the line it explains (trigger choice, approval gate, CLI install) instead, and drops the purely-descriptive one that just restated what the workflow/job/step names already say. Also tightens CLAUDE.md's wording so this doesn't happen again: a comment must be a single line AND not stacked with others as a paragraph substitute. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/claude-doc-sync.yml | 7 +++---- CLAUDE.md | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/claude-doc-sync.yml b/.github/workflows/claude-doc-sync.yml index 3d1a213e0..bb6c88908 100644 --- a/.github/workflows/claude-doc-sync.yml +++ b/.github/workflows/claude-doc-sync.yml @@ -1,9 +1,6 @@ name: Sync docs on merged PRs -# On merge to master, has Claude update existing docs affected by the change (never creating new ones); records a no-op decision as an artifact if nothing needs updating. -# Requires an approving review before running, since master's approval requirement can be bypassed by admins. -# Uses pull_request_target (not pull_request) for write access on fork-authored PRs too; safe since we only check out the already-merged base branch, never the PR's own head. -# Installs the Claude Code CLI directly instead of using a third-party action, since this repo's Actions policy only allows GitHub-owned actions. +# pull_request_target (not pull_request) for write access on fork-authored PRs too; safe since we only check out the already-merged base branch, never the PR's own head. on: pull_request_target: types: [closed] @@ -18,6 +15,7 @@ jobs: if: github.event.pull_request.merged == true runs-on: ubuntu-latest-large steps: + # Gates on approval since master's review requirement can be bypassed by admins. - name: Require an approved review id: gate env: @@ -45,6 +43,7 @@ jobs: with: node-version: 22 + # Installed directly rather than via a third-party action, since this repo's Actions policy only allows GitHub-owned actions. - name: Install Claude Code CLI if: steps.gate.outputs.approved == 'true' run: npm install -g @anthropic-ai/claude-code diff --git a/CLAUDE.md b/CLAUDE.md index a9a0ed10f..5cbe210ba 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,3 @@ # Agent instructions -- In-code comments must be no longer than 1 line. +- In-code comments must be a single line, placed next to the specific line it explains. Don't stack several one-line comments back to back - that's a multi-line comment in practice, not several 1-line ones.