Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions packages/guardrails/profile/plugins/guardrail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,32 @@ export default async function guardrail(input: {
throw new Error(text("merge blocked: run /review before merging"))
}
}
// CI hard block: verify all checks are green before gh pr merge
if (/\bgh\s+pr\s+merge\b/i.test(cmd)) {
try {
const prMatch = cmd.match(/\bgh\s+pr\s+merge\s+(\d+)/i)
const prArg = prMatch ? prMatch[1] : ""
const proc = Bun.spawn(["gh", "pr", "checks", ...(prArg ? [prArg] : [])], {
cwd: input.worktree,
stdout: "pipe",
stderr: "pipe",
})
const [ciOut] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
])
if (proc.exitCode !== 0 || /fail|pending/i.test(ciOut)) {
await mark({ last_block: "bash", last_command: cmd, last_reason: "CI checks not all green" })
throw new Error(text("merge blocked: CI checks not all green — run `gh pr checks` to verify"))
}
} catch (e) {
if (String(e).includes("blocked")) throw e
// gh unavailable or network failure — log so CI skip is observable
await mark({ last_block: "bash:ci-warn", last_command: cmd, last_reason: "CI check verification failed" })
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

In the fallback path (when gh pr checks can’t be executed), the code only calls mark(...) but doesn’t emit any user-visible warning. That contradicts the inline comment (“warn so user knows CI was not verified”) and the PR description’s fallback-to-warning behavior. Consider appending a warning to the bash tool output for gh pr merge (e.g., via tool.execute.after when the last state indicates bash:ci-warn) so the user actually sees it.

Suggested change
await mark({ last_block: "bash:ci-warn", last_command: cmd, last_reason: "CI check verification failed" })
await mark({ last_block: "bash:ci-warn", last_command: cmd, last_reason: "CI check verification failed" })
process.stderr.write(`${text("warning: unable to verify CI checks before merge; proceeding without CI verification")}\n`)

Copilot uses AI. Check for mistakes.
console.warn("[guardrail] CI check verification failed — gh may be unavailable: " + String(e))
}
}
// Direct push to protected branches
const protectedBranch = /^(main|master|develop|dev)$/
if (/\bgit\s+push\b/i.test(cmd)) {
Expand Down Expand Up @@ -614,11 +640,35 @@ export default async function guardrail(input: {
}
}

// Architecture layer advisory
if ((item.tool === "edit" || item.tool === "write") && file && code(file)) {
const relFile = rel(input.worktree, file)
const content = typeof item.args?.content === "string" ? item.args.content :
typeof item.args?.newString === "string" ? item.args.newString : ""
if (content) {
const isUI = /^(src\/(ui|components|tui)\/)/i.test(relFile)
const isAPI = /^(src\/(api|routes)\/)/i.test(relFile)
const importsDB = /from\s+['"].*\/(db|database|model|sql)\//i.test(content)
const importsUI = /from\s+['"].*\/(ui|components|tui)\//i.test(content)
if (isUI && importsDB) {
out.output += "\n⚠️ Architecture: UI layer importing from DB layer directly. Consider using a service/repository layer."
}
if (isAPI && importsUI) {
out.output += "\n⚠️ Architecture: API layer importing from UI layer. This creates a circular dependency risk."
}
}
}

// CI status advisory after push/PR create
if (item.tool === "bash" && /\b(git\s+push|gh\s+pr\s+create)\b/i.test(str(item.args?.command))) {
out.output = (out.output || "") + "\n⚠️ Remember to verify CI status: `gh pr checks`"
}

// Post-merge deployment verification advisory
if (item.tool === "bash" && /\bgh\s+pr\s+merge\b/i.test(str(item.args?.command))) {
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

The post-merge verification message is emitted for any gh pr merge invocation, even when the merge command fails. Since the bash tool provides out.metadata.exit, consider gating this advisory on a successful exit code (e.g., exit === 0) so it only triggers after a successful merge.

Suggested change
if (item.tool === "bash" && /\bgh\s+pr\s+merge\b/i.test(str(item.args?.command))) {
if (
item.tool === "bash" &&
/\bgh\s+pr\s+merge\b/i.test(str(item.args?.command)) &&
out.metadata?.exit === 0
) {

Copilot uses AI. Check for mistakes.
out.output = (out.output || "") + "\n🚀 Post-merge: verify deployment status and run smoke tests on the target environment."
}

if (item.tool === "task") {
const cmd = typeof item.args?.command === "string" ? item.args.command : ""
const agent = typeof item.args?.subagent_type === "string" ? item.args.subagent_type : ""
Expand Down
Loading