Skip to content
Santekno.com | Level Up Your Engineering Skills
EN
📖 0%
04 Sep 2026 · 16 min read ·Article 38 / 208
Go

Validating Implementation Against the Spec: Automated Audit in Golang

A complete guide to using specify audit for automated validation of a Golang implementation against its specification. Covers continuous validation, audit modes, custom rules, and CI integration.

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

Learning to validate a Golang implementation against its spec with an automated audit is what turns a specification from passive documentation into an active test suite. In Article 15 we met specify audit as a debugging tool. But audit is at its most powerful when it runs as continuous validation — a regular part of the workflow, not something you reach for only when something breaks.

In this article we go deep on audit: how it works internally, every mode it offers, how to write custom rules, and how to build a reliable quality gate that catches deviation at every stage of a feature’s life.


18.1 Philosophy: Spec as an Executable Test

In TDD, the test is the executable specification. In SDD with Spec Kit, spec.md + specify audit is the equivalent at a higher level. The comparison below places the two loops side by side.

text
1TDD Flow:
2  Write a failing test -> Implement -> Make the test pass -> Refactor
3
4SDD + Audit Flow:
5  Write a spec -> Implement -> Run audit -> Fix deviation -> Re-audit

The key difference is what each verifies: TDD checks code at the unit level, while an SDD audit checks code against business requirements. They are complementary, not competing — you want both.


18.2 How Audit Works Internally

Understanding the pipeline demystifies the score and helps you trust (or challenge) it. The stages below outline what happens from parsing the spec to producing a final report.

text
 1specify audit pipeline:
 2
 3Step 1: Parse spec.md
 4  -> Extract the AC list (AC1-AC16)
 5  -> Extract the EC list (EC1-EC3)
 6  -> Extract the NFRs
 7
 8Step 2: Build context
 9  -> Read CLAUDE.md + constitution.md
10  -> Read the existing code structure
11
12Step 3: Per-AC analysis
13  For each AC:
14    a. AI reasoning: which layer should handle this AC?
15    b. Static code scan: grep + AST analysis
16    c. Test coverage check: is there a test for this AC?
17    d. Response format check: HTTP status + error code match?
18    e. Result: PASS | FAIL | WARNING | PARTIAL | ACKNOWLEDGED
19
20Step 4: Architecture check
21  -> Cross-layer import detection
22  -> Error propagation pattern
23  -> Context propagation
24
25Step 5: Generate the report + score

The important takeaway is that audit blends static analysis with AI reasoning: grep and AST catch the mechanical checks, while the LLM reasons about which layer should satisfy each acceptance criterion. That combination is what lets it validate intent, not just syntax.


18.3 All Audit Modes

Audit is not one command but a family of modes tuned for different moments — a 30-second pre-commit check, a full CI run, a machine-readable score for scripting. The commands below cover all ten.

bash
 1# Mode 1: Basic (ACs only, ~3 min)
 2specify audit --feature product-review
 3
 4# Mode 2: Comprehensive (AC + EC + NFR + Architecture, ~6 min)
 5specify audit --feature product-review --comprehensive
 6
 7# Mode 3: Fast (static analysis only, no LLM, ~30s)
 8specify audit --feature product-review --fast
 9
10# Mode 4: Diff audit (only changes since a given commit)
11specify audit --feature product-review --since-commit abc123
12
13# Mode 5: Watch mode (re-run when files change)
14specify audit --feature product-review --watch
15
16# Mode 6: All features
17specify audit --all
18
19# Mode 7: Score only (for CI scripting)
20specify audit --feature product-review --score --format number
21
22# Mode 8: JSON output
23specify audit --feature product-review --output json > audit.json
24
25# Mode 9: GitHub-native summary
26specify audit --feature product-review --output github-summary
27
28# Mode 10: Executive summary
29specify audit --feature product-review --output executive-summary

The practical point is to match the mode to the moment: --fast for a pre-commit hook where you want speed and zero cost, --comprehensive for CI where thoroughness matters, and --score --format number when a shell script needs a single integer to gate on.


18.4 The Comprehensive Audit Report

A comprehensive run produces a detailed, per-AC report with file and line references. The example below shows what a real report looks like, including a failing AC and its suggested fix.

text
 1specify audit --feature product-review --comprehensive
 2
 3# Summary output:
 4# ========================================
 5# Spec Audit Report: product-review
 6# ========================================
 7#
 8# ACCEPTANCE CRITERIA (16 total)
 9# AC1  PASS  POST endpoint — router.go:45
