feat(Guardrails): add guardrails feature to Bosun#457
Conversation
|
Bosun PR classification: public.
|
There was a problem hiding this comment.
Pull request overview
Adds a new “Guardrails” policy surface across the runtime (push/hook enforcement + input-quality gating), workflows (PR quality templates + managed pre-push enforcement), and UI (new Guardrails tab + desktop layout improvements), plus supporting tooling and tests.
Changes:
- Introduces guardrails policy storage/assessment (
infra/guardrails.mjs) and wires it into server APIs, task creation, and manual flows. - Enforces managed-worktree push safety (pre-push bootstrap + skipHooks guardrails) and expands code-quality workflow templates for PR review/Sonar remediation.
- Adds Guardrails UI tab + state plumbing, improves demo boot reliability + desktop layout, and adds site serving/dev tooling.
Reviewed changes
Copilot reviewed 56 out of 59 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| workspace/worktree-setup.mjs | Exports ensureGitHooksPath for use by preflight/repairs. |
| workflow/workflow-templates.mjs | Registers/exports new code-quality templates. |
| workflow/workflow-nodes/actions.mjs | Adds cwd template fallback + managed pre-push bootstrap in push action. |
| workflow/workflow-nodes.mjs | Guardrails enforcement for action.push_branch (skipHooks restrictions + bootstrap). |
| workflow-templates/github.mjs | Adds GitHub PR quality signal collection snippet used by templates. |
| workflow-templates/code-quality.mjs | Adds PR Review Quality Striker + SonarQube PR Striker templates. |
| ui/tabs/guardrails.js | New Guardrails admin tab UI (runtime/policy/hooks/assessment). |
| ui/styles/layout.css | Adds desktop-specific layout grid styling. |
| ui/modules/state.js | Adds guardrails caching, loader, and WS channels. |
| ui/modules/router.js | Adds Guardrails route/tab registration. |
| ui/demo.html | Adds forced layout boot param + demo GitHub commit endpoints + boot failure detection changes. |
| ui/app.js | Adds Guardrails lazy tab + emits bosun:app-mounted signal after render. |
| tools/vitest-runner.mjs | Adjusts reporter arg filtering logic. |
| tools/site-serve.mjs | Adds simple static server for site/. |
| tests/workflow-templates.test.mjs | Adds/updates template expectations for new PR-quality templates and other template behavior. |
| tests/workflow-templates-e2e.test.mjs | Adds “doesn’t crash” E2E runs for new templates. |
| tests/workflow-task-lifecycle.test.mjs | Updates skipHooks default + adds managed-worktree skipHooks block test. |
| tests/workflow-guaranteed.test.mjs | Adds guaranteed runs for new templates. |
| tests/workflow-engine.test.mjs | Adds test for unresolved cwd fallback in action.run_agent. |
| tests/ui-server.test.mjs | Adds API coverage for guardrails endpoints (policy/runtime/assess). |
| tests/sandbox/fixtures.mjs | Adds fixtures for new templates. |
| tests/repo-config.test.mjs | Adds tests for Claude settings normalization/repair. |
| tests/preflight.test.mjs | Adds coverage for hooksPath auto-repair in preflight. |
| tests/portal-ui-smoke.node.test.mjs | Smoke coverage for Guardrails route/tab registration. |
| tests/hook-profiles.test.mjs | Updates expectations for portable bridge paths + adds gemini/opencode coverage. |
| tests/hook-library.test.mjs | Adds coverage for the new “block agent direct push” safety hook. |
| tests/guardrails.test.mjs | Adds unit tests for guardrails policy persistence, assessment, and repo guardrail detection. |
| tests/github-pr-trust-regression.test.mjs | Updates regressions for Bosun-created provenance detection and branch sync workflow. |
| tests/demo-api-sync.test.mjs | Marks guardrails endpoints as intentionally server-only in demo sync test. |
| tests/codex-shell.test.mjs | Updates Codex runtime/profile tests (home resolution, provider selection, sandbox injection). |
| tests/agent-supervisor.test.mjs | Updates supervisor behavior for workflow-owned push lifecycle. |
| tests/agent-pool.test.mjs | Adds Windows Codex runtime prereq checks + deterministic failure handling tests. |
| site/ui/styles/layout.css | Mirrors desktop layout styling into site UI copy. |
| site/ui/modules/state.js | Mirrors guardrails state plumbing into site UI copy. |
| site/ui/modules/router.js | Mirrors Guardrails route/tab registration into site UI copy. |
| site/ui/demo.html | Mirrors demo boot/layout + GitHub commit simulation logic into site UI copy. |
| site/ui/app.js | Mirrors Guardrails lazy tab + mount event signaling into site UI copy. |
| site/README.md | Updates local dev instructions to use npm run site:serve. |
| shell/opencode-providers.mjs | Replaces promisify wrappers with explicit Promise wrappers capturing stdout/stderr. |
| shell/codex-model-profiles.mjs | Adds env-driven home directory resolution for reading Codex config. |
| server/ui-server.mjs | Adds guardrails endpoints + enforces INPUT guardrails on task/manual-flow requests + broadcasts invalidations. |
| package.json | Adds site:serve script + ships infra/guardrails.mjs in package files allowlist. |
| package-lock.json | Updates lockfile dependency specifiers/peer flags (aligned with package.json deps). |
| infra/preflight.mjs | Auto-repairs git core.hooksPath drift to .githooks during preflight. |
| infra/maintenance.mjs | Tweaks git branch listing and adds encoding to spawnSync calls. |
| infra/guardrails.mjs | New guardrails policy + repo script detection + input-quality assessment implementation. |
| config/repo-config.mjs | Normalizes Claude permissions and uses portable bridge paths. |
| agent/hook-profiles.mjs | Uses portable agent/agent-hook-bridge.mjs path and updates generated hook configs. |
| agent/hook-library.mjs | Adds new blocking hook preventing direct git push when guardrails require workflow-owned pushes. |
| agent/agent-supervisor.mjs | Adjusts “commits not pushed” guidance for workflow-owned push lifecycle. |
| agent/agent-prompt-catalog.mjs | Updates managed-task agent instructions to avoid direct pushing and require local validation before handoff. |
| agent/agent-pool.mjs | Adds Windows Codex runtime prereq detection + deterministic failure suppression + env cleanup for Azure keys. |
| _docs/AZURE_AGENTIC_INFRAOPS_BACKPORT_ANALYSIS.md | Adds analysis doc (reference/background). |
| .vscode/tasks.json | Adds “Serve Bosun Site” task. |
| .vscode/launch.json | Adds “Serve Bosun Site” debug configuration. |
| .gitignore | Fixes tmp/* ignore entry formatting. |
| .githooks/pre-push | Extends adjacency patterns to include guardrails-related files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| globalThis.addEventListener('bosun:app-mounted', function handleMounted() { | ||
| bootReady = true; | ||
| clearTimeout(bootTimeoutId); | ||
| globalThis.removeEventListener('bosun:app-mounted', handleMounted); | ||
| }); |
There was a problem hiding this comment.
Same boot sequencing problem as ui/demo.html: the bosun:app-mounted listener is registered after the app.js module script, so the event can be emitted before the listener exists, triggering false boot-failure UI. Move the listener setup before the app.js script tag (or add a persistent mounted flag that the boot script can read).
workflow/workflow-nodes/actions.mjs
Outdated
| `Agent cwd resolved from unresolved template (${resolvedCwd}) to ${cwd}`, | ||
| "warn", | ||
| ); | ||
| } | ||
| const toolContract = buildWorkflowAgentToolContract(cwd, agentProfileId); | ||
| const trackedTaskId = String( |
There was a problem hiding this comment.
There is a stray early toolContract assignment here that references agentProfileId before it is declared, and it also duplicates the later toolContract definition. As written this will throw at runtime due to TDZ/ReferenceError and prevents action.run_agent from executing. Remove the premature toolContract line and keep only the later assignment after agentProfileId is computed.
| cwd, | ||
| repoRoot: ctx.data?.repoRoot || cwd, | ||
| repoRoot: | ||
| String(ctx.data?.repoRoot || "").trim() && !isUnresolvedTemplateToken(ctx.data?.repoRoot) | ||
| ? ctx.data.repoRoot | ||
| : cwd, |
There was a problem hiding this comment.
repoRoot is defined twice in the object passed into buildArchitectEditorFrame. The first value is silently overwritten by the second, which is confusing and can mask bugs when refactoring. Remove the duplicate key and keep a single repoRoot assignment (preferably the unresolved-template-safe one).
| globalThis.addEventListener('bosun:app-mounted', function handleMounted() { | ||
| bootReady = true; | ||
| clearTimeout(bootTimeoutId); | ||
| globalThis.removeEventListener('bosun:app-mounted', handleMounted); | ||
| }); |
There was a problem hiding this comment.
The boot-loader listens for bosun:app-mounted, but this listener is registered in a module script that runs after the external app.js module has already executed (module scripts run in document order). That means the event will likely be missed and the timeout will incorrectly show a boot failure even when the app mounted successfully. Register the bosun:app-mounted listener before loading app.js (or persist mount state on globalThis and check it immediately).
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
Co-authored-by: bosun-ve[bot] <262908237+bosun-ve[bot]@users.noreply.github.com>
|
@copilot This branch has conflicts that must be resolved Fix all conflicts (git merge origin main, fix conflicts, and push back to this branch) |
- Resolve merge conflicts in 13 files with origin/main - Fix boot sequencing race condition in ui/demo.html and site/ui/demo.html (set globalThis.__bosunAppMounted flag before dispatching event, check flag after listener registration to handle already-fired events) - Remove stray toolContract TDZ reference in workflow-nodes/actions.mjs - Fix duplicate repoRoot key in buildArchitectEditorFrame call in actions.mjs Co-authored-by: jaeko44 <9289791+jaeko44@users.noreply.github.com>
…ated templates - Resolve 13 merge conflicts with origin/main - Fix boot sequencing race in demo.html (persist __bosunAppMounted flag) - Remove stray toolContract TDZ in actions.mjs (PR review fix) - Remove duplicate repoRoot key in buildArchitectEditorFrame (PR review fix) - Use getProviderEndpointEnvKeys to remove non-selected Azure provider vars - Fix agent-supervisor assess() null -> HEALTHY for workflow-owned push - Fix ui-server guardrails test env isolation - Align test assertions with updated workflow templates - Regenerate demo-defaults after template changes" Agent-Logs-Url: https://github.com/virtengine/bosun/sessions/490bc647-e5a4-416f-9bac-31ec9ebfd9d5 Co-authored-by: jaeko44 <9289791+jaeko44@users.noreply.github.com>
Head branch was pushed to by a user without write access
... Resolved. Merged
All 238 test files (4536 tests) pass. Committed as |
| function buildCodexSdkRuntime(streamProviderOverrides, envInput = process.env, workingDirectory = DEFAULT_WORKING_DIRECTORY) { | ||
| const resolved = resolveCodexProfileRuntime(envInput); | ||
| const { env: resolvedEnv, configProvider } = resolved; | ||
| const runtimeDefaults = readCodexConfigRuntimeDefaults(envInput) || {}; |
| function buildCodexSdkRuntime(streamProviderOverrides, envInput = process.env, workingDirectory = DEFAULT_WORKING_DIRECTORY) { | ||
| const resolved = resolveCodexProfileRuntime(envInput); | ||
| const { env: resolvedEnv, configProvider } = resolved; | ||
| const runtimeDefaults = readCodexConfigRuntimeDefaults(envInput) || {}; |
No description provided.