ci: give @claude the workspace config through --add-dir - #7083
Conversation
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.
|
Claude finished @otavio's task in 12m 20s —— View job Code Review CompleteReviewed 1 file. 1 inline issue posted — Standards: 0 violations, 1 judgement call. Spec: axis did not run. Correctness: 0. Standards
SpecThe Spec axis did not run: the PR references no issue, only the paired PRs shellhub-io/claude#65 and shellhub-io/cloud#2554. A spec is never reconstructed from the PR title. Additional notesThe mechanism checks out end to end, against the action source at the pinned SHA rather than from memory: No correctness findings. One judgement call was dropped below the reporting bar: on plain-issue To request another review round, comment |
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_token: ${{ secrets.GITHUB_TOKEN }} | ||
| trigger_phrase: "@claude" | ||
| claude_args: | | ||
| --add-dir ${{ github.workspace }}/claude |
There was a problem hiding this comment.
Standards (judgement call) — writing-for-agents: "Check every line for relevance: does it still bear on what the document does? A line loses relevance by never bearing on the task … or by going stale as the behaviour or world it describes changes."
This line plus CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 loads claude/CLAUDE.md — the workspace-root memory that the step right above deliberately does not install here. workspace.sh:145-151 picks the project file instead when --project is set:
# In CI, we link the project-specific CLAUDE.md (e.g., claude/shellhub/CLAUDE.md)
# instead of the root one, since the workspace root IS the project checkout.
local claude_md="$SCRIPT_DIR/CLAUDE.md"
if [[ -n "${sync_project:-}" ]]; then
claude_md="$SCRIPT_DIR/$sync_project/CLAUDE.md"
fi
The env var reaches every added dir, so --add-dir .../claude brings that file back through a side channel. Its Projects table is the part that has gone stale for this job's layout — the checkout at line 62 has no path:, so the workspace root is the shellhub checkout, while cloud/ and claude/ are nested under it:
| Row | In this job |
|---|---|
ShellHub → shellhub/ |
wrong — no such directory |
Cloud → cloud/, Claude → claude/ |
correct |
Connect → connect/, Docs → docs/, Team → team/ |
not checked out |
So the session gets the lead row asserting a path that does not exist, sitting next to claude/shellhub/CLAUDE.md, which correctly assumes root-as-checkout. Concrete cost: an @claude run that takes the table at face value goes looking under shellhub/ for files that are at the root.
Worth noting the obvious fix is not dropping this line. On PR runs restoreConfigFromBase wipes the root .claude/ before the session starts, so --add-dir .../claude is exactly what delivers the rules and skills — remove it and you reopen the bug. The stale table is a claude/CLAUDE.md problem, so the fix belongs in shellhub-io/claude#65: scope that table, or note that in CI the workspace root is the project checkout.
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.
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.
Pareado com shellhub-io/claude#65 e shellhub-io/cloud#2554.
O bug
O
claude.ymlsincroniza a config e em seguida roda uma action que a apaga:.claude/eCLAUDE.mdda raiz do workspace são substituídos pelas cópias do branch base, e este repo não versiona nenhum dos dois. Todo@claudevinha rodando sem as rules, sem as skills e sem oCLAUDE.md.Aqui não existe prompt onde apontar um caminho sobrevivente, então a correção é automática:
--add-dirpara o checkoutclaude/maisCLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD. O segundo--add-dirtraz oclaude/shellhub/CLAUDE.md, que nunca chegou à sessão.Testes
actionlint e yamllint limpos. Como sempre neste repo, este PR não exercita a própria mudança —
pull_request_targeteissue_commentrodam o workflow do branch base. Quem verifica é o shellhub-io/cloud#2554, que dispara empull_request.Ordem de merge: claude#65, depois cloud#2554 (conferir o run), depois este.