fix: split night shift into agent decider + deterministic publisher#1050
Conversation
Third failure mode of the same class: even single-line script invocations
get denied - agent-composed commands with free-text args (quotes, commas,
parentheses in PR titles) keep failing Bash prefix permission matching /
injection heuristics. 13 denials in run 29440474890 after two previous
allowlist designs (claude_args, settings.json) failed the same way.
Root fix is architectural: stop giving the agent publish side effects.
- Agent step ('Claude decides') only reads git status/diff and writes
.decision/{signal,version,title,body.md} via the Write tool
- New 'Publish decision' step: plain bash - branch, commit, push,
gh pr create, self-merge minor PRs, bark on high-signal PRs
- 'Alert on failure' step barks when any step fails
- show_full_output stays on: transcripts are the only way to audit an
autonomous pipeline, and GitHub masks secrets in logs
- barkme MCP dropped from the fetch workflow (publisher curls Bark
directly); create-docs-pr.sh no longer used by CI, kept for humans
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR refactors the scheduled “night shift” pipeline so the Claude agent becomes a pure decision maker (writes .decision/* files) and all side-effecting work (branch/commit/PR/merge/notify) moves into a deterministic Bash publisher step, avoiding fragile Bash allowlist prefix-matching issues.
Changes:
- Add
.decision/to.gitignorefor CI decision artifacts. - Update
fetch-claude-docs.ymlto split “decide” (agent) from “publish” (Bash), add failure alerting, and keep the existing tripwire verification. - Extend
.claude/settings.jsonpermissions to allow editing.decision/**.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
.gitignore |
Ignores the new .decision/ directory used as CI handoff between agent and publisher. |
.github/workflows/fetch-claude-docs.yml |
Replaces agent-driven publishing with a deterministic Bash publisher step + failure alerting. |
.claude/settings.json |
Allows the agent to write decision files under .decision/**. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | ||
| github_token: ${{ steps.app-token.outputs.token }} | ||
| allowed_bots: "claude-yolo[bot]" | ||
| show_full_output: true |
| SIGNAL=$(tr -d '[:space:]' < "$D/signal") | ||
| VERSION=$(tr -d '[:space:]' < "$D/version") | ||
| TITLE=$(head -1 "$D/title") | ||
| case "$SIGNAL" in high|minor) ;; *) | ||
| echo "::error::Invalid signal '$SIGNAL' (want high|minor)"; exit 1;; | ||
| esac | ||
|
|
||
| BRANCH="docs/claude-code-v${VERSION}-$(date +%Y%m%d-%H%M)" | ||
| git checkout -b "$BRANCH" | ||
| git add -A | ||
| git commit -m "docs: ${TITLE}" -m "Co-Authored-By: claude-yolo[bot] <claude-yolo@lroole.com>" | ||
| git push -u origin HEAD |
| env: | ||
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| BARK_SERVER: ${{ secrets.BARK_SERVER }} | ||
| BARK_DEVICES: ${{ secrets.BARK_DEVICES }} |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9d5854d9ba
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| You DECIDE what the changes mean. You do NOT commit, push, or open PRs - | ||
| a deterministic publish step right after you handles all of that from |
There was a problem hiding this comment.
Remove publish-capable tools from the decider
In this decider step, the only barrier preventing Claude from committing or opening PRs is the prompt text, but the action still loads the project allowlist that grants Bash(git:*), Bash(gh pr:*), and Bash(./scripts/create-docs-pr.sh:*). When the fetched docs/diff contain prompt-injection text or the model simply chooses those tools, the decider can still run the same publishing side effects before Publish decision runs, recreating the side-effect path this split is meant to eliminate. Use a decider-specific settings/allowedTools list with only read-only inspection commands and Edit(.decision/**).
Useful? React with 👍 / 👎.
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | ||
| github_token: ${{ steps.app-token.outputs.token }} | ||
| allowed_bots: "claude-yolo[bot]" | ||
| show_full_output: true |
There was a problem hiding this comment.
Disable full Claude output in scheduled runs
When this scheduled workflow runs on the public repo, show_full_output: true makes the action log all Claude Code messages and tool outputs; the action’s security docs warn those outputs can include file contents, API responses, and secrets. Since this job has OAuth/GitHub/Bark secrets and reads external fetched content, a prompt-injected allowed command or file read can leave unmasked derived secret material in public Actions logs. Leave this false by default and enable it only for short-lived manual debugging.
Useful? React with 👍 / 👎.
Follow-up to #1049. Run 29440474890 showed 13 permission denials even with single-line invocation guidance — agent-composed publish commands with free-text arguments structurally cannot survive Bash prefix permission matching. Three allowlist designs failed identically (claude_args → settings.json → single-line prompt), so per the three-strikes rule this stops tuning and changes the architecture:
git status/git diff, writes.decision/{signal,version,title,body.md}(gitignored) with the Write tool. Zero side-effecting Bash.gh pr create, self-merge forsignal=minor, Bark push forsignal=high. Real error propagation, no permission system in the path.Verification: manual dispatch after merge; expect green + a real PR for the v2.1.205-210 backlog.
🤖 Generated with Claude Code