From 04cb7eca241e98abc693f3d5db67fe23d224d87f Mon Sep 17 00:00:00 2001 From: Otavio Salvador Date: Sat, 12 Sep 2026 17:43:52 -0300 Subject: [PATCH 1/4] ci: give @claude the workspace config through --add-dir This workflow syncs the config and then runs an action that deletes it: .claude/ and CLAUDE.md at the workspace root are replaced with the base branch's copies, and this repo tracks neither, so they are replaced with nothing. Every @claude invocation has been running without the repo rules, without the skills and without CLAUDE.md. Unlike the review workflow there is no prompt here to point at a surviving path, so the fix has to be one the runtime applies on its own. --add-dir for the claude checkout does it: skills become invocable by name, and CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD loads CLAUDE.md and every .claude/rules/*.md from the added directories. The second --add-dir carries this repo's own CLAUDE.md, which lives at claude/shellhub/ and never reached the session at all. Verified on cloud by planting a distinct token in each location with every file-reading tool denied, so a token could only appear if the runtime supplied it. --- .github/workflows/claude.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 4db669b981f..dfc3a590384 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -108,7 +108,12 @@ jobs: - name: Run Claude timeout-minutes: 60 uses: anthropics/claude-code-action@8251c103ac8c1d761882c86aba1412c7f583c844 # v1 + env: + CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD: "1" with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} trigger_phrase: "@claude" + claude_args: | + --add-dir ${{ github.workspace }}/claude + --add-dir ${{ github.workspace }}/claude/shellhub From 536d0a8dabbd715fec271bc9873e22df223e6c47 Mon Sep 17 00:00:00 2001 From: Otavio Salvador Date: Sat, 12 Sep 2026 17:56:34 -0300 Subject: [PATCH 2/4] ci: tell the finalize job how the review ended Without it the job cannot tell a review that finished quietly from one that failed, and it was rewriting both to "no new issues were found". On cloud#2554 that turned a review which exhausted its turn cap into a clean bill of health. --- .github/workflows/claude-code-review.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index c72cfcfc025..c7c97b2fae9 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -258,3 +258,4 @@ jobs: repo: ${{ github.repository }} pr-number: ${{ github.event.pull_request.number || github.event.issue.number }} github-token: ${{ secrets.GITHUB_TOKEN }} + review-result: ${{ needs.review.result }} From c9e934f5669b4c663f98384013296726d1e9d564 Mon Sep 17 00:00:00 2001 From: Otavio Salvador Date: Sat, 12 Sep 2026 18:12:34 -0300 Subject: [PATCH 3/4] ci: stop the cross-repo token persisting in the claude checkout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit claude-code-review.yml sets persist-credentials: false on the identical checkout; this one did not, so the app token stayed in claude/.git/config for the life of the job. Nothing here pushes from that checkout — it supplies config the CLI reads. --- .github/workflows/claude.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index dfc3a590384..1241107e20e 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -98,6 +98,7 @@ jobs: with: repository: shellhub-io/claude token: ${{ steps.app-token.outputs.token }} + persist-credentials: false fetch-depth: 1 path: claude From 3e6ef98b0aa6ba8af2667d1b2a13d6f50b43a90b Mon Sep 17 00:00:00 2001 From: Otavio Salvador Date: Sat, 12 Sep 2026 18:39:23 -0300 Subject: [PATCH 4/4] ci: fail loudly when the workspace config does not reach @claude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit workspace.sh sync cannot report this class of failure: every step in sync_root is a conditional skip, not a check, so the script exits 0 having linked nothing. That is how every @claude invocation ran without the repo rules, the skills and CLAUDE.md with a human present each time and nobody noticing — the symptom is "Claude seems worse lately", never a red check. --add-dir removes today's cause. This removes the silence, so the next regression in the same seam — a workspace.sh layout change, a renamed claude/shellhub/, a wrong path: — stops the job instead of quietly degrading the model. The review workflow already guards its own config this way. Raised by the review on cloud#2554. --- .github/workflows/claude.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 1241107e20e..f17633b7933 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -106,6 +106,19 @@ jobs: run: | "$GITHUB_WORKSPACE/claude/workspace.sh" sync -w "$GITHUB_WORKSPACE" --project shellhub + - name: Verify the workspace config is readable + run: | + for file in \ + "$GITHUB_WORKSPACE/claude/CLAUDE.md" \ + "$GITHUB_WORKSPACE/claude/shellhub/CLAUDE.md" \ + "$GITHUB_WORKSPACE/claude/.claude/rules/code-style.md" \ + "$GITHUB_WORKSPACE/claude/.claude/skills/code-review/SKILL.md"; do + if [[ ! -f "$file" ]]; then + echo "::error::Workspace config not found at $file" + exit 1 + fi + done + - name: Run Claude timeout-minutes: 60 uses: anthropics/claude-code-action@8251c103ac8c1d761882c86aba1412c7f583c844 # v1