
Ubs
- 8 installs
- 275 repo stars
- Updated August 5, 2026
- dicklesworthstone/ultimate_bug_scanner
Runs pre-commit static analysis across 8 languages to catch null-safety, async, security, and memory-leak bugs that AI agents commonly introduce.
About
Scans changed files in JS/TS, Python, Go, Rust, Java, C++, Ruby, and Swift across 18 detection categories and exits nonzero on issues. A developer uses it as a quality gate before committing or merging AI-generated code.
- 18 categories including null safety, XSS, missing await, and memory leaks
- Exit 0/1 gate with SARIF, JSON output and agent hook auto-config
Ubs by the numbers
- 8 all-time installs (skills.sh)
- Ranked #839 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/dicklesworthstone/ultimate_bug_scanner --skill ubsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 275 |
| Last updated | August 5, 2026 |
| Repository | dicklesworthstone/ultimate_bug_scanner ↗ |
What it does
Runs pre-commit static analysis across 8 languages to catch null-safety, async, security, and memory-leak bugs that AI agents commonly introduce.
Files
UBS - Ultimate Bug Scanner
Static analysis tool built for AI coding workflows. Catches bugs that AI agents commonly introduce: null safety, async/await issues, security holes, memory leaks. Scans JS/TS, Python, Go, Rust, Java, C++, Ruby, Swift in 3-5 seconds.
Why This Exists
AI agents move fast. Bugs move faster. You're shipping features in minutes, but:
- Null pointer crashes slip through
- Missing
awaitcauses silent failures - XSS vulnerabilities reach production
- Memory leaks accumulate
UBS is the quality gate: scan before commit, fix before merge.
Golden Rule
ubs <changed-files> --fail-on-warningExit 0 = safe to commit. Exit 1 = fix and re-run.
Essential Commands
Quick Scans (Use These)
ubs file.ts file2.py # Specific files (< 1s)
ubs $(git diff --name-only --cached) # Staged files
ubs --staged # Same, cleaner syntax
ubs --diff # Working tree vs HEADFull Project Scans
ubs . # Current directory
ubs /path/to/project # Specific path
ubs --only=js,python src/ # Language filter (faster)CI/CD Mode
ubs --ci --fail-on-warning . # Strict mode for CI
ubs --format=json . # Machine-readable
ubs --format=sarif . # GitHub code scanningOutput Format
⚠️ Category (N errors)
file.ts:42:5 – Issue description
💡 Suggested fix
Exit code: 1Parse: file:line:col → location | 💡 → how to fix | Exit 0/1 → pass/fail
The 18 Detection Categories
Critical (Always Fix)
| Category | What It Catches |
|---|---|
| Null Safety | Unguarded property access, missing null checks |
| Security | XSS, injection, prototype pollution, hardcoded secrets |
| Async/Await | Missing await, unhandled rejections, race conditions |
| Memory Leaks | Event listeners without cleanup, timer leaks |
| Type Coercion | == vs ===, parseInt without radix, NaN comparison |
Important (Production Risk)
| Category | What It Catches |
|---|---|
| Division Safety | Division without zero check |
| Resource Lifecycle | Unclosed files, connections, context managers |
| Error Handling | Empty catch blocks, swallowed errors |
| Promise Chains | .then() without .catch() |
| Array Mutations | Mutating during iteration |
Code Quality (Contextual)
| Category | What It Catches |
|---|---|
| Debug Code | console.log, debugger, print() statements |
| TODO Markers | TODO, FIXME, HACK comments |
| Type Safety | TypeScript any usage |
| Readability | Complex ternaries, deep nesting |
Language-Specific Detection
| Language | Key Patterns |
|---|---|
| JavaScript/TypeScript | innerHTML XSS, eval(), missing await, React hooks deps |
| Python | eval(), open() without with, missing encoding=, None checks |
| Go | Nil pointer, goroutine leaks, defer symmetry, context cancel |
| Rust | .unwrap() panics, unsafe blocks, Option handling |
| Java | Resource leaks (try-with-resources), null checks, JDBC |
| C/C++ | Buffer overflows, strcpy(), memory leaks, use-after-free |
| Ruby | eval(), send(), instance_variable_set |
| Swift | Force unwrap (!), ObjC bridging issues |
Profiles
ubs --profile=strict . # Fail on warnings, enforce high standards
ubs --profile=loose . # Skip TODO/debug nits when prototypingCategory Packs (Focused Scans)
ubs --category=resource-lifecycle . # Python/Go/Java resource hygieneNarrows scan to relevant languages and suppresses unrelated categories.
Comparison Mode (Regression Detection)
# Capture baseline
ubs --ci --report-json .ubs/baseline.json .
# Compare against baseline
ubs --ci --comparison .ubs/baseline.json --report-json .ubs/latest.json .Useful for CI to detect regressions vs. main branch.
Output Formats
| Format | Flag | Use Case |
|---|---|---|
| text | (default) | Human-readable terminal output |
| json | --format=json | Machine parsing, scripting |
| jsonl | --format=jsonl | Line-delimited, streaming |
| sarif | --format=sarif | GitHub code scanning |
| html | --html-report=file.html | PR attachments, dashboards |
Inline Suppression
When a finding is intentional:
eval(trustedCode); // ubs:ignore
// ubs:ignore-next-line
dangerousOperation();Exit Codes
| Code | Meaning |
|---|---|
0 | No critical issues (safe to commit) |
1 | Critical issues or warnings (with --fail-on-warning) |
2 | Environment error (missing ast-grep, etc.) |
Doctor Command
ubs doctor # Check environment
ubs doctor --fix # Auto-fix missing dependenciesChecks: curl/wget, ast-grep, ripgrep, jq, typos, Node.js + TypeScript.
Agent Integration
UBS auto-configures hooks for coding agents during install:
| Agent | Hook Location |
|---|---|
| Claude Code | .claude/hooks/on-file-write.sh |
| Cursor | .cursor/rules |
| Codex CLI | .codex/rules/ubs.md |
| Gemini | .gemini/rules |
| Windsurf | .windsurf/rules |
| Cline | .cline/rules |
Claude Code Hook Pattern
#!/bin/bash
# .claude/hooks/on-file-write.sh
if [[ "$FILE_PATH" =~ \.(js|jsx|ts|tsx|py|go|rs|java|rb)$ ]]; then
echo "🔬 Quality check running..."
if ubs "${PROJECT_DIR}" --ci 2>&1 | head -30; then
echo "✅ No critical issues"
else
echo "⚠️ Issues detected - review above"
fi
fiGit Pre-Commit Hook
#!/bin/bash
# .git/hooks/pre-commit
echo "🔬 Running bug scanner..."
if ! ubs . --fail-on-warning 2>&1 | tail -30; then
echo "❌ Critical issues found. Fix or: git commit --no-verify"
exit 1
fi
echo "✅ Quality check passed"Performance
Small (5K lines): 0.8 seconds
Medium (50K lines): 3.2 seconds
Large (200K lines): 12 seconds
Huge (1M lines): 58 seconds10,000+ lines per second. Use --jobs=N to control parallelism.
Speed Tips
1. Scope to changed files: ubs src/file.ts (< 1s) vs ubs . (30s) 2. Use --staged or --diff: Only scan what you're committing 3. Language filter: --only=js,python skips irrelevant scanners 4. Skip categories: --skip=11,14 to skip debug/TODO markers
Fix Workflow
1. Read finding → category + fix suggestion
2. Navigate file:line:col → view context
3. Verify real issue (not false positive)
4. Fix root cause (not symptom)
5. Re-run ubs <file> → exit 0
6. CommitBug Severity Guide
- Critical (always fix): Null safety, XSS/injection, async/await, memory leaks
- Important (production): Type narrowing, division-by-zero, resource leaks
- Contextual (judgment): TODO/FIXME, console logs
Common Anti-Patterns
| Don't | Do |
|---|---|
| Ignore findings | Investigate each |
| Full scan per edit | Scope to changed files |
Fix symptom (if (x) { x.y }) | Fix root cause (x?.y) |
| Suppress without understanding | Verify false positive first |
Installation
# One-liner (recommended)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh?$(date +%s)" | bash -s -- --easy-mode
# Manual
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/ubs \
-o /usr/local/bin/ubs && chmod +x /usr/local/bin/ubsCustom AST Rules
mkdir -p ~/.config/ubs/rules
cat > ~/.config/ubs/rules/no-console.yml <<'EOF'
id: custom.no-console
language: javascript
rule:
pattern: console.log($$$)
severity: warning
message: "Remove console.log before production"
EOF
ubs . --rules=~/.config/ubs/rulesExcluding Paths
ubs . --exclude=legacy,generated,vendorAuto-ignored: node_modules, .venv, dist, build, target, editor caches.
Session Logs
ubs sessions --entries 1 # View latest install sessionIntegration with Flywheel
| Tool | Integration |
|---|---|
| BV | --beads-jsonl=out.jsonl exports findings for Beads |
| CASS | Search past sessions for similar bug patterns |
| CM | Extract rules from UBS findings |
| Agent Mail | Notify agents of scan results |
| DCG | UBS runs inside DCG protection |
Troubleshooting
| Error | Fix |
|---|---|
| "Environment error" (exit 2) | ubs doctor --fix |
| "ast-grep not found" | brew install ast-grep or cargo install ast-grep |
| Too many false positives | Use --skip=N or // ubs:ignore |
| Slow scans | Scope to files: ubs <file> not ubs . |
# SQLite databases
*.db
*.db?*
*.db-journal
*.db-wal
*.db-shm
# Daemon runtime files
.write.lock
daemon.lock
daemon.log
daemon-*.log
daemon-*.log.gz
daemon.pid
bd.sock
# Local version tracking (prevents upgrade notification spam after git ops)
.local_version
# Legacy database files
db.sqlite
bd.db
# Merge artifacts (temporary files from 3-way merge)
beads.base.jsonl
beads.base.meta.json
beads.left.jsonl
beads.left.meta.json
beads.right.jsonl
beads.right.meta.json
*.migrated
# Keep JSONL exports and config (source of truth for git)
!issues.jsonl
!metadata.json
!config.json
# Local history backups
.br_history/
# bv (beads viewer) lock file
.bv.lock
sync-branch: beads-sync
issue_prefix: ultimate_bug_scanner
{"id":"ultimate_bug_scanner-install-ast-grep-required"}
{"id":"ultimate_bug_scanner-js-ts-degraded-mode-contract"}
{"id":"ultimate_bug_scanner-tests-js-env-error-no-ast2"}
{"id":"ultimate_bug_scanner-xkw"}
{"id":"ultimate_bug_scanner-mep"}
{"id":"ultimate_bug_scanner-js-fail-fast-without-ast2"}
{"id":"ultimate_bug_scanner-ubs-env-error-exitcode"}
{"id":"ultimate_bug_scanner-js-module-env-ast-grep-bin2"}
{"id":"ultimate_bug_scanner-ank"}
{"id":"ultimate_bug_scanner-iua"}
{"id":"ultimate_bug_scanner-js-global-pollution-fp-fix2"}
{
"database": "beads.db",
"jsonl_export": "issues.jsonl"
}Beads - AI-Native Issue Tracking
Welcome to Beads! This repository uses Beads for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
What is Beads?
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
Learn more: github.com/steveyegge/beads
Quick Start
Essential Commands
# Create new issues
bd create "Add user authentication"
# View all issues
bd list
# View issue details
bd show <issue-id>
# Update issue status
bd update <issue-id> --status in_progress
bd update <issue-id> --status done
# Sync with git remote
bd syncWorking with Issues
Issues in Beads are:
- Git-native: Stored in
.beads/issues.jsonland synced like code - AI-friendly: CLI-first design works perfectly with AI coding agents
- Branch-aware: Issues can follow your branch workflow
- Always in sync: Auto-syncs with your commits
Why Beads?
✨ AI-Native Design
- Built specifically for AI-assisted development workflows
- CLI-first interface works seamlessly with AI coding agents
- No context switching to web UIs
🚀 Developer Focused
- Issues live in your repo, right next to your code
- Works offline, syncs when you push
- Fast, lightweight, and stays out of your way
🔧 Git Integration
- Automatic sync with git commits
- Branch-aware issue tracking
- Intelligent JSONL merge resolution
Get Started with Beads
Try Beads in your own projects:
# Install Beads
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
# Initialize in your repo
bd init
# Create your first issue
bd create "Try out Beads"Learn More
- Documentation: github.com/steveyegge/beads/docs
- Quick Start Guide: Run
bd quickstart - Examples: github.com/steveyegge/beads/examples
---
Beads: Issue tracking that moves at the speed of thought ⚡
#!/usr/bin/env python3
"""
Git/filesystem safety guard for Claude Code.
Blocks destructive commands that can lose uncommitted work or delete files.
This hook runs before Bash commands execute and can deny dangerous operations.
Exit behavior:
- Exit 0 with JSON {"hookSpecificOutput": {"permissionDecision": "deny", ...}} = block
- Exit 0 with no output = allow
"""
import json
import os
import re
import shlex
import sys
# Destructive patterns to block - tuple of (regex, reason)
DESTRUCTIVE_PATTERNS = [
# Git commands that discard uncommitted changes
(
r"git\s+checkout\s+--\s+",
"git checkout -- discards uncommitted changes permanently. Use 'git stash' first."
),
(
r"git\s+checkout\s+(?!-b\b)(?!--orphan\b)[^\s]+\s+--\s+",
"git checkout <ref> -- <path> overwrites working tree. Use 'git stash' first."
),
(
r"git\s+restore\s+(?!--staged\b)[^\s]*\s*$",
"git restore discards uncommitted changes. Use 'git stash' or 'git diff' first."
),
(
r"git\s+restore\s+--worktree",
"git restore --worktree discards uncommitted changes permanently."
),
# Git reset variants
(
r"git\s+reset\s+--hard",
"git reset --hard destroys uncommitted changes. Use 'git stash' first."
),
(
r"git\s+reset\s+--merge",
"git reset --merge can lose uncommitted changes."
),
# Git clean
(
r"git\s+clean\s+-[a-z]*f",
"git clean -f removes untracked files permanently. Review with 'git clean -n' first."
),
# Force operations
(
r"git\s+push\s+.*--force(?!-with-lease)",
"Force push can destroy remote history. Use --force-with-lease if necessary."
),
(
r"git\s+push\s+-f\b",
"Force push (-f) can destroy remote history. Use --force-with-lease if necessary."
),
(
r"git\s+branch\s+-D\b",
"git branch -D force-deletes without merge check. Use -d for safety."
),
# Destructive filesystem commands
(
r"rm\s+-[a-z]*r[a-z]*f|rm\s+-[a-z]*f[a-z]*r",
"rm -rf is destructive. List files first, then delete individually with permission."
),
(
r"rm\s+-rf\s+[/~]",
"rm -rf on root or home paths is extremely dangerous."
),
# Git stash drop/clear without explicit permission
(
r"git\s+stash\s+drop",
"git stash drop permanently deletes stashed changes. List stashes first."
),
(
r"git\s+stash\s+clear",
"git stash clear permanently deletes ALL stashed changes."
),
]
RM_RF_ALLOWED_PREFIXES = (
os.path.join(os.sep, "tmp", ""),
os.path.join(os.sep, "var", "tmp", ""),
"${TMPDIR:-/tmp}/",
"${TMPDIR:-/var/tmp}/",
)
RM_SEPARATORS = {"&&", "||", ";", "|"}
def _is_rm_command(token: str) -> bool:
"""Check if a token is the rm command (handles full paths like /bin/rm)."""
if token == "rm":
return True
# Handle absolute paths: /bin/rm, /usr/bin/rm, etc.
if token.endswith("/rm"):
return True
return False
def has_rm_recursive_force(command: str) -> bool:
"""Check if command contains rm with both recursive and force flags.
Handles all variants:
- rm -rf, rm -fr (combined short flags)
- rm -r -f, rm -f -r (separate short flags)
- rm --recursive --force (long flags)
- rm -r --force, rm --recursive -f (mixed)
"""
try:
tokens = shlex.split(command, posix=True)
except ValueError:
return False
i = 0
while i < len(tokens):
if not _is_rm_command(tokens[i]):
i += 1
continue
i += 1
has_recursive = False
has_force = False
while i < len(tokens):
tok = tokens[i]
if tok == "--":
i += 1
break
if tok in RM_SEPARATORS:
break
if not tok.startswith("-"):
break
# Check for flags
if tok == "--recursive":
has_recursive = True
elif tok == "--force":
has_force = True
elif tok.startswith("--"):
pass # Other long options
else:
# Short options like -r, -f, -rf, -ri, etc.
if "r" in tok.lower():
has_recursive = True
if "f" in tok.lower():
has_force = True
i += 1
if has_recursive and has_force:
return True
# Skip to next command separator or end
while i < len(tokens) and tokens[i] not in RM_SEPARATORS:
i += 1
if i < len(tokens):
i += 1 # Skip the separator
return False
def rm_rf_targets_are_safe(command: str) -> bool:
"""Allow `rm -rf` only when *all* targets are clearly temp paths.
IMPORTANT: We intentionally avoid trying to evaluate variables like `$TMPDIR`
(it can be set to `/`). Only the explicit fallbacks `${TMPDIR:-/tmp}/...`
and `${TMPDIR:-/var/tmp}/...` are allowed.
"""
try:
tokens = shlex.split(command, posix=True)
except ValueError:
return False
i = 0
while i < len(tokens):
if not _is_rm_command(tokens[i]):
i += 1
continue
i += 1
flags = set()
end_of_opts = False
while i < len(tokens) and not end_of_opts:
tok = tokens[i]
if tok == "--":
end_of_opts = True
i += 1
break
if tok in RM_SEPARATORS:
break
if not tok.startswith("-"):
break
# Short options like -rf / -fr, plus long --recursive/--force variants.
if tok.startswith("--"):
if tok == "--recursive":
flags.add("r")
elif tok == "--force":
flags.add("f")
else:
# Use .lower() to catch both -r and -R (both valid for rm)
if "r" in tok.lower():
flags.add("r")
if "f" in tok.lower():
flags.add("f")
i += 1
targets: list[str] = []
while i < len(tokens) and tokens[i] not in RM_SEPARATORS:
targets.append(tokens[i])
i += 1
if "r" in flags and "f" in flags:
if not targets:
return False
for target in targets:
if not any(target.startswith(prefix) for prefix in RM_RF_ALLOWED_PREFIXES):
return False
return True
def main():
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError:
# Can't parse input, allow by default
sys.exit(0)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")
# Only check Bash commands
if tool_name != "Bash" or not command:
sys.exit(0)
# Check if command matches any destructive pattern
for pattern, reason in DESTRUCTIVE_PATTERNS:
if re.search(pattern, command, re.IGNORECASE):
if pattern.startswith("rm\\s+"):
if rm_rf_targets_are_safe(command):
# rm targets are safe temp paths, allow this pattern
continue
reason = (
"rm -rf is destructive. Only explicit temp paths are allowed "
f"({', '.join(RM_RF_ALLOWED_PREFIXES)})."
)
output = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": (
f"BLOCKED by git_safety_guard.py\n\n"
f"Reason: {reason}\n\n"
f"Command: {command}\n\n"
f"If this operation is truly needed, ask the user for explicit "
f"permission and have them run the command manually."
)
}
}
print(json.dumps(output))
sys.exit(0)
# Catch rm with recursive+force that bypassed the regex patterns
# (e.g., "rm -r -f /path" or "rm --recursive --force /path")
if has_rm_recursive_force(command) and not rm_rf_targets_are_safe(command):
output = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": (
f"BLOCKED by git_safety_guard.py\n\n"
f"Reason: rm -rf is destructive. Only explicit temp paths are allowed "
f"({', '.join(RM_RF_ALLOWED_PREFIXES)}).\n\n"
f"Command: {command}\n\n"
f"If this operation is truly needed, ask the user for explicit "
f"permission and have them run the command manually."
)
}
}
print(json.dumps(output))
sys.exit(0)
# Allow all other commands
sys.exit(0)
if __name__ == "__main__":
main()
#!/bin/bash
# Ultimate Bug Scanner - Claude Code Hook
# Runs on every file save for UBS-supported languages (JS/TS, Python, C/C++, Rust, Go, Java, Ruby)
if [[ "$FILE_PATH" =~ \.(js|jsx|ts|tsx|mjs|cjs|py|pyw|pyi|c|cc|cpp|cxx|h|hh|hpp|hxx|rs|go|java|rb)$ ]]; then
echo "🔬 Running bug scanner..."
if ! command -v ubs >/dev/null 2>&1; then
echo "⚠️ 'ubs' not found in PATH; install it before using this hook." >&2
exit 0
fi
ubs "${PROJECT_DIR}" --ci 2>&1 | head -50
fi
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/git_safety_guard.py"
}
]
}
]
}
}
````markdown
UBS Quick Reference for AI Agents
UBS stands for "Ultimate Bug Scanner": The AI Coding Agent's Secret Weapon: Flagging Likely Bugs for Fixing Early On
Install: curl -sSL https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh | bash
Golden Rule: ubs <changed-files> before every commit. Exit 0 = safe. Exit >0 = fix & re-run.
Commands:
ubs file.ts file2.py # Specific files (< 1s) — USE THIS
ubs $(git diff --name-only --cached) # Staged files — before commit
ubs --only=js,python src/ # Language filter (3-5x faster)
ubs --ci --fail-on-warning . # CI mode — before PR
ubs --help # Full command reference
ubs sessions --entries 1 # Tail the latest install session log
ubs . # Whole project (ignores things like .venv and node_modules automatically)Output Format:
⚠️ Category (N errors)
file.ts:42:5 – Issue description
💡 Suggested fix
Exit code: 1Parse: file:line:col → location | 💡 → how to fix | Exit 0/1 → pass/fail
Fix Workflow: 1. Read finding → category + fix suggestion 2. Navigate file:line:col → view context 3. Verify real issue (not false positive) 4. Fix root cause (not symptom) 5. Re-run ubs <file> → exit 0 6. Commit
Speed Critical: Scope to changed files. ubs src/file.ts (< 1s) vs ubs . (30s). Never full scan for small edits.
Bug Severity:
- Critical (always fix): Null safety, XSS/injection, async/await, memory leaks
- Important (production): Type narrowing, division-by-zero, resource leaks
- Contextual (judgment): TODO/FIXME, console logs
Anti-Patterns:
- ❌ Ignore findings → ✅ Investigate each
- ❌ Full scan per edit → ✅ Scope to file
- ❌ Fix symptom (
if (x) { x.y }) → ✅ Root cause (x?.y)
````
````markdown
## UBS Quick Reference for AI Agents
UBS stands for "Ultimate Bug Scanner": **The AI Coding Agent's Secret Weapon: Flagging Likely Bugs for Fixing Early On**
**Install:** `curl -sSL https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh | bash`
**Golden Rule:** `ubs <changed-files>` before every commit. Exit 0 = safe. Exit >0 = fix & re-run.
**Commands:**
```bash
ubs file.ts file2.py # Specific files (< 1s) — USE THIS
ubs $(git diff --name-only --cached) # Staged files — before commit
ubs --only=js,python src/ # Language filter (3-5x faster)
ubs --ci --fail-on-warning . # CI mode — before PR
ubs --help # Full command reference
ubs sessions --entries 1 # Tail the latest install session log
ubs . # Whole project (ignores things like .venv and node_modules automatically)
```
**Output Format:**
```
⚠️ Category (N errors)
file.ts:42:5 – Issue description
💡 Suggested fix
Exit code: 1
```
Parse: `file:line:col` → location | 💡 → how to fix | Exit 0/1 → pass/fail
**Fix Workflow:**
1. Read finding → category + fix suggestion
2. Navigate `file:line:col` → view context
3. Verify real issue (not false positive)
4. Fix root cause (not symptom)
5. Re-run `ubs <file>` → exit 0
6. Commit
**Speed Critical:** Scope to changed files. `ubs src/file.ts` (< 1s) vs `ubs .` (30s). Never full scan for small edits.
**Bug Severity:**
- **Critical** (always fix): Null safety, XSS/injection, async/await, memory leaks
- **Important** (production): Type narrowing, division-by-zero, resource leaks
- **Contextual** (judgment): TODO/FIXME, console logs
**Anti-Patterns:**
- ❌ Ignore findings → ✅ Investigate each
- ❌ Full scan per edit → ✅ Scope to file
- ❌ Fix symptom (`if (x) { x.y }`) → ✅ Root cause (`x?.y`)
````
````markdown
## UBS Quick Reference for AI Agents
UBS stands for "Ultimate Bug Scanner": **The AI Coding Agent's Secret Weapon: Flagging Likely Bugs for Fixing Early On**
**Install:** `curl -sSL https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh | bash`
**Golden Rule:** `ubs <changed-files>` before every commit. Exit 0 = safe. Exit >0 = fix & re-run.
**Commands:**
```bash
ubs file.ts file2.py # Specific files (< 1s) — USE THIS
ubs $(git diff --name-only --cached) # Staged files — before commit
ubs --only=js,python src/ # Language filter (3-5x faster)
ubs --ci --fail-on-warning . # CI mode — before PR
ubs --help # Full command reference
ubs sessions --entries 1 # Tail the latest install session log
ubs . # Whole project (ignores things like .venv and node_modules automatically)
```
**Output Format:**
```
⚠️ Category (N errors)
file.ts:42:5 – Issue description
💡 Suggested fix
Exit code: 1
```
Parse: `file:line:col` → location | 💡 → how to fix | Exit 0/1 → pass/fail
**Fix Workflow:**
1. Read finding → category + fix suggestion
2. Navigate `file:line:col` → view context
3. Verify real issue (not false positive)
4. Fix root cause (not symptom)
5. Re-run `ubs <file>` → exit 0
6. Commit
**Speed Critical:** Scope to changed files. `ubs src/file.ts` (< 1s) vs `ubs .` (30s). Never full scan for small edits.
**Bug Severity:**
- **Critical** (always fix): Null safety, XSS/injection, async/await, memory leaks
- **Important** (production): Type narrowing, division-by-zero, resource leaks
- **Contextual** (judgment): TODO/FIXME, console logs
**Anti-Patterns:**
- ❌ Ignore findings → ✅ Investigate each
- ❌ Full scan per edit → ✅ Scope to file
- ❌ Fix symptom (`if (x) { x.y }`) → ✅ Root cause (`x?.y`)
````
# Use bd merge for beads JSONL files
.beads/beads.jsonl merge=beads
#!/usr/bin/env bash
# Pre-commit hook to ensure pinned checksums stay up-to-date.
set -euo pipefail
compute_sha256() {
local file="$1"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$file" | awk '{print $1}'
return 0
fi
if command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$file" | awk '{print $1}'
return 0
fi
if command -v openssl >/dev/null 2>&1; then
openssl dgst -sha256 "$file" | awk '{print $NF}'
return 0
fi
if command -v python3 >/dev/null 2>&1; then
python3 - "$file" <<'PY'
import hashlib
import sys
from pathlib import Path
data = Path(sys.argv[1]).read_bytes()
print(hashlib.sha256(data).hexdigest())
PY
return 0
fi
return 1
}
# Check if any pinned checksum inputs changed.
# - Pinned module sources: modules/ubs-*.sh, modules/helpers/*.{py,go,js}
# - Release checksums: install.sh, ubs
CHECKSUM_TRIGGER_CHANGED="$(
git diff --cached --name-only | grep -E '^(install\.sh|ubs|modules/(ubs-.*\.sh|helpers/[^/]+\.(py|go|js))$)' || true
)"
if [[ -n "$CHECKSUM_TRIGGER_CHANGED" ]]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Checksum inputs changed - updating checksums..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
./scripts/update_checksums.sh
if ! git diff --exit-code ubs >/dev/null 2>&1; then
echo "✓ Updated pinned checksums in 'ubs'"
git add ubs
fi
if ! git diff --exit-code SHA256SUMS >/dev/null 2>&1; then
echo "✓ Updated release checksums in SHA256SUMS"
git add SHA256SUMS
fi
echo ""
fi
# Always verify checksums match before allowing commit
echo "Verifying all checksums match..."
if ! ./scripts/verify_checksums.sh; then
echo ""
echo "❌ PRE-COMMIT HOOK FAILED"
echo "Module checksums do NOT match!"
echo ""
echo "This should not happen if auto-update worked."
echo "Please run: ./scripts/update_checksums.sh"
exit 1
fi
verify_sha256sums_entry() {
local file="$1"
local expected actual
expected="$(awk -v f="$file" '$2==f{print $1}' SHA256SUMS | head -n 1)"
if [[ -z "${expected:-}" ]]; then
echo "❌ SHA256SUMS missing entry for $file" >&2
exit 1
fi
actual="$(compute_sha256 "$file")" || {
echo "❌ Could not compute SHA256 (need sha256sum, shasum, openssl, or python3)" >&2
exit 1
}
if [[ "$expected" != "$actual" ]]; then
echo "❌ SHA256SUMS mismatch for $file" >&2
echo " Expected: $expected" >&2
echo " Actual: $actual" >&2
echo " Fix: ./scripts/update_checksums.sh" >&2
exit 1
fi
}
verify_sha256sums_entry install.sh
verify_sha256sums_entry ubs
echo "✓ Pre-commit checks passed"
name: Notify ACFS checksum monitor
on:
push:
branches: [main]
paths:
- 'install.sh'
- 'scripts/install.sh'
release:
types: [published]
workflow_dispatch:
jobs:
dispatch:
runs-on: ubuntu-latest
env:
ACFS_TOKEN: ${{ secrets.ACFS_REPO_DISPATCH_TOKEN }}
steps:
- name: Skip dispatch when token missing
if: ${{ env.ACFS_TOKEN == '' }}
run: echo "ACFS_REPO_DISPATCH_TOKEN not set; skipping ACFS dispatch."
- name: Dispatch to ACFS
if: ${{ env.ACFS_TOKEN != '' }}
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ env.ACFS_TOKEN }}
repository: Dicklesworthstone/agentic_coding_flywheel_setup
event-type: upstream-changed
client-payload: |
{"repo":"${{ github.repository }}","ref":"${{ github.ref }}","sha":"${{ github.sha }}","event":"${{ github.event_name }}"}
name: Checksum Health Check
# Scheduled canary that detects stale checksums even if no pushes occur.
# Runs twice daily and creates a GitHub issue if checksums are out of sync.
on:
schedule:
# Run at 06:00 and 18:00 UTC every day
- cron: '0 6,18 * * *'
workflow_dispatch: # Allow manual trigger
permissions:
contents: read
issues: write
jobs:
health-check:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# The version-tag drift check below resolves
# `git show v$UBS_VERSION:modules/...`. The default fetch-depth
# of 1 doesn't include tag refs, so use 0 (full history +
# tags) — silently missing tags would let drift slip past
# the gate.
fetch-depth: 0
- name: Verify SHA256SUMS integrity
id: verify
run: |
set -euo pipefail
# Compute actual checksums
ACTUAL_UBS=$(sha256sum ubs | awk '{print $1}')
ACTUAL_INSTALL=$(sha256sum install.sh | awk '{print $1}')
# Extract expected checksums (2>/dev/null suppresses error if file missing)
EXPECTED_UBS=$(grep ' ubs$' SHA256SUMS 2>/dev/null | awk '{print $1}' || echo "")
EXPECTED_INSTALL=$(grep ' install.sh$' SHA256SUMS 2>/dev/null | awk '{print $1}' || echo "")
echo "ubs:"
echo " Expected: $EXPECTED_UBS"
echo " Actual: $ACTUAL_UBS"
echo ""
echo "install.sh:"
echo " Expected: $EXPECTED_INSTALL"
echo " Actual: $ACTUAL_INSTALL"
ERRORS=""
if [[ "$ACTUAL_UBS" != "$EXPECTED_UBS" ]]; then
ERRORS="${ERRORS}ubs checksum mismatch\n"
fi
if [[ "$ACTUAL_INSTALL" != "$EXPECTED_INSTALL" ]]; then
ERRORS="${ERRORS}install.sh checksum mismatch\n"
fi
# Always output the checksum values (needed for issue body even if only modules fail)
echo "actual_ubs=$ACTUAL_UBS" >> "$GITHUB_OUTPUT"
echo "expected_ubs=$EXPECTED_UBS" >> "$GITHUB_OUTPUT"
echo "actual_install=$ACTUAL_INSTALL" >> "$GITHUB_OUTPUT"
echo "expected_install=$EXPECTED_INSTALL" >> "$GITHUB_OUTPUT"
if [[ -n "$ERRORS" ]]; then
echo "healthy=false" >> "$GITHUB_OUTPUT"
echo "errors<<EOF" >> "$GITHUB_OUTPUT"
echo -e "$ERRORS" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
else
echo "healthy=true" >> "$GITHUB_OUTPUT"
echo "::notice::All checksums are healthy"
fi
- name: Verify module checksums
id: verify_modules
run: |
# Capture full output (with ANSI stripped) for the issue body so
# operators can see *which* module drifted, not just that something did.
if ./scripts/verify_checksums.sh > /tmp/modules.log 2>&1; then
echo "modules_healthy=true" >> "$GITHUB_OUTPUT"
else
echo "modules_healthy=false" >> "$GITHUB_OUTPUT"
fi
# Strip ANSI color codes; keep only mismatch / failure lines for the body.
sed -E 's/\x1B\[[0-9;]*[A-Za-z]//g' /tmp/modules.log \
| grep -E '✗|MISMATCH|FAILED|Expected|Actual' \
| head -40 > /tmp/modules_summary.log || true
- name: Verify v$UBS_VERSION tag matches main's MODULE_CHECKSUMS (issue #45)
id: verify_version_tag_drift
run: |
if ./scripts/check-version-tag-drift.sh > /tmp/tag_drift.log 2>&1; then
echo "tag_drift_healthy=true" >> "$GITHUB_OUTPUT"
else
echo "tag_drift_healthy=false" >> "$GITHUB_OUTPUT"
fi
# Keep the operator-facing diff intact (no color codes here in
# check-version-tag-drift.sh, but strip just in case).
sed -E 's/\x1B\[[0-9;]*[A-Za-z]//g' /tmp/tag_drift.log \
| head -60 > /tmp/tag_drift_summary.log || true
- name: Check for existing issue
id: check_issue
if: steps.verify.outputs.healthy == 'false' || steps.verify_modules.outputs.modules_healthy == 'false' || steps.verify_version_tag_drift.outputs.tag_drift_healthy == 'false'
run: |
# Check if there's already an open issue with our label
EXISTING=$(gh issue list --label "checksum-drift" --state open --json number --jq '.[0].number // empty')
if [[ -n "$EXISTING" ]]; then
echo "existing_issue=$EXISTING" >> "$GITHUB_OUTPUT"
echo "::notice::Existing issue #$EXISTING found, skipping creation"
else
echo "existing_issue=" >> "$GITHUB_OUTPUT"
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Ensure checksum-drift label exists
if: (steps.verify.outputs.healthy == 'false' || steps.verify_modules.outputs.modules_healthy == 'false' || steps.verify_version_tag_drift.outputs.tag_drift_healthy == 'false') && steps.check_issue.outputs.existing_issue == ''
run: |
# Create label if it doesn't exist (ignore error if it already exists)
gh label create "checksum-drift" --description "Automated: SHA256SUMS out of sync" --color "B60205" 2>/dev/null || true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build accurate issue body
id: build_body
if: (steps.verify.outputs.healthy == 'false' || steps.verify_modules.outputs.modules_healthy == 'false' || steps.verify_version_tag_drift.outputs.tag_drift_healthy == 'false') && steps.check_issue.outputs.existing_issue == ''
env:
FILE_HEALTHY: ${{ steps.verify.outputs.healthy }}
MODULES_HEALTHY: ${{ steps.verify_modules.outputs.modules_healthy }}
TAG_HEALTHY: ${{ steps.verify_version_tag_drift.outputs.tag_drift_healthy }}
EXPECTED_UBS: ${{ steps.verify.outputs.expected_ubs }}
ACTUAL_UBS: ${{ steps.verify.outputs.actual_ubs }}
EXPECTED_INSTALL: ${{ steps.verify.outputs.expected_install }}
ACTUAL_INSTALL: ${{ steps.verify.outputs.actual_install }}
run: |
set -euo pipefail
# Compose a category-aware issue body. Earlier versions only showed the
# top-level file table, which masked the real failure when modules or
# the version-tag drift check were the offenders (see #47).
status_icon() { [[ "$1" == "true" ]] && echo "✅" || echo "❌"; }
file_icon() { [[ "$1" == "$2" ]] && echo "✅" || echo "❌"; }
# Pick a title that reflects the actual failure category. If multiple
# categories fail we fall back to a composite title.
fail_count=0
[[ "$FILE_HEALTHY" == "false" ]] && fail_count=$((fail_count+1))
[[ "$MODULES_HEALTHY" == "false" ]] && fail_count=$((fail_count+1))
[[ "$TAG_HEALTHY" == "false" ]] && fail_count=$((fail_count+1))
# IMPORTANT: keep titles to plain ASCII-ish text — no literal "$",
# no backticks, no "$(...)" command substitutions. The title is
# consumed by the next step via the ISSUE_TITLE env var (bash expands
# the env-var value once as a string, so a "$" inside the value is
# safe today), but the env-var pass is the only thing protecting us.
# If a future maintainer switches the consumer back to direct GitHub
# Actions expression interpolation into the bash command, any "$"
# in the title would be re-evaluated by bash as a parameter expansion
# and silently drop unset variables. Body is fine because it is
# read via `--body-file`, never interpolated by bash.
if [[ $fail_count -gt 1 ]]; then TITLE="URGENT: Multiple checksum drift categories detected"
elif [[ "$FILE_HEALTHY" == "false" ]]; then TITLE="URGENT: Top-level checksum drift (ubs/install.sh) — users cannot install"
elif [[ "$MODULES_HEALTHY" == "false" ]]; then TITLE="Module checksum drift — module file does not match expected hash in ubs"
else TITLE="Version-tag checksum drift — main modules differ from the tagged release"
fi
# Use the multi-line GITHUB_OUTPUT delimiter syntax so a future title
# containing "=" or other delimiter-significant chars stays intact.
{
echo "title<<TITLE_EOF"
printf '%s\n' "$TITLE"
echo "TITLE_EOF"
} >> "$GITHUB_OUTPUT"
{
echo "## Checksum Health Check Failed"
echo ""
echo "| Category | Status |"
echo "|----------|--------|"
echo "| Top-level files (\`ubs\`, \`install.sh\`) | $(status_icon "$FILE_HEALTHY") |"
echo "| Module file hashes (\`MODULE_CHECKSUMS\` in ubs) | $(status_icon "$MODULES_HEALTHY") |"
echo "| Version-tag drift (main vs the tagged release) | $(status_icon "$TAG_HEALTHY") |"
echo ""
if [[ "$FILE_HEALTHY" == "false" ]]; then
echo "### Top-Level File Drift"
echo ""
echo "| File | Expected | Actual | Status |"
echo "|------|----------|--------|--------|"
echo "| \`ubs\` | \`$EXPECTED_UBS\` | \`$ACTUAL_UBS\` | $(file_icon "$EXPECTED_UBS" "$ACTUAL_UBS") |"
echo "| \`install.sh\` | \`$EXPECTED_INSTALL\` | \`$ACTUAL_INSTALL\` | $(file_icon "$EXPECTED_INSTALL" "$ACTUAL_INSTALL") |"
echo ""
echo "**Impact:** Users running \`install.sh\` will get checksum mismatch errors."
echo ""
echo "**Fix:**"
echo ""
echo '```bash'
echo "./scripts/update_checksums.sh"
echo "git add SHA256SUMS ubs"
echo "git commit -m \"fix(checksum): sync SHA256SUMS\""
echo "git push"
echo '```'
echo ""
fi
if [[ "$MODULES_HEALTHY" == "false" ]]; then
echo "### Module Checksum Drift"
echo ""
echo "One or more module/helper files do not match the hash declared in the \`MODULE_CHECKSUMS\` table inside the \`ubs\` script. The runner will refuse to load these modules at runtime."
echo ""
echo "Failing entries (from \`scripts/verify_checksums.sh\`):"
echo ""
echo '```'
if [[ -s /tmp/modules_summary.log ]]; then
cat /tmp/modules_summary.log
else
echo "(no diagnostic lines captured — re-run scripts/verify_checksums.sh locally)"
fi
echo '```'
echo ""
echo "**Fix:** Update the module hash in \`ubs\` (search for the failing module name in the \`MODULE_CHECKSUMS\` block), or revert the module change."
echo ""
fi
if [[ "$TAG_HEALTHY" == "false" ]]; then
echo "### Version-Tag Drift (issue #45 follow-up)"
echo ""
echo "The current release tag's modules no longer match what main's \`ubs\` script expects (run \`scripts/check-version-tag-drift.sh\` for the exact tag name and per-module deltas). Users pinned to that tag will see runtime mismatches."
echo ""
echo "Drift report (from \`scripts/check-version-tag-drift.sh\`):"
echo ""
echo '```'
if [[ -s /tmp/tag_drift_summary.log ]]; then
cat /tmp/tag_drift_summary.log
else
echo "(no diagnostic lines captured — re-run scripts/check-version-tag-drift.sh locally)"
fi
echo '```'
echo ""
echo "**Fix one of:**"
echo ""
echo "1. Bump \`UBS_VERSION\` in \`ubs\` and cut a new tag whose modules match main."
echo "2. Revert the module change(s) on main so the runner stays compatible with the existing tag."
echo "3. Move the existing tag to the current commit (only safe if no users are pinned to it)."
echo ""
fi
echo "### Why This Happened"
echo ""
echo "The \`Checksum Sync\` workflow auto-fixes top-level drift on push to main, but module-level and version-tag drift require human judgement (which release category they belong to). See the [Checksum Sync workflow runs](https://github.com/${{ github.repository }}/actions/workflows/checksum-sync.yml) for top-level recovery status."
echo ""
echo "---"
echo "*This issue was automatically created by the Checksum Health Check workflow.*"
} > /tmp/issue_body.md
- name: Create issue for checksum drift
if: (steps.verify.outputs.healthy == 'false' || steps.verify_modules.outputs.modules_healthy == 'false' || steps.verify_version_tag_drift.outputs.tag_drift_healthy == 'false') && steps.check_issue.outputs.existing_issue == ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Pass the title through an env var rather than ${{ }} interpolation
# directly into the bash command. Direct interpolation would let bash
# re-evaluate any "$WORD" inside the title (silently dropping unset
# vars and, in principle, executing $(cmd) substitutions). The env-var
# form passes the value as a string that bash expands once via "$ISSUE_TITLE".
ISSUE_TITLE: ${{ steps.build_body.outputs.title }}
run: |
gh issue create \
--title "$ISSUE_TITLE" \
--label "checksum-drift" \
--body-file /tmp/issue_body.md
- name: Fail if unhealthy
if: steps.verify.outputs.healthy == 'false' || steps.verify_modules.outputs.modules_healthy == 'false' || steps.verify_version_tag_drift.outputs.tag_drift_healthy == 'false'
run: |
echo "::error::Checksum drift detected! SHA256SUMS is out of sync."
exit 1
name: Checksum Sync
on:
push:
branches: ["main"]
paths:
- "ubs"
- "install.sh"
- "SHA256SUMS"
pull_request:
paths:
- "ubs"
- "install.sh"
- "SHA256SUMS"
workflow_dispatch:
permissions:
contents: write
concurrency:
group: checksum-sync-${{ github.ref_name }}
cancel-in-progress: true
jobs:
sync-checksums:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# Need write access for auto-commit on push to main
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 1
- name: Compute current checksums
id: compute
run: |
# Compute actual checksums for both files
ACTUAL_UBS=$(sha256sum ubs | awk '{print $1}')
ACTUAL_INSTALL=$(sha256sum install.sh | awk '{print $1}')
# Extract expected checksums (grep for specific file to avoid multi-line issues)
EXPECTED_UBS=$(grep ' ubs$' SHA256SUMS 2>/dev/null | awk '{print $1}' || echo "")
EXPECTED_INSTALL=$(grep ' install.sh$' SHA256SUMS 2>/dev/null | awk '{print $1}' || echo "")
echo "actual_ubs=$ACTUAL_UBS" >> "$GITHUB_OUTPUT"
echo "actual_install=$ACTUAL_INSTALL" >> "$GITHUB_OUTPUT"
echo "expected_ubs=$EXPECTED_UBS" >> "$GITHUB_OUTPUT"
echo "expected_install=$EXPECTED_INSTALL" >> "$GITHUB_OUTPUT"
# Check if both match
if [[ "$ACTUAL_UBS" == "$EXPECTED_UBS" && "$ACTUAL_INSTALL" == "$EXPECTED_INSTALL" ]]; then
echo "match=true" >> "$GITHUB_OUTPUT"
echo "::notice::SHA256SUMS is up to date"
else
echo "match=false" >> "$GITHUB_OUTPUT"
if [[ "$ACTUAL_UBS" != "$EXPECTED_UBS" ]]; then
echo "::warning::ubs checksum mismatch (expected $EXPECTED_UBS, actual $ACTUAL_UBS)"
fi
if [[ "$ACTUAL_INSTALL" != "$EXPECTED_INSTALL" ]]; then
echo "::warning::install.sh checksum mismatch (expected $EXPECTED_INSTALL, actual $ACTUAL_INSTALL)"
fi
fi
- name: Verify checksums (PR only)
if: github.event_name == 'pull_request' && steps.compute.outputs.match == 'false'
run: |
echo "::error::SHA256SUMS does not match current files!"
echo ""
echo "ubs checksum:"
echo " Expected: ${{ steps.compute.outputs.expected_ubs }}"
echo " Actual: ${{ steps.compute.outputs.actual_ubs }}"
echo ""
echo "install.sh checksum:"
echo " Expected: ${{ steps.compute.outputs.expected_install }}"
echo " Actual: ${{ steps.compute.outputs.actual_install }}"
echo ""
echo "Please run: ./scripts/update_checksums.sh"
exit 1
- name: Auto-update SHA256SUMS (push to main only)
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.compute.outputs.match == 'false'
run: |
# Create SHA256SUMS with both entries (install.sh first, then ubs)
echo "${{ steps.compute.outputs.actual_install }} install.sh" > SHA256SUMS
echo "${{ steps.compute.outputs.actual_ubs }} ubs" >> SHA256SUMS
echo "Updated SHA256SUMS:"
cat SHA256SUMS
- name: Commit and push updated checksums
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.compute.outputs.match == 'false'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add SHA256SUMS
git commit -m "chore(checksum): auto-update SHA256SUMS [skip ci]
Updated checksums:
install.sh: ${{ steps.compute.outputs.actual_install }}
ubs: ${{ steps.compute.outputs.actual_ubs }}
This commit was automatically generated by the checksum-sync workflow."
git push
name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
env:
UBS_NO_AUTO_UPDATE: "1"
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# The version-tag drift check below resolves
# `git show v$UBS_VERSION:modules/...`. The default fetch-depth
# of 1 doesn't include tag refs, so use 0 (full history +
# tags) — the repo is small enough that the cost is
# negligible and silently missing tags would let drift slip
# past the check.
fetch-depth: 0
- name: Syntax-check shell scripts
run: |
bash -n install.sh
bash -n ubs
- name: Verify entrypoint loads
run: ./ubs --help >/dev/null
- name: Verify v$UBS_VERSION tag matches MODULE_CHECKSUMS (issue #45)
# Catches the recurring distribution-drift bug: install.sh
# ships the latest `main` runner, but `ubs` fetches modules
# from v${UBS_VERSION}. If main's MODULE_CHECKSUMS table
# advances past the tag without bumping UBS_VERSION, every
# fresh install hits checksum mismatch on `ubs doctor --fix`.
# See https://github.com/Dicklesworthstone/ultimate_bug_scanner/issues/45
run: ./scripts/check-version-tag-drift.sh
test:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up uv with caching
uses: astral-sh/setup-uv@v7
with:
python-version: "3.13"
enable-cache: true
cache-dependency-glob: |
uv.lock
- name: Install system tools
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends jq ripgrep
- name: Install ast-grep
run: npm install -g @ast-grep/cli >/dev/null
- name: Sync Python dependencies
run: uv sync --locked --python 3.13
- name: Run manifest smoke suite
env:
NO_COLOR: "1"
UBS_LOG_JSON: "1"
run: |
cd test-suite && uv run python run_manifest.py --fail-fast \
--case js-core-buggy \
--case js-core-clean \
--case js-node-buggy \
--case js-node-clean \
--case golang-buggy \
--case golang-clean \
--case cpp-buggy \
--case cpp-clean \
--case rust-buggy \
--case rust-clean \
--case java-buggy \
--case java-clean \
--case ruby-buggy \
--case ruby-clean
- name: Run helper test suites
env:
NO_COLOR: "1"
UBS_LOG_JSON: "1"
run: |
uv run python test-suite/shareable/test_shareable_reports.py
uv run python test-suite/python/tests/test_resource_helper.py
uv run python test-suite/python/tests/test_git_safety_guard.py
uv run python test-suite/java/tests/test_resource_lifecycle_helper.py
uv run python test-suite/csharp/tests/test_helper_scanners.py
lint:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install shellcheck
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends shellcheck
- name: Run shellcheck
run: |
shellcheck -S error install.sh ubs modules/*.sh scripts/*.sh
- name: Compile-check Python files
run: python3 -m compileall scripts modules/helpers
name: UBS Manifest
on:
push:
branches: ["main"]
pull_request:
workflow_dispatch:
jobs:
manifest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up uv with caching
uses: astral-sh/setup-uv@v7
with:
python-version: "3.13"
enable-cache: true
cache-dependency-glob: |
uv.lock
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y python3 jq ripgrep >/dev/null
- name: Install ast-grep (npm)
run: npm install -g @ast-grep/cli >/dev/null
- name: Run UBS manifest suite
env:
NO_COLOR: "1"
UBS_NO_AUTO_UPDATE: "1"
UBS_LOG_JSON: "1"
run: |
cd test-suite
uv run python run_manifest.py --fail-fast
name: Nix Flake
on:
push:
branches: ["main"]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
flake-check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v27
with:
install_url: https://releases.nixos.org/nix/nix-2.18.1/install
extra_nix_config: |
experimental-features = nix-command flakes
- name: Run nix flake check
run: nix flake check
# installer-notify.yml
# Copy this to .github/workflows/ in your project
# Notifies ACFS when install.sh changes
#
# Setup:
# 1. Create a GitHub PAT with `repo` scope
# 2. Add it as ACFS_DISPATCH_TOKEN secret in your repo
# 3. Copy this file to .github/workflows/
name: Notify ACFS of Installer Change
on:
push:
branches: [main]
paths:
- 'install.sh'
- 'scripts/install.sh'
- '**/install.sh'
pull_request:
branches: [main]
paths:
- 'install.sh'
- 'scripts/install.sh'
- '**/install.sh'
concurrency:
group: installer-notify-${{ github.ref }}
cancel-in-progress: true
jobs:
notify-acfs:
# Only notify on push to main, not PRs
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Compute installer SHA256
id: checksum
run: |
# Find the installer file
if [ -f install.sh ]; then
INSTALLER_PATH="install.sh"
elif [ -f scripts/install.sh ]; then
INSTALLER_PATH="scripts/install.sh"
else
echo "No installer found"
exit 1
fi
SHA256=$(sha256sum "$INSTALLER_PATH" | cut -d' ' -f1)
echo "sha256=$SHA256" >> $GITHUB_OUTPUT
echo "Computed SHA256: $SHA256"
- name: Notify ACFS
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.ACFS_DISPATCH_TOKEN }}
repository: Dicklesworthstone/agentic_coding_flywheel_setup
event-type: installer-updated
client-payload: |
{
"tool": "${{ github.event.repository.name }}",
"repo": "${{ github.repository }}",
"commit": "${{ github.sha }}",
"new_sha256": "${{ steps.checksum.outputs.sha256 }}",
"ref": "${{ github.ref }}",
"actor": "${{ github.actor }}"
}
- name: Log notification
run: |
echo "::notice::Notified ACFS about installer change"
echo "Repository: ${{ github.repository }}"
echo "Commit: ${{ github.sha }}"
echo "SHA256: ${{ steps.checksum.outputs.sha256 }}"
# Validate installer syntax on PRs
validate-installer:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install shellcheck
run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: Shellcheck installer
run: |
EXIT_CODE=0
for script in install.sh scripts/install.sh; do
if [ -f "$script" ]; then
echo "Checking $script..."
shellcheck "$script" || EXIT_CODE=1
fi
done
exit $EXIT_CODE
name: OCI Image
on:
push:
branches: ["main"]
tags:
- "v*"
workflow_dispatch:
permissions:
contents: read
packages: write
id-token: write
jobs:
build-sign-push:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Normalize image name
run: echo "IMAGE_NAME=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/ubs-tools" >> "$GITHUB_ENV"
- name: Compute image tags
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
{
echo "IMAGE_TAGS<<EOF"
echo "${IMAGE_NAME}:${GITHUB_SHA}"
echo "EOF"
} >> "$GITHUB_ENV"
else
{
echo "IMAGE_TAGS<<EOF"
echo "${IMAGE_NAME}:${GITHUB_SHA}"
echo "${IMAGE_NAME}:latest"
# Add version tag if this is a tag push (e.g., v5.0.2)
if [[ "${{ github.ref_type }}" == "tag" ]]; then
echo "${IMAGE_NAME}:${{ github.ref_name }}"
fi
echo "EOF"
} >> "$GITHUB_ENV"
fi
- name: Set up QEMU (for multi-platform builds)
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
id: build
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
load: false
tags: ${{ env.IMAGE_TAGS }}
provenance: false
sbom: false
- name: Capture image digest
id: digest
if: github.event_name != 'pull_request'
run: |
DIGEST="${{ steps.build.outputs.digest }}"
if [ -z "$DIGEST" ]; then
echo "Image digest missing from docker/build-push-action output" >&2
exit 1
fi
DIGEST_HASH="${DIGEST#sha256:}"
IMAGE_DIGEST="${IMAGE_NAME}@sha256:${DIGEST_HASH}"
echo "digest=$IMAGE_DIGEST" >> "$GITHUB_OUTPUT"
echo "sha=$DIGEST_HASH" >> "$GITHUB_OUTPUT"
- name: Install syft
if: github.event_name != 'pull_request'
uses: anchore/sbom-action/download-syft@v0
with:
syft-version: v1.4.1
- name: Generate SBOM (SPDX JSON)
if: github.event_name != 'pull_request'
run: |
IMAGE_REF="${{ steps.digest.outputs.digest }}"
syft "$IMAGE_REF" -o spdx-json > sbom.spdx.json
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@v3.6.0
with:
cosign-release: v2.2.4
- name: Sign image (keyless)
env:
COSIGN_EXPERIMENTAL: "1"
if: github.event_name != 'pull_request'
run: cosign sign --yes ${{ steps.digest.outputs.digest }}
- name: Attach SBOM attestation
env:
COSIGN_EXPERIMENTAL: "1"
if: github.event_name != 'pull_request'
run: cosign attest --yes --predicate sbom.spdx.json --type spdx ${{ steps.digest.outputs.digest }}
- name: Generate provenance predicate (SLSA v1)
if: github.event_name != 'pull_request'
run: |
cat > provenance.json <<'EOF'
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{ "name": "${{ env.IMAGE_NAME }}", "digest": { "sha256": "${{ steps.digest.outputs.sha }}" } }
],
"predicateType": "https://slsa.dev/provenance/v1",
"predicate": {
"buildDefinition": {
"buildType": "https://slsa.dev/provenance/v1",
"externalParameters": {
"source": "${{ github.repository }}",
"ref": "${{ github.ref }}",
"sha": "${{ github.sha }}"
}
},
"runDetails": {
"builder": { "id": "https://github.com/actions" },
"metadata": { "invocationId": "${{ github.run_id }}" }
}
}
}
EOF
- name: Attach provenance attestation (SLSA v1)
env:
COSIGN_EXPERIMENTAL: "1"
if: github.event_name != 'pull_request'
run: cosign attest --yes --predicate provenance.json --type https://slsa.dev/provenance/v1 ${{ steps.digest.outputs.digest }}
- name: Upload SBOM artifact
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4
with:
name: sbom-spdx
path: sbom.spdx.json
- name: Upload provenance artifact
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4
with:
name: oci-provenance
path: provenance.json
name: Legacy Release Artifacts (disabled)
# Retained to honor the no-delete policy. The actual release process now lives in
# .github/workflows/release.yml. This workflow is gated off by default to avoid
# duplicate releases; run manually if you need the older path.
on:
workflow_dispatch:
jobs:
noop:
runs-on: ubuntu-latest
steps:
- run: echo "Legacy release workflow replaced by release.yml"
name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
permissions:
contents: write
packages: write
id-token: write
env:
COSIGN_VERSION: v2.2.4
SYFT_VERSION: v1.4.1
UV_VERSION: "0.4.20"
JQ_VERSION: "1.7.1"
RG_VERSION: "13.0.0"
jobs:
gather:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.meta.outputs.version }}
tag: ${{ steps.meta.outputs.tag }}
steps:
- name: Compute release metadata
id: meta
run: |
TAG="${GITHUB_REF_NAME}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
nix-check:
runs-on: ubuntu-latest
needs: gather
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v27
with:
install_url: https://releases.nixos.org/nix/nix-2.18.1/install
extra_nix_config: |
experimental-features = nix-command flakes
- name: Run nix flake check
run: nix flake check
build-artifacts:
runs-on: ubuntu-latest
needs: gather
env:
TAG: ${{ needs.gather.outputs.tag }}
VERSION: ${{ needs.gather.outputs.version }}
steps:
- name: Ensure minisign secret is present
run: |
if [ -z "${{ secrets.MINISIGN_SECRET_KEY }}" ]; then
echo "MINISIGN_SECRET_KEY is required for releases" >&2
exit 1
fi
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install pinned tooling
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends ca-certificates curl dpkg-dev coreutils minisign
# jq (static binary)
curl -fsSLo /usr/local/bin/jq "https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/jq-linux-amd64"
echo "5942c9b0934e510ee61eb3e30273f1b3fe2590df93933a93d7c58b81d19c8ff5 /usr/local/bin/jq" | sha256sum --check --status
chmod +x /usr/local/bin/jq
# ripgrep (13.0.0 deb)
curl -fsSLo "/tmp/ripgrep_${RG_VERSION}_amd64.deb" "https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/ripgrep_${RG_VERSION}_amd64.deb"
echo "6d78bed13722019cb4f9d0cf366715e2dcd589f4cf91897efb28216a6bb319f1 /tmp/ripgrep_${RG_VERSION}_amd64.deb" | sha256sum --check --status
sudo dpkg -i "/tmp/ripgrep_${RG_VERSION}_amd64.deb"
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
python-version: "3.13"
enable-cache: false
version: ${{ env.UV_VERSION }}
- name: Verify tag matches VERSION file
run: |
FILE_VERSION=$(tr -d '\r\n' < VERSION)
if [ "$FILE_VERSION" != "$VERSION" ]; then
echo "VERSION file ($FILE_VERSION) does not match tag ($VERSION)" >&2
exit 1
fi
- name: Create dist directory
run: mkdir -p dist
- name: Generate checksums
run: |
sha256sum install.sh ubs scripts/verify.sh > dist/SHA256SUMS
- name: Sign checksums with minisign
env:
MINISIGN_SECRET_KEY: ${{ secrets.MINISIGN_SECRET_KEY }}
run: |
printf "%s" "$MINISIGN_SECRET_KEY" > dist/minisign.key
chmod 600 dist/minisign.key
minisign -S -s dist/minisign.key -m dist/SHA256SUMS -x dist/SHA256SUMS.minisig
rm dist/minisign.key
- name: Install syft
uses: anchore/sbom-action/download-syft@v0
with:
syft-version: ${{ env.SYFT_VERSION }}
- name: Generate project SBOM (SPDX JSON)
run: syft dir:. -o spdx-json > dist/sbom.spdx.json
- name: Build Homebrew formula
run: |
UBS_SHA=$(sha256sum ubs | cut -d' ' -f1)
cat > dist/ubs.rb <<'RUBY'
class Ubs < Formula
desc "Ultimate Bug Scanner meta-runner"
homepage "https://github.com/Dicklesworthstone/ultimate_bug_scanner"
RUBY
{
echo " version \"$VERSION\""
echo " url \"https://github.com/Dicklesworthstone/ultimate_bug_scanner/releases/download/v$VERSION/ubs\""
echo " sha256 \"$UBS_SHA\""
} >> dist/ubs.rb
cat >> dist/ubs.rb <<'RUBY'
license "MIT"
def install
chmod 0555, "ubs"
bin.install "ubs"
end
test do
system "#{bin}/ubs", "--help"
end
end
RUBY
perl -pi -e 's/^ {10}//' dist/ubs.rb
- name: Assemble release payload
run: |
cp install.sh dist/
cp ubs dist/
cp scripts/verify.sh dist/
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: release-artifacts
path: dist
oci-image:
runs-on: ubuntu-latest
needs: gather
env:
TAG: ${{ needs.gather.outputs.tag }}
VERSION: ${{ needs.gather.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Normalize image name
run: echo "IMAGE_NAME=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/ubs-tools" >> "$GITHUB_ENV"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
id: build
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.IMAGE_NAME }}:${{ env.TAG }}
${{ env.IMAGE_NAME }}:latest
provenance: false
sbom: false
- name: Capture image digest
id: digest
run: |
DIGEST="${{ steps.build.outputs.digest }}"
if [ -z "$DIGEST" ]; then
echo "Image digest missing from docker/build-push-action output" >&2
exit 1
fi
DIGEST_HASH="${DIGEST#sha256:}"
IMAGE_DIGEST="${IMAGE_NAME}@sha256:${DIGEST_HASH}"
echo "digest=$IMAGE_DIGEST" >> "$GITHUB_OUTPUT"
echo "sha=$DIGEST_HASH" >> "$GITHUB_OUTPUT"
- name: Install syft
uses: anchore/sbom-action/download-syft@v0
with:
syft-version: ${{ env.SYFT_VERSION }}
- name: Generate SBOM (SPDX JSON)
run: syft ${{ steps.digest.outputs.digest }} -o spdx-json > sbom.spdx.json
- name: Install cosign
uses: sigstore/cosign-installer@v3.6.0
with:
cosign-release: ${{ env.COSIGN_VERSION }}
- name: Sign image (keyless)
env:
COSIGN_EXPERIMENTAL: "1"
run: cosign sign --yes ${{ steps.digest.outputs.digest }}
- name: Attach SBOM attestation
env:
COSIGN_EXPERIMENTAL: "1"
run: cosign attest --yes --predicate sbom.spdx.json --type spdx ${{ steps.digest.outputs.digest }}
- name: Generate provenance predicate (SLSA v1)
run: |
cat > provenance.json <<'EOF'
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{ "name": "${{ env.IMAGE_NAME }}", "digest": { "sha256": "${{ steps.digest.outputs.sha }}" } }
],
"predicateType": "https://slsa.dev/provenance/v1",
"predicate": {
"buildDefinition": {
"buildType": "https://slsa.dev/provenance/v1",
"externalParameters": {
"source": "${{ github.repository }}",
"ref": "${{ github.ref }}",
"sha": "${{ github.sha }}"
}
},
"runDetails": {
"builder": { "id": "https://github.com/actions" },
"metadata": { "invocationId": "${{ github.run_id }}" }
}
}
}
EOF
perl -pi -e 's/^ {10}//' provenance.json
- name: Attach provenance attestation (SLSA v1)
env:
COSIGN_EXPERIMENTAL: "1"
run: cosign attest --yes --predicate provenance.json --type https://slsa.dev/provenance/v1 ${{ steps.digest.outputs.digest }}
- name: Upload OCI artifacts
uses: actions/upload-artifact@v4
with:
name: oci-artifacts
path: |
sbom.spdx.json
provenance.json
publish:
runs-on: ubuntu-latest
needs:
- build-artifacts
- oci-image
- nix-check
steps:
- name: Download release artifacts
uses: actions/download-artifact@v4
with:
name: release-artifacts
path: dist
- name: Download OCI artifacts
uses: actions/download-artifact@v4
with:
name: oci-artifacts
path: oci
- name: Normalize OCI artifact names
run: |
mv oci/sbom.spdx.json oci/oci-sbom.spdx.json
mv oci/provenance.json oci/oci-provenance.json
- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
files: |
dist/install.sh
dist/ubs
dist/SHA256SUMS
dist/SHA256SUMS.minisig
dist/ubs.rb
dist/sbom.spdx.json
oci/oci-sbom.spdx.json
oci/oci-provenance.json
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
outputs:
version: ${{ steps.version.outputs.version }}
# ==========================================================================
# Notify Package Managers to Update
# ==========================================================================
notify-homebrew-tap:
name: Notify Homebrew Tap
runs-on: ubuntu-latest
needs: publish
env:
TAP_TOKEN: ${{ secrets.ACFS_REPO_DISPATCH_TOKEN }}
steps:
- name: Skip dispatch when token missing
if: ${{ env.TAP_TOKEN == '' }}
run: |
echo "::warning::ACFS_REPO_DISPATCH_TOKEN not set; skipping Homebrew tap dispatch."
echo "The tap's scheduled check (every 6h) will pick up the new version automatically."
- name: Trigger formula update
if: ${{ env.TAP_TOKEN != '' }}
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ env.TAP_TOKEN }}
repository: Dicklesworthstone/homebrew-tap
event-type: formula-update
client-payload: |
{
"tool": "ubs",
"version": "${{ needs.publish.outputs.version }}"
}
- name: Log dispatch
if: ${{ env.TAP_TOKEN != '' }}
run: |
echo "✅ Dispatched formula-update event to homebrew-tap"
echo " Tool: ubs"
echo " Version: ${{ needs.publish.outputs.version }}"
name: UBS Test Suite
on:
push:
branches: ["main"]
pull_request:
workflow_dispatch:
permissions:
contents: read
actions: write
concurrency:
group: test-suite-${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
tests:
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
shell: bash
env:
UBS_NO_AUTO_UPDATE: "1"
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Verify checksums (early fail if stale)
run: |
echo "Verifying SHA256SUMS integrity..."
ACTUAL_UBS=$(sha256sum ubs | awk '{print $1}')
ACTUAL_INSTALL=$(sha256sum install.sh | awk '{print $1}')
EXPECTED_UBS=$(grep ' ubs$' SHA256SUMS 2>/dev/null | awk '{print $1}' || echo "")
EXPECTED_INSTALL=$(grep ' install.sh$' SHA256SUMS 2>/dev/null | awk '{print $1}' || echo "")
FAIL=0
if [[ "$ACTUAL_UBS" != "$EXPECTED_UBS" ]]; then
echo "::error::ubs checksum mismatch (expected $EXPECTED_UBS, got $ACTUAL_UBS)"
FAIL=1
fi
if [[ "$ACTUAL_INSTALL" != "$EXPECTED_INSTALL" ]]; then
echo "::error::install.sh checksum mismatch (expected $EXPECTED_INSTALL, got $ACTUAL_INSTALL)"
FAIL=1
fi
if [[ "$FAIL" -eq 1 ]]; then
echo ""
echo "SHA256SUMS is out of sync! Run: ./scripts/update_checksums.sh"
exit 1
fi
echo "All checksums verified!"
- name: Set up uv with caching
uses: astral-sh/setup-uv@v7
with:
python-version: "3.13"
enable-cache: true
cache-dependency-glob: |
uv.lock
- name: Install system tools
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends jq ripgrep
- name: Install ast-grep (npm)
run: npm install -g @ast-grep/cli >/dev/null
- name: Sync Python dependencies
run: uv sync --locked --python 3.13
- name: Run full test suite
env:
NO_COLOR: "1"
UBS_NO_AUTO_UPDATE: "1"
UBS_LOG_JSON: "1"
run: ./test-suite/run_all.sh
# Test files
test/
*.test.js
*.test.txt
# Backup files
*.backup
*.bak
# OS files
.DS_Store
Thumbs.db
# Editor files
.vscode/
.idea/
*.swp
*.swo
*~
# Temporary files
tmp/
temp/
*.tmp
.bv/
# Beads ephemeral files
.beads/last-touched
.beads/.bv.lock
.beads/.local_version
.beads/daemon.lock
.beads/daemon.log
.beads/daemon-*.log
.beads/daemon-*.log.gz
.beads/*.db
.beads/*.db-*
.beads/*.sqlite
.beads/*.sqlite-*
.beads/*.sqlite3
.beads/*.sqlite3-*
.beads/*.migrated
# Python environments
.venv/
__pycache__/
*.pyc
*.pyo
# Rust build artifacts
**/target/
**/*.rs.bk
Cargo.lock
# Test artifacts
test-suite/artifacts/
rule-quality-variants/
# Compiled test artifacts
a.out
# Bead dump exports (dated ephemeral reports)
*_bead_dump_*.txt
# Beads history snapshots
.beads/.br_history/
# Profiling data
perf.data
perf.data.*
# Local scratch DBs
storage.sqlite3
storage.sqlite3-*
*.sqlite3.bak
# Prompt dump files (agent-generated diagnostic exports)
prompt_dump*.md
modules/prompt_dump*.md
# Agent-generated planning/reasoning scratch artifacts
modes-of-reasoning-*.md
reasoning-mode-*.md
*_modes_of_reasoning*.md
MOR_*.md
artifact-*.json
artifacts/*.json
# Tool caches
.ruff_cache/
# macOS resource fork files
._*
# bv graph output
--graph-format.svg
# ACFS ephemeral patterns (auto-generated agent artifacts)
core
core.*
cline.mcp.json
codex.mcp.json
cursor.mcp.json
gemini.mcp.json
opencode.json
windsurf.mcp.json
*.mcp.json.*.bak
.opencode.json.*.bak
.windsurf.mcp.json.*.bak
.agent-mail-project-id
.agent-mail.yaml
*.sqlite3.corrupt*
storage.sqlite3.corrupt-*
storage.sqlite3-shm.corrupt-*
storage.sqlite3-wal.corrupt-*
.beads/*.corrupt*
.beads/*.pre_repair_*
.beads/*.repair_candidate_*
.beads/*.bak-corrupt
tmp-rel-*
````markdown
## UBS Quick Reference for AI Agents
UBS stands for "Ultimate Bug Scanner": **The AI Coding Agent's Secret Weapon: Flagging Likely Bugs for Fixing Early On**
**Install:** `curl -sSL https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/install.sh | bash`
**Golden Rule:** `ubs <changed-files>` before every commit. Exit 0 = safe. Exit >0 = fix & re-run.
**Commands:**
```bash
ubs file.ts file2.py # Specific files (< 1s) — USE THIS
ubs $(git diff --name-only --cached) # Staged files — before commit
ubs --only=js,python src/ # Language filter (3-5x faster)
ubs --ci --fail-on-warning . # CI mode — before PR
ubs --help # Full command reference
ubs sessions --entries 1 # Tail the latest install session log
ubs . # Whole project (ignores things like .venv and node_modules automatically)
```
**Output Format:**
```
⚠️ Category (N errors)
file.ts:42:5 – Issue description
💡 Suggested fix
Exit code: 1
```
Parse: `file:line:col` → location | 💡 → how to fix | Exit 0/1 → pass/fail
**Fix Workflow:**
1. Read finding → category + fix suggestion
2. Navigate `file:line:col` → view context
3. Verify real issue (not false positive)
4. Fix root cause (not symptom)
5. Re-run `ubs <file>` → exit 0
6. Commit
**Speed Critical:** Scope to changed files. `ubs src/file.ts` (< 1s) vs `ubs .` (30s). Never full scan for small edits.
**Bug Severity:**
- **Critical** (always fix): Null safety, XSS/injection, async/await, memory leaks
- **Important** (production): Type narrowing, division-by-zero, resource leaks
- **Contextual** (judgment): TODO/FIXME, console logs
**Anti-Patterns:**
- ❌ Ignore findings → ✅ Investigate each
- ❌ Full scan per edit → ✅ Scope to file
- ❌ Fix symptom (`if (x) { x.y }`) → ✅ Root cause (`x?.y`)
````
## Ultimate Bug Scanner ignore list
# Paths/globs listed here are skipped by `./ubs` (similar to .gitignore semantics).
# Useful for excluding intentionally buggy fixtures, generated assets, or vendor dirs.
test-suite/
rule-quality-variants/
modules/helpers/*.go
modules/helpers/resource_lifecycle_go.go
modules/helpers/type_narrowing_ts.js
AGENTS.md — ultimate_bug_scanner
Guidelines for AI coding agents working in this Bash/Shell codebase.
---
RULE 0 - THE FUNDAMENTAL OVERRIDE PREROGATIVE
If I tell you to do something, even if it goes against what follows below, YOU MUST LISTEN TO ME. I AM IN CHARGE, NOT YOU.
---
RULE NUMBER 1: NO FILE DELETION
YOU ARE NEVER ALLOWED TO DELETE A FILE WITHOUT EXPRESS PERMISSION. Even a new file that you yourself created, such as a test code file. You have a horrible track record of deleting critically important files or otherwise throwing away tons of expensive work. As a result, you have permanently lost any and all rights to determine that a file or folder should be deleted.
YOU MUST ALWAYS ASK AND RECEIVE CLEAR, WRITTEN PERMISSION BEFORE EVER DELETING A FILE OR FOLDER OF ANY KIND.
---
Irreversible Git & Filesystem Actions — DO NOT EVER BREAK GLASS
1. Absolutely forbidden commands: git reset --hard, git clean -fd, rm -rf, or any command that can delete or overwrite code/data must never be run unless the user explicitly provides the exact command and states, in the same message, that they understand and want the irreversible consequences. 2. No guessing: If there is any uncertainty about what a command might delete or overwrite, stop immediately and ask the user for specific approval. "I think it's safe" is never acceptable. 3. Safer alternatives first: When cleanup or rollbacks are needed, request permission to use non-destructive options (git status, git diff, git stash, copying to backups) before ever considering a destructive command. 4. Mandatory explicit plan: Even after explicit user authorization, restate the command verbatim, list exactly what will be affected, and wait for a confirmation that your understanding is correct. Only then may you execute it—if anything remains ambiguous, refuse and escalate. 5. Document the confirmation: When running any approved destructive command, record (in the session notes / final response) the exact user text that authorized it, the command actually run, and the execution time. If that record is absent, the operation did not happen.
---
Git Branch: ONLY Use main, NEVER master
The default branch is `main`. The `master` branch exists only for legacy URL compatibility.
- All work happens on `main` — commits, PRs, feature branches all merge to
main - Never reference `master` in code or docs — if you see
masteranywhere, it's a bug that needs fixing - The `master` branch must stay synchronized with `main` — after pushing to
main, also push tomaster:
git push origin main:masterIf you see `master` referenced anywhere: 1. Update it to main 2. Ensure master is synchronized: git push origin main:master
---
Toolchain: Bash & Shell
UBS is a pure Bash project — the meta-runner (ubs) and all language modules (modules/ubs-*.sh) are Bash scripts. Helper assets use Python and Go/JS for AST-level analysis.
- Shell dialect: Bash 5+ with
set -Eeuo pipefail - Package management: Nix flake (
flake.nix) for reproducible dev shells and packaging;pyproject.toml(uv-managed, Python 3.13) for helper tooling only - Core runtime dependencies:
bash,jq,ripgrep,git,curl,python3 - Version: Tracked in
VERSIONfile (currently 5.0.7) - Unsafe code: N/A (shell scripts)
Key Dependencies
| Tool / Library | Purpose |
|---|---|
bash | Meta-runner and all language module scripts |
ripgrep (rg) | Fast file scanning within language modules |
jq | JSON/SARIF output merging in the meta-runner |
python3 | AST helpers (resource lifecycle, type narrowing), ignore-file parsing, checksum updates |
curl | Lazy module download from GitHub |
shellcheck | Linting for shell scripts (dev shell) |
cmake | Build dependency for certain test fixtures |
minisign / cosign | Release artifact and OCI image signing |
Nix Packaging
The project provides a Nix flake with:
- `packages.default` — installs
ubsto$out/bin/ubs - `devShells.default` —
bashInteractive,shellcheck,git,cmake,python3,jq,ripgrep,uv - `nixosModules.ubs` — NixOS module with
programs.ubs.enable - Docker —
Dockerfilebased ondebian:bookworm-slim
---
Code Editing Discipline
No Script-Based Changes
NEVER run a script that processes/changes code files in this repo. Brittle regex-based transformations create far more problems than they solve.
- Always make code changes manually, even when there are many instances
- For many simple changes: use parallel subagents
- For subtle/complex changes: do them methodically yourself
No File Proliferation
If you want to change something or add a feature, revise existing code files in place.
NEVER create variations like:
ubs-pythonV2.shubs-python_improved.shubs-python_enhanced.sh
New files are reserved for genuinely new functionality that makes zero sense to include in any existing file. The bar for creating new files is incredibly high.
---
Backwards Compatibility
We do not care about backwards compatibility—we're in early development with no users. We want to do things the RIGHT way with NO TECH DEBT.
- Never create "compatibility shims"
- Never create wrapper functions for deprecated APIs
- Just fix the code directly
---
Quality Checks (CRITICAL)
After any substantive code changes, you MUST verify no errors were introduced:
# Lint all shell scripts with ShellCheck
shellcheck ubs modules/ubs-*.sh scripts/*.sh
# Verify module checksums are current
./scripts/update_checksums.sh
# Run the test suite
cd test-suite && ./run_all.sh
# Verify SHA256SUMS
./scripts/verify_sha256sums.shIf you see errors, carefully understand and resolve each issue. Read sufficient context to fix them the RIGHT way.
---
Testing
Testing Policy
The test suite lives in test-suite/ and is organized by language. Each language has buggy/ (known-bad) and clean/ (known-good) fixtures to validate detection accuracy and false-positive rates.
Running Tests
# Run all test suites
cd test-suite && ./run_all.sh
# Run via manifest (structured, tracks expected results)
python3 test-suite/run_manifest.py
# Run a specific language module directly
modules/ubs-rust.sh test-suite/rust/buggy/
modules/ubs-python.sh test-suite/python/buggy/
# Run the meta-runner on the whole project
./ubs .
# CI mode (stable timestamps, strict)
./ubs . --ci --fail-on-warningTest Categories
| Directory | Focus Areas |
|---|---|
test-suite/buggy/ | Multi-language intentionally buggy files for cross-language scanning |
test-suite/clean/ | Clean files that must produce zero findings (false-positive regression) |
test-suite/rust/ | Rust-specific test cases (buggy/, clean/, async_errors/) |
test-suite/python/ | Python-specific test cases |
test-suite/js/ | JavaScript/TypeScript test cases |
test-suite/cpp/ | C/C++ test cases |
test-suite/golang/ | Go test cases |
test-suite/java/ | Java test cases |
test-suite/ruby/ | Ruby test cases |
test-suite/swift/ | Swift test cases |
test-suite/csharp/ | C#/.NET test cases |
test-suite/kotlin/ | Kotlin test cases |
test-suite/edge-cases/ | Tricky edge cases across languages |
test-suite/frameworks/ | Framework-specific patterns |
test-suite/realistic/ | Real-world-style code samples |
test-suite/shareable/ | Shareable test utilities |
test-suite/artifacts/ | Generated test artifacts (gitignored) |
Test Fixtures
The test-suite/manifest.json tracks expected outcomes per file so run_manifest.py can detect regressions automatically.
---
Third-Party Library Usage
If you aren't 100% sure how to use a third-party library, SEARCH ONLINE to find the latest documentation and current best practices.
---
ultimate_bug_scanner — This Project
This is the project you're working on. The Ultimate Bug Scanner (ubs) is a multi-language static analysis meta-runner that dispatches language-specific scanning modules concurrently, merges their outputs, and reports findings in text, JSON, or SARIF format. It covers 9 languages: JavaScript/TypeScript, Python, C/C++, Rust, Go, Java, Ruby, Swift, and C#.
What It Does
Detects real bugs and security issues using fast regex/heuristic-based analysis modules, each tailored to language-specific bug patterns. Runs in under a second on targeted files and supports CI integration with --fail-on-warning mode.
Architecture
Invocation → Parse CLI args → Detect languages → ┬─ ubs-js.sh (JS/TS)
├─ ubs-python.sh (Python)
├─ ubs-cpp.sh (C/C++)
├─ ubs-rust.sh (Rust)
├─ ubs-golang.sh (Go)
├─ ubs-java.sh (Java)
├─ ubs-ruby.sh (Ruby)
├─ ubs-swift.sh (Swift)
└─ ubs-csharp.sh (C#)
│
(concurrent execution)
│
Merge outputs (jq)
│
text / JSON / SARIF report
│
Exit 0 (clean) or 1 (issues)Project Structure
ultimate_bug_scanner/
├── ubs # Meta-runner: language detection, dispatch, merge
├── VERSION # Semver version file
├── install.sh # Signed installer script
├── SHA256SUMS # Signed checksums for supply-chain integrity
├── Dockerfile # OCI image (debian:bookworm-slim)
├── flake.nix # Nix flake: packaging, dev shell, NixOS module
├── pyproject.toml # Python helper tooling (uv-managed)
├── .ubsignore # Paths/globs skipped by ubs (like .gitignore)
├── modules/
│ ├── ubs-js.sh # JavaScript/TypeScript scanner
│ ├── ubs-python.sh # Python scanner
│ ├── ubs-cpp.sh # C/C++ scanner
│ ├── ubs-rust.sh # Rust scanner
│ ├── ubs-golang.sh # Go scanner
│ ├── ubs-java.sh # Java scanner
│ ├── ubs-ruby.sh # Ruby scanner
│ ├── ubs-swift.sh # Swift scanner
│ ├── ubs-csharp.sh # C# scanner
│ ├── README.md # Module interface contract
│ └── helpers/ # AST correlation & type narrowing helpers
│ ├── async_task_handles_csharp.py # C# async task-handle analysis
│ ├── resource_lifecycle_csharp.py # C# resource lifecycle analysis
│ ├── resource_lifecycle_go.go # Go resource lifecycle analysis
│ ├── resource_lifecycle_java.py # Java resource lifecycle analysis
│ ├── resource_lifecycle_py.py # Python resource lifecycle analysis
│ ├── type_narrowing_csharp.py # C# type narrowing
│ ├── type_narrowing_kotlin.py # Kotlin type narrowing
│ ├── type_narrowing_rust.py # Rust type narrowing
│ ├── type_narrowing_swift.py # Swift type narrowing
│ └── type_narrowing_ts.js # TypeScript type narrowing
├── scripts/
│ ├── setup_dev.sh # Dev environment setup
│ ├── update_checksums.sh # Regenerate module checksums in ubs
│ ├── update_checksums.py # Python helper for checksum generation
│ ├── update_sha256sums.sh # Update SHA256SUMS file
│ ├── verify.sh # Verify installer signature + checksums
│ ├── verify_checksums.sh # Verify module checksums
│ └── verify_sha256sums.sh # Verify SHA256SUMS file
├── test-suite/ # Language-organized test fixtures + manifest
├── docs/
│ ├── release.md # Release process documentation
│ └── security.md # Threat model and integrity controls
└── notes/ # Design notesKey Files
| File | Purpose |
|---|---|
ubs | Meta-runner: CLI parsing, language detection, .ubsignore support, module dispatch (concurrent), output merging (jq), supply-chain checksum verification, auto-update |
modules/ubs-*.sh | Per-language scanners: file detection, ripgrep-based heuristics, JSON/SARIF output, severity classification |
modules/helpers/ | AST-level analysis helpers (Python/Go/JS/C#): resource lifecycle tracking, type narrowing |
install.sh | Signed installer for `curl \ |
scripts/update_checksums.sh | Regenerates SHA-256 checksums in the ubs meta-runner after module changes |
test-suite/manifest.json | Expected results manifest for regression testing |
test-suite/run_manifest.py | Manifest-driven test runner |
SHA256SUMS | Release artifact checksums (signed with minisign) |
Output Formats
ubs . --format=text # Human-readable (default)
ubs . --format=json # Machine-parseable JSON
ubs . --format=sarif # SARIF for GitHub Code Scanning / IDE integrationCLI Reference
ubs file.rs file2.rs # Specific files (< 1s)
ubs $(git diff --name-only --cached) # Staged files (pre-commit)
ubs --only=rust,toml src/ # Language filter (3-5x faster)
ubs --ci --fail-on-warning . # CI mode (UTC timestamps, strict)
ubs . # Whole project (respects .ubsignore)
ubs -v . # Verbose mode (more examples)
ubs doctor --fix # Verify/repair cached modulesSeverity Levels
| Level | Action | Examples |
|---|---|---|
| Critical | Fix IMMEDIATELY | Memory safety, use-after-free, data races, SQL injection, crashes, security, data corruption |
| Warning | Fix before commit | Unwrap panics, resource leaks, overflow checks, performance, maintenance |
| Info | Consider improving | TODO/FIXME, println! debugging, code quality, best practices |
Exit Codes
| Code | Meaning |
|---|---|
0 | No critical issues (safe to proceed) |
1 | Critical issues found (MUST fix before committing) |
Supply Chain Security
The ubs meta-runner embeds SHA-256 checksums for every language module and helper asset. Downloads are verified before execution; invalid checksums fail closed.
*Whenever you modify any module script (`modules/ubs-.sh`) or helper, you MUST update checksums:**
./scripts/update_checksums.shAdditional integrity controls:
- Installer signing:
SHA256SUMSsigned with minisign;scripts/verify.shvalidates before execution - OCI image signing: Cosign keyless signing by digest, Rekor transparency log, SBOM + SLSA attestations
- Auto-update opt-in:
UBS_ENABLE_AUTO_UPDATE=1to enable;UBS_NO_AUTO_UPDATE=1to force-disable
Key Design Decisions
- Pure Bash meta-runner — zero compiled dependencies for the dispatcher; language modules are also Bash scripts using
ripgrepfor fast scanning - Concurrent module execution — language modules run in parallel; outputs merged by
jq - Lazy module download — modules fetched from GitHub on first use, cached locally, checksum-verified
- `.ubsignore` support — gitignore-like exclusion for intentionally buggy fixtures, generated assets, vendor directories
- Three output formats — text (human), JSON (automation), SARIF (GitHub Code Scanning / IDEs)
- `--ci` mode — stable UTC ISO-8601 timestamps for reproducible CI output
- Helper assets for deep analysis — Python/Go/JS/C# helpers handle AST-level resource lifecycle and type narrowing checks that regex alone cannot express
- Nix flake for packaging — reproducible builds, dev shell, NixOS module
- Docker image —
debian:bookworm-slimbase for containerized CI use - Console output should be informative, detailed, stylish, and colorful, fully leveraging appropriate libraries/escape sequences wherever possible
---
MCP Agent Mail — Multi-Agent Coordination
A mail-like layer that lets coding agents coordinate asynchronously via MCP tools and resources. Provides identities, inbox/outbox, searchable threads, and advisory file reservations with human-auditable artifacts in Git.
Why It's Useful
- Prevents conflicts: Explicit file reservations (leases) for files/globs
- Token-efficient: Messages stored in per-project archive, not in context
- Quick reads:
resource://inbox/...,resource://thread/...
Same Repository Workflow
1. Register identity:
ensure_project(project_key=<abs-path>)
register_agent(project_key, program, model)2. Reserve files before editing:
file_reservation_paths(project_key, agent_name, ["src/**"], ttl_seconds=3600, exclusive=true)3. Communicate with threads:
send_message(..., thread_id="FEAT-123")
fetch_inbox(project_key, agent_name)
acknowledge_message(project_key, agent_name, message_id)4. Quick reads:
resource://inbox/{Agent}?project=<abs-path>&limit=20
resource://thread/{id}?project=<abs-path>&include_bodies=trueMacros vs Granular Tools
- Prefer macros for speed:
macro_start_session,macro_prepare_thread,macro_file_reservation_cycle,macro_contact_handshake - Use granular tools for control:
register_agent,file_reservation_paths,send_message,fetch_inbox,acknowledge_message
Common Pitfalls
"from_agent not registered": Alwaysregister_agentin the correctproject_keyfirst"FILE_RESERVATION_CONFLICT": Adjust patterns, wait for expiry, or use non-exclusive reservation- Auth errors: If JWT+JWKS enabled, include bearer token with matching
kid
---
Beads (br) — Dependency-Aware Issue Tracking
Beads provides a lightweight, dependency-aware issue database and CLI (br - beads_rust) for selecting "ready work," setting priorities, and tracking status. It complements MCP Agent Mail's messaging and file reservations.
Important: br is non-invasive—it NEVER runs git commands automatically. You must manually commit changes after br sync --flush-only.
Conventions
- Single source of truth: Beads for task status/priority/dependencies; Agent Mail for conversation and audit
- Shared identifiers: Use Beads issue ID (e.g.,
br-123) as Mailthread_idand prefix subjects with[br-123] - Reservations: When starting a task, call
file_reservation_paths()with the issue ID inreason
Typical Agent Flow
1. Pick ready work (Beads):
br ready --json # Choose highest priority, no blockers2. Reserve edit surface (Mail):
file_reservation_paths(project_key, agent_name, ["src/**"], ttl_seconds=3600, exclusive=true, reason="br-123")3. Announce start (Mail):
send_message(..., thread_id="br-123", subject="[br-123] Start: <title>", ack_required=true)4. Work and update: Reply in-thread with progress
5. Complete and release:
br close 123 --reason "Completed"
br sync --flush-only # Export to JSONL (no git operations) release_file_reservations(project_key, agent_name, paths=["src/**"])Final Mail reply: [br-123] Completed with summary
Mapping Cheat Sheet
| Concept | Value |
|---|---|
Mail thread_id | br-### |
| Mail subject | [br-###] ... |
File reservation reason | br-### |
| Commit messages | Include br-### for traceability |
---
bv — Graph-Aware Triage Engine
bv is a graph-aware triage engine for Beads projects (.beads/beads.jsonl). It computes PageRank, betweenness, critical path, cycles, HITS, eigenvector, and k-core metrics deterministically.
Scope boundary: bv handles what to work on (triage, priority, planning). For agent-to-agent coordination (messaging, work claiming, file reservations), use MCP Agent Mail.
*CRITICAL: Use ONLY `--robot- flags. Bare bv` launches an interactive TUI that blocks your session.**
The Workflow: Start With Triage
`bv --robot-triage` is your single entry point. It returns:
quick_ref: at-a-glance counts + top 3 picksrecommendations: ranked actionable items with scores, reasons, unblock infoquick_wins: low-effort high-impact itemsblockers_to_clear: items that unblock the most downstream workproject_health: status/type/priority distributions, graph metricscommands: copy-paste shell commands for next steps
bv --robot-triage # THE MEGA-COMMAND: start here
bv --robot-next # Minimal: just the single top pick + claim commandCommand Reference
Planning:
| Command | Returns |
|---|---|
--robot-plan | Parallel execution tracks with unblocks lists |
--robot-priority | Priority misalignment detection with confidence |
Graph Analysis:
| Command | Returns |
|---|---|
--robot-insights | Full metrics: PageRank, betweenness, HITS, eigenvector, critical path, cycles, k-core, articulation points, slack |
--robot-label-health | Per-label health: health_level, velocity_score, staleness, blocked_count |
--robot-label-flow | Cross-label dependency: flow_matrix, dependencies, bottleneck_labels |
--robot-label-attention [--attention-limit=N] | Attention-ranked labels |
History & Change Tracking:
| Command | Returns |
|---|---|
--robot-history | Bead-to-commit correlations |
--robot-diff --diff-since <ref> | Changes since ref: new/closed/modified issues, cycles |
Other:
| Command | Returns |
|---|---|
--robot-burndown <sprint> | Sprint burndown, scope changes, at-risk items |
| `--robot-forecast <id\ | all>` |
--robot-alerts | Stale issues, blocking cascades, priority mismatches |
--robot-suggest | Hygiene: duplicates, missing deps, label suggestions |
| `--robot-graph [--graph-format=json\ | dot\ |
--export-graph <file.html> | Interactive HTML visualization |
Scoping & Filtering
bv --robot-plan --label backend # Scope to label's subgraph
bv --robot-insights --as-of HEAD~30 # Historical point-in-time
bv --recipe actionable --robot-plan # Pre-filter: ready to work
bv --recipe high-impact --robot-triage # Pre-filter: top PageRank
bv --robot-triage --robot-triage-by-track # Group by parallel work streams
bv --robot-triage --robot-triage-by-label # Group by domainUnderstanding Robot Output
All robot JSON includes:
data_hash— Fingerprint of source beads.jsonlstatus— Per-metric state:computed|approx|timeout|skipped+ elapsed msas_of/as_of_commit— Present when using--as-of
Two-phase analysis:
- Phase 1 (instant): degree, topo sort, density
- Phase 2 (async, 500ms timeout): PageRank, betweenness, HITS, eigenvector, cycles
jq Quick Reference
bv --robot-triage | jq '.quick_ref' # At-a-glance summary
bv --robot-triage | jq '.recommendations[0]' # Top recommendation
bv --robot-plan | jq '.plan.summary.highest_impact' # Best unblock target
bv --robot-insights | jq '.status' # Check metric readiness
bv --robot-insights | jq '.Cycles' # Circular deps (must fix!)---
UBS — Ultimate Bug Scanner
Golden Rule: ubs <changed-files> before every commit. Exit 0 = safe. Exit >0 = fix & re-run.
Commands
ubs file.rs file2.rs # Specific files (< 1s) — USE THIS
ubs $(git diff --name-only --cached) # Staged files — before commit
ubs --only=rust,toml src/ # Language filter (3-5x faster)
ubs --ci --fail-on-warning . # CI mode — before PR
ubs . # Whole project (ignores target/, Cargo.lock)Output Format
Warning Category (N errors)
file.rs:42:5 - Issue description
Suggested fix
Exit code: 1Parse: file:line:col -> location | fix suggestion -> how to fix | Exit 0/1 -> pass/fail
Fix Workflow
1. Read finding -> category + fix suggestion 2. Navigate file:line:col -> view context 3. Verify real issue (not false positive) 4. Fix root cause (not symptom) 5. Re-run ubs <file> -> exit 0 6. Commit
Bug Severity
- Critical (always fix): Memory safety, use-after-free, data races, SQL injection
- Important (production): Unwrap panics, resource leaks, overflow checks
- Contextual (judgment): TODO/FIXME, println! debugging
---
RCH — Remote Compilation Helper
RCH offloads cargo build, cargo test, cargo clippy, and other compilation commands to a fleet of 8 remote Contabo VPS workers instead of building locally. This prevents compilation storms from overwhelming csd when many agents run simultaneously.
RCH is installed at `~/.local/bin/rch` and is hooked into Claude Code's PreToolUse automatically. Most of the time you don't need to do anything if you are Claude Code — builds are intercepted and offloaded transparently.
To manually offload a build:
rch exec -- cargo build --release
rch exec -- cargo test
rch exec -- cargo clippyQuick commands:
rch doctor # Health check
rch workers probe --all # Test connectivity to all 8 workers
rch status # Overview of current state
rch queue # See active/waiting buildsIf rch or its workers are unavailable, it fails open — builds run locally as normal.
Note for Codex/GPT-5.2: Codex does not have the automatic PreToolUse hook, but you can (and should) still manually offload compute-intensive compilation commands using rch exec -- <command>. This avoids local resource contention when multiple agents are building simultaneously.
---
ast-grep vs ripgrep
Use `ast-grep` when structure matters. It parses code and matches AST nodes, ignoring comments/strings, and can safely rewrite code.
- Refactors/codemods: rename APIs, change import forms
- Policy checks: enforce patterns across a repo
- Editor/automation: LSP mode,
--jsonoutput
Use `ripgrep` when text is enough. Fastest way to grep literals/regex.
- Recon: find strings, TODOs, log lines, config values
- Pre-filter: narrow candidate files before ast-grep
Rule of Thumb
- Need correctness or applying changes ->
ast-grep - Need raw speed or hunting text ->
rg - Often combine:
rgto shortlist files, thenast-grepto match/modify
Bash Examples
# Find structured code (ignores comments)
ast-grep run -l Bash -p 'if [[ $$$COND ]]; then $$$BODY fi'
# Quick textual hunt
rg -n 'set -Eeuo pipefail' -t sh
# Combine speed + precision
rg -l -t sh 'eval ' | xargs ast-grep run -l Bash -p 'eval $EXPR' --json---
Morph Warp Grep — AI-Powered Code Search
Use `mcp__morph-mcp__warp_grep` for exploratory "how does X work?" questions. An AI agent expands your query, greps the codebase, reads relevant files, and returns precise line ranges with full context.
Use `ripgrep` for targeted searches. When you know exactly what you're looking for.
Use `ast-grep` for structural patterns. When you need AST precision for matching/rewriting.
When to Use What
| Scenario | Tool | Why |
|---|---|---|
| "How does module dispatch work?" | warp_grep | Exploratory; don't know where to start |
| "Where is checksum verification implemented?" | warp_grep | Need to understand architecture |
"Find all uses of json_escape" | ripgrep | Targeted literal search |
"Find files with set -Eeuo" | ripgrep | Simple pattern |
"Replace all eval with safer alternative" | ast-grep | Structural refactor |
warp_grep Usage
mcp__morph-mcp__warp_grep(
repoPath: "/dp/ultimate_bug_scanner",
query: "How does the meta-runner dispatch language modules concurrently?"
)Returns structured results with file paths, line ranges, and extracted code snippets.
Anti-Patterns
- Don't use
warp_grepto find a specific function name -> useripgrep - Don't use
ripgrepto understand "how does X work" -> wastes time with manual reads - Don't use
ripgrepfor codemods -> risks collateral edits
<!-- bv-agent-instructions-v1 -->
---
Beads Workflow Integration
This project uses beads_rust (br) for issue tracking. Issues are stored in .beads/ and tracked in git.
Important: br is non-invasive—it NEVER executes git commands. After br sync --flush-only, you must manually run git add .beads/ && git commit.
Essential Commands
# View issues (launches TUI - avoid in automated sessions)
bv
# CLI commands for agents (use these instead)
br ready # Show issues ready to work (no blockers)
br list --status=open # All open issues
br show <id> # Full issue details with dependencies
br create --title="..." --type=task --priority=2
br update <id> --status=in_progress
br close <id> --reason "Completed"
br close <id1> <id2> # Close multiple issues at once
br sync --flush-only # Export to JSONL (NO git operations)Workflow Pattern
1. Start: Run br ready to find actionable work 2. Claim: Use br update <id> --status=in_progress 3. Work: Implement the task 4. Complete: Use br close <id> 5. Sync: Run br sync --flush-only then manually commit
Key Concepts
- Dependencies: Issues can block other issues.
br readyshows only unblocked work. - Priority: P0=critical, P1=high, P2=medium, P3=low, P4=backlog (use numbers, not words)
- Types: task, bug, feature, epic, question, docs
- Blocking:
br dep add <issue> <depends-on>to add dependencies
Session Protocol
Before ending any session, run this checklist:
git status # Check what changed
git add <files> # Stage code changes
br sync --flush-only # Export beads to JSONL
git add .beads/ # Stage beads changes
git commit -m "..." # Commit everything together
git push # Push to remoteBest Practices
- Check
br readyat session start to find available work - Update status as you work (in_progress -> closed)
- Create new issues with
br createwhen you discover tasks - Use descriptive titles and set appropriate priority/type
- Always
br sync --flush-only && git add .beads/before ending session
<!-- end-bv-agent-instructions -->
cass — Cross-Agent Session Search
cass indexes prior agent conversations (Claude Code, Codex, Cursor, Gemini, ChatGPT, Aider, etc.) into a unified, searchable index so you can reuse solved problems.
NEVER run bare `cass` — it launches an interactive TUI. Always use --robot or --json.
Quick Start
# Check if index is healthy (exit 0=ok, 1=run index first)
cass health
# Search across all agent histories
cass search "authentication error" --robot --limit 5
# View a specific result (from search output)
cass view /path/to/session.jsonl -n 42 --json
# Expand context around a line
cass expand /path/to/session.jsonl -n 42 -C 3 --json
# Learn the full API
cass capabilities --json # Feature discovery
cass robot-docs guide # LLM-optimized docsKey Flags
| Flag | Purpose |
|---|---|
--robot / --json | Machine-readable JSON output (required!) |
--fields minimal | Reduce payload: source_path, line_number, agent only |
--limit N | Cap result count |
--agent NAME | Filter to specific agent (claude, codex, cursor, etc.) |
--days N | Limit to recent N days |
stdout = data only, stderr = diagnostics. Exit 0 = success.
Exit Codes
| Code | Meaning | Retryable |
|---|---|---|
| 0 | Success | N/A |
| 1 | Health check failed | Yes — run cass index --full |
| 2 | Usage/parsing error | No — fix syntax |
| 3 | Index/DB missing | Yes — run cass index --full |
Treat cass as a way to avoid re-solving problems other agents already handled.
---
Landing the Plane (Session Completion)
When ending a work session, you MUST complete ALL steps below.
MANDATORY WORKFLOW:
1. File issues for remaining work - Create issues for anything that needs follow-up 2. Run quality gates (if code changed) - Tests, linters, builds 3. Update issue status - Close finished work, update in-progress items 4. Sync beads - br sync --flush-only to export to JSONL 5. Hand off - Provide context for next session
---
Note for Codex/GPT-5.2:
You constantly bother me and stop working with concerned questions that look similar to this:
Unexpected changes (need guidance)
- Working tree still shows edits I did not make in Cargo.toml, Cargo.lock, src/cli/commands/upgrade.rs, src/storage/sqlite.rs, tests/conformance.rs, tests/storage_deps.rs. Please advise whether to keep/commit/revert these before any further work. I did not touch them.
Next steps (pick one)
1. Decide how to handle the unrelated modified files above so we can resume cleanly.
2. Triage beads_rust-orko (clippy/cargo warnings) and beads_rust-ydqr (rustfmt failures).
3. If you want a full suite run later, fix conformance/clippy blockers and re-run cargo test --all.NEVER EVER DO THAT AGAIN. The answer is literally ALWAYS the same: those are changes created by the potentially dozen of other agents working on the project at the same time. This is not only a common occurrence, it happens multiple times PER MINUTE. The way to deal with it is simple: you NEVER, under ANY CIRCUMSTANCE, stash, revert, overwrite, or otherwise disturb in ANY way the work of other agents. Just treat those changes identically to changes that you yourself made. Just fool yourself into thinking YOU made the changes and simply don't recall it for some reason.
---
Note on Built-in TODO Functionality
Also, if I ask you to explicitly use your built-in TODO functionality, don't complain about this and say you need to use beads. You can use built-in TODOs if I tell you specifically to do so. Always comply with such orders.
FROM debian:bookworm-slim
ENV UBS_NO_AUTO_UPDATE=1 \
DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
bash ca-certificates curl jq ripgrep \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY ubs install.sh README.md /app/
RUN chmod +x /app/ubs /app/install.sh
ENTRYPOINT ["/app/ubs"]
CMD ["--help"]
LABEL org.opencontainers.image.title="Ultimate Bug Scanner" \
org.opencontainers.image.description="Meta-runner for multi-language bug scanning" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.source="https://github.com/Dicklesworthstone/ultimate_bug_scanner"
Plan: Reflecting Agent Feedback into UBS Evolution
Status: Draft Target Version: 5.0 Objective: Transform Ultimate Bug Scanner (UBS) from a "powerful but noisy" tool into a "high-precision, agent-native" quality assurance partner.
Based on detailed feedback from 6 distinct AI agents across various tech stacks (iOS, Python, Mixed, Rust), this plan outlines the roadmap to address common pain points: Noise, False Positives, Integration Friction, and Configuration Overhead.
---
1. 🔇 Epic: The "Silence the Noise" Initiative (Signal-to-Noise Ratio)
The #1 complaint was excessive noise from dependencies, build artifacts, and non-source files.
1.1 Universal Dependency & Artifact Exclusion
Problem: Agents reported scans taking 20+ minutes and generating 1000+ warnings by scanning venv, node_modules, vendor, and build dirs. Solution:
- Centralized Ignore Logic: Move exclusion logic from individual language modules to the core
ubsrunner. - Hardened Defaults: Enforce strict default ignores for:
- Deps:
node_modules,venv,.venv,env,site-packages,vendor,bundle,.gem,target(Rust),go/pkg. - Builds:
dist,build,out,DerivedData,.gradle,.pytest_cache,__pycache__. - Docs/Config:
*.plist(prevents DTD noise),*.lock,*.json(prevent regex false matches on data). - Mechanism: Ensure these are passed to
rg(glob ignores) andfind(prune) consistently across ALL modules.
1.2 Context-Aware Test Scanning
Problem: Security rules (e.g., assert usage, hardcoded values) flag valid patterns in test files, creating noise. Solution:
- Test Detection: Identify test files (
*_test.go,test_*.py,*.spec.ts,tests/). - Rule Scoping: Modify language modules to automatically suppress specific rules (like
B101/Asserts) when scanning identified test contexts.
---
2. 🎯 Epic: From Heuristic to Semantic (False Positive Reduction)
Regex is fast but brittle. Agents complained about Swift `!` negation being flagged as force-unwraps and Python variables looking like secrets.
2.1 Accelerate AST Adoption
Problem: Regex cannot distinguish between if !isValid (negation) and value! (unwrap), or config = "$VAR" (shell var) vs secrets. Solution:
- Deprecate Regex for Ambiguous Syntax: Identify high-noise regex patterns and replace them with
ast-greprules. - Priorities:
- Swift: Migrate force-unwrap detection to AST to distinguish negation.
- Python: Migrate secret detection to AST to verify assignment context.
- Rust: Better distinction between safe
unwrap()(in tests/examples) and unsafe ones.
2.2 Inline Suppression Support
Problem: Agents cannot suppress a single false positive without configuring global excludes. Solution:
- Standardize Comments: Support inline ignores consistent with widely used linters.
- Format:
# ubs:ignore [rule-id]or// ubs:ignore. - Implementation: Update the unified runner to post-process findings and filter out lines containing these markers if the underlying tool doesn't support it.
---
3. 🤖 Epic: Agent-Native Integration (Machine Readability)
Agents struggle to parse output when ASCII banners or logs mix with JSON.
3.1 Strict JSON Purity
Problem: "ASCII art banner breaks naive json.load". Agents need pure data streams. Solution:
- Stream Separation: When
--format=jsonis active: - STDOUT: MUST contain ONLY the valid JSON payload.
- STDERR: All banners, progress logs, debug info, and ASCII art go here.
- Structure: Ensure the root JSON object contains a
statusfield and asummaryobject for easier parsing.
3.2 "Diff Mode" / Baseline Support
Problem: "Too many issues to fix at once." Agents working on legacy code need to see only the problems they introduced. Solution:
- First-Class Baseline: Enhance
--comparisonto be more prominent. ubs --save-baseline .ubs-baseline.jsonubs --diff-only(automatically compares against saved baseline).- Output: In Diff Mode, report ONLY new findings.
3.3 Quick Scan (Staged/Changed Files)
Problem: Full repo scans are too slow for "pre-commit" checks in large repos. Solution:
- Git Integration: Add a
--stagedor--changedflag. ubs --staged: Automatically runsgit diff --name-only --cachedand passes those files to scanners.- Optimization: Skip project-wide analysis steps (like unused code) when in this mode.
---
4. ⚙️ Epic: Configuration & Profiles
Agents requested "Library vs App" modes and easier config management.
4.1 Persistent Configuration
Problem: Passing CLI flags (--exclude=...) every time is brittle for agents. Solution:
- Config File: Support
.ubs.conf(YAML/TOML) orpyproject.tomlconfiguration. - Allow defining excludes, enabled languages, and rule overrides persistently.
4.2 Strictness Profiles
Problem: "One size fits all" doesn't work for throwaway scripts vs. high-assurance libraries. Solution:
- Profiles:
--profile=strict(Library/Production): Fail on warnings, no TODOs allowed.--profile=loose(Prototype/Script): Ignore TODOs, allow some print statements, focus only on CRITICAL security/crash bugs.
---
5. 🧪 Validation Strategy
To ensure these changes effectively address the feedback, we will add a Reflexive Test Suite: 1. The "Venv" Test: Create a dummy venv with buggy code and ensure UBS ignores it by default. 2. The "JSON" Test: Run ubs --format=json | jq . to verify zero stdout pollution. 3. The "Test-In-Test" Test: Place an assert in a test_*.py file and ensure it does not trigger a warning.
Ultimate Bug Scanner – Work Log & TODOs
All tasks reference Beads issue IDs so progress stays traceable. Update this list whenever you discover new work or finish a sub-task.
1. Root UBS triage (ultimate_bug_scanner-9dk)
- [x] Run per-language UBS scans to get baseline counts. (
./ubs --format=json --ci --only=<lang> .) - [x] Record baseline in notes/root-scan-2025-11-16.md.
- [ ] Triage JS critical categories (group by filename / category, identify quick wins).
- [ ] Create Beads sub-issues for JS hotspots (null safety, math pitfalls, parsing, security).
- [ ] Repeat triage for Python, Go, Rust, C++, Java, Ruby, and Swift after JS plan is in place.
2. Manifest coverage expansion (ultimate_bug_scanner-d5z, ultimate_bug_scanner-aqd, ultimate_bug_scanner-o3l)
- [x] Go: enable buggy/clean manifest entries with
--only=golang+ substring requirement. - [ ] Add substring/rule expectations for Rust/C++/Java/Ruby fixtures once ready.
- [ ] Create dedicated manifest cases for each language (buggy + clean).
- [ ] Add edge-case directories (unicode/timezone/fp) with explicit thresholds.
- [ ] Wire
test-suite/run_manifest.pyinto CI so regressions fail PRs.
3. Framework & module hygiene (ultimate_bug_scanner-dmo)
- [ ] Fix
modules/ubs-js.shfile counting so both module + meta-runner agree. - [ ] Audit other modules for similar counting or summary issues.
4. Documentation / developer experience
- [ ] Merge Beads instructions + manifest workflow into README sections as they mature.
- [ ] Ensure AGENTS.md references Beads issue IDs whenever handoffs occur.
5. Resource lifecycle fixtures (ultimate_bug_scanner-6ig)
- [x] Investigate
modules/ubs-python.shsingle-file runs (resource_lifecycle) reporting zero files/warnings. - [x] Do the same for Go and Java fixtures (confirm detection logic).
- [x] Restore warnings so
--fail-on-warningtriggers and manifest passes.
6. Resource/Shareable follow-up (tracking new CLI/features)
- [x] Document lifecycle heuristics + shareable workflow in README/test-suite docs.
- [x] Update per-language module help text to mention category filter env support.
- [x] Tighten manifest expectations for python/go/java resource cases (assert new messages).
- [x] Add automated regression that runs
ubs --report-json/--html-report/--comparisonand validates outputs.
7. AST migration backlog
- [ ] See beads
ultimate_bug_scanner-mma,ultimate_bug_scanner-5wx,ultimate_bug_scanner-6x4,ultimate_bug_scanner-41t,ultimate_bug_scanner-7g7for the plan to move lifecycle heuristics + non-AST modules onto ast-grep/semantic helpers.
_Last updated: 2025-11-16 22:58 UTC_
Release Playbook
This playbook documents how to cut and publish a signed UBS release. The release workflow (.github/workflows/release.yml) runs automatically on git tags that start with v (for example v5.1.0).
Prerequisites
- Maintainer with push rights to
mainand tags. MINISIGN_SECRET_KEYstored as an org/repo secret (base64 of the minisign secret key). The matching public key is published for users (seedocs/security.md).- OIDC-enabled GitHub Actions (default) for keyless Cosign signing.
- GHCR write access (uses
${GITHUB_REPOSITORY_OWNER,,}/ubs-tools).
One-time setup
1. Generate minisign keys locally (run from a secure machine):
minisign -G -p minisign.pub -s minisign.key2. Base64-encode minisign.key and store it as the MINISIGN_SECRET_KEY GitHub secret. Keep the private key offline; rotate if leaked. 3. Publish the public key string in docs/security.md and the README example env var. 4. Confirm OIDC trust for GitHub Actions with Sigstore (default trust policy works for keyless signing).
Release steps
1. Bump version
- Update
VERSIONto the new semantic version (for example5.1.0). - Update docs/readme snippets if they mention the version.
2. Commit and tag
git commit -am "chore: bump version to 5.1.0"
git tag v5.1.0
git push origin main --tags3. Workflow runs automatically on the pushed tag:
nix-check: runsnix flake checkfor determinism.build-artifacts: installs pinned toolchain (jq 1.7.1, ripgrep 13.0.0, uv 0.4.20), generatesSHA256SUMS, signs it with minisign, buildsubs.rbHomebrew formula, and producesdist/sbom.spdx.jsonfor the repo snapshot.oci-image: builds and pushesghcr.io/<owner>/ubs-tools:{sha,tag,latest}, signs the digest with Cosign keyless, attaches SBOM + provenance attestations, and uploads the SBOM/provenance artifacts.publish: attachesinstall.sh,ubs,SHA256SUMS,SHA256SUMS.minisig,ubs.rb, repo SBOM, and OCI SBOM/provenance to the GitHub Release for the tag.
4. Validate release artifacts
- Download the release assets locally and run:
UBS_MINISIGN_PUBKEY="<public-key-line>" scripts/verify.sh --version 5.1.0 --install-args "--dry-run"- Verify OCI signature and attestations:
cosign verify $IMAGE_DIGEST
cosign verify-attestation --type spdx $IMAGE_DIGEST
cosign verify-attestation --type https://slsa.dev/provenance/v1 $IMAGE_DIGESTKey management
- Rotation: generate a new minisign keypair, update the GitHub secret, and publish the new public key. Keep the old public key listed in
docs/security.mduntil all releases signed with it are retired. - Revocation: if a key is compromised, remove it from secrets immediately, publish a revocation notice in
docs/security.md, and cut a new release signed with the new key. - Access: restrict
MINISIGN_SECRET_KEYsecret to maintainers only. Do not reuse this key for other projects.
Troubleshooting
- Missing secret: the release workflow fails early with
MINISIGN_SECRET_KEY is required for releases. - Tag/version mismatch: the workflow stops if
VERSIONin the repo does not match the pushed tag. - GHCR failures: ensure the owner name is lowercase and the
packages: writepermission is present (both handled in the workflow).
UBS Language Modules
Each ubs-<lang>.sh provides a consistent CLI (current modules: js, python, cpp, rust, golang, java, ruby, swift, csharp):
ubs-<lang>.sh [PROJECT_DIR] [options]
Options:
--format=FMT text|json|sarif (default: text)
--ci stable timestamps (UTC ISO8601)
--fail-on-warning exit non-zero if any warnings or critical
-v, --verbose print more samples in text mode
--jobs=N parallel hint (propagated to ripgrep/child tools)
-h, --help this helpResponsibilities:
- Detect files for the given language
- Apply fast heuristics using ripgrep/grep (or language-native tooling)
- Emit native JSON/SARIF where possible so the meta-runner never needs to parse text
- Exit non-zero on critical issues (or warnings when
--fail-on-warningis set)
Modules are auto-downloaded by the ubs meta-runner with this priority: 1. User PATH (ubs-<lang> available globally) 2. Local repository modules/ubs-<lang>.sh 3. Cached modules under ${XDG_DATA_HOME:-$HOME/.local/share}/ubs/modules
When a module is missing, ubs fetches it from https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/main/modules/ubs-<lang>.sh, validates the shebang, marks it executable, and caches it for future runs.
5.3.2