10# AC2  PASS  Purchase verify — create_review.go:67
11# AC3  PASS  Status PUBLISHED — entity.go:12
12# AC4  PASS  Atomic TX — review_repository.go:234
13# AC5  PASS  Response 201 — handler.go:98
14# AC6  PASS  GET + pagination — handler.go:134
15# AC7  PASS  Avg rating in response — list_reviews.go:89
16# AC8  PASS  Admin-only DELETE — delete_review.go:45
17# AC9  PASS  Soft delete — review_repository.go:456
18# AC10 PASS  Rating recalculate — review_repository.go:478
19# AC11 PASS  Rating 1-5 validation — entity.go:67
20# AC12 FAIL  Content length validation — MISSING
21#         entity.go only checks empty, not length
22#         Action: Add len(r.Content) <= 1000 to validate()
23# AC13 PASS  PURCHASE_REQUIRED — handler.go:145 (exact match)
24# AC14 PASS  ALREADY_REVIEWED — handler.go:157
25# AC15 PASS  404 NOT_FOUND — handler.go:167
26# AC16 PASS  403 FORBIDDEN — handler.go:177
27#
28# AC Score: 15/16 (93.8%)
29#
30# EDGE CASES: 2/3 + 1 acknowledged (100% effective)
31# ARCHITECTURE: 3/4 (context timeout missing in 3 repo methods)
32# NFR: 0/1 (missing pagination index)
33#
34# OVERALL: 83.2/100 — WARNING (threshold: 85)
35#
36# Priority fixes:
37# AC12: content length (10 min)
38# NFR: pagination index (15 min)
39# Arch: context timeout (30 min, 3 locations)

What makes this report actionable rather than just informative is the specificity: AC12 isn’t merely “failed” — it points at entity.go, explains that only emptiness is checked, and hands you the exact fix. The prioritized list at the end even estimates the effort, so you know where to spend the next 30 minutes.


18.5 Custom Audit Rules

General audits can’t know your project’s private conventions, so Spec Kit lets you add custom rules. The configuration below defines four — from error-code casing to test-naming — each with a severity and a check type.

yaml
 1# .speckit-config.yaml
 2
 3audit:
 4  custom_rules:
 5    - name: "error-code-uppercase"
 6      description: "All error codes must be UPPERCASE_SNAKE_CASE"
 7      severity: "critical"
 8      check_type: "grep"
 9      pattern: '"error":\s*"[a-z]'
10      target: "internal/delivery/http/handler/"
11      fail_message: "Found a lowercase error code in a handler"
12
13    - name: "no-float64-money"
14      description: "No float64 for monetary values"
15      severity: "critical"
16      check_type: "grep"
17      pattern: 'float64.*[Pp]rice|float64.*[Aa]mount|float64.*[Cc]ents'
18      target: "internal/"
19
20    - name: "context-timeout-required"
21      description: "Repository methods must use a context timeout"
22      severity: "warning"
23      check_type: "script"
24      script: |
25        find internal/repository -name "*.go" -not -name "*_test.go" | \
26        xargs grep -L "context.WithTimeout\|context.WithDeadline" || true
27      fail_on_output: true
28
29    - name: "test-naming-convention"
30      description: "Test functions must follow: Test{Subject}_{Scenario}"
31      severity: "info"
32      check_type: "regex"
33      pattern: "func Test[A-Z][a-zA-Z]+_[A-Z][a-zA-Z]+"
34      target: "internal/**/*_test.go"
35      check_mode: "all_match"

Custom rules are how you encode the conventions a general audit can’t infer — the no-float64-money rule, for instance, enforces a constitution decision that no generic linter would ever catch. Tag each with a severity so critical rules fail the build while info-level rules merely report.


18.6 Audit Scoring Configuration

You control how the score is composed and where the thresholds sit. The configuration below sets category weights, pass/warn/fail thresholds, and documents an acknowledged edge case.

yaml
 1# .speckit-config.yaml
 2
 3audit:
 4  scoring:
 5    ac_weight: 50
 6    ec_weight: 25
 7    architecture_weight: 15
 8    nfr_weight: 10
 9
10    pass_threshold: 90
11    warning_threshold: 85
12    fail_threshold: 85
13
14  # Documented exceptions
15  acknowledged_ecs:
16    - ec_id: "EC3"
17      reason: "Product cascade: reviews orphaned when a product is deleted (Out of Scope per spec)"
18      acknowledged_by: "@budi"
19      acknowledged_date: "2026-07-02"

