Skip to content

fix: resolve develop CI failures (cli # escape + skip-check + allowed_bots)#2520

Merged
bpamiri merged 3 commits intodevelopfrom
claude/fix-cli-hash-escape
May 9, 2026
Merged

fix: resolve develop CI failures (cli # escape + skip-check + allowed_bots)#2520
bpamiri merged 3 commits intodevelopfrom
claude/fix-cli-hash-escape

Conversation

@bpamiri
Copy link
Copy Markdown
Collaborator

@bpamiri bpamiri commented May 9, 2026

Summary

Three sequentially-discovered fixes that together restore develop CI to green. Each new fix was uncovered by the previous one passing.

1. cli/lucli/Module.cfc:3723 — unescaped # in CFML string literal

PR #2517 added a help banner whose URL fragment contains testing#testing-against-different-engines. Lucee's parser interprets unescaped # in CFScript string literals as expression delimiters and crashes the file's compilation with Invalid Syntax Closing [#] not found.

Crashed Module.cfc, which broke wheels new and the Wheels Snapshots → Smoke Test Installed Distribution pipeline on every push since #2517.

Fix: one character — testing#testing##. CFML rule: ## in a string literal outputs a literal #.

2. .github/actions/wheels-bot-skip-check/action.ymlvars context not accessible in composite actions

The skip-check composite action's internal env: block declared WHEELS_BOT_ENABLED: ${{ vars.WHEELS_BOT_ENABLED }}. Composite actions don't have access to the vars context — only workflow files do. Every invocation failed with Unrecognized named-value: 'vars', causing Reviewer A to fail-fast in 13 seconds on every PR after the kill switch was activated.

The kill switch is already enforced at the job level via if: vars.WHEELS_BOT_ENABLED == 'true' in every wheels-bot workflow, so the action's internal check was redundant as well as broken.

Fix: remove the unreachable check. The action now focuses on label/title/marker checks; the kill switch lives at the job level.

3. All wheels-bot workflows — claude-code-action blocks bot-initiated runs by default

After fix #2, Reviewer A passed and triggered Reviewer B. Reviewer B failed in 17s with:

Action failed with error: Workflow initiated by non-human actor: wheels-bot (type: Bot). Add bot to allowed_bots list or use '*' to allow all bots.

The anthropics/claude-code-action defaults to blocking workflow runs initiated by any GitHub Bot identity. Same shape would hit bot-research, bot-propose-fix, and bot-auto-close (cron actor is github-actions[bot]) as the rollout progresses through Phases 2-5.

Fix: add allowed_bots: 'wheels-bot[bot],github-actions[bot]' to all 6 wheels-bot workflows (review-a, review-b, research, propose-fix, triage, auto-close). Specific allowlist preferred over '*' because the repo is public — '*' would let any external GitHub App invoke the action with prompts they could influence.

Why one PR for three fixes

Each new fix was a consequence of the prior one passing. Without #1, the smoke test failure masked everything else. Without #2, Reviewer A never ran successfully so the bot-initiator block in #3 was never surfaced. Splitting into three PRs would mean each ran into the unfixed downstream issues in CI; bundling resolves them as a unit.

Test plan

  • CI passes (smoke test, full Lucee suite, Reviewer A, Reviewer B all green)
  • Reviewer B successfully runs on this PR (after Reviewer A's review triggers it)
  • After merge, wheels test --db=mysql (without --core) prints the help banner with a literal # in the URL
  • Future bot-triggered workflows (research, propose-fix in Phase 4; auto-close cron in Phase 5) don't hit the bot-actor block

🤖 Generated with Claude Code

Module.cfc:3723 prints a help URL containing the unescaped fragment
"testing#testing-against-different-engines". Lucee's parser interprets
unescaped # in CFScript string literals as expression delimiters and
crashes the file's compilation with "Invalid Syntax Closing [#] not
found" when no closing # is found.

This crashed Module.cfc compilation, which broke `wheels new` and
the Wheels Snapshots smoke test pipeline (build / Smoke Test Installed
Distribution). Every push to develop has been failing this gate since
PR #2517 introduced the line.

CFML rule: ## inside a string literal outputs a literal #. The fix is
a one-character change.

See CLAUDE.md "# escape gotcha" for the same bug class historical
context.
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

LGTM — one-character escape of # to ## in a CFML string literal, exactly the gotcha documented in CLAUDE.md.

Extended reasoning...

Overview

Single-line change in cli/lucli/Module.cfc escaping # to ## inside a string passed to out(). The visible URL fragment for end users remains testing#testing-against-different-engines (CFML doubles ## to render a single #).

Security risks

None. The change is in a help banner string and does not affect parsing, auth, I/O, or any user-controlled data path.

Level of scrutiny

Minimal. This is a mechanical, one-character correctness fix for a CFML/Lucee parser quirk that was crashing Module.cfc compilation in the smoke test pipeline. The fix exactly matches the pattern documented in the project's own CLAUDE.md ("# escape gotcha").

Other factors

No bugs were flagged by the bug hunting system. The PR description clearly explains the root cause, why other tests missed it, and what to verify post-merge. The change is self-contained with no risk to other code paths.

…action

Composite actions cannot read the `vars` context — that lookup is only
available in workflow files. The skip-check action's internal kill-switch
check failed every time the action was invoked, with:

  Unrecognized named-value: 'vars'.
  Located at position 1 within expression: vars.WHEELS_BOT_ENABLED

This silently broke from PR #2518 onwards but only surfaced after the
kill switch was activated and Reviewer A actually fired on a real PR.

The kill switch is already enforced at the job level via
`if: vars.WHEELS_BOT_ENABLED == 'true'` in every wheels-bot workflow,
so the action's internal check was redundant as well as broken.

Removed:
- `WHEELS_BOT_ENABLED` env binding (couldn't resolve)
- The `if [[ ... != "true" ]]; then skip=true ...` block (redundant)

Description updated to note that the kill switch lives at the job
level, not in this action.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bpamiri bpamiri changed the title fix(cli): escape # in test help URL fragment fix: resolve develop CI failures (cli # escape + skip-check action) May 9, 2026
wheels-bot[bot]
wheels-bot Bot previously approved these changes May 9, 2026
Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: Two focused bug fixes bundled together because the smoke test that surfaces fix 1 was also triggering fix 2's failure mode. Both fixes are correct, minimal, and well-explained. Approving.


Correctness

Both fixes address the stated root causes accurately.

Fix 1 — cli/lucli/Module.cfc:3723

- out("See: command-line-tools/wheels-commands/testing#testing-against-different-engines", "yellow");
+ out("See: command-line-tools/wheels-commands/testing##testing-against-different-engines", "yellow");

Correct. Lucee's CFScript parser treats unescaped # in string literals as expression delimiters and crashes the file's compilation. ## is the canonical escape that emits a literal # at runtime. This is the exact pattern documented in CLAUDE.md under Testing Quick Reference:

Unescaped # in strings causes "Invalid Syntax Closing [#] not found" compilation errors that crash the entire test suite (not just that file).

Fix 2 — .github/actions/wheels-bot-skip-check/action.yml

The removed block:

env:
  WHEELS_BOT_ENABLED: ${{ vars.WHEELS_BOT_ENABLED }}
if [[ "${WHEELS_BOT_ENABLED:-}" != "true" ]]; then
  skip=true
  reason="vars.WHEELS_BOT_ENABLED is not 'true'"
fi

Correct diagnosis. GitHub Actions composite actions cannot access the vars context — only caller workflow files can. The check would always evaluate WHEELS_BOT_ENABLED as an empty string, causing every invocation to set skip=true regardless of the repository variable's actual value.

The kill-switch coverage claim is verified by reading the workflow files: all six bot workflows (bot-triage.yml, bot-research.yml, bot-propose-fix.yml, bot-review-a.yml, bot-review-b.yml, bot-auto-close.yml) gate on if: vars.WHEELS_BOT_ENABLED == 'true' at the job level. The action-level check was both broken and redundant.

The refactored script correctly flattens the previously nested conditionals (which were gated on skip == "false" from the now-removed block). Logic is unchanged for the remaining checks: label scan, title substring match, idempotency marker grep.


Commits

Both commits conform to commitlint.config.js:

  • fix(cli): escape # in test help URL fragment — type fix, scope cli (valid), subject within 100 chars.
  • ci: remove unreachable vars.WHEELS_BOT_ENABLED check from skip-check action — type ci, no scope (correct — CI infrastructure does not map cleanly to a layer scope), subject within 100 chars.

Tests

No automated test changes — appropriate for both fixes:

  • Fix 1 is a one-character string literal correction; the smoke test pipeline that was crashing serves as the integration validator.
  • Fix 2 is CI infrastructure; the action's behavior is validated by the bot workflows running successfully post-merge.

Security

No concerns. Neither change touches authentication, user-controlled data paths, or secrets handling.

The anthropics/claude-code-action defaults to blocking workflow runs
initiated by non-human actors (any GitHub Bot identity). Without an
explicit allowlist, every bot-triggered workflow fails fast with:

  Action failed with error: Workflow initiated by non-human actor:
  wheels-bot (type: Bot). Add bot to allowed_bots list or use '*' to
  allow all bots.

This surfaced first on bot-review-b.yml — Reviewer B is triggered by
wheels-bot[bot] submitting a Reviewer A review, and the action blocked
on the bot initiator. Same shape would hit bot-research, bot-propose-fix,
and bot-auto-close (cron actor is github-actions[bot]) as the rollout
progresses through Phases 2-5.

Adding `allowed_bots: 'wheels-bot[bot],github-actions[bot]'` to all six
wheels-bot workflows. Specific allowlist (not '*') because the repo is
public — '*' would let any external GitHub App invoke the action with
prompts they could influence (per the action's docs/security.md).

The two identities allowed:
- wheels-bot[bot]: our App's bot identity
- github-actions[bot]: GitHub's own actor for scheduled and
  workflow-internal triggers

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bpamiri bpamiri changed the title fix: resolve develop CI failures (cli # escape + skip-check action) fix: resolve develop CI failures (cli # escape + skip-check + allowed_bots) May 9, 2026
@bpamiri bpamiri merged commit 3d14e1f into develop May 9, 2026
6 checks passed
@bpamiri bpamiri deleted the claude/fix-cli-hash-escape branch May 9, 2026 19:41
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.

1 participant