Skip to content

feat(guardrails): add CI hard block, architecture advisory, and post-deploy verify hooks#119

Merged
terisuke merged 1 commit intodevfrom
feat/guardrails-ci-hardblock-hooks
Apr 6, 2026
Merged

feat(guardrails): add CI hard block, architecture advisory, and post-deploy verify hooks#119
terisuke merged 1 commit intodevfrom
feat/guardrails-ci-hardblock-hooks

Conversation

@terisuke
Copy link
Copy Markdown

@terisuke terisuke commented Apr 6, 2026

Issue for this PR

Closes #115

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds 3 high-priority guardrail hooks to guardrail.ts (708 → 757 lines):

  1. CI Hard Block (tool.execute.before): Runs gh pr checks before allowing gh pr merge. Blocks if any check is failing/pending. Falls back to warning if gh is unavailable (with state tracking via mark()).

  2. Architecture Layer Advisory (tool.execute.after): Warns when source file edits introduce cross-layer imports:

    • UI/components/TUI files importing from DB/database/model/SQL
    • API/routes files importing from UI/components/TUI
  3. Post-deploy Verification Advisory (tool.execute.after): Reminds to verify deployment status after gh pr merge succeeds.

All existing functionality is preserved. The file remains under 800 lines (757).

How did you verify your code works?

  1. Typecheck: bun turbo typecheck — 13/13 pass
  2. Code review: Fixed HIGH-severity silent error swallowing in CI hard block catch clause (now logs warning via mark())
  3. Verified all 3 hooks are at expected locations in the file

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Copilot AI review requested due to automatic review settings April 6, 2026 10:25
… post-deploy verify hooks

- CI hard block: runs `gh pr checks` before allowing `gh pr merge`, blocks if any check is failing/pending
- Architecture layer advisory: warns when UI layer imports DB layer or API layer imports UI layer
- Post-deploy verification advisory: reminds to verify deployment status after `gh pr merge`

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds additional high-priority Guardrails hooks to improve merge safety (CI gating), enforce architectural boundaries (advisory), and prompt operational follow-ups after merges.

Changes:

  • Add a pre-merge CI gate for gh pr merge by running gh pr checks and blocking merges when checks are failing/pending.
  • Add an architecture-layer advisory on edits/writes to warn about UI↔DB and API↔UI import direction violations.
  • Add a post-merge advisory to remind users to verify deployments after gh pr merge.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +518 to +534
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 — warn so user knows CI was not verified
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.

gh pr checks is spawned without any timeout/abort handling. If gh hangs (network/auth prompt), this hook can block gh pr merge indefinitely. Consider adding a reasonable timeout and killing the process (or treating timeout as a non-verifiable-CI warning) so merges don’t stall the session.

Suggested change
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 — warn so user knows CI was not verified
const ciCheckTimeoutMs = 15000
const proc = Bun.spawn(["gh", "pr", "checks", ...(prArg ? [prArg] : [])], {
cwd: input.worktree,
stdout: "pipe",
stderr: "pipe",
})
let timeoutId: ReturnType<typeof setTimeout> | undefined
try {
const timeout = new Promise<never>((_, reject) => {
timeoutId = setTimeout(() => {
try { proc.kill() } catch { }
reject(new Error("gh pr checks timed out"))
}, ciCheckTimeoutMs)
})
const [ciOut] = await Promise.race([
Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]),
timeout,
])
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"))
}
} finally {
if (timeoutId) clearTimeout(timeoutId)
}
} catch (e) {
if (String(e).includes("blocked")) throw e
// gh unavailable, network failure, or timeout — warn so user knows CI was not verified

Copilot uses AI. Check for mistakes.
} catch (e) {
if (String(e).includes("blocked")) throw e
// gh unavailable or network failure — warn so user knows CI was not verified
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.
}

// 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.
@terisuke terisuke force-pushed the feat/guardrails-ci-hardblock-hooks branch from de46803 to 4e3c710 Compare April 6, 2026 10:30
@terisuke terisuke merged commit 4387f92 into dev Apr 6, 2026
3 of 6 checks passed
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.

feat(guardrails): high-priority guardrail hooks — CI hard block + architecture enforcement

2 participants