Skip to content

fix: split night shift into agent decider + deterministic publisher#1050

Merged
lroolle merged 1 commit into
mainfrom
fix/decider-publisher-split
Jul 15, 2026
Merged

fix: split night shift into agent decider + deterministic publisher#1050
lroolle merged 1 commit into
mainfrom
fix/decider-publisher-split

Conversation

@lroolle

@lroolle lroolle commented Jul 15, 2026

Copy link
Copy Markdown
Member

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:

  • Agent = pure decider. Reads git status/git diff, writes .decision/{signal,version,title,body.md} (gitignored) with the Write tool. Zero side-effecting Bash.
  • Publisher = plain bash step. Branch, commit, push, gh pr create, self-merge for signal=minor, Bark push for signal=high. Real error propagation, no permission system in the path.
  • Failure alert step barks with the run URL when anything fails.
  • Tripwire from fix: unbreak scheduled PR pipeline (silent gh-pr-create denials since June 14) #1047 stays as belt-and-suspenders.

Verification: manual dispatch after merge; expect green + a real PR for the v2.1.205-210 backlog.

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings July 15, 2026 18:37
@lroolle
lroolle merged commit 6402c37 into main Jul 15, 2026
@lroolle
lroolle deleted the fix/decider-publisher-split branch July 15, 2026 18:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 .gitignore for CI decision artifacts.
  • Update fetch-claude-docs.yml to split “decide” (agent) from “publish” (Bash), add failure alerting, and keep the existing tripwire verification.
  • Extend .claude/settings.json permissions 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
Comment on lines +187 to +198
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
Comment on lines +166 to +169
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
BARK_SERVER: ${{ secrets.BARK_SERVER }}
BARK_DEVICES: ${{ secrets.BARK_DEVICES }}

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +84 to +85
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants