
Validate
- 45 installs
- 416 repo stars
- Updated August 5, 2026
- boshu2/agentops
validate is a Claude Code skill that produces PASS/WARN/FAIL verdicts for artifacts, plans, code, PRs, or gates via multi-judge review.
About
validate is the canonical validator skill that produces PASS/WARN/FAIL verdicts for artifacts, plans, code, PRs, or gates. It runs multi-judge consensus reviews, readiness and completion audits, and PR-shape checks, and requires the acceptance verdict to come from a blind judge that did not author the code. A developer uses it to prove a plan or change is ready before a bead closes.
- Emits a PASS/WARN/FAIL verdict.v1 with rationale and findings
- Eight modes from single-agent quick review to 4-judge deep and cross-vendor
- No-self-grading: the acceptance judge must not be the code author
Validate by the numbers
- 45 all-time installs (skills.sh)
- Ranked #604 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
validate capabilities & compatibility
- Capabilities
- code review · pr review · acceptance validation
- Works with
- github
- Use cases
- code review · testing
- Pricing
- Free
What validate says it does
Produce PASS/WARN/FAIL verdicts for artifacts, plans, code, PRs, or gates
Input = artifact (plan, spec, code, PR, fitness gate). Output = `verdict.v1`
the acceptance verdict **must be produced by a blind, context-isolated sub-agent judge that did not author the code**
npx skills add https://github.com/boshu2/agentops --skill validateAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 45 |
|---|---|
| repo stars | ★ 416 |
| Last updated | August 5, 2026 |
| Repository | boshu2/agentops ↗ |
What it does
Produce a PASS/WARN/FAIL verdict on an artifact, plan, code, or PR via multi-judge review with a no-self-grading acceptance gate.
Who is it for?
Validating a plan, spec, code change, or PR before it is accepted or a bead closes.
When should I use this skill?
When you need a PASS/WARN/FAIL verdict on an artifact, a readiness or sanity check before commit, or a completion audit.
What you get
A verdict.v1 (PASS/WARN/FAIL with rationale and findings) produced by a blind judge distinct from the code author.
- A verdict.v1 (PASS/WARN/FAIL with rationale and findings)
By the numbers
- Eight modes within the mode-flag budget
- Consensus scales from 1 to 4 judges by mode
Files
/validate — Canonical Validator Skill
Loop position: move 6 (prove acceptance) of the operating loop — the driving adapter for the validate_acceptance port: every Given/When/Then must map to a passing test before a bead closes.Role: validator. Input = artifact (plan, spec, code, PR, fitness gate). Output = verdict.v1 (PASS / WARN / FAIL with rationale + findings).Status (2026-05-08): introduced ADDITIVE in Phase 1 (m6v5.D.1 / soc-78s2v). Existing validators (councilvibe/pre-mortem/red-team/review/eval-outcomes plus retired pr-validate and validation lanes) stayed until Phase 2 shim conversion (m6v5.D.2). Fix-C smoke (soc-wb2aa) gates Phase 2./validate is a driving adapter for the validate_acceptance port in the Intent-to-Loop Hexagon. When the artifact contains a hexagon: block, preserve the bounded context, context packet, guard adapters, and done state in the verdict. When the artifact claims DONE/closed/green, apply the Completion-Claim Kernel before returning PASS.
A verdict is re-plan evidence, not just a retry trigger. Under /rpi, a FAIL/WARN (and its findings) surfaces UP to the orchestrator's Agile Re-Plan Loop: the remaining waves may be refactored, inserted, dropped, or reordered in response — not only the failed objective re-cranked. Under --auto that pivot is autonomous. Looping a failed objective forever without asking whether the plan should change is the waterfall anti-pattern.
Modes (≤8 per Fix-F mode-flag budget)
| Mode | Purpose | Replaces (post-Phase 2) |
|---|---|---|
| (default) | 2-judge multi-judge consensus on any artifact | /council default |
--quick | Inline single-agent structured review | /council --quick |
--deep | 4-judge thorough review | /council --deep |
--mixed | Cross-vendor (Claude + Codex), N×2 judges | /council --mixed |
--debate | Adversarial 2-round refinement | /council --debate, /red-team |
--mode=post-impl | Code-readiness pipeline (complexity → bug-hunt → council) | vibe |
--mode=pre-impl [--target=X] | Plan/spec validation; target ∈ {scenario,fitness,ratchet,scope,skill,health} | /pre-mortem, /eval-outcomes, /goals measure, /flywheel, /scope, /skill-auditor, ao doctor |
--mode=pr | PR-shape verdict (diff review + acceptance check) | /review |
Mode-budget assertion: 8 modes. Adding a 9th requires demoting an existing one OR refusing the addition (per Fix-F § continuous CI gate).
Folded skills (cp-ki8): validation + pr-validate retired into these modes
The retired validation and pr-validate lanes were the Phase-1 placeholders for --mode=post-impl and --mode=pr; both are now retired (cp-ki8) and their load-bearing contract folded here so no capability is lost:
- `--mode=post-impl` (was the validation lane) — full close-out + no-self-grading invariant.
Beyond the inline complexity → bug-hunt → council pipeline, this mode owns the validate_acceptance port: every Given/When/Then from the intent issue must map to a passing test (criterion→test roll-up; activity logs do not close beads), and the acceptance verdict must be produced by a blind, context-isolated sub-agent judge that did not author the code (author ≠ validator — ag-9jle.5 / ag-lmdx.4). Refuse to certify acceptance when judge_id == author_id; the only escape is an inline-fallback self-grade that is stamped as waived, not independently validated. Apply the Completion-Claim Kernel before accepting any DONE/closed/green claim. For epic-scope close-out this mode may delegate to vibe, /post-mortem, and /forge rather than inlining them.
- `--mode=pr` (was the pr-validate lane) — submission-readiness checks. In addition to the
diff/acceptance verdict, run, in order: (1) upstream alignment FIRST (BLOCKING — git rev-list --count HEAD..origin/main; fail if many commits behind or merge would conflict), (2) CONTRIBUTING.md compliance (BLOCKING), (3) isolation — single commit type + thematic files + atomic scope, (4) scope-creep containment, (5) quality gate (tests/lint, non-blocking). On FAIL, emit remediation steps (split-by-type cherry-pick, rebase-on-upstream) so the verdict is actionable.
Folded triggers (ag-s43tg wave 1): vibe + bead-completion-audit route here
- `vibe` → `--mode=post-impl`. Use when doing a quick readiness or sanity check
that code is ready to commit or ship, short of a full review — the post-impl pipeline (complexity → bug-hunt → council) is the vibe check.
- `bead-completion-audit` → `--mode=post-impl` close-out. Use when
auditing closed beads for real shipped evidence, acceptance proof, and truthful closeout — the Completion-Claim Kernel and the no-self-grading invariant above own this audit.
Quick Start
/validate path/to/plan.md # default 2-judge consensus
/validate --quick path/to/plan.md # inline single-agent
/validate --deep path/to/spec.md # 4-judge thorough
/validate --mode=pre-impl path/to/plan.md # pre-mortem mode
/validate --mode=post-impl recent # vibe mode (post-implement)
/validate --mode=pr 123 # PR review by PR number
/validate --mode=pre-impl --target=fitness # fitness gate against GOALS.mdDefault uses runtime-native subagent spawning. Falls back to --quick (inline) when no multi-agent capability detected.
Execution
Step 1: Resolve mode + target
Parse --mode and --target. Default mode is multi-judge. Validate combinations:
| Mode | Allowed --target |
|---|---|
| default, --quick, --deep, --mixed, --debate | n/a |
| --mode=post-impl | n/a (pipeline scope is recent code changes) |
| --mode=pre-impl | scenario, fitness, ratchet, scope, skill, health (default: pre-mortem on plan) |
| --mode=pr | n/a (PR ID/path is positional) |
Reject invalid combinations (e.g., --mode=pr --target=fitness).
Step 2: Load artifact + context
# resolve artifact:
ARTIFACT="${1:-recent}" # path, PR ID, or "recent"
# load FAIL patterns:
# (folded into skill body; not a separate hook)For --mode=pre-impl, also load:
.agents/planning-rules/*.md(compiled planning rules).agents/findings/registry.jsonl(active findings).agents/pre-mortem-checks/*.md(compiled prevention)
For --mode=post-impl, run pre-checks:
- complexity audit (radon for python, gocyclo for go)
- bug-hunt sweep (skill-body convention; no
/reviewskill needed)
For --mode=pr, fetch the PR diff (gh pr diff <id> or path).
Step 3: Determine spawn backend
1. spawn_agent available → Codex sub-agent 2. TeamCreate available → Claude native team 3. task (read-only skill tool, OpenCode) → opencode subagent 4. None → fall back to --quick (inline single-agent)
Log selected backend in the verdict frontmatter.
Step 4: Run judges
| Mode | Judges | Perspectives |
|---|---|---|
| default | 2 | independent (no labeled perspectives) |
| --deep | 4 | missing-requirements, feasibility, scope, spec-completeness |
| --mixed | 2N (default N=3) | same N perspectives across Claude + Codex |
| --debate | 2+ rounds | adversarial; 2 rounds with critique-rebuttal |
| --quick | 0 (inline self) | structured review |
| --mode=post-impl | 2 + pipeline | complexity → bug-hunt → 2-judge council |
| --mode=pre-impl | 2-4 | per target preset |
| --mode=pr | 2 | diff-review + acceptance-check |
Each judge gets:
- artifact path
- relevant context (planning rules, findings)
- council FAIL pattern check prompt (top 8)
- temporal interrogation prompt (--deep + --target=plan)
Step 5: Mandatory checks (auto-trigger)
For --mode=pre-impl --target=plan:
- temporal interrogation (auto for plans with 5+ files or 3+ deps)
- error & rescue map
- council FAIL pattern check (top 8)
- test pyramid coverage check
- input validation check (enum-like fields)
For --mode=post-impl:
- L0/L1/L2 coverage check on changed files
For --mode=pre-impl --target=fitness:
- read GOALS.md
- evaluate each gate against current state
- report PASS/WARN/FAIL per gate + aggregate
Step 6: Consolidate to verdict
Each judge returns a per-judge result. Consolidate:
- PASS only if all judges PASS (or majority for --deep)
- WARN if any judge raises a warning the others don't dispute
- FAIL if any judge raises a blocker the others don't override
Step 7: Write verdict
Output path: .agents/council/YYYY-MM-DD-validate-<topic-slug>.md
---
id: validate-YYYY-MM-DD-<slug>
type: verdict
date: YYYY-MM-DD
mode: <mode>
target: <target or n/a>
artifact: <path>
backend: <codex-subagents | claude-teams | opencode | inline>
---
# Validate Verdict — <topic>
## Council Verdict: PASS / WARN / FAIL
| Failure mode | Risk | Severity | Addressed? |
|---|---|---|---|
| ... | ... | ... | ... |
## Pseudocode Fixes (when WARN/FAIL)
(copy-pastable into affected issues per pre-mortem 4.6 contract)
## FAIL Pattern Check
(top 8 patterns — status per pattern)
## Verdict
PASS — proceed
WARN — review concerns, accept risk, or apply fixes
FAIL — block; revise artifact and rerunThe exact heading ## Council Verdict: PASS / WARN / FAIL is mandatory — downstream validators and ledger readers parse it with anchored regex.
Step 8: Persist findings (when applicable)
For --mode=pre-impl reusable findings: append to .agents/findings/registry.jsonl (atomic temp+rename).
Step 9: Report
1. Verdict (PASS/WARN/FAIL). 2. Key concerns (when not PASS). 3. Output path. 4. Recommended next action.
--target taxonomy (pre-impl)
--target | What gets graded | Replaces |
|---|---|---|
| (default) | Plan/spec for an upcoming /implement | /pre-mortem |
| scenario | Holdout scenario gate | /eval-outcomes |
| fitness | GOALS.md fitness gates | /goals measure, ao goals measure |
| ratchet | Brownian Ratchet checkpoint | /flywheel, ao ratchet status |
| scope | Frozen-dirs declaration | /scope |
| skill | SKILL.md hygiene + audit | /skill-auditor, /heal-skill (audit half) |
| health | Repo health probe | ao doctor |
Each target has its own inline check rubric until Phase 2 extraction.
Validation discipline (2026-06-09, cards 6–10, cp-hhd7)
Verdict form — the gate parses these lines anchored
VERDICT: PASS
(blank line)
COMMANDS RUN:
<actual commands + verbatim output snippets>
REASONS:
- bullet citing a COMMANDS RUN lineA verdict with no COMMANDS RUN: section is unverified — reject it and dispatch a fresh validator. A verdict whose COMMANDS RUN: lists only commands the author ran (not the judge) is a counterfeit judge — treat as FAIL and re-route to a genuinely independent validator. No ## headings or parentheticals on VERDICT: or COMMANDS RUN: lines; the gate parses them anchored.
Cross-family floor (POLICY → gate icb6 enforces; this skill supports)
For assurance closes (the control-plane verdict-gate, cp-icb6), the floor is ≥2 verdicts from ≥2 distinct model families, author family excluded, fail-closed. This skill supports that policy via `--mixed` mode and the verdict form above; the policy itself lives in the gate, not here. A same-model council is valid for non-assurance decisions (design brainstorms, quick checks) — do not refuse those. Tier mapping:
- STRICT (irreversible, security, production close): Codex + strong-Gemini (A1); or Fable + Codex (A2).
- ROUTINE (everyday close): Codex + non-author-Claude (A3); or Fable + non-author (A4).
The A7 ruling (2026-06-09, memory validation-family-policy-risk-tiered): Gemini is currently benched for STRICT validation — use Codex + Fable for A1/A2 tiers. Gemini may return for STRICT when Bo graduates it from the bench. Do not present Gemini paths as live for tier A1/A2 until then.
Judge empirically on a differentiating fixture (card 9, cp-8720)
When two implementations of the same intent exist, do NOT award based on authorship or surface aesthetics. Run both on a differentiating fixture (an input that exposes their behavioral difference), record the outputs verbatim, and graft the loser's unique assets onto the winner. "My worker wrote it" is not evidence.
Dispatch record first (card 3, cp-hhtu)
Before dispatching a validator, register intent on the bead graph (update status, assign actor). Two parallel validators on the same bead produce a dedup incident, not a cross-family quorum. Check for an existing actor before spawning.
Judges re-measure; they do not read (card 8)
A judge re-runs the cited commands on the actual artifacts. It does not read the author's evidence file and agree. Attest judge_source: <model> inside COMMANDS RUN so the gate can confirm the judge identity. A judge that ran nothing is a reader, not a verifier — discard its verdict.
Evidence figures are measured, never inferred (the cp-801l lesson)
A worker's evidence file may only contain numbers and outputs that were captured — pasted verbatim from a command's output — never reconstructed from memory. The canonical failure: "36 checks — 35 pass" stated with confidence was inference; the measured reality was 36 run / 34 pass / 1 fail / 1 skip, on a different commit.
- Validators: treat any uncited figure (a count, a pass-rate, a timing, a commit
hash) as unverifiable → FAIL until a log is produced or the claim is corrected.
- Corrections happen by APPENDED erratum — a dated erratum block crediting the
source measurement — never by silently editing the original figure. A silently edited evidence file is indistinguishable from a fabricated one.
Constraints (one-role-per-skill)
- One role: validator. Output is always a verdict. Never mutates code (delegates to
/implementfor fixes). - No new modes without dropping/merging an existing one (Fix-F mode-budget cap = 8).
- Verdict heading is regex-anchored — do not alter the
## Council Verdict: ...text format.
See Also
skills/rpi/SKILL.md— orchestrator that fires/validate --mode=pre-implafter/planskills/curate/SKILL.md— miner role (paired canonical skill)schemas/verdict.v1.schema.json— output contract- `pre-land-refuters` — the mutate-shared-trunk pawl: a self-administered DONE is a claim, so an unbiased fresh-context refuter (model-agnostic by default; multi-model ≥2 families opt-in for the highest-irreversibility doors) attacks it at the push boundary regardless of complexity (complexity scales the panel's depth, never exempts the gate); its CONFIRMED verdict is enforced executably by
scripts/reconcile-pr.sh
Reference Documents
- references/validate.feature — Executable spec: verdict.v1 PASS/WARN/FAIL for any artifact, --mode selects shape, 8-mode budget (soc-qk4b)
Reference library (incl. rescued vibe references, ag-s43tg)
- references/complexity-analysis.md
- references/deep-audit-protocol.md
- references/deep-checks.md
- references/examples.md
- references/go-patterns.md
- references/go-standards.md
- references/json-standards.md
- references/markdown-standards.md
- references/patterns.md
- references/post-verdict-actions.md
- references/python-standards.md
- references/quick-mode-vibe.md
- references/report-format.md
- references/rust-standards.md
- references/shell-standards.md
- references/test-pyramid-inventory.md
- references/test-pyramid-weighting.md
- references/typescript-standards.md
- references/verification-report.md
- references/vibe-coding.md
- references/vibe-suppressions.md
- references/write-time-quality.md
- references/yaml-standards.md
- references/validate.feature
- references/vibe.feature — rescued vibe executable spec
- scripts/prescan.sh — rescued vibe pre-scan helper
Complexity Analysis (Step 2)
Filter by language present in the change set first. Run only the analyzers whose language actually appears in the diff. A docs/shell/BATS-only epic must NOT trigger gocyclo against the entire cli/ tree (it has hung in past runs); a Python-free epic must NOT trigger radon.
# Detect which languages are present in the diff (or in <path> for full audits).
# Use `git diff --name-only <base>...HEAD` for a PR; fall back to listing
# files under <path> when no diff base is available.
mkdir -p .agents/council
HAS_GO=false; HAS_PY=false
DIFF_FILES="$(git diff --name-only "${BASE:-HEAD~1}"...HEAD 2>/dev/null || find <path> -type f)"
echo "$DIFF_FILES" | grep -q '\.go$' && HAS_GO=true
echo "$DIFF_FILES" | grep -q '\.py$' && HAS_PY=true
echo "$(date -Iseconds) preflight: HAS_GO=$HAS_GO HAS_PY=$HAS_PY" >> .agents/council/preflight.logFor Python (only when `HAS_PY=true`):
if [ "$HAS_PY" = "true" ]; then
echo "$(date -Iseconds) preflight: checking radon" >> .agents/council/preflight.log
if ! which radon >> .agents/council/preflight.log 2>&1; then
echo "⚠️ COMPLEXITY SKIPPED: radon not installed (pip install radon)"
else
radon cc <path> -a -s 2>/dev/null | head -30
radon mi <path> -s 2>/dev/null | head -30
fi
else
echo "ℹ️ COMPLEXITY SKIPPED: no .py files in diff"
fiFor Go (only when `HAS_GO=true`):
if [ "$HAS_GO" = "true" ]; then
echo "$(date -Iseconds) preflight: checking gocyclo" >> .agents/council/preflight.log
if ! which gocyclo >> .agents/council/preflight.log 2>&1; then
echo "⚠️ COMPLEXITY SKIPPED: gocyclo not installed (go install github.com/fzipp/gocyclo/cmd/gocyclo@latest)"
else
gocyclo -over 10 <path> 2>/dev/null | head -30
fi
else
echo "ℹ️ COMPLEXITY SKIPPED: no .go files in diff"
fiFor other languages: Skip complexity with explicit note: "⚠️ COMPLEXITY SKIPPED: No analyzer for <language>"
Interpret results:
| Score | Rating | Action |
|---|---|---|
| A (1-5) | Simple | Good |
| B (6-10) | Moderate | OK |
| C (11-20) | Complex | Flag for council |
| D (21-30) | Very complex | Recommend refactor |
| F (31+) | Untestable | Must refactor |
Include complexity findings in council context.
Deep Audit Protocol
Used by/vibe --deep,/vibe --sweep, and/post-mortem(unless--skip-sweep).
Two-phase architecture: cheap per-file explorer sweep (discovery) followed by council judges (adjudication). All reporting caps removed.
Files -> chunk into batches of 3-5
-> [up to 8 Explore agents in parallel, 8-category checklist per file] -> raw findings
-> sweep manifest (ALL findings merged)
-> [council judges] adjudicate + add cross-cutting findings -> ALL findings reported---
Phase 1: Explorer Sweep
File Chunking Rules
Sort target files by line count (largest first), then chunk:
| File Size | Batch Size | Rationale |
|---|---|---|
| <= 100 lines | 5 per batch | Small files — explorers can handle many |
| 101–300 lines | 3 per batch | Medium files — moderate depth needed |
| > 300 lines | 1 per batch (solo) | Large files — full attention required |
Max 8 batches. If chunking produces more than 8 batches, merge the smallest batches until you have 8. If fewer than 8 batches, use fewer explorers.
Explorer Prompt Template
Each explorer receives this prompt with its assigned file batch:
You are a Deep Audit Explorer. Your job is DISCOVERY — find every concrete issue in
the assigned files. You are NOT a judge; you do not decide severity thresholds or
ship-readiness. Find problems. Be thorough. Be specific.
## Assigned Files
{FILE_LIST}
## Mandatory 8-Category Checklist
For EACH file, you MUST check all 8 categories. Do not skip any category for any file.
1. **Resource Leaks** — Unclosed files/connections/handles, missing defer/finally/cleanup,
goroutine leaks, listener leaks, temp file accumulation
2. **String Safety** — Unsanitized user input, format string injection, path traversal,
SQL/command injection, XSS vectors, unsafe interpolation.
**Boundary with Cat 8:** String Safety covers injection at the data layer (SQL, command, format string).
HTTP-layer concerns (XSS in HTML output, CORS headers, CSRF tokens) belong to Cat 8.
Path traversal appears in both: Cat 2 when sanitizing input strings, Cat 8 when serving HTTP file requests.
3. **Dead Code** — Unreachable branches, unused imports/variables/functions, commented-out
code left behind, feature flags that are always on/off
4. **Hardcoded Values** — Magic numbers, hardcoded paths/URLs/credentials, environment-
specific values not in config, hardcoded timeouts/limits
5. **Edge Cases** — Nil/null/zero handling, empty collections, boundary values, integer
overflow, off-by-one, Unicode edge cases, concurrent access
6. **Concurrency** — Data races, missing locks, lock ordering issues, channel misuse,
deadlock potential, shared state without synchronization
7. **Error Handling** — Swallowed errors, generic catch-all, missing error propagation,
panic/crash on recoverable errors, unclear error messages
8. **HTTP/Web Security** — XSS vectors (innerHTML, document.write, dangerouslySetInnerHTML),
path traversal (../ sequences, directory escape), CORS misconfiguration, CSRF tokens missing,
HTTP response splitting, Content-Type mismatches, missing rate limiting, open redirects,
SSRF (outbound URL/IP validation), missing security headers (Strict-Transport-Security,
X-Frame-Options, Content-Security-Policy), credential/token exposure in logs
## Per-File Coverage Certification
For each file, you MUST either:
- Report at least 1 finding, OR
- Explicitly certify all 8 categories clean with a brief reason per category
Do NOT skip a file. Do NOT say "looks fine" without checking each category.
## Output Format
For each file, produce:
1. A category coverage checklist (checked = issue found, certified = explicitly clean)
2. A findings table
### Category Coverage
File: {filename} [x] Resource Leaks — found: unclosed DB connection at line 45 [ ] String Safety — CLEAN: all inputs validated via sanitize() helper [x] Dead Code — found: unused import "fmt" at line 3 [ ] Hardcoded Values — CLEAN: all values from config package [x] Edge Cases — found: nil map access at line 78 [ ] Concurrency — CLEAN: single-goroutine function, no shared state [ ] Error Handling — CLEAN: all errors returned with context wrapping [ ] HTTP/Web Security — CLEAN: no HTTP handlers, outbound requests, or log statements with credentials in this file
### Findings Table
| File | Line | Category | Severity | Description | Evidence |
|------|------|----------|----------|-------------|----------|
| auth.go | 45 | Resource Leaks | high | DB connection opened but never closed in error path | `db.Open()` at L45, return at L52 skips `defer db.Close()` |
| auth.go | 3 | Dead Code | low | Unused import "fmt" | No fmt.* calls in file |
| auth.go | 78 | Edge Cases | high | Nil map access when config missing | `config["key"]` without nil check, panics if config unset |
Severity levels:
- **critical** — Security vulnerability, data loss, crash in production
- **high** — Bug that will manifest under normal usage
- **medium** — Code smell, maintainability issue, minor bug in edge case
- **low** — Style issue, minor dead code, documentation gap
## Rules
- Read each file completely. Do not skim.
- Every finding MUST have a specific line number and evidence quote.
- Do not invent findings. If a category is clean, certify it clean.
- Prefer false negatives over false positives — only report what you can evidence.
- Do NOT assess overall quality or make ship/no-ship recommendations.Dispatching Explorers
Use the Task tool with subagent_type: "Explore" for each batch. Launch all batches in parallel (single message, multiple tool calls):
Task(
subagent_type="Explore",
description="Sweep batch N of M",
prompt="<explorer prompt with FILE_LIST filled in>"
)Each explorer writes findings to .agents/council/sweep-batch-{N}.md.
---
Phase 2: Sweep Manifest
After all explorers complete, merge their findings into a single sweep manifest:
Write to: .agents/council/sweep-manifest.md
# Sweep Manifest
**Files Scanned:** {total_file_count}
**Batches:** {batch_count}
**Total Findings:** {finding_count}
## All Findings
| # | File | Line | Category | Severity | Description | Evidence |
|---|------|------|----------|----------|-------------|----------|
| 1 | auth.go | 45 | Resource Leaks | high | DB connection unclosed in error path | `db.Open()` at L45... |
| 2 | ... | ... | ... | ... | ... | ... |
## Category Summary
| Category | Findings | Files Affected |
|----------|----------|----------------|
| Resource Leaks | 3 | 2 |
| String Safety | 1 | 1 |
| Dead Code | 5 | 4 |
| Hardcoded Values | 2 | 2 |
| Edge Cases | 4 | 3 |
| Concurrency | 0 | 0 |
| Error Handling | 3 | 2 |
| HTTP/Web Security | 1 | 1 |
## Clean Certifications
| File | Categories Certified Clean |
|------|---------------------------|
| utils.go | All 8 |
| config.go | Resource Leaks, String Safety, Dead Code, Edge Cases, Concurrency |No caps. Include ALL findings from ALL explorers. Do not filter, rank, or truncate.
---
Phase 3: Council Adjudication
When a sweep manifest exists, inject it into the council packet and shift judges from discovery to adjudication mode.
Council Injection Text
Add this to the council packet's context.sweep_manifest:
{
"sweep_manifest": {
"source": "deep-audit-protocol explorer sweep",
"file": ".agents/council/sweep-manifest.md",
"finding_count": N,
"summary": "<first 3000 chars of sweep manifest>"
}
}Judge Mode Shift
When sweep_manifest is present in the council context, judges operate in adjudication mode instead of discovery mode. The judge prompt receives an additional section (see council/references/agent-prompts.md for the exact injection text).
In adjudication mode, judges: 1. Confirm or reject each sweep finding (with brief rationale) 2. Reclassify severity where the explorer got it wrong 3. Add cross-file findings that individual explorers couldn't see (architectural issues, inconsistent patterns across files, missing integration points) 4. Assess overall readiness considering the full sweep manifest
Reporting
The final report includes ALL findings — both confirmed sweep findings and judge-added cross-cutting findings. No caps on finding count. If more than 20 findings, group by category in the report.
---
Flag Behavior
| Flag | Sweep? | Judges | Notes |
|---|---|---|---|
/vibe (default) | No | 2 | Unchanged: lightweight bug-hunt + council |
/vibe --quick | No | 1 (inline) | Unchanged: fast inline check |
/vibe --deep | Yes | 3 | Enhanced: sweep + 3 judges in adjudication mode |
/vibe --sweep | Yes | 2 | New: sweep + 2 judges in adjudication mode |
/vibe --sweep recent | Yes | 2 | Same, targeting recent changes |
/post-mortem | Yes | 3 | Enhanced: sweep before retrospective council |
/post-mortem --skip-sweep | No | 3 | Old behavior: 3 judges, no sweep |
/post-mortem --quick | No | 1 (inline) | Unchanged: fast inline check |
Deep Checks (Steps 2.5, 2a–2f, 2h, 3–3.6)
Extracted from: skills/validate/SKILL.md.These are the pre-council deep analysis and preparation checks. Loaded automatically unless --quick mode is set.---
Step 2.4: Compiled Prevention Check
Before reading .agents/rpi/next-work.jsonl, load compiled prevention context from .agents/pre-mortem-checks/*.md and .agents/planning-rules/*.md when they exist. This is the primary reusable-prevention surface for review.
Use the tracked contracts in docs/contracts/finding-compiler.md and docs/contracts/finding-registry.md:
- prefer compiled pre-mortem checks and planning rules first
- rank by severity,
applicable_whenoverlap, language overlap, changed-file overlap, and literal target-text overlap - keep the ranking order consistent with
/planand/pre-mortem; do not invent a separate review-only heuristic - cap at top 5 findings / compiled files
- if compiled outputs are missing, incomplete, or fewer than the matched finding set, fall back to
.agents/findings/registry.jsonl - fail open:
- missing compiled directory or registry -> skip silently
- empty compiled directory or registry -> skip silently
- malformed line -> warn and ignore that line
- unreadable file -> warn once and continue without findings
Include matched entries in the council packet as known_risks / checklist context with:
idpatterndetection_questionchecklist_item
Step 2.5: Prior Findings Check
Skip if `--quick` (see Step 1.5).
Read .agents/rpi/next-work.jsonl and find unconsumed items with severity=high that match the target area. Include them in the council packet as context.prior_findings so judges have carry-forward context.
Treat these high-severity queue items as part of the same ranked packet used earlier in discovery/plan/pre-mortem. The review stage should inherit and refine prior findings context, not restart retrieval from scratch.
# Count unconsumed high-severity items
if [ -f .agents/rpi/next-work.jsonl ] && command -v jq &>/dev/null; then
prior_count=$(jq -s '[.[] | select(.consumed == false) | .items[] | select(.severity == "high")] | length' \
.agents/rpi/next-work.jsonl 2>/dev/null || echo 0)
if [ "$prior_count" -gt 0 ]; then
echo "Prior findings: $prior_count unconsumed high-severity items from next-work.jsonl"
jq -s '[.[] | select(.consumed == false) | .items[] | select(.severity == "high")]' \
.agents/rpi/next-work.jsonl 2>/dev/null
fi
fiIf unconsumed high-severity items are found, include them in the council packet context:
"prior_findings": {
"source": ".agents/rpi/next-work.jsonl",
"count": 3,
"items": [/* array of high-severity unconsumed items */]
}Skip conditions:
--quickmode → skip.agents/rpi/next-work.jsonldoes not exist → skip silentlyjqnot on PATH → skip silently- No unconsumed high-severity items found → skip (do not add empty
prior_findingsto packet)
Step 2a: Run Constraint Tests
Skip if `--quick` (see Step 1.5).
If the project has constraint tests, run them before council:
# Check if constraint tests exist
if [ -d "internal/constraints" ] && ls internal/constraints/*_test.go &>/dev/null; then
echo "Running constraint tests..."
go test ./internal/constraints/ -run TestConstraint -v 2>&1
# If FAIL → include failures in council context as CRITICAL findings
# If PASS → note "N constraint tests passed" in report
fiWhy: Constraint tests catch mechanical violations (ghost references, TOCTOU races, dead code at entry points) that council judges miss.
Include constraint test results in the council packet context. Failed constraint tests are CRITICAL findings that override council PASS verdict.
Step 2b: Metadata Verification Checklist (MANDATORY)
Skip if `--quick` (see Step 1.5).
Run mechanical checks BEFORE council — catches errors LLMs estimate instead of measure: 1. File existence — every path in git diff --name-only HEAD~3 must exist on disk 2. Line counts — if a file claims "N lines", verify with wc -l 3. Cross-references — internal markdown links resolve to existing files 4. Diagram sanity — files with >3 ASCII boxes should have matching labels
Include failures in council packet as context.metadata_failures (MECHANICAL findings). If all pass, note in report.
Step 2d: Codex Review (opt-in via --mixed)
Skip unless `--mixed` is passed. Also skip if --quick (see Step 1.5).
Codex review is opt-in because it adds 30–60s latency and token cost. Users explicitly request cross-vendor input with --mixed.
echo "$(date -Iseconds) preflight: checking codex" >> .agents/council/preflight.log
if which codex >> .agents/council/preflight.log 2>&1; then
codex review --uncommitted > .agents/council/codex-review-pre.md 2>&1 && \
echo "Codex review complete — output at .agents/council/codex-review-pre.md" || \
echo "Codex review skipped (failed)"
else
echo "Codex review skipped (CLI not found)"
fiIf output exists, summarize and include in council packet (cap at 2000 chars to prevent context bloat):
"codex_review": {
"source": "codex review --uncommitted",
"content": "<first 2000 chars of .agents/council/codex-review-pre.md>"
}IMPORTANT: The raw codex review can be 50k+ chars. Including the full text in every judge's packet multiplies token cost by N judges. Truncate to the first 2000 chars (covers the summary and top findings). Judges can read the full file from disk if they need more detail.
This gives council judges a Codex-generated review as pre-existing context — cheap, fast, diff-focused. It does NOT replace council judgment; it augments it.
Skip conditions:
--mixednot passed → skip (opt-in only)- Codex CLI not on PATH → skip silently
codex reviewfails → skip silently, proceed with council only- No uncommitted changes → skip (nothing to review)
Step 2e: Search Knowledge Flywheel
Skip if `--quick` (see Step 1.5).
if command -v ao &>/dev/null; then
ao search "code review findings <target>" 2>/dev/null | head -10
fiIf ao returns prior code review patterns for this area, include them in the council packet context. Skip silently if ao is unavailable or returns no results.
Step 2f: Bug Hunt or Deep Audit Sweep
Skip if `--quick` (see Step 1.5).
Path A — Deep Audit Sweep (`--deep` or `--sweep`):
Read references/deep-audit-protocol.md for the full protocol. In summary:
1. Chunk target files into batches of 3–5 (by line count — see protocol for rules) 2. Dispatch up to 8 Explore agents in parallel, each with a mandatory 8-category checklist per file 3. Merge all explorer findings into a sweep manifest at .agents/council/sweep-manifest.md 4. Include sweep manifest in council packet (judges shift to adjudication mode — see Step 4)
Why: Generalist judges exhibit satisfaction bias — they stop at ~10 findings regardless of actual issue count. Per-file explorers with category checklists eliminate this bias and find 3x more issues in a single pass.
Path B — Lightweight Bug Hunt (default, no `--deep`/`--sweep`):
Run a proactive bug hunt on the target files before council review:
/bug-hunt --audit <target>If bug-hunt produces findings, include them in the council packet as context.bug_hunt:
"bug_hunt": {
"source": "/bug-hunt --audit",
"findings_count": 3,
"high": 1,
"medium": 1,
"low": 1,
"summary": "<first 2000 chars of bug hunt report>"
}Why: Bug hunt catches concrete line-level bugs (resource leaks, truncation errors, dead code) that council judges — reviewing holistically — often miss.
Skip conditions (both paths):
--quickmode → skip (fast path)- No source files in target → skip (nothing to audit)
- Target is non-code (pure docs/config) → skip
Step 2h: Check for Product Context
Skip if `--quick` (see Step 1.5).
if [ -f PRODUCT.md ]; then
# PRODUCT.md exists — include developer-experience perspectives
fiWhen PRODUCT.md exists in the project root AND the user did NOT pass an explicit --preset override: 1. Read PRODUCT.md content and include in the council packet via context.files 2. Add a single consolidated developer-experience perspective to the council invocation:
- With spec:
/council --preset=code-review --perspectives="developer-experience" validate <target>(3 judges: 2 code-review + 1 DX) - Without spec:
/council --perspectives="developer-experience" validate <target>(3 judges: 2 independent + 1 DX)
The DX judge covers api-clarity, error-experience, and discoverability in a single review. 3. With --deep: adds 1 more judge per mode (4 judges total).
When PRODUCT.md exists BUT the user passed an explicit --preset: skip DX auto-include (user's explicit preset takes precedence).
When PRODUCT.md does not exist: proceed to Step 3 unchanged.
Tip: CreatePRODUCT.mdfromdocs/PRODUCT-TEMPLATE.mdto enable developer-experience-aware code review.
Step 3: Load the Spec (New)
Skip if `--quick` (see Step 1.5).
Before invoking council, try to find the relevant spec/bead:
1. If target looks like a bead ID (e.g., na-0042): bd show <id> to get the spec 2. Search for plan doc: ls .agents/plans/ | grep <target-keyword> 3. Check git log: git log --oneline | head -10 to find the relevant bead reference
If a spec is found, include it in the council packet's context.spec field:
{
"spec": {
"source": "bead na-0042",
"content": "<the spec/bead description text>"
}
}Step 3.5: Load Suppressions
Before invoking council, load the default suppression list from references/vibe-suppressions.md and any project-level overrides from .agents/vibe-suppressions.jsonl. Suppressions are applied post-verdict to classify findings as CRITICAL vs INFORMATIONAL and to filter known false positives. See references/vibe-suppressions.md for the full pattern list.
Step 3.6: Load Pre-Mortem Predictions (Correlation)
When a pre-mortem report exists for the current epic, load prediction IDs for downstream correlation:
# Find the most recent pre-mortem report
PM_REPORT=$(ls -t .agents/council/*pre-mortem*.md 2>/dev/null | head -1)
if [ -n "$PM_REPORT" ]; then
# Extract prediction IDs from frontmatter
PREDICTION_IDS=$(sed -n '/^prediction_ids:/,/^[^ -]/p' "$PM_REPORT" | grep '^\s*-' | sed 's/^\s*- //')
fiFor each vibe finding, check if it matches a pre-mortem prediction:
- Match found: Tag finding with
predicted_by: pm-YYYYMMDD-NNN - No match: Tag finding with
predicted_by: none(surprise issue)
Include the prediction correlation in the vibe report's findings table. This feeds the post-mortem's Prediction Accuracy section. Skip silently if no pre-mortem report exists.
Model Cost Tiers
Vibe passes the model cost tier through to council for all validation calls. Tier resolution:
1. Explicit --tier=<name> flag on /vibe 2. Skill-specific config: models.skill_overrides.vibe in .agentops/config.yaml 3. Global default: models.default_tier in .agentops/config.yaml 4. Built-in default: balanced
# Example: force quality tier for all vibe reviews
models:
skill_overrides:
vibe: quality/vibe Examples
Security Audit with Spec Compliance
User says: /vibe --preset=security-audit src/auth/
What happens: 1. Agent searches for spec (checks bd show, .agents/plans/, git log) 2. Agent runs complexity analysis (radon/gocyclo) on src/auth/ 3. Agent runs constraint tests (internal/constraints/*_test.go) if present 4. Agent runs codex review --uncommitted for diff-focused review 5. Agent invokes /council --deep --preset=security-audit validate src/auth/ with spec in packet 6. Spec found: 3 judges use security-audit personas + spec-compliance judge added (4 total) 7. Report written to .agents/council/<timestamp>-vibe-src-auth.md
Result: Security-focused review with attacker/defender/compliance perspectives and spec validation.
Developer-Experience Code Review (PRODUCT.md detected)
User says: /vibe recent
What happens: 1. Agent detects PRODUCT.md in project root 2. Agent searches for spec (found: bead na-0042) 3. Agent runs complexity + constraint tests + codex review 4. Agent invokes /council --deep --preset=code-review --perspectives="api-clarity,error-experience,discoverability" validate recent 5. Auto-escalation: 6 judges spawn (3 code-review + 3 DX perspectives) 6. Judges review against spec + developer experience criteria
Result: Code review augmented with API clarity, error messages, and discoverability checks.
Fast Inline Check (No Spawning)
User says: /vibe --quick recent
What happens: 1. Agent runs complexity analysis inline (radon/gocyclo) 2. Agent runs constraint tests and codex review 3. Agent performs structured self-review using council schema (no subprocess spawning) 4. Report written to .agents/council/<timestamp>-vibe-recent.md labeled Mode: quick (single-agent)
Result: Sub-60s validation for routine pre-commit checks, no multi-agent overhead.
Go Patterns Quick Reference - Vibe
Quick reference for Go patterns. Copy-paste these examples when writing new code.
---
Error Handling
✅ Wrap Errors with %w
// DO THIS
if err != nil {
return fmt.Errorf("failed to initialize: %w", err)
}
// NOT THIS - Breaks error chains (triggers P14)
if err != nil {
return fmt.Errorf("failed to initialize: %v", err)
}Prescan: P14 detects %v in fmt.Errorf when wrapping errors
✅ Custom Error Creation
// Define custom error type
type AppError struct {
Code string
Message string
Cause error
}
func (e *AppError) Error() string {
if e.Cause != nil {
return fmt.Sprintf("[%s] %s: %v", e.Code, e.Message, e.Cause)
}
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
}
func (e *AppError) Unwrap() error { return e.Cause }
func (e *AppError) Is(target error) bool {
t, ok := target.(*AppError)
return ok && e.Code == t.Code
}
// Use with errors.Is() and errors.As()
if errors.Is(err, &AppError{Code: "NOT_FOUND"}) {
// Handle not found
}
var appErr *AppError
if errors.As(err, &appErr) {
log.Printf("Error code: %s", appErr.Code)
}✅ Document Intentional Error Ignores
// DO THIS (passes P13)
defer func() {
_ = conn.Close() // nolint:errcheck - best effort cleanup
}()
// NOT THIS (triggers P13)
defer func() {
_ = conn.Close() // Silent ignore
}()Prescan: P13 detects _ = without nolint:errcheck comment
---
Concurrency
✅ Always Use context.Context
// DO THIS
func (c *Client) SendTask(ctx context.Context, task *Task) error {
req, err := http.NewRequestWithContext(ctx, "POST", url, body)
if err != nil {
return fmt.Errorf("creating request: %w", err)
}
// ...
}
// NOT THIS - No cancellation support
func (c *Client) SendTask(task *Task) error {
req, err := http.NewRequest("POST", url, body)
// ...
}✅ WaitGroup Pattern
var wg sync.WaitGroup
for name, agent := range agents {
wg.Add(1)
// CRITICAL: Capture loop variables
name := name
agent := agent
go func() {
defer wg.Done() // Always defer, protects against panic
if err := agent.Process(ctx); err != nil {
mu.Lock()
results[name] = err
mu.Unlock()
}
}()
}
wg.Wait()Common Mistake: Forgetting to capture loop variables causes race conditions
✅ Mutex for Shared State
type Registry struct {
items map[string]Item
mu sync.RWMutex // Read-write mutex
}
// Read operations use RLock (concurrent reads OK)
func (r *Registry) Get(name string) (Item, error) {
r.mu.RLock()
defer r.mu.RUnlock()
item, ok := r.items[name]
if !ok {
return Item{}, ErrNotFound
}
return item, nil
}
// Write operations use Lock (exclusive)
func (r *Registry) Register(name string, item Item) error {
r.mu.Lock()
defer r.mu.Unlock()
r.items[name] = item
return nil
}✅ Backpressure in Channels
select {
case eventChan <- event:
// Event sent successfully
case <-time.After(30 * time.Second):
return fmt.Errorf("event channel blocked - consumer too slow")
case <-ctx.Done():
return ctx.Err()
}Why: Prevents unbounded memory growth from fast producer, slow consumer
---
Security
✅ Constant-Time Comparison
import "crypto/subtle"
// DO THIS - Timing attack resistant
if subtle.ConstantTimeCompare([]byte(token), []byte(expectedToken)) != 1 {
return ErrUnauthorized
}
// NOT THIS - Vulnerable to timing attacks
if token == expectedToken {
// Attacker can brute-force byte-by-byte
}Use Cases: API keys, tokens, passwords, secrets
✅ HMAC Signature Validation
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func validateHMAC(payload []byte, signature, secret string) bool {
expectedMAC := hmac.New(sha256.New, []byte(secret))
expectedMAC.Write(payload)
expected := hex.EncodeToString(expectedMAC.Sum(nil))
// Use constant-time comparison
return hmac.Equal([]byte(expected), []byte(signature))
}Use Cases: Webhook signatures (GitHub, GitLab, Slack)
✅ Timestamp Validation (Replay Attack Prevention)
func validateTimestamp(ts string, maxAge time.Duration) error {
timestamp, err := time.Parse(time.RFC3339, ts)
if err != nil {
return fmt.Errorf("invalid timestamp: %w", err)
}
age := time.Since(timestamp)
if age > maxAge {
return fmt.Errorf("timestamp too old: %v > %v", age, maxAge)
}
if age < -1*time.Minute {
return fmt.Errorf("timestamp in future: %v", age)
}
return nil
}Typical maxAge: 5 minutes for webhooks, 1 minute for API requests
---
HTTP Clients
✅ Proper Body Handling
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Printf("Failed to close response body: %v", err)
}
}()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("reading response: %w", err)
}CRITICAL: Always close response body, even on error paths
✅ Retry Logic with Exponential Backoff
func (c *Client) doWithRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
var lastErr error
for attempt := 0; attempt < c.maxRetries; attempt++ {
if attempt > 0 {
backoff := time.Duration(math.Pow(2, float64(attempt))) * time.Second
select {
case <-time.After(backoff):
case <-ctx.Done():
return nil, ctx.Err()
}
}
resp, err := c.httpClient.Do(req)
if err != nil {
lastErr = err
continue
}
// Retry on 5xx
if resp.StatusCode >= 500 {
_ = resp.Body.Close() // nolint:errcheck - best effort
lastErr = fmt.Errorf("server error: %d", resp.StatusCode)
continue
}
return resp, nil
}
return nil, fmt.Errorf("max retries exceeded: %w", lastErr)
}---
Interface Design
✅ Accept Interfaces, Return Structs
// Define interface
type Processor interface {
Process(ctx context.Context, data []byte) error
}
// Functions accept interface (flexible for testing)
func RunPipeline(ctx context.Context, processor Processor, data []byte) error {
if err := processor.Process(ctx, data); err != nil {
return fmt.Errorf("processing failed: %w", err)
}
return nil
}
// Constructors return struct (concrete)
func NewDataProcessor() *DataProcessor {
return &DataProcessor{
cache: make(map[string][]byte),
mu: sync.RWMutex{},
}
}Why: Callers can pass any implementation (testability), return type can add methods
✅ Small, Focused Interfaces
// DO THIS - Single responsibility
type Initializer interface {
Initialize(ctx context.Context) error
}
type Processor interface {
Process(ctx context.Context, data []byte) error
}
// Compose when needed
type Service interface {
Initializer
Processor
}
// NOT THIS - God interface
type Service interface {
Initialize(ctx context.Context) error
Process(ctx context.Context, data []byte) error
Shutdown(ctx context.Context) error
HealthCheck(ctx context.Context) error
GetMetrics() *Metrics
SetConfig(cfg *Config)
// ... 20 more methods
}---
Testing
✅ Table-Driven Tests
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
{"valid", "user@example.com", false},
{"missing @", "userexample.com", true},
{"empty", "", true},
{"no domain", "user@", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateEmail(tt.email)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
tt.email, err, tt.wantErr)
}
})
}
}✅ Test Helpers
func setupTestServer(t *testing.T) *httptest.Server {
t.Helper() // Mark as helper - failures report caller line
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Mock responses
}))
t.Cleanup(func() {
server.Close()
})
return server
}
func TestClient(t *testing.T) {
server := setupTestServer(t) // Failures report this line, not inside helper
client := NewClient(server.URL)
// ... test code
}✅ Mock Interfaces
// Define mockable interface
type Repository interface {
GetUser(ctx context.Context, id string) (*User, error)
}
// Create mock
type MockRepository struct {
GetUserFn func(ctx context.Context, id string) (*User, error)
}
func (m *MockRepository) GetUser(ctx context.Context, id string) (*User, error) {
if m.GetUserFn != nil {
return m.GetUserFn(ctx, id)
}
return nil, nil
}
// Use in tests
func TestService(t *testing.T) {
mock := &MockRepository{
GetUserFn: func(ctx context.Context, id string) (*User, error) {
return &User{ID: id, Name: "Test"}, nil
},
}
service := NewService(mock)
user, err := service.FetchUser(ctx, "123")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if user.Name != "Test" {
t.Errorf("got name %q, want %q", user.Name, "Test")
}
}---
Common Mistakes to Avoid
❌ Don't: Ignore Context
// BAD
func (c *Client) Send(req *Request) error {
// No way to cancel or timeout
return c.process(req)
}
// GOOD
func (c *Client) Send(ctx context.Context, req *Request) error {
select {
case result := <-c.process(req):
return result
case <-ctx.Done():
return ctx.Err()
}
}❌ Don't: Use Pointer to Interface
// BAD
func Process(agent *Agent) error { // Interface is already a reference
// ...
}
// GOOD
func Process(agent Agent) error {
// ...
}❌ Don't: Naked Returns with Named Results
// BAD
func calculate() (result int, err error) {
result = 42
return // What's being returned? Unclear!
}
// GOOD
func calculate() (int, error) {
result := 42
return result, nil // Explicit and clear
}❌ Don't: Use panic in Library Code
// BAD
func GetItem(key string) Item {
item, ok := registry[key]
if !ok {
panic("item not found") // Caller can't recover
}
return item
}
// GOOD
func GetItem(key string) (Item, error) {
item, ok := registry[key]
if !ok {
return Item{}, ErrItemNotFound
}
return item, nil
}❌ Don't: Forget to Capture Loop Variables
// BAD
for _, item := range items {
go func() {
process(item) // Race condition! All goroutines see last item
}()
}
// GOOD
for _, item := range items {
item := item // Capture variable
go func() {
process(item) // Each goroutine has its own copy
}()
}❌ Don't: Leak Goroutines
// BAD - Goroutine never exits
go func() {
for {
work() // No way to stop
}
}()
// GOOD - Context-based cancellation
go func() {
for {
select {
case <-ctx.Done():
return
default:
work()
}
}
}()---
Code Review Checklist
Before submitting PR, verify:
- [ ] All errors wrapped with
%w(P14 passing) - [ ] All long operations accept
context.Context - [ ] All
deferstatements have error checking where needed - [ ] Loop variables captured before goroutines
- [ ] HTTP response bodies closed with defer
- [ ] Secrets compared with
subtle.ConstantTimeCompare() - [ ] Intentional error ignores documented with
nolint:errcheck(P13 passing) - [ ] Tests use table-driven pattern
- [ ] Test helpers use
t.Helper() - [ ] Interfaces are small and focused
- [ ] Functions return concrete types (not interfaces)
- [ ] golangci-lint passes (P15)
- [ ] gofmt applied
- [ ] Complexity < 10 per function
---
golangci-lint Commands
# Run all linters
golangci-lint run ./...
# Run specific linter
golangci-lint run --enable=errcheck ./...
# Fix auto-fixable issues
golangci-lint run --fix ./...
# Show configuration
golangci-lint linters---
Useful Commands
# Format code
gofmt -w .
# Check formatting
gofmt -l .
# Vet code
go vet ./...
# Run tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Check for race conditions
go test -race ./...
# Build all binaries
go build ./cmd/...
# Tidy dependencies
go mod tidy
# Check cyclomatic complexity
gocyclo -over 10 .---
Prescan Pattern Reference
| Pattern | Severity | Triggers On |
|---|---|---|
| P13 | HIGH | _ = without nolint:errcheck comment |
| P14 | MEDIUM | fmt.Errorf.*%v when wrapping errors |
| P15 | HIGH | golangci-lint violations (requires golangci-lint installed) |
Run prescan: ~/.claude/skills/validate/scripts/prescan.sh recent
---
Additional Resources
---
See Also: go-standards.md for comprehensive catalog with detailed explanations
Go Standards Catalog - Vibe Canonical Reference
Version: 1.0.0 Last Updated: 2026-01-20 Purpose: Canonical Go standards for vibe skill validation
---
Table of Contents
1. Error Handling Patterns 2. Interface Design 3. Concurrency Patterns 4. Security Practices 5. Package Organization 6. Testing Patterns 7. Documentation Standards 8. Code Quality Metrics 9. Anti-Patterns Avoided
---
Error Handling Patterns
✅ Custom Error Types
Production-grade error types follow these patterns:
type AppError struct {
Code string // Machine-readable error code
Message string // Human-readable message
Cause error // Wrapped error (optional)
Metadata map[string]any // Additional context
}
// Implements error interface
func (e *AppError) Error() string {
if e.Cause != nil {
return fmt.Sprintf("[%s] %s: %v", e.Code, e.Message, e.Cause)
}
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
}
// Supports errors.Unwrap()
func (e *AppError) Unwrap() error {
return e.Cause
}
// Supports errors.Is() for sentinel comparison
func (e *AppError) Is(target error) bool {
t, ok := target.(*AppError)
if !ok {
return false
}
return e.Code == t.Code
}Requirements:
- ✅ Implements
errorinterface - ✅ Implements
Unwrap()for error chain inspection - ✅ Implements
Is()for sentinel error comparison - ✅ Structured error codes enable programmatic handling
- ✅ Preserves context with metadata
- ✅ Proper nil-safety in
Unwrap()andIs()
✅ Error Wrapping with %w
Use fmt.Errorf with %w verb for error wrapping:
// CORRECT
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("sending request: %w", err)
}
// INCORRECT - Breaks error chains
if err != nil {
return nil, fmt.Errorf("sending request: %v", err)
}Why This Matters:
%wpreserves error chain forerrors.Is()anderrors.As()%vbreaks the chain - root cause is lost- Error context adds debugging information
⚠️ Intentional Error Ignores
Document why errors are intentionally ignored:
// CORRECT
defer func() {
_ = conn.Close() // nolint:errcheck - best effort cleanup
}()
// INCORRECT - Silent ignore
defer func() {
_ = conn.Close()
}()Validation: Prescan pattern P13 detects undocumented ignores
---
Interface Design
✅ Accept Interfaces, Return Structs
Pattern:
// Define interface
type Agent interface {
Initialize(ctx context.Context) error
Invoke(ctx context.Context, req *Request) (*Response, error)
}
// Functions accept interface (flexible)
func ProcessAgent(ctx context.Context, agent Agent) error {
if err := agent.Initialize(ctx); err != nil {
return fmt.Errorf("initialization failed: %w", err)
}
// ...
}
// Constructors return struct (concrete)
func NewRegistry() *Registry {
return &Registry{
agents: make(map[string]Agent),
mu: sync.RWMutex{},
}
}Why This Matters:
- Callers can pass any implementation (testability)
- Return type can add methods without breaking callers
- Follows Go proverb: "Be conservative in what you send, liberal in what you accept"
✅ Small, Focused Interfaces
Good Example:
type Initializer interface {
Initialize(ctx context.Context) error
}
type Invoker interface {
Invoke(ctx context.Context, req *Request) (*Response, error)
}
// Compose interfaces
type Agent interface {
Initializer
Invoker
}Anti-Pattern (God Interface):
type Agent interface {
Initialize(ctx context.Context) error
Invoke(ctx context.Context, req *Request) (*Response, error)
Shutdown(ctx context.Context) error
HealthCheck(ctx context.Context) error
GetMetrics() *Metrics
SetConfig(cfg *Config)
// ... 20 more methods
}---
Concurrency Patterns
✅ Context Propagation (Required)
Every I/O or long-running operation accepts context.Context:
// HTTP Requests
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
// Database Operations
rows, err := db.QueryContext(ctx, query)
// Custom Functions
func (c *Client) Invoke(ctx context.Context, req *Request) (*Response, error)Benefits:
- Timeout propagation
- Cancellation support
- Request-scoped values (tracing)
✅ Proper WaitGroup Usage
var wg sync.WaitGroup
for name, agent := range agents {
wg.Add(1)
// Capture loop variables
name := name
agent := agent
go func() {
defer wg.Done() // Always defer, protects against panic
if err := agent.Process(ctx); err != nil {
mu.Lock()
results[name] = err
mu.Unlock()
}
}()
}
wg.Wait()Requirements:
- ✅ Variables captured before goroutine (avoids closure bug)
- ✅
defer wg.Done()ensures decrement on panic - ✅ Mutex protects shared data structures
- ✅ Context cancellation checked in each goroutine
✅ Thread-Safe Data Structures
type Registry struct {
items map[string]Item
mu sync.RWMutex // Read-write mutex
}
// Read operations use RLock
func (r *Registry) Get(key string) (Item, error) {
r.mu.RLock()
defer r.mu.RUnlock()
// ...
}
// Write operations use Lock
func (r *Registry) Set(key string, item Item) error {
r.mu.Lock()
defer r.mu.Unlock()
// ...
}Pattern Benefits:
- Multiple concurrent reads
- Exclusive writes
- Zero race conditions
✅ Backpressure in Streaming
select {
case eventChan <- event:
// Event sent successfully
case <-time.After(30 * time.Second):
return fmt.Errorf("event channel blocked - consumer too slow (backpressure triggered)")
case <-ctx.Done():
return ctx.Err()
}Why This Matters:
- Prevents unbounded memory growth
- Handles fast producer, slow consumer scenario
- Explicit timeout for debugging
---
Security Practices
✅ Constant-Time Comparison (Timing Attack Prevention)
import "crypto/subtle"
// CORRECT - Timing attack resistant
token := r.Header.Get("Authorization")
if subtle.ConstantTimeCompare([]byte(token), []byte(expectedToken)) != 1 {
return ErrUnauthorized
}
// INCORRECT - Vulnerable to timing attacks
if token == expectedToken {
// Attacker can brute-force byte-by-byte
}Why This Matters:
- String comparison (
==) leaks timing information - Attacker can brute-force secrets byte-by-byte
subtle.ConstantTimeCompare()runs in constant time- Critical for API keys, tokens, passwords
✅ HMAC Signature Validation
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func validateHMAC(payload []byte, signature, secret string) bool {
if !strings.HasPrefix(signature, "sha256=") {
return false
}
expectedMAC := hmac.New(sha256.New, []byte(secret))
expectedMAC.Write(payload)
expected := "sha256=" + hex.EncodeToString(expectedMAC.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}Security Features:
- ✅ HMAC prevents payload tampering
- ✅ Uses
hmac.Equal()(constant-time) - ✅ Verifies signature format first
- ✅ SHA-256 (secure hash function)
✅ Replay Attack Prevention
func validateTimestamp(timestamp string, maxAge time.Duration) error {
ts, err := time.Parse(time.RFC3339, timestamp)
if err != nil {
return fmt.Errorf("invalid timestamp format")
}
age := time.Since(ts)
if age > maxAge || age < -1*time.Minute {
return fmt.Errorf("request too old or in future: age=%v max=%v", age, maxAge)
}
return nil
}Protection Against:
- Replay attacks (old requests resubmitted)
- Clock skew (1 minute tolerance for future timestamps)
- DoS via timestamp manipulation
✅ TLS Configuration
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS13, // Only TLS 1.3+
// No InsecureSkipVerify - validates certificates
}---
Package Organization
✅ Layered Architecture
project/
├── cmd/ # Binaries (main packages)
│ ├── server/ # Server binary
│ ├── worker/ # Worker binary
│ └── cli/ # CLI tool
├── internal/ # Private packages (cannot be imported externally)
│ ├── domain/ # Business logic
│ ├── handlers/ # HTTP handlers
│ ├── repository/ # Data access
│ └── sdk/ # External SDK clients
├── pkg/ # Public packages (can be imported)
│ ├── api/ # API types
│ └── client/ # Client library
└── tests/ # Test suites
├── e2e/ # End-to-end tests
└── integration/ # Integration testsPrinciples:
- ✅
cmd/for binaries (no importable code) - ✅
internal/prevents external imports - ✅
pkg/for public APIs - ✅ Domain-driven structure
- ✅ Tests at package level, e2e/integration separate
✅ Import Grouping (Go Convention)
import (
// Standard library
"context"
"fmt"
"time"
// External dependencies
"github.com/external/package"
// Internal packages
"myproject.com/internal/domain"
)---
Testing Patterns
✅ Table-Driven Tests
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
{"valid", "user@example.com", false},
{"missing @", "userexample.com", true},
{"empty", "", true},
{"no domain", "user@", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateEmail(tt.email)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
tt.email, err, tt.wantErr)
}
})
}
}Benefits:
- Easy to add test cases
- Clear test names with
t.Run() - DRY (Don't Repeat Yourself)
✅ Test Helpers with t.Helper()
func setupTestServer(t *testing.T) *httptest.Server {
t.Helper() // Marks this as a helper function
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Mock responses
}))
t.Cleanup(func() {
server.Close()
})
return server
}
func TestClient(t *testing.T) {
server := setupTestServer(t) // Failures report this line, not inside helper
// ... test code
}Why t.Helper() Matters:
- Test failures report the calling line, not helper line
- Makes test output more useful
- Standard Go testing pattern
✅ Mock Interfaces
// Define mockable interface
type Invoker interface {
Invoke(ctx context.Context, req *Request) (*Response, error)
}
// Create mock
type MockInvoker struct {
InvokeFn func(ctx context.Context, req *Request) (*Response, error)
}
func (m *MockInvoker) Invoke(ctx context.Context, req *Request) (*Response, error) {
if m.InvokeFn != nil {
return m.InvokeFn(ctx, req)
}
return nil, nil
}
// Use in tests
func TestProcessor(t *testing.T) {
mock := &MockInvoker{
InvokeFn: func(ctx context.Context, req *Request) (*Response, error) {
return &Response{Status: "success"}, nil
},
}
processor := NewProcessor(mock)
// ... test with mock
}Test Double Types
| Type | Purpose | When to Use |
|---|---|---|
| Stub | Returns canned data | Simple happy path |
| Mock | Verifies interactions | Behavior verification |
| Fake | Working implementation | Integration-like tests |
| Spy | Records calls | Interaction counting |
---
Documentation Standards
✅ Godoc Format
Document all exported symbols with a comment directly above the declaration:
// Registry manages agent lifecycle and discovery.
// It is safe for concurrent use.
type Registry struct {
agents map[string]Agent
mu sync.RWMutex
}
// Get returns the agent registered under the given key.
// It returns ErrNotFound if no agent is registered with that key.
func (r *Registry) Get(key string) (Agent, error) {
// ...
}Rules:
- Comment starts with the name of the symbol
- First sentence is a complete summary (used by
go doc -short) - Use
//comments, not/* */blocks (except for package-level docs)
✅ Package-Level Comments
For packages with significant public API, use a doc.go file:
// Package registry provides agent lifecycle management
// including registration, discovery, and health monitoring.
//
// Basic usage:
//
// reg := registry.New()
// reg.Register("my-agent", agent)
// a, err := reg.Get("my-agent")
package registryWhen to use `doc.go`:
- Package has 3+ exported symbols
- Package is part of a public API (
pkg/) - Package needs usage examples beyond a single line
✅ Testable Examples
Write Example* functions in _test.go files — they appear in generated docs and are compiled/run by go test:
func ExampleRegistry_Get() {
reg := registry.New()
reg.Register("agent-1", &MyAgent{Name: "alpha"})
agent, err := reg.Get("agent-1")
if err != nil {
log.Fatal(err)
}
fmt.Println(agent.Name)
// Output: alpha
}Naming Convention:
ExampleTypeName— type-level exampleExampleTypeName_MethodName— method-level exampleExample— package-level example
✅ Doc Generation
# View docs in terminal
go doc ./pkg/registry
go doc ./pkg/registry.Registry.Get
# Run local doc server (pkgsite)
go install golang.org/x/pkgsite/cmd/pkgsite@latest
pkgsite -open .✅ Interface Documentation
Interfaces define contracts — document the behavioral expectations, not just the signature:
// Store persists agent state across restarts.
//
// Implementations must be safe for concurrent use.
// All methods must respect context cancellation.
type Store interface {
// Save persists the agent. It returns ErrConflict if the agent
// was modified since it was last read (optimistic locking).
Save(ctx context.Context, agent *Agent) error
// Load retrieves an agent by ID. It returns ErrNotFound if
// no agent exists with the given ID.
Load(ctx context.Context, id string) (*Agent, error)
}Guidelines:
- Document concurrency guarantees on the interface comment
- Document error contracts on each method (which sentinel errors are returned)
- Document preconditions and postconditions when non-obvious
✅ Internal Package Documentation
internal/ packages cannot be imported outside the module, but still need documentation for team maintainability:
// Package repository implements data access for agent storage.
//
// This is an internal package — it should not be imported outside
// the module. Use pkg/client for the public API.
package repositoryGuidelines:
- Every
internal/package needs a package comment explaining its role - Note the public alternative if one exists (e.g.,
pkg/client) - Document non-obvious design constraints (e.g., "not safe for concurrent use")
✅ Package README Files
For packages with significant scope, include a README.md alongside the Go source:
pkg/registry/
├── README.md # Setup instructions, architecture notes
├── doc.go # Godoc package comment
├── registry.go # Implementation
└── registry_test.go # Tests with examplesWhen to include a README:
- Package requires setup steps (config, env vars, migrations)
- Package has architecture or design decisions worth explaining
- Package is a top-level entry point (
cmd/, majorpkg/packages)
README vs doc.go:
doc.go→ API usage shown ingo docoutputREADME.md→ Setup, architecture, diagrams, non-API context
✅ Comment Style
Follow Go's documentation conventions for consistent, tooling-friendly comments:
// ProcessBatch sends all queued events to the remote collector.
// It returns the number of events successfully delivered and
// a non-nil error if the connection to the collector fails.
//
// ProcessBatch is safe for concurrent use. Each call acquires
// a connection from the pool and releases it on return.
func (c *Client) ProcessBatch(ctx context.Context) (int, error) {
// ...
}
// ErrRateLimited is returned when the collector rejects a request
// due to rate limiting. Callers should back off and retry.
var ErrRateLimited = errors.New("rate limited")Rules:
- Write complete sentences with proper punctuation
- First word is the name of the declared thing (
ProcessBatch sends...,ErrRateLimited is...) - First sentence stands alone as a summary —
go doc -shortshows only this - Use third-person declarative ("ProcessBatch sends...") not imperative ("Send...")
- Separate paragraphs with a blank
//line - Use
[Registry.Get]syntax (Go 1.19+) to link to other symbols in doc comments - Keep line length under 80 characters for readability in terminals
Anti-Patterns:
// BAD - Doesn't start with symbol name
// This function processes a batch of events.
func (c *Client) ProcessBatch(ctx context.Context) (int, error)
// BAD - Not a complete sentence
// process batch
func (c *Client) ProcessBatch(ctx context.Context) (int, error)
// BAD - Imperative instead of declarative
// Send all queued events to the remote collector.
func (c *Client) ProcessBatch(ctx context.Context) (int, error)ALWAYS / NEVER Rules
| Rule | Rationale |
|---|---|
| ALWAYS document exported types, functions, and methods | Required by revive linter, enables go doc |
| ALWAYS start doc comments with the symbol name | Standard godoc convention, enables tooling |
| ALWAYS write doc comments as complete sentences | Consistent style, readable in go doc output |
ALWAYS include // Output: in Example functions | Makes examples testable by go test |
| ALWAYS document interface contracts (thread-safety, errors, lifecycle) | Callers depend on the contract, not the implementation |
| NEVER document unexported symbols unless logic is non-obvious | Noise — internal code changes frequently |
NEVER use @param / @return javadoc-style annotations | Not idiomatic Go — godoc ignores them |
| NEVER duplicate the function signature in prose | Redundant — the signature is right below |
| NEVER use imperative voice in doc comments | Go convention is declarative third-person |
---
Structured Logging (slog)
✅ Use log/slog (Go 1.21+)
import "log/slog"
func main() {
// Production: JSON handler for log aggregation
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
slog.SetDefault(logger)
// Include correlation IDs for tracing
slog.Info("request processed",
"request_id", reqID,
"user_id", userID,
"duration_ms", duration.Milliseconds(),
)
}Handler Selection
| Environment | Handler | Use Case |
|---|---|---|
| Production | slog.JSONHandler | Elasticsearch, Loki, CloudWatch |
| Development | slog.TextHandler | Human-readable console output |
❌ Logging Anti-Patterns
| Pattern | Problem | Instead |
|---|---|---|
fmt.Println in library | Not parseable, no levels | Use slog.Info |
log.Printf | No structure | Use slog with attributes |
| Logging secrets | Security risk | Use ReplaceAttr to redact |
| Missing correlation ID | Can't trace requests | Always include request_id |
Talos check: PRE-007 detects fmt.Print* debug statements in non-CLI code.---
Benchmarking and Profiling
✅ Writing Benchmarks
func BenchmarkProcess(b *testing.B) {
data := setupTestData()
b.ResetTimer() // Exclude setup from timing
for i := 0; i < b.N; i++ {
Process(data)
}
}
// Memory allocation benchmark
func BenchmarkProcessAllocs(b *testing.B) {
data := setupTestData()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
Process(data)
}
}Running Benchmarks
# Run benchmarks
go test -bench=. -benchmem ./...
# Compare before/after
go test -bench=. -count=10 > old.txt
# make changes
go test -bench=. -count=10 > new.txt
benchstat old.txt new.txt✅ Profiling with pprof
import _ "net/http/pprof"
// Profiles available at:
// /debug/pprof/profile - CPU profile
// /debug/pprof/heap - Memory profile
// /debug/pprof/goroutine - Goroutine stacksAnalyze Profiles:
# CPU profile (30 seconds)
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
# Memory profile
go tool pprof http://localhost:6060/debug/pprof/heap
# Interactive commands
(pprof) top10 # Top 10 functions
(pprof) web # Open flame graph in browser---
Configuration Management
✅ Single Config Struct Pattern
type Config struct {
Server ServerConfig `yaml:"server"`
Database DatabaseConfig `yaml:"database"`
Log LogConfig `yaml:"log"`
}
type ServerConfig struct {
Port int `yaml:"port" env:"PORT"`
ReadTimeout time.Duration `yaml:"read_timeout"`
WriteTimeout time.Duration `yaml:"write_timeout"`
}
// Load with precedence: flags > env > file > defaults
func Load() (*Config, error) {
cfg := &Config{}
setDefaults(cfg)
if err := loadFromFile(cfg); err != nil {
return nil, fmt.Errorf("load config file: %w", err)
}
loadFromEnv(cfg)
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("validate config: %w", err)
}
return cfg, nil
}❌ Configuration Anti-Patterns
| Pattern | Problem | Instead |
|---|---|---|
| Global config var | Hard to test | Pass as dependency |
| Reading env in functions | Scattered config | Centralize in Load() |
| No validation | Runtime errors | Validate at startup |
| Secrets in config files | Security risk | Use env vars or vault |
---
HTTP API Standards
✅ API Versioning
mux := http.NewServeMux()
// Health endpoints (unversioned - K8s standard)
mux.HandleFunc("/health", healthHandler)
mux.HandleFunc("/healthz", healthHandler) // K8s liveness
mux.HandleFunc("/readyz", readyHandler) // K8s readiness
// API v1
mux.HandleFunc("/v1/webhook/gitlab", handler.ServeHTTP)
// API documentation
mux.HandleFunc("/openapi.json", openAPIHandler)✅ Health Endpoints
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"healthy"}`))
}
func readyHandler(w http.ResponseWriter, r *http.Request) {
if !dependenciesReady() {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte(`{"status":"not ready"}`))
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"ready"}`))
}✅ Server Configuration
server := &http.Server{
Addr: ":" + port,
Handler: loggingMiddleware(mux),
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
// Graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
go func() {
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("Server failed: %v", err)
}
}()
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
server.Shutdown(ctx)❌ HTTP API Anti-Patterns
| Pattern | Problem | Instead |
|---|---|---|
| Unversioned API | Breaking changes affect all | /v1/webhook/gitlab |
| No Health Endpoint | K8s can't probe | Add /health, /readyz |
| No OpenAPI Spec | Undocumented API | Serve OpenAPI 3.0 |
| No Timeout Config | Slow clients block | Set Read/Write timeouts |
| No Graceful Shutdown | Dropped requests | Catch signals, drain |
---
Kubernetes Operator Patterns
✅ Controller Reconciliation
func (r *MyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var resource myv1.MyResource
if err := r.Get(ctx, req.NamespacedName, &resource); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Handle deletion with finalizer
if !resource.DeletionTimestamp.IsZero() {
return r.handleDeletion(ctx, &resource)
}
// Add finalizer if not present
if !controllerutil.ContainsFinalizer(&resource, myFinalizer) {
controllerutil.AddFinalizer(&resource, myFinalizer)
if err := r.Update(ctx, &resource); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, nil
}
// State machine based on desired state
switch resource.Spec.DesiredState {
case myv1.StateActive:
return r.ensureActive(ctx, &resource)
case myv1.StateIdle:
return r.ensureIdle(ctx, &resource)
}
return ctrl.Result{}, nil
}Return Patterns
| Result | Meaning |
|---|---|
ctrl.Result{}, nil | Success, no requeue |
ctrl.Result{Requeue: true}, nil | Requeue immediately |
ctrl.Result{RequeueAfter: time.Minute}, nil | Requeue after duration |
ctrl.Result{}, err | Error, controller-runtime handles backoff |
❌ Operator Anti-Patterns
| Pattern | Problem | Instead |
|---|---|---|
| Status as Spec | Status is observed, not desired | Use Spec for desired |
| Missing Finalizer | Orphaned external resources | Add finalizer first |
| No Context Timeout | Hung operations | context.WithTimeout |
| Condition Storms | Triggers unnecessary watches | Update only on change |
| Direct Status Update | Conflicts with spec updates | Use r.Status().Update() |
---
Code Quality Metrics
See common-standards.md for universal coverage targets and testing principles.✅ golangci-lint Configuration
Minimum recommended linters:
# .golangci.yml
linters:
enable:
- errcheck # Check error returns
- govet # Go vet
- staticcheck # Advanced static analysis
- unused # Detect unused code
- gosimple # Simplification suggestions
- gocritic # Opinionated checks
- misspell # Spell checking
- errorlint # Error wrapping checks
- goimports # Auto-organize imports
- revive # Exported name checks
linters-settings:
gocyclo:
min-complexity: 10 # Cyclomatic complexity threshold📊 Complexity Thresholds
| Complexity Range | Status | Action |
|---|---|---|
| CC 1-5 (Simple) | ✅ Excellent | Maintain |
| CC 6-10 (OK) | ✅ Acceptable | Monitor |
| CC 11-15 (High) | ⚠️ Warning | Refactor recommended |
| CC 16+ (Very High) | ❌ Critical | Refactor required |
Refactoring Strategies:
- Strategy maps (replace switch statements)
- Guard clauses (early returns)
- Helper functions (extract validation)
- Interface composition
---
Anti-Patterns Avoided
See common-standards.md for universal anti-patterns across all languages.❌ No Naked Returns
// BAD
func bad() (err error) {
err = doSomething()
return // Naked return
}
// GOOD
func good() error {
err := doSomething()
return err // Explicit return
}❌ No init() Abuse
- No
init()functions with side effects - Configuration via constructors
- Explicit initialization with error handling
❌ No Panics in Library Code
- All errors returned via
errorinterface paniconly used in tests for assertion failures- No
panicin production paths
❌ No Global Mutable State
// BAD
var globalRegistry *Registry
// GOOD
type Server struct {
registry *Registry // Instance field
}❌ No Pointer to Interface
// BAD
func bad(agent *Agent) // Interface is already a reference
// GOOD
func good(agent Agent)❌ No Goroutine Leaks
// BAD - Goroutine never exits
go func() {
for {
work() // No way to stop
}
}()
// GOOD - Context-based cancellation
go func() {
for {
select {
case <-ctx.Done():
return
default:
work()
}
}
}()---
Compliance Assessment
Use letter grades + evidence, NOT numeric scores.
| Category | Assessment Criteria | Evidence Required |
|---|---|---|
| Error Handling | Custom errors, %w wrapping, documented ignores | Count proper wrappings, undocumented ignores |
| Interface Design | Accept interfaces, return structs, small interfaces | Count interfaces, methods per interface |
| Concurrency | Context propagation, WaitGroups, mutexes | Activities with context, race condition count |
| Security | Constant-time comparison, HMAC, replay prevention | Prescan P2 findings, hardcoded secrets count |
| Code Organization | Layered architecture, import grouping | Package structure review, import violations |
| Testing | Table-driven, helpers, mocks | Test pattern count, coverage percentage |
Grading Scale:
| Grade | Finding Threshold | Description |
|---|---|---|
| A+ | 0-2 minor findings | Exemplary - industry best practices |
| A | <5 HIGH findings | Excellent - strong practices |
| A- | 5-15 HIGH findings | Very Good - solid practices |
| B+ | 15-25 HIGH findings | Good - acceptable practices |
| B | 25-40 HIGH findings | Satisfactory - needs improvement |
| C+ | 40-60 HIGH findings | Needs Improvement - multiple issues |
| C | 60+ HIGH findings | Significant Issues - major refactoring |
| D | 1+ CRITICAL findings | Major Problems - not production-ready |
| F | Multiple CRITICAL | Critical Issues - complete rewrite |
Example Assessment:
| Category | Grade | Evidence |
|---|---|---|
| Error Handling | A- | 131 proper %w wrappings, 5 undocumented ignores, 0 %v issues |
| Interface Design | A+ | 9 small interfaces (avg 4 methods), proper composition |
| Concurrency | A | 24/24 activities use context, 0 race conditions (go test -race) |
| Security | A | 0 CRITICAL, 2 HIGH (P2 findings), timing-safe comparisons |
| OVERALL | A- (Excellent) | 12 HIGH, 34 MEDIUM findings |
---
Vibe Integration
Prescan Patterns
| Pattern | Severity | Detection |
|---|---|---|
| P13: Undocumented Error Ignores | HIGH | _ = without nolint:errcheck |
| P14: Error Wrapping with %v | MEDIUM | fmt.Errorf.*%v with error args |
| P15: golangci-lint Violations | HIGH | JSON output parsing |
Semantic Analysis
Deep validation includes:
- Error chain inspection (
errors.Is,errors.Asusage) - Interface segregation (ISP compliance)
- Goroutine lifecycle analysis
- Security vulnerability detection
JIT Loading
Tier 1 (Fast): Load ~/.agents/skills/standards/references/go.md (5KB) Tier 2 (Deep): Load this document (16KB) for comprehensive audit Override: Use .agents/validation/GO_*.md if project-specific standards exist
---
Additional Resources
---
Related: go-patterns.md for quick reference examples
JSON/JSONL Standards Catalog - Vibe Canonical Reference
Version: 1.0.0 Last Updated: 2026-01-21 Purpose: Canonical JSON/JSONL standards for vibe skill validation
---
Table of Contents
1. JSON Formatting 2. JSONL Format 3. Beads JSONL Schema 4. Configuration Files 5. JSON Schema 6. Tooling 7. Anti-Patterns 8. Code Quality Metrics 9. Prescan Patterns 10. Compliance Assessment
---
JSON Formatting
Standard Format
{
"name": "example",
"version": "1.0.0",
"config": {
"timeout": 30,
"retries": 3,
"enabled": true
},
"items": [
"first",
"second",
"third"
]
}Formatting Rules
| Rule | Example | Why |
|---|---|---|
| 2-space indent | "key": "value" | Readability |
| Double quotes only | "key" not 'key' | JSON spec |
| No trailing commas | ["a", "b"] | JSON spec |
| Trailing newline | File ends with \n | POSIX, git diffs |
| UTF-8 encoding | Always | Compatibility |
Key Naming Conventions
| Convention | Use For | Example |
|---|---|---|
camelCase | JavaScript/TypeScript | "apiVersion" |
snake_case | Python, beads | "issue_type" |
kebab-case | Avoid | - |
UPPER_CASE | Environment vars | "DATABASE_URL" |
Rule: Be consistent within a file. Match ecosystem convention.
---
JSONL Format
What is JSONL?
JSON Lines: one valid JSON object per line, newline-delimited.
{"id": "abc-123", "status": "open", "title": "First issue"}
{"id": "abc-124", "status": "closed", "title": "Second issue"}
{"id": "abc-125", "status": "open", "title": "Third issue"}When to Use
| Use JSONL | Use JSON |
|---|---|
| Append-only data | Single config |
| Streaming ingestion | Nested data |
| Line-by-line processing | Small datasets |
| Beads issues | API responses |
| Large datasets | Human-edited |
JSONL Rules
| Rule | Rationale |
|---|---|
| One object per line | Enables grep/head/tail |
| No trailing comma | Each line is complete |
| No array wrapper | Not [{...}, {...}] |
| Newline after last | Append-friendly |
| UTF-8, no BOM | Compatibility |
Processing JSONL
# Count records
wc -l issues.jsonl
# Filter by field
jq -c 'select(.status == "open")' issues.jsonl
# Extract field
jq -r '.title' issues.jsonl
# Pretty-print one record
head -1 issues.jsonl | jq .
# Append new record
echo '{"id": "new", "status": "open"}' >> issues.jsonl
# Convert JSON array to JSONL
jq -c '.[]' array.json > data.jsonl
# Convert JSONL to JSON array
jq -s '.' data.jsonl > array.json---
Beads JSONL Schema
Issue Record Schema
{
"id": "prefix-xxxx",
"title": "Issue title",
"status": "open",
"priority": 2,
"issue_type": "task",
"owner": "user@example.com",
"created_at": "2026-01-15T08:18:34.317984-05:00",
"created_by": "User Name",
"updated_at": "2026-01-15T08:42:39.253689-05:00",
"closed_at": null,
"close_reason": null,
"dependencies": []
}Field Reference
| Field | Type | Required | Values |
|---|---|---|---|
id | string | Yes | prefix-xxxx |
title | string | Yes | Brief description |
status | string | Yes | open, in_progress, closed |
priority | integer | Yes | 0-4 (0=critical) |
issue_type | string | Yes | task, bug, feature, epic |
owner | string | No | Email address |
created_at | string | Yes | ISO 8601 |
updated_at | string | Yes | ISO 8601 |
closed_at | string | No | ISO 8601 or null |
dependencies | array | No | Dependency objects |
Dependency Object
{
"issue_id": "prefix-child",
"depends_on_id": "prefix-parent",
"type": "parent-child",
"created_at": "2026-01-15T08:19:32.440350-05:00"
}---
Configuration Files
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"strict": true,
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}package.json
{
"name": "package-name",
"version": "1.0.0",
"description": "Brief description",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"test": "jest",
"lint": "eslint ."
},
"dependencies": {},
"devDependencies": {}
}VS Code settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true
}---
JSON Schema
Defining Schemas
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/config.schema.json",
"title": "Configuration",
"type": "object",
"required": ["name", "version"],
"properties": {
"name": {
"type": "string",
"description": "Project name",
"minLength": 1
},
"version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$"
},
"enabled": {
"type": "boolean",
"default": true
}
},
"additionalProperties": false
}Schema Validation
# Using ajv-cli
npx ajv validate -s schema.json -d config.json
# Using Python jsonschema
python -c "
import json
from jsonschema import validate
with open('schema.json') as s, open('config.json') as c:
validate(json.load(c), json.load(s))
"---
Tooling
Formatting
# jq - Format and validate
jq . config.json > formatted.json
# Prettier - Format with config
npx prettier --write '**/*.json'
# Python - Format
python -m json.tool config.jsonValidation
# jq - Check valid JSON
jq empty config.json && echo "Valid"
# Python - Check valid JSON
python -c "import json; json.load(open('config.json'))"
# Node - Check valid JSON
node -e "require('./config.json')"Editor Configuration
.editorconfig:
[*.json]
indent_style = space
indent_size = 2
insert_final_newline = true
charset = utf-8
[*.jsonl]
indent_style = space
indent_size = 0
insert_final_newline = true.prettierrc:
{
"tabWidth": 2,
"useTabs": false,
"trailingComma": "none",
"singleQuote": false
}---
Anti-Patterns
Deeply Nested Objects
Nesting beyond 4 levels indicates missing abstraction or flattening opportunity.
// BAD - 5 levels deep
{"config": {"server": {"auth": {"oauth": {"scopes": ["read"]}}}}}
// GOOD - flattened
{"auth_oauth_scopes": ["read"]}Inconsistent Key Naming
Mixing camelCase and snake_case within a single file breaks grep-ability and signals multiple authors without review.
Missing Schema References
JSON config files without a $schema field cannot be validated automatically. Always include $schema when a schema exists.
Magic Values Without Documentation
Undocumented numeric or string constants embedded in JSON (e.g., "timeout": 86400) should use descriptive keys or adjacent comments in the referencing code.
Oversized Arrays or Objects
Arrays with >1000 elements or objects with >100 keys in a single file suggest the data belongs in JSONL or a database, not a monolithic JSON file.
Duplicate Keys
JSON parsers silently drop earlier values when duplicate keys exist. This is always a bug.
---
Code Quality Metrics
Validation Thresholds
| Metric | Threshold | Severity |
|---|---|---|
| Schema coverage | 100% of config files have $schema | Warning |
| Nesting depth | ≤4 levels | Error above 4 |
| File size | ≤100KB per JSON file | Warning above 100KB |
| Key consistency | Single naming convention per file | Error if mixed |
| JSONL line validity | 100% lines parse | Error on any failure |
| Duplicate keys | 0 per file | Error |
Grading Impact
| Violation | Grade Impact |
|---|---|
| Parse failure | Automatic C |
| Nesting >4 levels | Cap at B+ |
| Mixed key naming | Cap at A- |
| Missing schema ref | -0.5 grade step |
| File >100KB | -0.5 grade step |
---
Prescan Patterns
Automated detection commands for CI or pre-commit validation.
P01: Nesting Depth Check
| Field | Value |
|---|---|
| Pattern | Nesting depth exceeds 4 levels |
| Detection | `jq '[paths \ |
| Severity | Error |
P02: Inconsistent Key Naming
| Field | Value |
|---|---|
| Pattern | Mixed camelCase and snake_case keys in same file |
| Detection | `jq '[paths \ |
| Severity | Error |
P03: Duplicate Keys
| Field | Value |
|---|---|
| Pattern | Same key appears twice in an object |
| Detection | python -c "import json,sys; json.load(open(sys.argv[1]),object_pairs_hook=lambda p: (_ for k,v in p if sum(1 for k2,_ in p if k2==k)>1).__next__())" file.json or use jq --jsonargs strict mode |
| Severity | Error |
P04: Missing Schema Reference
| Field | Value |
|---|---|
| Pattern | Config file lacks $schema field |
| Detection | jq 'has("$schema")' file.json — fails if result is false |
| Severity | Warning |
P05: Oversized Values
| Field | Value |
|---|---|
| Pattern | File exceeds 100KB or arrays exceed 1000 elements |
| Detection | stat -f%z file.json (macOS) or stat -c%s file.json (Linux) — fails if >102400; `jq '[.. \ |
| Severity | Warning |
---
Compliance Assessment
Use letter grades + evidence, NOT numeric scores.
Assessment Categories
| Category | Evidence Required |
|---|---|
| Formatting | jq validation, indentation, newlines |
| Schema | Validation errors, required fields |
| Key Naming | Consistency check |
| JSONL Integrity | Line count = record count |
Grading Scale
| Grade | Criteria |
|---|---|
| A+ | All files validate, 2-space, UTF-8, schema valid |
| A | Valid JSON, consistent formatting |
| A- | Minor formatting inconsistencies |
| B | Valid but poorly formatted |
| C | Parse errors |
Validation Commands
# Validate JSON
find . -name '*.json' -exec jq empty {} \; 2>&1 | grep -c "parse error"
# Should be 0
# Check indentation
jq . config.json | head -5
# JSONL: validate line count
wc -l data.jsonl
jq -c '.' data.jsonl | wc -l
# Should match
# JSONL: validate each line
while IFS= read -r line; do echo "$line" | jq empty; done < data.jsonlExample Assessment
## JSON/JSONL Standards Compliance
| Category | Grade | Evidence |
|----------|-------|----------|
| Formatting | A+ | 18/18 validate, 2-space |
| Schema | A+ | 1247/1247 records pass |
| Key Naming | A | Consistent snake_case |
| JSONL | A+ | Line count matches |
| **OVERALL** | **A+** | **0 findings** |---
Additional Resources
---
Related: Quick reference in Tier 1 json.md
Markdown Standards Catalog - Vibe Canonical Reference
Version: 1.0.0 Last Updated: 2026-01-21 Purpose: Canonical Markdown standards for vibe skill validation
---
Table of Contents
1. AI-Agent Optimization 2. Document Structure 3. Heading Conventions 4. Code Blocks 5. Tables 6. Links 7. Lists 8. Emphasis and Blockquotes 9. Validation 10. Compliance Assessment
---
AI-Agent Optimization
Principles
| Principle | Implementation | Why |
|---|---|---|
| Tables over prose | Use tables for comparisons | Parallel parsing, scannable |
| Explicit rules | ALWAYS/NEVER, not "try to" | Removes ambiguity |
| Decision trees | If/then logic in lists | Executable reasoning |
| Named patterns | Anti-patterns with names | Recognizable error states |
| Progressive disclosure | Quick ref → details JIT | Context window efficiency |
| Copy-paste ready | Complete examples | Reduces inference errors |
---
Document Structure
SKILL.md Template
# Skill Name
> **Triggers:** "phrase 1", "phrase 2", "phrase 3"
## Quick Reference
| Action | Command | Notes |
|--------|---------|-------|
| ... | ... | ... |
## When to Use
| Scenario | Action |
|----------|--------|
| Condition A | Do X |
| Condition B | Do Y |
## Workflow
1. Step one
2. Step two
3. Step three
## Common Errors
| Symptom | Cause | Fix |
|---------|-------|-----|
| Error message | Root cause | Solution |
## References
- [Reference 1](./references/detail1.md) - Load when needed
- [Reference 2](./references/detail2.md) - Load when neededReference Doc Template
# Reference: Topic Name
<!-- Load JIT when skill needs deep context -->
## Context
Brief overview of when this reference applies.
## Details
### Section 1
...
## Decision Tree
Is X true? ├─ Yes → Do A │ └─ Did A fail? → Try B └─ No → Do C
## Anti-Patterns
| Name | Pattern | Why Bad | Instead |
|------|---------|---------|---------|
| ... | ... | ... | ... |---
Heading Conventions
Hierarchy Rules
| Level | Use For | Example |
|---|---|---|
# | Document title (one per file) | # Style Guide |
## | Major sections | ## Installation |
### | Subsections | ### macOS Setup |
#### | Minor divisions (sparingly) | #### Homebrew |
NEVER:
- Skip heading levels (
#→###) - Use bold text as fake headings
- Start with
##(missing#title)
Heading Text
# Good - Title Case for Title
## Good - Sentence case for sections
### Good - Sentence case continues
# Bad - all lowercase title
## Bad - ALL CAPS SECTION
### Bad - Using: Colons: Everywhere---
Code Blocks
Language Hints (Required)
ALWAYS specify language for syntax highlighting:
````markdown
def hello():
print("world")````
Common Language Hints
| Language | Fence | Use For |
|---|---|---|
bash | `bash | Shell commands |
python | `python | Python code |
go | `go | Go code |
typescript | `typescript | TypeScript |
yaml | `yaml | YAML config |
json | `json | JSON data |
text | `text | Plain text, diagrams |
diff | `diff | Code diffs |
Command Output
$ kubectl get pods NAME READY STATUS RESTARTS AGE my-pod 1/1 Running 0 5m
---
Tables
When to Use
| Situation | Use Table? | Alternative |
|---|---|---|
| Comparing 3+ items | Yes | - |
| Key-value mappings | Yes | - |
| Command reference | Yes | - |
| Step-by-step | No | Numbered list |
| Narrative | No | Paragraphs |
| Two items only | No | Inline comparison |
Table Formatting
# Good - Aligned, readable
| Column A | Column B | Column C |
|----------|----------|----------|
| Value 1 | Value 2 | Value 3 |
# Bad - Misaligned
|Column A|Column B|Column C|
|-|-|-|
|Value 1|Value 2|Value 3|Table Cell Content
| Content Type | Formatting |
|---|---|
| Code/commands | Backticks: ` cmd ` |
| Emphasis | Bold: **required** |
| Links | Inline: [text](url) |
| Long text | Under 50 chars |
---
Links
Internal Links
# Good - Relative paths
[Guide](./other-doc.md)
# Good - Anchor links
[Code Blocks](#code-blocks)
# Bad - Absolute paths
[Guide](/Users/me/project/docs/guide.md)Reference Links
For repeated URLs:
See the [official docs][k8s-docs] for more info.
The [Kubernetes documentation][k8s-docs] covers this.
[k8s-docs]: https://kubernetes.io/docs/---
Lists
Unordered Lists
Use - consistently:
# Good
- Item one
- Item two
- Nested item
# Bad - Mixed markers
* Item one
+ Item two
- Item threeOrdered Lists
Use 1. for all items:
# Good - All 1s
1. First step
1. Second step
1. Third step
# Acceptable - Explicit numbering
1. First step
2. Second step
3. Third stepTask Lists
- [ ] Incomplete task
- [x] Completed task
- [ ] Another incomplete---
Emphasis and Blockquotes
Emphasis
| Purpose | Syntax | Example |
|---|---|---|
| Important terms | **bold** | required |
| File names, commands | ` backticks ` | config.yaml |
| Titles, emphasis | *italic* | optional |
| Keyboard keys | <kbd> | <kbd>Ctrl</kbd>+<kbd>C</kbd> |
NEVER use bold for:
- Entire paragraphs
- Headings (use
#) - Code (use backticks)
Callout Patterns
> **Note:** Supplementary information.
> **Warning:** Something that could cause issues.
> **Important:** Critical information.
> **Tip:** Helpful suggestion.---
Validation
markdownlint Configuration
# .markdownlint.yml
default: true
MD013:
line_length: 100
code_blocks: false
tables: false
MD033:
allowed_elements:
- kbd
- br
- details
- summary
MD034: false
MD004:
style: dash
MD003:
style: atxValidation Commands
# Lint Markdown files
npx markdownlint '**/*.md' --ignore node_modules
# Check links
npx markdown-link-check README.md
# Format with Prettier
npx prettier --write '**/*.md'---
Compliance Assessment
Use letter grades + evidence, NOT numeric scores.
Assessment Categories
| Category | Evidence Required |
|---|---|
| Structure | Heading hierarchy, single H1 |
| Formatting | markdownlint violations, code fence hints |
| Links | Broken link count, relative paths |
| AI Optimization | Table usage, explicit rules |
| Accessibility | Alt text, semantic markup |
Grading Scale
| Grade | Criteria |
|---|---|
| A+ | 0 errors, single H1, 100% code hints, 0 broken links |
| A | <5 warnings, good structure |
| A- | <15 warnings, mostly correct |
| B | <30 warnings |
| C | Significant issues |
Validation Commands
# Lint Markdown
npx markdownlint '**/*.md' --ignore node_modules
# Check heading hierarchy
grep -r "^# " docs/*.md | wc -l
ls docs/*.md | wc -l
# Should match (1 H1 per file)
# Code blocks without language
grep -rP '```\s*$' docs/ | wc -l
# Should be 0
# Check links
npx markdown-link-check docs/**/*.mdExample Assessment
## Markdown Standards Compliance
| Category | Grade | Evidence |
|----------|-------|----------|
| Structure | A+ | 47/47 single H1, 0 skipped |
| Formatting | A- | 18 warnings (MD013) |
| Links | A | 0 broken, 93% relative |
| AI Optimization | A | 85 tables, 23 decision trees |
| **OVERALL** | **A** | **18 MEDIUM findings** |---
Additional Resources
---
Related: Quick reference in Tier 1 markdown.md
Vibe Pattern Reference
Comprehensive pattern catalog for Talos validation.
Pattern Categories
| Category | Prefix | Phase | Description |
|---|---|---|---|
| Prescan | P1-P10 | Static | Fast, no LLM required |
| Quality | QUAL-xxx | Semantic | Code smells, patterns |
| Security | SEC-xxx | Semantic | OWASP, injection, auth |
| Architecture | ARCH-xxx | Semantic | Boundaries, coupling |
| Accessibility | A11Y-xxx | Semantic | WCAG, keyboard |
| Complexity | CMPLX-xxx | Both | Cyclomatic, cognitive |
| Semantic | SEM-xxx | Semantic | Names, docstrings |
| Performance | PERF-xxx | Semantic | N+1, leaks |
| Slop | SLOP-xxx | Semantic | AI artifacts |
---
Prescan Patterns (Static Detection)
Fast static analysis - no LLM required.
Supported Languages: Python, Go, Bash, TypeScript, JavaScript
P1: Phantom Modifications (CRITICAL)
What: Committed lines that don't exist in current file.
Why Critical: Indicates broken git workflow - changes were committed but then removed or lost.
Detection: Compare git show HEAD -- <file> with actual file content.
Fix: Re-commit or investigate git history.
---
P2: Hardcoded Secrets (CRITICAL)
What: API keys, passwords, tokens in source code.
Why Critical: Credential exposure leads to immediate compromise.
Detection:
- gitleaks scan
- Regex for common patterns (AWS keys, JWT, password=)
Patterns:
(password|secret|api_key|token)\s*[=:]\s*["'][^"']{8,}["']
AKIA[0-9A-Z]{16}
eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+Fix: Use environment variables or secrets manager.
---
P3: SQL Injection Patterns (CRITICAL)
What: String concatenation in SQL queries.
Why Critical: Direct path to data breach.
Detection:
(execute|query)\s*\(\s*f?["'].*\{.*\}
cursor\.(execute|query)\s*\([^)]*%Fix: Use parameterized queries.
---
P4: TODO/FIXME/Commented Code (HIGH)
What: TODO markers, FIXME, commented-out code blocks.
Why High: Incomplete work or tech debt markers.
Detection:
grep -E "TODO|FIXME|XXX|HACK|BUG"
grep -E "^\s*#\s*(def |class |if |for |while )" # Commented codeFix: Complete or remove with explanation.
---
P5: Cyclomatic Complexity (HIGH)
What: Functions with CC > 15.
Why High: Too complex to maintain safely.
Detection by Language:
| Language | Tool | Command |
|---|---|---|
| Python | radon | radon cc <file> -s -n E |
| Go | gocyclo | gocyclo -over 15 <file> |
| TypeScript | escomplex | escomplex <file> |
Thresholds:
- CC > 10: Warning
- CC > 15: Flag as complex
- CC > 20: Critical
Fix: Extract functions, simplify logic.
---
P6: Long Functions (HIGH)
What: Functions exceeding 50 lines.
Why High: Long functions are hard to test and maintain.
Detection: AST parsing, line counting.
Thresholds:
- Lines > 30: Warning
- Lines > 50: Flag
- Lines > 100: Critical
Fix: Extract helper functions.
---
P7: Cargo Cult Error Handling (HIGH)
What: Empty except blocks, pass-only handlers, bare except.
Why High: Swallowed errors hide bugs.
Detection by Language:
| Language | Pattern |
|---|---|
| Python | except: pass, except Exception: pass |
| Go | if err != nil { } (empty block) |
| Bash | shellcheck SC2181 |
Fix: Handle or propagate errors explicitly.
---
P8: Unused Imports/Functions (MEDIUM)
What: Imported modules or defined functions never used.
Why Medium: Dead code clutters codebase.
Detection: AST analysis, import tracking.
Fix: Remove unused code.
---
P9: Docstring Mismatches (MEDIUM)
What: Docstrings claiming behavior not implemented.
Why Medium: False security from lying documentation.
Detection: Match docstring claims vs implementation:
- "validates" but no raise/ValueError
- "encrypts" but no crypto imports
- "authenticates" but no token handling
Fix: Update docs or implement claimed behavior.
---
P10: Missing Error Handling (MEDIUM)
What: Operations that can fail without error handling.
Why Medium: Silent failures cause hard-to-debug issues.
Detection:
- File operations without try/except
- Network calls without timeout/retry
- Parsing without validation
Fix: Add appropriate error handling.
---
Semantic Patterns (LLM-Powered)
Deep analysis requiring semantic understanding.
Quality (QUAL-xxx)
| Code | Pattern | Severity |
|---|---|---|
| QUAL-001 | Dead code paths | MEDIUM |
| QUAL-002 | Inconsistent naming | MEDIUM |
| QUAL-003 | Magic numbers/strings | MEDIUM |
| QUAL-004 | Missing tests for complex code | HIGH |
| QUAL-005 | Copy-paste with variations | HIGH |
| QUAL-006 | Feature envy (method uses another class more) | MEDIUM |
| QUAL-007 | Primitive obsession | LOW |
| QUAL-008 | Long parameter lists | MEDIUM |
---
Security (SEC-xxx)
| Code | Pattern | Severity |
|---|---|---|
| SEC-001 | Injection (SQL, command, XSS, template) | CRITICAL |
| SEC-002 | Authentication bypass | CRITICAL |
| SEC-003 | Authorization missing/weak | CRITICAL |
| SEC-004 | Cryptographic weakness | HIGH |
| SEC-005 | Sensitive data exposure | HIGH |
| SEC-006 | Security theater (looks secure, isn't) | HIGH |
| SEC-007 | Insecure deserialization | HIGH |
| SEC-008 | SSRF/path traversal | HIGH |
| SEC-009 | Race conditions | MEDIUM |
| SEC-010 | Debug mode in production | MEDIUM |
---
Architecture (ARCH-xxx)
| Code | Pattern | Severity |
|---|---|---|
| ARCH-001 | Layer boundary violation | HIGH |
| ARCH-002 | Circular dependency | HIGH |
| ARCH-003 | God class/function | HIGH |
| ARCH-004 | Missing abstraction | MEDIUM |
| ARCH-005 | Inappropriate coupling | MEDIUM |
| ARCH-006 | Scalability concern | MEDIUM |
| ARCH-007 | Single point of failure | HIGH |
| ARCH-008 | Hardcoded configuration | MEDIUM |
| ARCH-009 | Missing retry/circuit breaker | MEDIUM |
| ARCH-010 | Synchronous where async needed | MEDIUM |
---
Accessibility (A11Y-xxx)
| Code | Pattern | Severity |
|---|---|---|
| A11Y-001 | Missing ARIA labels | HIGH |
| A11Y-002 | Keyboard navigation broken | CRITICAL |
| A11Y-003 | Color contrast insufficient | HIGH |
| A11Y-004 | Missing alt text | HIGH |
| A11Y-005 | Focus management issues | HIGH |
| A11Y-006 | Missing skip links | MEDIUM |
| A11Y-007 | Form labels missing | HIGH |
| A11Y-008 | Dynamic content not announced | MEDIUM |
| A11Y-009 | Touch target too small | MEDIUM |
| A11Y-010 | Motion without reduced-motion support | LOW |
---
Complexity (CMPLX-xxx)
| Code | Pattern | Severity | Threshold |
|---|---|---|---|
| CMPLX-001 | Cyclomatic complexity | HIGH | CC > 10 |
| CMPLX-002 | Cognitive complexity | HIGH | > 15 |
| CMPLX-003 | Nesting depth | MEDIUM | > 4 |
| CMPLX-004 | Parameter count | MEDIUM | > 5 |
| CMPLX-005 | File too long | MEDIUM | > 500 lines |
| CMPLX-006 | Class too large | MEDIUM | > 20 methods |
| CMPLX-007 | Inheritance depth | LOW | > 3 |
| CMPLX-008 | Fan-out too high | MEDIUM | > 10 dependencies |
---
Semantic (SEM-xxx)
| Code | Pattern | Severity |
|---|---|---|
| SEM-001 | Docstring lies | HIGH |
| SEM-002 | Misleading function name | HIGH |
| SEM-003 | Misleading variable name | MEDIUM |
| SEM-004 | Comment rot | MEDIUM |
| SEM-005 | API contract violation | HIGH |
| SEM-006 | Type annotation mismatch | MEDIUM |
| SEM-007 | Inconsistent return types | MEDIUM |
| SEM-008 | Side effects in getter | HIGH |
---
Performance (PERF-xxx)
| Code | Pattern | Severity |
|---|---|---|
| PERF-001 | N+1 query | HIGH |
| PERF-002 | Unbounded loop/recursion | CRITICAL |
| PERF-003 | Missing pagination | HIGH |
| PERF-004 | Resource leak | HIGH |
| PERF-005 | Blocking in async context | HIGH |
| PERF-006 | Inefficient algorithm | MEDIUM |
| PERF-007 | Repeated computation | MEDIUM |
| PERF-008 | Missing caching | LOW |
| PERF-009 | Large object in memory | MEDIUM |
| PERF-010 | Excessive logging | LOW |
---
Slop (SLOP-xxx)
| Code | Pattern | Severity |
|---|---|---|
| SLOP-001 | Hallucinated imports/APIs | CRITICAL |
| SLOP-002 | Cargo cult patterns | HIGH |
| SLOP-003 | Excessive boilerplate | MEDIUM |
| SLOP-004 | AI conversation artifacts | HIGH |
| SLOP-005 | Over-engineering | MEDIUM |
| SLOP-006 | Unnecessary abstractions | MEDIUM |
| SLOP-007 | Copy-paste from tutorials | MEDIUM |
| SLOP-008 | Sycophantic comments | LOW |
| SLOP-009 | Redundant type annotations | LOW |
| SLOP-010 | Verbose where concise works | LOW |
---
Severity Mapping
| Level | Definition | Action | Exit Code |
|---|---|---|---|
| CRITICAL | Security vuln, data loss, broken build | Block merge | 2 |
| HIGH | Significant quality/security gap | Fix before merge | 3 |
| MEDIUM | Technical debt, minor issues | Follow-up issue | 0 |
| LOW | Nitpicks, style preferences | Optional | 0 |
---
Tool Requirements
| Tool | Languages | Install |
|---|---|---|
| radon | Python | pip install radon |
| gocyclo | Go | go install github.com/fzipp/gocyclo/cmd/gocyclo@latest |
| shellcheck | Bash | brew install shellcheck |
| gitleaks | All | brew install gitleaks |
| eslint | JS/TS | npm install eslint |
Post-Verdict Actions (Steps 9 & 9.5)
Procedural detail extracted from SKILL.md so the billboard stays compact.
Step 9: Record Ratchet Progress
After council verdict: 1. If verdict is PASS or WARN:
- Run:
ao ratchet record vibe --output "<report-path>" 2>/dev/null || true - Suggest: "Run /post-mortem to capture learnings and complete the cycle."
2. If verdict is FAIL:
- Do NOT record ratchet progress.
- Extract ALL findings from the council report for structured retry context (group by category if >20):
Read the council report. For each finding, format as:
FINDING: <description> | FIX: <fix or recommendation> | REF: <ref or location>
Fallback for v1 findings (no fix/why/ref fields):
fix = finding.fix || finding.recommendation || "No fix specified"
ref = finding.ref || finding.location || "No reference"- Tell user to fix issues and re-run /vibe, including the formatted findings as actionable guidance.
Step 9.5: Feed Findings to Flywheel
If verdict is WARN or FAIL, persist reusable findings to .agents/findings/registry.jsonl and optionally mirror the broader narrative to a learning file.
Registry write rules:
- persist only reusable issues that should change future review or implementation behavior
- require
dedup_key, provenance,pattern,detection_question,checklist_item,applicable_when, andconfidence applicable_whenmust use the controlled vocabulary from the finding-registry contract- append or merge by
dedup_key - use the contract's temp-file-plus-rename atomic write rule
If a broader prose summary still helps, also write the existing anti-pattern learning file to .agents/learnings/YYYY-MM-DD-vibe-<target>.md. Skip both if verdict is PASS.
After the registry update, if hooks/finding-compiler.sh exists, run:
bash hooks/finding-compiler.sh --quiet 2>/dev/null || trueThis keeps the same-session post-mortem path synchronized with the latest reusable findings. session-end-maintenance.sh remains the idempotent backstop.
Vibe Report Formats
Output Files
| File | Purpose |
|---|---|
reports/vibe-report.json | Full JSON findings |
reports/vibe-junit.xml | CI integration (JUnit XML) |
.agents/assessments/{date}-vibe-validate-{target}.md | Knowledge artifact |
---
Vibe Report Markdown Template
Used by Step 7 in SKILL.md. Write to .agents/council/YYYY-MM-DD-vibe-<target>.md.---
id: council-YYYY-MM-DD-vibe-<target-slug>
type: council
date: YYYY-MM-DD
---
# Vibe Report: <Target>
**Files Reviewed:** <count>
## Complexity Analysis
**Status:** Completed | Skipped (<reason>)
| File | Score | Rating | Notes |
|------|-------|--------|-------|
| src/auth.py | 15 | C | Consider breaking up |
| src/utils.py | 4 | A | Good |
**Hotspots:** <list files with C or worse>
**Skipped reason:** <if skipped, explain why - e.g., "radon not installed">
## Council Verdict: PASS / WARN / FAIL
| Judge | Verdict | Key Finding |
|-------|---------|-------------|
| Error-Paths | ... | ... (with spec — code-review preset) |
| API-Surface | ... | ... (with spec — code-review preset) |
| Spec-Compliance | ... | ... (with spec — code-review preset) |
| Judge 1 | ... | ... (no spec — 2 independent judges) |
| Judge 2 | ... | ... (no spec — 2 independent judges) |
| Judge 3 | ... | ... (no spec — 2 independent judges) |
## Shared Findings
- ...
## CRITICAL Findings (blocks ship)
- ... (findings that indicate correctness, security, or data-safety issues)
## INFORMATIONAL Findings (include in PR body)
- ... (style suggestions, minor improvements, suppressed/downgraded items)
## Concerns Raised
- ...
## All Findings
> Included when `--deep` or `--sweep` produces a sweep manifest. Lists ALL findings
> from explorer sweep + council adjudication. Grouped by category if >20 findings.
| # | File | Line | Category | Severity | Description | Source |
|---|------|------|----------|----------|-------------|--------|
| 1 | ... | ... | ... | ... | ... | sweep / council |
## Recommendation
For performance-sensitive code, run `/perf profile <target>` to identify optimization opportunities.
<council recommendation>
## Decision
[ ] SHIP - Complexity acceptable, council passed
[ ] FIX - Address concerns before shipping
[ ] REFACTOR - High complexity, needs rework---
JSON Report Structure
{
"summary": {
"critical": 0,
"high": 2,
"medium": 5,
"low": 1,
"total": 8
},
"prescan": [
{
"id": "P4",
"pattern": "Invisible Undone",
"severity": "HIGH",
"file": "services/auth/main.py",
"line": 42,
"message": "TODO marker"
}
],
"semantic": [
{
"id": "FAITH-001",
"category": "docstrings",
"severity": "HIGH",
"file": "services/auth/main.py",
"function": "validate_token",
"message": "Docstring claims validation but no raise/return False"
}
]
}---
JUnit XML Format
For CI integration:
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="vibe-validate" tests="8" failures="7" errors="1">
<testsuite name="prescan" tests="3" failures="3">
<testcase name="P4-services/auth/main.py:42" classname="prescan.invisible_undone">
<failure message="TODO marker" type="HIGH"/>
</testcase>
</testsuite>
<testsuite name="semantic" tests="5" failures="4">
<testcase name="FAITH-001-validate_token" classname="semantic.docstrings">
<failure message="Docstring mismatch" type="HIGH"/>
</testcase>
</testsuite>
</testsuites>---
Assessment Artifact Format
Saved to .agents/assessments/:
---
date: 2025-01-03
type: Assessment
assessment_type: vibe-validate
scope: recent
target: HEAD~1..HEAD
status: PASS_WITH_WARNINGS
severity: HIGH
findings:
critical: 0
high: 2
medium: 5
low: 1
total: 8
tags: [assessment, vibe-validate, validation, recent]
---
# Vibe Validation: recent
## Summary
| Severity | Count |
|----------|-------|
| CRITICAL | 0 |
| HIGH | 2 |
| MEDIUM | 5 |
| LOW | 1 |
## Critical Findings
None.
## High Findings
1. **P4** `services/auth/main.py:42` - TODO marker
2. **FAITH-001** `validate_token()` - Docstring mismatch
## Recommendations
1. Complete or remove TODO at services/auth/main.py:42
2. Update validate_token() docstring to match implementation---
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success, no CRITICAL findings |
| 1 | Argument/usage error |
| 2 | CRITICAL findings detected |
| 3 | HIGH findings detected (no CRITICAL) |
Test Pyramid Inventory (Step 2g, MANDATORY)
Assess test coverage against the test pyramid standard (the test pyramid standard (loaded via /standards)).
Read skills/validate/references/test-pyramid-weighting.md for test pyramid weighting — L3+ tests found all production bugs, weight them 5x.
Test Pyramid Weighting: Weight test coverage by level: L0–L1 at 1x, L2 at 3x, L3+ at 5x. Unit-only coverage is a WARN signal, not a PASS. See references/test-pyramid-weighting.md.
Run even in `--quick` mode — this is cheap (file existence checks) and high-signal.
1. Identify changed modules from git diff or target scope 2. For each changed module, check coverage pyramid (L0–L3):
- L0: Does a contract/spec enforcement test cover this module?
- L1: Does a unit test file exist for this module?
- L2: If module crosses boundaries, does an integration test exist?
3. For boundary-touching code, check bug-finding pyramid (BF1–BF5):
- BF4 (Chaos): Do external call sites have failure injection tests?
- BF1 (Property): Do data transformations have property tests?
- BF2 (Golden): Do output generators have golden file tests?
4. Compute weighted pyramid score for changed code paths:
Formula:
weighted_score = (L0_count x 1 + L1_count x 1 + L2_count x 3 + L3_count x 5 + L4_count x 5) / max_possibleWhere max_possible = total_test_count x 5 (the score if every test were L3+).
Count tests at each level for changed code paths:
- L0: Build/compile checks (weight 1)
- L1: Unit tests (weight 1)
- L2: Integration tests (weight 3)
- L3: E2E/system tests (weight 5)
- L4: Smoke/fresh-context tests (weight 5)
Interpretation:
weighted_score >= 0.6— strong pyramid, L2+ tests present0.3 <= weighted_score < 0.6— acceptable, but recommend more integration testsweighted_score < 0.3AND all tests are L0-L1 only — WARN: unit-only test coverage (feeds into vibe verdict as a WARN signal, not a separate gate)
Satisfaction exposure: The weighted_score is also exposed as satisfaction_score (with source "test-pyramid-weighted") in the test_pyramid output block AND promoted to the top-level verdict JSON as satisfaction_score (verdict schema field, skills/council/schemas/verdict.json: number 0.0-1.0, "Probabilistic satisfaction score (0.0 = unsatisfied, 1.0 = fully satisfied). Optional — absent means not computed."). Downstream consumers (e.g., /validate STEP 1.8 holdout evaluation) can use satisfaction_score as a normalized quality signal.
Include in council packet and vibe report output:
## Test Pyramid Score
| Level | Count | Weight | Contribution |
|-------|-------|--------|--------------|
| L0 | 2 | 1x | 2 |
| L1 | 8 | 1x | 8 |
| L2 | 0 | 3x | 0 |
| L3 | 0 | 5x | 0 |
| L4 | 0 | 5x | 0 |
| **Total** | **10** | | **10 / 50 = 0.20** |
WARN: weighted_score 0.20 < 0.3 and all tests are L0-L1 only5. Build coverage table and include in council packet as context.test_pyramid:
"test_pyramid": {
"coverage": {
"L0": {"status": "pass", "files": ["test_spec_enforcement.py"]},
"L1": {"status": "pass", "files": ["test_module.py"]},
"L2": {"status": "gap", "reason": "crosses subsystem boundary, no integration test"}
},
"bug_finding": {
"BF4_chaos": {"status": "gap", "reason": "external API calls without failure injection"},
"BF1_property": {"status": "na", "reason": "no data transformations in scope"}
},
"weighted_score": 0.20,
"satisfaction_score": 0.20,
"satisfaction_source": "test-pyramid-weighted",
"score_breakdown": {"L0": 2, "L1": 8, "L2": 0, "L3": 0, "L4": 0},
"max_possible": 50,
"warn_unit_only": true,
"verdict": "WARN: weighted_score 0.20 < 0.3, all tests L0-L1 only"
}Verdict rules:
weighted_score < 0.3AND all tests L0-L1 only — WARN: unit-only coverage (include in council findings)- Missing L1 on feature code — WARN (include in council findings)
- Missing L0 on spec-changing code — WARN
- Missing BF4 on boundary code — WARN (advisory, not blocking)
- All levels covered with
weighted_score >= 0.6— no mention needed
When coverage gaps are found, run /test <module> to generate test candidates for uncovered code.
# Executable spec for the /validate skill — the unified validator (driving-adapter).
# /validate takes any artifact (plan, spec, code, PR, fitness gate) and emits a verdict.v1:
# PASS, WARN, or FAIL with rationale and findings. The artifact shape is selected by --mode,
# and the mode set is budget-capped. Hexagon: driving-adapter; consumes validation; produces
# result.json; customer-of validation. (soc-qk4b)
Feature: Validate produces a PASS/WARN/FAIL verdict for any artifact
As the unified validator
I want any artifact judged to a verdict.v1 with rationale and findings
So that plans, code, PRs, and gates all share one verdict contract
Scenario: an artifact is validated to a verdict
When /validate runs on an artifact (plan, spec, code, PR, or fitness gate)
Then it emits a PASS, WARN, or FAIL verdict with rationale and findings
And the verdict conforms to the verdict.v1 schema
Scenario: the mode selects the validation shape
When /validate runs with --mode
Then --mode=pr produces a PR-shape verdict (diff review + acceptance check)
And --mode=pre-impl --target=fitness validates the fitness gate against GOALS.md
Scenario: the mode set is budget-capped
Then /validate exposes at most 8 modes
And adding a 9th requires demoting an existing mode or refusing the addition
Related skills
FAQ
Can the code author validate their own change?
No. The acceptance verdict must be produced by a blind, context-isolated sub-agent judge that did not author the code; it refuses to certify when judge equals author.
What modes does it offer?
Eight modes, from default 2-judge consensus and --quick single-agent to --deep 4-judge, --mixed cross-vendor, --debate, plus --mode=post-impl, pre-impl, and pr.