all skills
build

OC · Bug Check

Pre-commit QA gate — fast checks on every commit. v1.2 attaches the failure report to the linked PM ticket on block.

buildv2.0.4
Commands
/oc-enroll/oc-bugcheck/oc-bugcheck run/oc-bugcheck fix/oc-bugcheck config/oc-bugcheck report/oc-bugcheck history/oc-bugcheck bypass
Pipeline phase

build

Get this skill

Drop the bundle into .claude/skills/ and Claude Code auto-discovers it on the next session — or point Codex / any MCP agent at the hosted opchain.dev/mcp endpoint.

In the 2.0 loopv2.0 · shipped
runs

Verify the actual staged candidate in an isolated checkout.

requires

Explicit repository enrollment installs the Git commit check.

records

The verification receipt identifies the code, policy and checks that ran.

Provider access, separate evaluator sessions and an external reviewer are configured by your team. Learning is off until you enable it.

How you'll use it

Pre-commit QA gate that runs on every commit. Fast, opinionated checks: type safety, lint, tests, anti-pattern scan, secret detection, build verification, and dependency vulnerability scan. Blocks commits on failures, warns on cautions, passes silently on clean code. Invoked by oc-git-ops before every /oc-commit and /oc-git-sync. Use for /oc-bugcheck, "check this before I commit", "run the checks", "is this safe to commit", "pre-commit", "quick audit", "lint and test", "any bugs in this?", "sanity check".

Trigger with natural language or a slash command:

