Skip to content

Commit c1e68c9

Browse files
NiveditJainclaude
andcommitted
[luv-336] fix: anchor workflow-policy regexes to command boundaries
The `release-prep-check`, `changelog-check`, `docs-check`, and `pr-description-check` policies used unanchored regexes against the Bash command string, so the literal phrases (`gh pr create`, `git commit`, `git push`) matched anywhere — including inside HEREDOC bodies or quoted arguments. Concrete failure: editing a PR body that contained the words "gh pr create" inside backticks fired the new `release-prep-check` policy on the `gh pr edit` invocation. Extracts a shared `matchesCommand(cmd, verbPattern)` helper that anchors the verb-phrase match to a command boundary (`^`, `;`, `&&`, `||`, `|`, or newline). All four policies route through it. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 3284b8e commit c1e68c9

2 files changed

Lines changed: 18 additions & 5 deletions

File tree

.failproofai/policies/workflow-policies.mjs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@
55
*/
66
import { customPolicies, allow, instruct } from "failproofai";
77

8+
/**
9+
* Match `<verb-phrase>` only when it appears at a command boundary (start of
10+
* string, `;`, `&&`, `||`, `|`, or newline). Avoids false-positive matches
11+
* when the literal phrase appears inside a HEREDOC or a quoted argument
12+
* (e.g. `gh pr edit --body "...gh pr create..."` would previously trigger
13+
* `release-prep-check` because the regex matched anywhere in the string).
14+
*/
15+
function matchesCommand(cmd, verbPhrasePattern) {
16+
return new RegExp(
17+
String.raw`(?:^|[;\n|]|&&|\|\|)\s*` + verbPhrasePattern + String.raw`\b`,
18+
).test(cmd);
19+
}
20+
821
// Remind to update CHANGELOG before committing
922
customPolicies.add({
1023
name: "changelog-check",
@@ -13,7 +26,7 @@ customPolicies.add({
1326
fn: async (ctx) => {
1427
if (ctx.toolName !== "Bash") return allow();
1528
const cmd = String(ctx.toolInput?.command ?? "");
16-
if (/git\s+commit/.test(cmd)) {
29+
if (matchesCommand(cmd, String.raw`git\s+commit`)) {
1730
return instruct(
1831
"Check whether CHANGELOG.md needs an update for this commit. " +
1932
"Every PR must include an entry under the current `## <version> — <YYYY-MM-DD>` section " +
@@ -34,7 +47,7 @@ customPolicies.add({
3447
fn: async (ctx) => {
3548
if (ctx.toolName !== "Bash") return allow();
3649
const cmd = String(ctx.toolInput?.command ?? "");
37-
if (/git\s+commit/.test(cmd)) {
50+
if (matchesCommand(cmd, String.raw`git\s+commit`)) {
3851
return instruct(
3952
"Check whether documentation needs updating for this change. " +
4053
"Consider: docs/*.mdx files, README.md, and examples/ directory. " +
@@ -53,7 +66,7 @@ customPolicies.add({
5366
fn: async (ctx) => {
5467
if (ctx.toolName !== "Bash") return allow();
5568
const cmd = String(ctx.toolInput?.command ?? "");
56-
if (/git\s+push/.test(cmd)) {
69+
if (matchesCommand(cmd, String.raw`git\s+push`)) {
5770
return instruct(
5871
"After pushing, check if there is an open PR for this branch. " +
5972
"If so, update the PR description to reflect the latest changes."
@@ -74,7 +87,7 @@ customPolicies.add({
7487
fn: async (ctx) => {
7588
if (ctx.toolName !== "Bash") return allow();
7689
const cmd = String(ctx.toolInput?.command ?? "");
77-
if (!/\bgh\s+pr\s+create\b/.test(cmd)) return allow();
90+
if (!matchesCommand(cmd, String.raw`gh\s+pr\s+create`)) return allow();
7891
return instruct(
7992
"Before creating the PR, ensure CHANGELOG.md entries land under a versioned section so the PR ships release-ready:\n" +
8093
" 1. Read `version` from package.json (e.g. `0.0.10-beta.10`).\n" +

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
### Fixes
66
- `lib/opencode-projects.ts`: align `ProjectFolder.name` for OpenCode projects with the URL-slug encoding every other CLI uses (`encodeFolderName(worktree)`, e.g. `-home-nivedit-dev-purge`), replacing the previous `proj.name`/`basename(worktree)` fallback chain that produced bare names like `dev-purge`. The `[name]` route handler resolves OpenCode sessions via `encodeFolderName(p.worktree) === name` in `getOpenCodeSessionsByEncodedName`, and the Projects-list link in `app/components/project-list.tsx:330` is built from `folder.name` — so an OpenCode-only project at `/home/nivedit/dev-purge` was emitting `/project/dev-purge`, which 404'd, while the session-row URL built elsewhere via `encodeCwdForUrl(cwd)` correctly used `-home-nivedit-dev-purge` and worked. Side benefit: `mergeProjectFolders` (`lib/projects.ts:107`) keys by `f.name`, so OpenCode projects now merge with their Claude / Codex / Copilot / Cursor / Pi / Gemini siblings into a single row with multiple CLI badges instead of appearing as two separate rows. Display via `decodeFolderName(folder.name)` in the Projects table now shows the full path (`/home/nivedit/dev-purge`) instead of the prior `dev/purge`, matching every other CLI. Three test assertions in `__tests__/lib/opencode-projects.test.ts` updated to pin the encoded form (`-repo`, `-other`); existing `getOpenCodeSessionsByEncodedName` tests already asserted the correct contract and didn't need changes (#335).
7-
- `.failproofai/policies/workflow-policies.mjs`: new `release-prep-check` policy fires on `gh pr create` and instructs the agent to ensure CHANGELOG entries land under a versioned `## <version> — <YYYY-MM-DD>` heading (creating that heading above the previous version's section if needed) so each merged feature PR ships release-ready. The existing `changelog-check` policy is updated to match — the `## Unreleased` section is gone; entries always go under a dated, versioned heading. Version bumps in `package.json` remain reserved for `luv-cut-*` PRs by the existing `block-version-bumps` policy.
7+
- `.failproofai/policies/workflow-policies.mjs`: new `release-prep-check` policy fires on `gh pr create` and instructs the agent to ensure CHANGELOG entries land under a versioned `## <version> — <YYYY-MM-DD>` heading (creating that heading above the previous version's section if needed) so each merged feature PR ships release-ready. The existing `changelog-check` policy is updated to match — the `## Unreleased` section is gone; entries always go under a dated, versioned heading. Version bumps in `package.json` remain reserved for `luv-cut-*` PRs by the existing `block-version-bumps` policy. All four policies in this file (`changelog-check`, `docs-check`, `pr-description-check`, `release-prep-check`) now route through a shared `matchesCommand` helper that anchors verb-phrase matches to a command boundary (`^`, `;`, `&&`, `||`, `|`, or newline), so the literal phrases `git commit` / `git push` / `gh pr create` no longer false-positive when they appear inside HEREDOC bodies or quoted arguments (e.g. `gh pr edit --body "...gh pr create..."` no longer triggers `release-prep-check`).
88

99
## 0.0.10-beta.9 — 2026-05-09
1010

0 commit comments

Comments
 (0)