The acknowledged_ecs block is the important detail: it lets you formally document a deliberate deviation with an owner and a date, so it counts as a decision rather than a bug. That is very different from silently ignoring a failing check.


18.7 Audit in a Pre-commit Hook

The cheapest place to catch obvious gaps is before the code ever leaves the developer’s machine. The pre-push hook below runs a fast, LLM-free audit on feature branches and blocks the push on failure.

bash
 1# .git/hooks/pre-push
 2#!/bin/bash
 3BRANCH=$(git rev-parse --abbrev-ref HEAD)
 4
 5if [[ "$BRANCH" == feature/* ]]; then
 6    FEATURE=$(echo "$BRANCH" | sed 's/feature\/[A-Z0-9]*-//')
 7
 8    if [ -d ".specify/features/$FEATURE" ]; then
 9        echo "Fast spec audit for $FEATURE..."
10        specify audit --feature "$FEATURE" --fast
11
12        if [ $? -ne 0 ]; then
13            echo "Spec audit failed. Run: specify audit --feature $FEATURE"
14            exit 1
15        fi
16        echo "Fast audit passed"
17    fi
18fi

Installing the hook is a one-liner, shown below.

bash
1# Install the hook automatically
2specify hooks install --hook pre-push

Because the fast mode uses only static analysis, this gate is free and near-instant — it catches the obvious misses (a whole missing endpoint, a lowercase error code) before they ever reach CI, which keeps the expensive LLM-powered checks focused on subtler issues.


18.8 Audit-Driven Development

You can flip audit around and use it as the driver of implementation, exactly like TDD uses failing tests. The loop below shows the red-to-green progression driven by re-running audit.

bash
 1# 1. Write the spec
 2specify feature
 3
 4# 2. Audit immediately (before implementing) -> everything fails
 5specify audit --feature product-review
 6# Output: 0/16 ACs (0%)
 7
 8# 3. Implement the first task
 9specify implement --task 01
10
11# 4. Re-audit -> progress is visible
12specify audit --feature product-review
13# Output: 2/16 ACs (12.5%)
14
15# 5. Continue to 100%
16# Just like TDD: red -> green -> refactor

Audit-Driven Development gives you the same psychological loop as TDD but at the requirement level — the score climbing from 0/16 toward 16/16 is your progress bar, and “done” has an unambiguous, measurable definition.


18.9 Phased Release Support

Sometimes you ship a feature in phases, and the audit should validate only the ACs in the current phase. The configuration and command below scope an audit to Phase 1’s subset of criteria.

yaml
 1# .speckit-config.yaml
 2audit:
 3  phases:
 4    phase_1:
 5      included_acs: ["AC1", "AC2", "AC3", "AC5", "AC11", "AC13"]
 6      excluded_acs: ["AC4", "AC6", "AC7", "AC8", "AC9", "AC10", "AC12", "AC14"]
 7      threshold: 95
 8
 9    phase_2:
10      included_acs: all
11      threshold: 90

With the feature audited, the command below narrows the audit to a single phase for a focused check.

bash
1# Audit Phase 1 only
2specify audit --feature product-review --phase 1
3# Output: 6/6 ACs (100%) PASS — Phase 1 ready for release
4# Note: 10 ACs deferred to Phase 2

Phased auditing lets you ship a subset of a feature honestly — Phase 1 can hit 100% of its own scope and pass, while the deferred ACs are explicitly recorded rather than silently missing. It reconciles incremental delivery with strict validation.


18.10 Audit Evidence Trail

Audits are worth keeping, because their history tells a story of quality over time. The commands below save full evidence per run and let you review the trail.

bash
 1# Save an audit with full evidence
 2specify audit --feature product-review --save-evidence
 3
 4# Output at:
 5# .specify/audit/product-review/2026-07-02-14-30.json
 6# .specify/audit/product-review/2026-07-02-14-30.md
 7# .specify/audit/product-review/latest.json
 8
 9# View the history
10specify audit --history --feature product-review
11# 2026-07-02 14:30  Score: 83.2  WARNING (pre-fix)
12# 2026-07-02 16:00  Score: 96.4  PASS    (post-fix)
13# 2026-07-03 09:00  Score: 97.1  PASS    (CI verify)

An evidence trail turns quality into something auditable after the fact — you can point to the exact run where a feature crossed the threshold, which is invaluable for compliance reviews and for understanding when and why a regression crept in.


18.11 Scheduled Weekly Audit

Spec debt accumulates quietly, so it pays to sweep the whole repo on a schedule. The workflow below runs a comprehensive audit every Monday and opens an issue if scores have degraded.

yaml
 1# .github/workflows/weekly-audit.yml
 2on:
 3  schedule:
 4    - cron: '0 8 * * MON'
 5
 6jobs:
 7  weekly-audit:
 8    steps:
 9      - run: specify audit --all --comprehensive --output markdown > weekly-report.md
10
11      - name: Check for degradation vs last week
12        run: |
13          python3 detect_degradation.py weekly-scores.json prev-scores.json
14
15      - name: Create an issue if degraded
16        if: failure()
17        run: |
18          gh issue create \
19            --title "Weekly Audit: Score Degradation" \
20            --body "$(head -100 weekly-report.md)" \
21            --label "spec-debt,priority-high"

The degradation check is the clever part: a weekly full audit that automatically files an issue when scores slip means spec debt gets surfaced and triaged before it compounds, rather than being discovered months later during an incident.


18.12 Audit for Legacy Code

Audit isn’t only for new features — it can characterize existing code by first reverse-engineering a spec. The commands below generate a spec from code and then measure the resulting technical debt.

bash
1# Create a spec from existing code (reverse engineering)
2specify reverse-spec --from-code internal/usecase/order/cancel_order.go
3
4# The audit produces a technical-debt list
5specify audit --feature cancel-order
6# Score: 72/100 — shows the technical debt that needs to be addressed

Running audit against reverse-engineered specs is a pragmatic way to put a number on legacy technical debt — a 72/100 gives you a concrete, prioritizable target instead of a vague sense that “this old code needs work.”


18.13 Audit Report as a PR Artifact

Audit results are most useful where developers already look: the pull request. The workflow steps below upload the report as an artifact and post the score as a PR comment.

yaml
 1# Upload the audit report to GitHub Actions
 2- name: Upload audit report
 3  uses: actions/upload-artifact@v4
 4  with:
 5    name: spec-audit-${{ github.run_number }}
 6    path: .specify/audit/*/latest.md
 7
 8# Comment on the PR
 9- name: Comment the audit score
10  uses: actions/github-script@v7
11  with:
12    script: |
13      const score = '${{ steps.audit.outputs.score }}';
14      const status = parseInt(score) >= 90 ? 'PASS' : 'WARN';
15      github.rest.issues.createComment({
16        issue_number: context.issue.number,
17        owner: context.repo.owner,
18        repo: context.repo.repo,
19        body: `## Spec Audit\n\n${status} Score: **${score}/100**`
20      });

Surfacing the score directly on the PR is what makes audit part of the review conversation — the reviewer sees the compliance number next to the diff, so “does this meet the spec?” is answered before they even start reading code.


18.14 The ROI of Continuous Audit

It’s fair to ask whether continuous audit pays for itself, and Spec Kit can report the answer. The command below computes audit effectiveness over a period, including cost against deviations caught.

bash
 1specify metrics --audit-effectiveness --since "2026-01-01"
 2
 3# Output:
 4# Audit Effectiveness — Jan-Jul 2026
 5#
 6# Deviations caught by audit:     47
 7# Deviations that reached production:   3
 8#
 9# Cost of post-deploy fixes: 12 hours x $80 = $960
10# Cost of audit (tokens + time): ~$1,245
11# Net: -$285 (but: fewer regressions, faster review, better docs)
12#
13# Token ROI on post-deploy saves: 21.2x

The headline is not the raw dollar net but the ratio: catching 47 deviations before production versus 3 that slipped through is a 94% catch rate, and the token ROI on prevented post-deploy fixes alone is over 21x. The unmeasured benefits — faster reviews, better docs — make the case even stronger.


18.15 Audit in the Definition of Done

Audit belongs in the team’s Definition of Done, alongside tests and lint. The checklist below shows where the audit-score gate slots in.

markdown
 1## Team Definition of Done
 2
 3A feature is DONE when:
 4- [ ] All tasks checked in tasks.md
 5- [ ] go test -race passes
 6- [ ] Coverage >= 85%
 7- [ ] golangci-lint — 0 issues
 8- [ ] specify audit --score >= 90   <- added
 9- [ ] The PR description includes the audit score
10- [ ] CLAUDE.md updated if new patterns were introduced
11- [ ] Code review approved

Adding the audit score to the Definition of Done is what makes spec compliance non-negotiable — “done” now means “provably meets the spec,” not “the author believes it works.” It closes the gap between intent and verified reality.


18.16 Prometheus Integration for Dashboards

Audit scores can flow into your existing observability stack. The command below exports audit metrics in Prometheus format for scraping into Grafana.

bash
1# Export audit metrics for Grafana
2specify audit --all --output prometheus > /tmp/spec_metrics.txt
3
4# Output:
5# spec_audit_score{feature="product-review"} 96.4
6# spec_audit_score{feature="cancel-order"} 97.1
7# spec_audit_score{feature="flash-sale"} 82.3
8# spec_ac_compliance{feature="product-review"} 1.0
9# spec_coverage_pct{feature="product-review"} 89.4

Emitting Prometheus metrics lets spec compliance live on the same dashboards as latency and error rate — you can alert on a score dropping below threshold exactly as you would on any other production signal, making spec health a first-class operational concern.


18.17 Audit as an Onboarding Checkpoint

An audit run is also a surprisingly good comprehension test for new hires. The checkpoint below asks a new developer to run a comprehensive audit and explain the results.

markdown
 1## Developer Onboarding: Week 1 Spec Assessment
 2
 3### Task
 4Run: specify audit --feature cancel-order --comprehensive
 5
 6### Expected Outcomes
 7You can:
 8- [ ] Explain why each AC passed or failed
 9- [ ] Identify the root cause of a deviation
10- [ ] Suggest the correct fix
11- [ ] Understand the architecture-check results
12
13This proves you understand the codebase, not just that you read the code.

Using audit as an onboarding checkpoint is elegant because it tests understanding, not memorization — a developer who can explain why each AC passed or failed has genuinely internalized how the spec maps to the code.


18.18 Audit Quality Gate: Full Configuration

Putting it together, here is a CI gate that reads the score and decides pass, warn, or fail. The workflow step below encodes the three-band threshold logic.

yaml
 1# .github/workflows/spec-audit.yml
 2- name: Run the spec audit
 3  id: audit
 4  env:
 5    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
 6  run: |
 7    SCORE=$(specify audit --feature $FEATURE --score --format number)
 8    echo "score=$SCORE" >> $GITHUB_OUTPUT
 9
10    # Warning: 85-89
11    if [ "$SCORE" -ge "85" ] && [ "$SCORE" -lt "90" ]; then
12      echo "WARNING: Score $SCORE (below the recommended 90)"
13    fi
14
15    # Fail: below 85
16    if [ "$SCORE" -lt "85" ]; then
17      echo "FAIL: Score $SCORE < 85 minimum"
18      exit 1
19    fi
20
21    echo "PASS: Score $SCORE/100"

This gate is intentionally three-banded rather than binary: scores of 85-89 warn without blocking, while anything below 85 fails the build. That nuance prevents a healthy team from being blocked by a single borderline AC while still enforcing a hard floor on quality.


18.19 The Three-Layer Audit Strategy

The most robust setup runs audit at three levels, each with a different cost and depth. The breakdown below assigns each layer its role and estimates the total monthly cost.

Layer 1 — Pre-commit (30s, fast mode): Catch obvious problems before push. No LLM, free.

Layer 2 — CI per PR (5 min, full mode): Comprehensive AC + architecture + NFR check. LLM-powered, ~$0.05-0.10 per run.

Layer 3 — Weekly scheduled (15-30 min, all features): Detect accumulated spec debt. Auto-file an issue if degradation is detected.

text
1Cost breakdown (estimate per month, 10 features, 4 sprints):
2Layer 1: Free (static analysis)
3Layer 2: ~$2-5 (CI, every PR)
4Layer 3: ~$1-2 (weekly, all features)
5Total: ~$3-7/month
6
7Value: prevents post-deploy debugging that can cost 10-100x more

The strategy works because each layer catches what the previous one can’t afford to: pre-commit is free but shallow, CI is thorough but per-PR, and the weekly sweep catches drift across the whole repo. Together they cost a few dollars a month to prevent bugs that cost orders of magnitude more.


18.20 Summary

specify audit as continuous validation transforms a spec from passive documentation into an active test suite that runs throughout a feature’s lifecycle.

Three layers: pre-commit (fast, free), CI per PR (LLM-powered), and a weekly scheduled sweep (comprehensive, all features).

Custom rules enforce project-specific conventions that general rules can’t capture.

Acknowledged exceptions are the formal way to document a deliberate deviation — a decision, not a bypass.

ROI: 47 deviations caught pre-production versus 3 that reached production — a 21x token ROI on post-deploy fix savings alone.

In the next article, we cover integrating Spec Kit with GitHub Actions comprehensively — a full, lightweight CI pipeline with spec validation, audit, a quality gate, and PR automation.

Related Articles

💬 Comments