/oc-enroll/oc-bugcheck/oc-bugcheck run/oc-bugcheck fix/oc-bugcheck config/oc-bugcheck report+2 more
SKILL.md≈ 21 min read
Below is the file Claude reads on invocation. It's written in the model's voice — "read this", "do that" — not a user guide. The How you'll use it section above is the one for you.
On this page

    Bug Check

    On first invocation, read references/orchestrator.md and follow its welcome protocol.

    Pre-commit QA gate. Runtime depends on the configured checks and project size. Catches the bugs, type errors, test failures, and anti-patterns that shouldn’t make it into a commit — before they cost real debugging time downstream.

    This is NOT oc-code-auditor. Code-auditor runs a deep tri-agent sweep (Auditor → Fixer → Verifier) that takes 30+ minutes and produces a graded report. Bug-check is the metal detector at the door — fast and blunt: the gate verdict is PASS, FAIL, or UNSUPPORTED (a required check cannot run — never a pass). Individual checks may emit advisory WARN notes (e.g. “no test suite detected”) that ride on a PASS as a count; in strict mode they become FAIL. Only PASS clears the commit.

    /oc-bugcheck — Command Reference

    BUG CHECK COMMANDS
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    
      GATE
      /oc-bugcheck              Run all checks on staged/changed files
      /oc-bugcheck run          Same as /oc-bugcheck
      /oc-bugcheck run --all    Run on entire codebase (not just changes)
      /oc-bugcheck fix          Auto-fix what's fixable (lint, formatting)
    
      CONFIG
      /oc-bugcheck config       Show or edit check configuration
      /oc-bugcheck config strict    Enable strict mode (zero warnings allowed)
      /oc-bugcheck config lenient   Allow warnings, block only on errors
    
      REPORT
      /oc-bugcheck report       Show last run results from checkpoint
      /oc-bugcheck history      Show pass/fail trend from checkpoint
    
      OVERRIDE
      /oc-bugcheck bypass       Skip gate this once (logs the bypass in checkpoint)
    
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      Runs automatically before /oc-commit and /oc-git-sync.

    --all, strict, and lenient are arguments to the declared run and config verbs, not separate commands.


    Session Persistence (Checkpoint Protocol)

    The shared checkpoint schema, write rules and resume protocol live in references/checkpoint-protocol.md, bundled with this skill. This section adds only what is specific to oc-bug-check.

    Checkpoint: {project-dir}/.checkpoints/oc-bug-check.checkpoint.json

    Resume on Start

    When /oc-bugcheck is invoked:

    1. Check for checkpoint
    2. If exists: show last verdict, streak, carried debt count
    3. If carried debt > 0: surface bypassed issues before running new checks
    4. Run the check suite (no “continue/restart” prompt — oc-bug-check always runs fresh)

    Bug-check differs from other opchain skills: there’s no “resume” decision. The gate always runs the full check suite. The checkpoint provides context (streak, debt, history), not resumable state.

    Write on Finish

    Every run ends with one informational checkpoint write carrying last_run, last_run_verdict, and any legacy verified_tree summary from that run. The same write restates the verdict, warning count, and carried debt in the protocol-public progress_summary (and appends eval_scores). Checkpoints are workflow history; they do not authorize a commit. The Git commit boundary runs the candidate verifier and writes its separate immutable receipt after all staging mutations.


    How This Skill Fits the Pipeline

    oc-app-architect /oc-build ──► BUG-CHECK (gate) ──► oc-git-ops /oc-commit ──► oc-deploy-ops
                                  │
                             ┌────┴────┐
                             │         │
                          PASS →    FAIL →
                        proceed    block commit,
                        silently   show what broke

    Auto-invocation: oc-git-ops calls oc-bug-check before every /oc-commit and /oc-git-sync. If oc-bug-check fails, the commit is blocked with a clear failure report. The user can override explicitly: /oc-bugcheck bypass records it (logged, not silent), and the commit itself carries OPCHAIN_BYPASS=1 or --no-verify where the commit-gate hook is installed — the record alone does not clear the hook.

    Position in the every-PR gate (v1.8): bug-check is step 3 of the required order oc-repo-ops enforces — oc-docs-forge (docs packet) → oc-repo-ops (repo readiness) → oc-bug-check (code gate, already run at commit time) → PR. A missing or stale bug-check verdict causes oc-repo-ops to chain back here before the PR opens.

    Relationship to oc-code-auditor: Bug-check is a subset. It runs the checks that are fast enough for every commit. Code-auditor’s deep sweep runs before deploy (gate) or on demand (ad-hoc). They complement, not compete:

    Bug CheckCode Auditor
    WhenEvery commitBefore deploy, on demand
    Speed<2 min30+ min
    DepthSurface: types, lint, tests, patternsDeep: tri-agent, security, architecture
    VerdictPASS / FAIL / UNSUPPORTEDGrade A-F (nuanced)
    FixesAuto-fix lint/formatFixer → Verifier loop
    ScopeChanged files by defaultFull codebase

    The Check Suite

    Seven checks, run in order. Each produces PASS, WARN, or FAIL.

    Check 1: Type Safety

    npx tsc --noEmit
    ResultVerdict
    Exit 0PASS
    Type errorsFAIL — list errors with file:line

    Why it blocks: Type errors propagate. A wrong type in a utility function breaks every consumer. Catching at commit is 10x cheaper than catching at runtime.

    Check 2: Lint

    npx eslint . --ext .ts,.tsx --max-warnings 0
    ResultVerdict
    Exit 0, no warningsPASS
    Warnings onlyWARN (pass in lenient mode, fail in strict)
    ErrorsFAIL — list errors with file:line

    Auto-fixable? Yes. /oc-bugcheck fix runs eslint --fix and prettier --write.

    Check 3: Test Suite

    npx vitest run --reporter=verbose 2>&1
    ResultVerdict
    All passPASS (report count: “42 tests passed”)
    Any failFAIL — list failing test names + assertion errors
    No tests foundWARN — “No test suite detected”

    Why no tests is a warning, not a pass: Zero tests means zero regression protection. The warning nudges toward coverage without blocking early-stage commits.

    QA-manifest awareness (v1.9, additive): when .opchain/qa.yaml exists (written by oc-qa-ops), and the runner emits coverage, compare it against the manifest’s coverage.global budget and report a miss as WARN — never FAIL, and never a change to the PASS/FAIL semantics above — unless the manifest sets coverage.enforce: fail, in which case a global-budget miss is FAIL (a human wrote that line; the opt-in is theirs, and the normal bypass protocol applies). An unparseable manifest, or a version this check doesn’t recognize, is treated exactly as absent plus a single WARN line (“qa.yaml unreadable — budgets not applied; run /oc-qa status”) — never FAIL on a parse problem. No manifest → this check behaves exactly as before v1.9. Suggest /oc-qa coverage when the budget is missed repeatedly.

    Check 4: Anti-Pattern Scan

    Implementation detail for Checks 4–7 (false-positive handling, per-language scanners): references/check-patterns.md.

    Fast grep-based checks for patterns that indicate bugs, not style preferences:

    # console.log in production code (not test files)
    grep -rn "console\.log\b" --include="*.ts" --include="*.tsx" \
      --exclude-dir=test --exclude-dir=__tests__ --exclude-dir=node_modules src/
    
    # Debugger statements
    grep -rn "debugger" --include="*.ts" --include="*.tsx" \
      --exclude-dir=node_modules src/
    
    # TODO/FIXME/HACK in changed files
    git diff --cached --name-only | xargs grep -n "TODO\|FIXME\|HACK" 2>/dev/null
    
    # .only on test files (focused tests that skip the rest)
    grep -rn "\.only\b" --include="*.test.*" --include="*.spec.*" \
      --exclude-dir=node_modules .
    
    # any type in TypeScript (explicit any, not inferred)
    grep -rn ": any\b\|as any\b\|<any>" --include="*.ts" --include="*.tsx" \
      --exclude-dir=node_modules --exclude="*.d.ts" src/
    
    # @ts-ignore / @ts-nocheck
    grep -rn "@ts-ignore\|@ts-nocheck\|@ts-expect-error" --include="*.ts" --include="*.tsx" \
      --exclude-dir=node_modules src/
    
    # Empty catch blocks
    grep -rn "catch.*{[[:space:]]*}" --include="*.ts" --include="*.tsx" \
      --exclude-dir=node_modules src/
    PatternVerdictRationale
    console.log in src/WARNDebug artifact, remove before commit
    debuggerFAILWill pause execution in production
    .only in testsFAILSilently skips other tests
    Explicit anyWARNType safety hole
    @ts-ignoreWARNSuppressed type error may hide real bug
    Empty catch {}WARNSwallowed error, invisible failure
    TODO/FIXME in diffWARNAcknowledged tech debt in new code

    Check 5: Secret Detection

    # API keys, tokens, passwords in source.
    # NOT --include-scoped: a hardcoded key is a secret in any language. Scoping this to
    # *.ts made it blind to Swift, Kotlin, Ruby, PHP, Java — every non-JS repo got a
    # silent zero-hit PASS.
    grep -rn -E "(api[_-]?key|secret|password|token|credential).*['\"][A-Za-z0-9+/=]{16,}['\"]" \
      --exclude-dir=node_modules --exclude-dir=.build --exclude-dir=vendor \
      --exclude-dir=Pods --exclude-dir=.git --exclude=".env*" .
    
    # AWS-style keys
    grep -rn -E "AKIA[0-9A-Z]{16}" --exclude-dir=node_modules .
    
    # Private-key headers. `--` keeps the leading hyphens out of option parsing.
    grep -rn -E --exclude-dir=node_modules -- \
      "-----BEGIN ((RSA|EC|DSA|OPENSSH|ENCRYPTED) )?PRIVATE KEY-----|-----BEGIN PGP PRIVATE KEY BLOCK-----" .
    
    # Common service prefixes (sk_test_ included — a test key is still a leaked credential)
    grep -rn -E "(sk-|sk_live_|sk_test_|pk_live_|ghp_|gho_|github_pat_)" \
      --exclude-dir=node_modules --exclude=".env*" .
    
    # JWTs — Supabase anon/service-role keys, Auth0, Firebase. Three base64url segments.
    # This is what a hardcoded `let supabaseKey = "eyJhbGci..."` looks like in Swift.
    grep -rn -E "eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}" \
      --exclude-dir=node_modules --exclude-dir=.build --exclude=".env*" .
    ResultVerdict
    No matchesPASS
    Any matchFAIL — always blocks; the only override is an explicit bypass (see Bypass Protocol)

    Why secrets are always FAIL: A committed secret is a security incident. There’s no “warning” threshold for leaked credentials.

    Check 6: Build Verification

    npm run build 2>&1
    ResultVerdict
    Exit 0PASS
    Build failureFAIL — show error output
    No build script, stack recognizedPASS (skip) — this stack legitimately has no build step
    No build script, stack not recognizedUNSUPPORTED — see below. Never PASS.

    Check 7: Dependency Vulnerability Scan

    npm audit --audit-level=critical 2>&1
    ResultVerdict
    No critical/highPASS
    High vulnerabilitiesWARN
    Critical vulnerabilitiesFAIL

    Verdicts

    The gate produces one of three verdicts: PASS, FAIL, or UNSUPPORTED (no recognized stack — see Stack-Specific Adaptations). Warnings are not a fourth verdict: they ride on PASS as a count, and strict mode turns them into FAIL. A bypass is a logged event, not a verdict. Report shapes: references/output-templates.md.

    PASS

    All checks pass. No output needed — silence is the signal. Git-ops proceeds to commit.

    BUG CHECK — PASS ✅
    ━━━━━━━━━━━━━━━━━━━━
      7 checks passed (1.2s)
      Proceeding to commit.

    PASS with warnings

    Verdict PASS; no blocking issues, but warnings exist. Show them briefly, proceed to commit.

    BUG CHECK — PASS with warnings ⚠️
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      ⚠️ 2 console.log statements in src/routes/users.ts:14, src/lib/utils.ts:28
      ⚠️ 1 TODO in src/middleware/auth.ts:42
      ⚠️ npm audit: 1 high vulnerability in lodash (update recommended)
    
      5 checks passed, 2 warnings. Proceeding to commit.

    In strict mode, warnings become failures.

    FAIL

    Blocking issues found. Commit is blocked. Show what broke with actionable fixes.

    BUG CHECK — FAIL ❌
    ━━━━━━━━━━━━━━━━━━━━
      ❌ TypeScript: 3 errors
         src/routes/users.ts:14 — Type 'string' is not assignable to type 'number'
         src/lib/auth.ts:28 — Property 'userId' does not exist on type 'Session'
         src/db/schema.ts:42 — Cannot find module 'drizzle-orm/sqlite-core'
    
      ❌ Tests: 2 failing
         GET /api/users — expected 200, got 500
         auth middleware — session token validation fails
    
      ❌ Secret detected
         src/config.ts:8 — possible API key: sk_live_...
    
      Fix these before committing. Run /oc-bugcheck fix for auto-fixable issues.

    Auto-Fix (/oc-bugcheck fix)

    Fixes what’s auto-fixable, then re-runs the check suite:

    # Fix lint errors
    npx eslint . --ext .ts,.tsx --fix
    
    # Fix formatting
    npx prettier --write "src/**/*.{ts,tsx}"
    
    # Re-run checks
    /oc-bugcheck run

    What’s auto-fixable:

    CheckAuto-fixable?
    Type errorsNo — requires code changes
    Lint errorsPartially — style rules yes, logic rules no
    FormattingYes
    Test failuresNo — requires debugging
    Anti-patternsconsole.log removal: only if src/lib/logger.ts exists (structured logger available as replacement). Others: no
    SecretsNo — requires manual removal + rotation
    Build errorsNo
    Dep vulnerabilitiesPartially — npm audit fix for compatible updates

    Bypass Protocol (/oc-bugcheck bypass)

    Sometimes you need to commit WIP or unblock a pipeline. /oc-bugcheck bypass allows it, but with accountability:

    1. User confirms they want to bypass
    2. Bug-check logs the bypass in the checkpoint: which checks failed, when, who bypassed
    3. The commit message gets a [BYPASS] prefix: [BYPASS] feat(auth): WIP session handling. Where the commit-gate hook is installed, the commit also carries its explicit bypass (OPCHAIN_BYPASS=1 git commit …) — the logged entry alone does not clear the hook
    4. Next /oc-bugcheck run shows: “Last commit bypassed gate — N issues carried forward”

    Bypass is NOT silent. It creates visible debt that surfaces on every subsequent run until resolved. This prevents bypass-as-habit.

    Bypass Tracking

    {
      "bypasses": [
        {
          "at": "2026-04-23T15:30:00Z",
          "commit": "abc1234",
          "failed_checks": ["type_safety", "tests"],
          "reason": "WIP — types incomplete, will fix in next commit"
        }
      ]
    }

    Configuration (/oc-bugcheck config)

    Stored in .bugcheck.json at project root:

    {
      "mode": "lenient",
      "checks": {
        "type_safety": { "enabled": true, "verdict_on_fail": "FAIL" },
        "lint": { "enabled": true, "verdict_on_fail": "FAIL", "warnings_as": "WARN" },
        "tests": { "enabled": true, "verdict_on_fail": "FAIL", "no_tests_as": "WARN" },
        "anti_patterns": {
          "enabled": true,
          "console_log": "WARN",
          "debugger": "FAIL",
          "test_only": "FAIL",
          "explicit_any": "WARN",
          "ts_ignore": "WARN",
          "empty_catch": "WARN",
          "todo_in_diff": "WARN"
        },
        "secrets": { "enabled": true, "verdict_on_fail": "FAIL" },
        "build": { "enabled": true, "verdict_on_fail": "FAIL" },
        "dependencies": { "enabled": true, "critical_as": "FAIL", "high_as": "WARN" }
      },
      "scope": "changed",
      "auto_fix_on_fail": false
    }

    Modes

    ModeBehavior
    lenient (default)Warnings don’t block. Good for active development.
    strictWarnings become failures. Good for pre-merge or production branches.

    Per-Project Overrides

    Projects can override individual check verdicts. Example: a prototype with no tests can set "no_tests_as": "PASS" to suppress the warning. But secrets are never configurable — they always FAIL.


    Scope: Changed vs. All

    By default, oc-bug-check only checks changed files (staged + unstaged changes). This keeps runs fast (<30s for typical commits).

    Scope-aware vs. full-project checks: Not all checks support file-level scoping. Type safety (tsc --noEmit) and build verification always process the full project because TypeScript and bundlers resolve the complete dependency graph. The “changed files” scope applies to lint (via --file flag where supported), anti-patterns, secrets, and tests (via --changed or related-test detection).

    /oc-bugcheck run --all checks the entire codebase explicitly. Use for:

    • First run on a new project
    • Before a major release
    • After merging a large PR
    • When oc-code-auditor’s last checkpoint is stale

    Detecting Changed Files

    # Source extensions for the detected stack. Keep this in sync with the stack table
    # below — a filter that drops your language silently empties the whole gate.
    SRC='\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|swift|kt|rb|java|php)$'
    
    # Staged files
    git diff --cached --name-only --diff-filter=ACMR | grep -E "$SRC"
    
    # All changed (staged + unstaged)
    git diff --name-only --diff-filter=ACMR HEAD | grep -E "$SRC"
    
    # If no git context (new project, no commits)
    # Fall back to --all mode

    Git-Ops Integration

    Auto-Invocation

    When oc-git-ops receives /oc-commit or /oc-git-sync:

    1. Check for .bugcheck.json at project root (or use defaults)
    2. Run /oc-bugcheck run
    3. If PASS (warnings, in lenient mode, ride on PASS as a count): proceed to commit
    4. If FAIL: block commit, show failure report, suggest /oc-bugcheck fix
    5. If UNSUPPORTED: block like FAIL — the gate did not read the code (see Stack-Specific Adaptations)
    6. User can fix and re-run, or bypass explicitly: /oc-bugcheck bypass records it and the commit carries OPCHAIN_BYPASS=1 / --no-verify (see Commit gate contract)

    Commit Message Annotation

    Convention: if oc-bug-check ran and passed, the committing session may append to the commit footer:

    bugcheck: pass (7/7, 0 warnings, 1.2s)

    If bypassed:

    bugcheck: bypassed (type_safety FAIL, tests FAIL)

    This is an agent-executed naming convention, not a mechanism: oc-git-ops’s commit template does not add it, and no hook, script, or workflow reads the footer or the [BYPASS] prefix today. The accountability records that are read are the checkpoint’s bypasses[] and the commit-gate hook’s bypass warning.


    Eval Score Emission (v1.6 — the instrumented pipeline)

    Bug-check’s gate verdict stays categorical (PASS/FAIL/UNSUPPORTED — that’s the contract). But v1.6 also asks every quality skill to emit a score against a stable rubric so the pipeline can read trend, not just the latest pass/fail. On each run, bug-check appends an entry to the wire-1.1 eval_scores checkpoint field:

    "eval_scores": [
      { "rubric": "oc-bug-check", "score": 0.86, "max": 1, "at": "2026-06-25T12:00:00Z",
        "dimensions": { "checks_passed": 6, "checks_total": 7, "warnings": 1 } }
    ]

    The rubric is fixed so the number is comparable run-to-run: score = checks_passed / checks_total (0..1, max: 1), with the per-check breakdown in dimensions. A FAIL still blocks the commit regardless of score — the score is for trend (oc-telemetry-ops aggregates eval_score_trend; oc-orchestrator reads it as a “what’s drifting?” signal), not a softening of the gate. The score is additive: it does not change the PASS/FAIL logic, the streak, or the bypass protocol.

    Checkpoint Schema

    Extends the session persistence section above with full schema details.

    FieldPurpose
    last_run_verdictCompatibility summary of the last run. Always equal to last_run.verdict; not commit authorization
    verified_treeOptional legacy working-tree summary; not an immutable candidate receipt and not commit authorization
    last_runTimestamp + verdict + duration + per-check results
    run_historyLast 10 runs with verdicts (trend tracking)
    bypassesEvery bypass with failed checks and reason
    carried_debtIssues from bypassed commits still unresolved
    config_hashHash of .bugcheck.json to detect config drift

    skill_state

    {
      "last_run_verdict": "PASS",
      "verified_tree": "9d5b4d3c1f0e8a7b6c5d4e3f2a1b0c9d8e7f6a5b",
      "last_run": {
        "at": "2026-04-23T15:30:00Z",
        "verdict": "PASS",
        "duration_ms": 1200,
        "checks": {
          "type_safety": "PASS",
          "lint": "PASS",
          "tests": "PASS",
          "anti_patterns": "WARN",
          "secrets": "PASS",
          "build": "PASS",
          "dependencies": "PASS"
        },
        "warnings": 2,
        "errors": 0
      },
      "run_history": [
        { "at": "2026-04-23T15:30:00Z", "verdict": "PASS", "warnings": 2 },
        { "at": "2026-04-23T14:00:00Z", "verdict": "FAIL", "errors": 3 },
        { "at": "2026-04-22T16:00:00Z", "verdict": "PASS", "warnings": 0 }
      ],
      "bypasses": [],
      "carried_debt": 0,
      "streak": { "passes": 5, "since": "2026-04-20T10:00:00Z" }
    }

    Commit gate contract

    The sole authoritative local boundary is the Git pre-commit hook installed by scripts/install-git-drivers.mjs. Claude PreToolUse hooks do not authorize or deny commits. This avoids trying to predict shell timing, substitutions, git -C, or explicit Git environments before the commit operation actually starts.

    Enrollment is explicit and actionable:

    • From a repository checkout, run node scripts/install-git-drivers.mjs --enroll --repo "$(git rev-parse --show-toplevel)".
    • From the packaged plugin, run /oc-enroll; its wrapper invokes the bundled installer, verifier, and receipt library.

    The installer copies the verifier and receipt library beneath the Git common directory, then installs the Git hook and creates .opchain/. Later commits use that installed runtime rather than repository working files or the plugin cache. An existing foreign pre-commit hook is never overwritten: required enrollment exits nonzero with BLOCKED and prints the exact final-decision snippet for manual hook-manager integration.

    For an explicitly enrolled repository (.opchain/, .checkpoints/, or OPCHAIN_GATE=1), the Git hook:

    1. in an opchain authoring checkout, finishes and stages its generated mirror updates;
    2. snapshots the effective Git index with git write-tree;
    3. materializes that immutable candidate in an isolated directory;
    4. executes every required policy check there; and
    5. writes and revalidates a create-once receipt beneath the Git common directory.

    Automatic defaults discover npm scripts and bounded built-in scanners. Declare verification.required_checks and argv-based verification.commands in .bugcheck.json for the project’s required coverage, including other languages, nested packages, and coverage budgets. PASS means the declared policy passed; it does not certify checks that were never configured.

    The receipt binds repository identity, candidate tree, policy/config, toolchain, verifier, individual results, exit statuses, and timestamps. Missing tools, non-PASS checks, a changed index, or a mismatched receipt block the commit. The checkpoint above remains useful history but is never accepted as this receipt.

    The materialized candidate has an isolated single-commit Git repository whose HEAD^{tree} equals the tested tree. Inherited Git directory, worktree, index, object, and namespace pointers are stripped before declared checks execute, so a check can use git ls-files or revision lookup without writing live refs or the live index. Installed dependency directories are mounted only at node_modules paths adjacent to candidate package.json files (including nested packages such as site/); those mounts are excluded from the isolated Git view and never replace tracked candidate source. Live history, refs, remotes, and arbitrary host paths are not supplied to checks through inherited Git pointers. Checks still run as the local user and can access host files; the temporary checkout is not a security sandbox.

    git commit --no-verify remains Git’s explicit local bypass. Local hooks cannot defend against an actor who deliberately disables them, so protected CI must verify the received candidate independently.

    Cross-Skill Reads

    Reads fromWhy
    oc-code-auditorGrade + counts (progress_summary) → context for what an audit already flagged; per-finding detail lives in the audit report, not the checkpoint
    oc-app-architectSprint contract → know which files are in-scope for this commit
    oc-deploy-opsEnvironment config → which build command to use
    oc-qa-ops.opchain/qa.yaml coverage.global budget (when present) → the test check’s coverage comparison
    Read byWhy
    oc-git-opsInformational last-run verdict; commit authorization comes from the candidate receipt at the Git boundary
    oc-repo-opsstatus + updated_at staleness → PR readiness gate (a missing/stale code gate blocks the PR)
    oc-docs-forgeprogress_summary quality notes → PR testing/audit documentation
    oc-deploy-opsLast gate status (status + progress_summary) → the Bug-check: line on the deploy ticket
    oc-qa-opsCurrent gate behavior + suite runtime → pyramid and budget design
    oc-cost-opsWhich phase a gate run belongs to, for cost attribution
    oc-orchestratoreval_scores trend + progress_summary carried debt → project health

    These compatibility fields may still be read by workflow skills, but the Git commit boundary does not trust skill_state; it trusts only a matching candidate receipt.


    Streak Tracking

    Bug-check tracks consecutive passes as a streak. This is a dopamine hack — seeing “12 clean commits in a row” creates positive momentum.

    BUG CHECK — PASS ✅  (streak: 12)
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      7 checks passed (0.9s)

    A bypass or fail resets the streak. The highest streak is preserved in the checkpoint for bragging rights.


    Report (/oc-bugcheck report)

    Shows the last run and trend:

    BUG CHECK REPORT — [project]
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      Last run:     2 min ago — PASS ✅
      Streak:       12 clean commits
      Carried debt: 0 (no bypass backlog)
    
      Last 10 runs:
        ✅ ✅ ✅ ❌ ✅ ✅ ✅ ⚠️ ✅ ✅
    
      Most common failure: type_safety (3 of last 20 fails)
      Auto-fix rate: 60% of warnings resolved by /oc-bugcheck fix
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

    Stack-Specific Adaptations

    Bug-check auto-detects the stack from config files and adapts:

    StackDetect viaType CheckLintTestBuild
    TypeScript (Hono, React, Next.js)package.jsontsc --noEmiteslintvitest runnpm run build
    Python (FastAPI)pyproject.toml / requirements.txtmypy . or pyright .ruff check .pytestN/A
    Gogo.modgo vet ./...golangci-lint rungo test ./...go build ./...
    Swift (SwiftPM / iOS)Package.swift / *.xcodeproj / project.ymlswift build (compiler is the type check)swiftlintswift test or xcodebuild testswift build

    Commands come from oc-stack-forge/packs/<stack>/pack.yml where a pack exists — read the pack rather than hardcoding, so the two never drift.

    No recognized stack → UNSUPPORTED, not PASS

    If no stack is detected, oc-bug-check runs only the universal checks (anti-patterns, secrets) — the dependency scan runs only when a lockfile its scanners understand is present, and is reported as skipped otherwise — and returns UNSUPPORTED, a distinct terminal verdict.

    UNSUPPORTED is not PASS. It means this gate did not read your code. Report it as:

    ⚠ UNSUPPORTED — no recognized stack (looked for: package.json, pyproject.toml,
      go.mod, Package.swift, *.xcodeproj). Ran 2 of 7 checks (3 if a lockfile was
      scanned); types, lint, tests and build were NOT run. This is not a passing
      grade — it is an absence of evidence.

    Callers must treat UNSUPPORTED as blocking-with-override, never as a green light. The failure mode this prevents: on a Swift repo the scope filter (grep -E '\.(ts|tsx|js|jsx)$') matches nothing, the build check finds no package.json, npm audit errors with ENOLOCK, and the gate historically reported PASS on code it never opened. Silence is not safety.

    Python Adaptations

    # Type check
    mypy . --ignore-missing-imports 2>&1 || pyright . 2>&1
    
    # Lint
    ruff check . 2>&1
    
    # Tests
    python -m pytest --tb=short 2>&1
    
    # Anti-patterns
    grep -rn "import pdb\|breakpoint()" --include="*.py" --exclude-dir=venv .
    grep -rn "print(" --include="*.py" --exclude-dir=venv --exclude-dir=test .

    PM-Tool MCP Integration (v1.2+)

    oc-bug-check is the cheapest skill to run; its job is to be silent on clean code and loud on bad. v1.2 makes the loud case visible in the PM tool when a linked ticket is in play. See oc-integrations-engineer for the canonical PM-MCP patterns; the comment write and its update-in-place idempotency follow oc-integrations-engineer/references/pm-mcp-protocol.md.

    On PASS (clean)

    Nothing posted. The principle is that the PM tool is for state changes the team needs to see, not for “everything is fine.” A clean oc-bug-check run is the default; advertising it dilutes signal.

    On FAIL (gate blocks the commit)

    If oc-git-ops invoked oc-bug-check with a ticket in context (the typical /oc-git-sync TICKET-1234 flow), post a comment to the linked ticket:

    oc-bug-check FAIL — commit blocked.
    Failed checks: {N of M}
    First failure:
      - type-check: 3 errors in src/api/customers.csv.ts
      - tests: 2 failed in tests/api/customers.csv.spec.ts
    Suggested next step: /oc-bugcheck fix
    Full report: .checkpoints/oc-bug-check.checkpoint.json

    The comment is a one-shot status, not a thread. Subsequent re-runs that still fail update the same comment rather than spam.

    On bypass (rare; explicit override)

    If the user invokes /oc-bugcheck bypass (the explicit override path), post:

    oc-bug-check BYPASSED on user request — {reason if provided}.
    Skipped checks: {list}.
    Reviewer: please confirm this is acceptable.

    Bypass is an audit event in regulated environments. The comment is the trail. (In brokered, regulated deployments the broker’s audit log carries the canonical record; the PM comment is the human-readable reflection.)

    Failure modes

    • No linked ticket → no PM write; checkpoint records intent only.
    • oc-bug-check is invoked outside oc-git-ops (manual /oc-bugcheck) → no PM write; user can pass --ticket TICKET-1234 to opt in.
    • PM provider rate-limits during a CI burst → batch failures into a daily roll-up comment on the team’s standing-board ticket rather than per-commit.

    Principles

    1. Fast or useless. If oc-bug-check takes >2 minutes, developers will bypass it every time. Speed is the feature.
    2. Categorical verdict. PASS, FAIL, or UNSUPPORTED. No grades, no nuance, no “mostly good.” That’s oc-code-auditor’s job.
    3. Changed files by default. Checking the whole codebase on every commit is slow and noisy. Check what changed. Run --all periodically.
    4. Secrets always block. There is no “warning” for leaked credentials. This is the one check with no configurable severity.
    5. Bypass is accountable, not forbidden. Sometimes you need to commit WIP. The system tracks it and reminds you until the debt is resolved.
    6. Silence is success (with one exception). A passing gate should be invisible. The streak counter is the deliberate exception — a small dopamine reward for maintaining quality momentum. Everything else stays quiet on pass.
    7. Complement, don’t compete. Bug-check catches the easy stuff fast. Code-auditor catches the hard stuff thoroughly. Both have a job.

    Use OC · Bug Check in your project

    Drop the SKILL.md into .claude/skills/ or.codex/skills/, download the bundle, or reach it over the hosted MCP endpoint.