Codebase Awareness

Haft doesn't just track decisions — it understands your codebase. It knows which modules exist, how they depend on each other, which parts of your architecture are governed by decisions, and which invariants apply to every file you touch.

Module detection

When you run /h-status or haft_query(action="coverage"), haft scans your project and detects modules using language-specific conventions:

Language What counts as a module How it's detected
Go Every directory with .go files Uses Go's stdlib go/parser
JS/TS package.json boundaries + directories with index.ts Monorepo workspaces + barrel file detection
Python Directories with __init__.py Package marker detection
Rust Cargo.toml crates + mod.rs modules Crate boundary + module tree detection
C/C++ Directories with .c, .cpp, .h, .hpp files compile_commands.json (CMake, Bazel, Meson) with directory-based fallback

Module scanning respects .gitignore (local and global) and an optional .haftignore file for project-specific exclusions.

Decision coverage

Coverage answers: "which parts of my architecture have engineering decisions, and which are blind spots?"

## Module Coverage (9 modules, 77% governed)
  ✓ src/internal/artifact   — 3 decisions
  ✓ src/internal/codebase   — 2 decisions
  ✗ src/assurance            — no decisions (blind)
  ✗ src/cmd/indexer          — no decisions (blind)

Coverage uses three states:

  • Covered — at least one active decision with R_eff >= 0.5 covers files in this module
  • Partial — has decisions, but all evidence is degraded (R_eff < 0.5)
  • Blind — no decisions reference any file in this module

Only DecisionRecord artifacts count as governance. Notes are observations, not architectural contracts — they don't inflate coverage.

Dependency graph

Haft parses import statements to build a module dependency graph. For Go, it uses the stdlib go/parser. JS/TS, Python, Rust, and C/C++ use regex-based import detection. C/C++ resolves #include "..." paths using -I flags from compile_commands.json when available.

The dependency graph powers impact propagation: when drift is detected in module A, haft checks which modules depend on A and flags their decisions too.

Drift detected in auth/:
  auth/middleware.go — MODIFIED (+8 -2)

Impact propagation:
  → api/ depends on auth/ — governed by dec-003 "API rate limiting"
  → payments/ depends on auth/ — governed by dec-007 "Payment auth flow"
  ⚠ billing/ depends on auth/ — no decisions (blind, potential unmonitored impact)

Drift detection

After a decision is implemented, the agent snapshots the SHA-256 hashes of affected files (the baseline). On every /h-verify scan, haft recomputes hashes and detects:

  • MODIFIED — file changed since baseline
  • FILE MISSING — file deleted or moved
  • No drift — file unchanged

Drift is a signal, not automatic invalidation. The agent reads the actual diff and judges whether the change is material (broke an invariant) or cosmetic (comments, formatting).

Knowledge graph

Beyond flat module detection, haft maintains a knowledge graph that connects files, modules, decisions, and invariants. This graph powers several queries that agents and CLI commands use internally:

Query What it returns
FindDecisionsForFile All active decisions whose affected files include the given path
FindInvariantsForFile All invariants from all decisions governing a file — collected transitively through the dependency graph
FindModuleForFile The module boundary a file belongs to
TransitiveDependents All modules that depend on a given module, directly or transitively
ComputeImpactSet Given a set of changed files, returns all decisions and invariants that may be affected — the union of direct governance and transitive dependency impact

ComputeImpactSet is what /h-verify scan uses under the hood. It's also available to any agent through MCP, so custom workflows can query impact before making changes.

Invariant injection

When an agent begins implementing a decision, haft collects invariants from all decisions governing the affected files — not just the decision being implemented. The agent receives these as constraints in its prompt.

Implementing dec-012 "Payment retry logic"
Affected files: payments/retry.go, payments/circuit.go

Injected invariants (from all governing decisions):
  [dec-007] "Payment auth flow"
    — auth token must be refreshed before retry, not reused from failed attempt
    — max 3 retries with exponential backoff
  [dec-003] "API rate limiting"
    — retry requests count against rate limit budget
    — no bypass of rate limiter for internal retries

This prevents the classic problem: implementing one decision while accidentally violating invariants from another decision that governs the same files.

Invariant verification

Some invariants are structural — they describe relationships between modules, not runtime behavior. Haft can verify these against the live dependency graph:

  • "no dependency from X to Y" — verified by checking that the dependency graph has no path from module X to module Y
  • "no circular dependencies" — verified by checking the dependency graph for cycles

These checks run during /h-verify scan. When a structural invariant is violated, the decision is flagged with the specific dependency chain that breaks it.

Invariant violation: dec-005 "Layered architecture"
  Invariant: "no dependency from domain/ to infrastructure/"
  Violation: domain/user.go imports infrastructure/postgres/conn.go
  Chain: domain/ → infrastructure/postgres/

Structural invariant verification is deterministic — it checks the actual import graph, not heuristics. If the graph says there's a dependency, there is one.

Using it in practice

When What to do
After /h-onboard Check coverage — which modules are blind? Prioritize the critical ones.
During code review If a PR touches files under a decision, the drift report will flag it.
Before a release /h-verify scan — see all stale decisions, code drift, and invariant violations in one view.
New team member /h-status shows the full architecture decision landscape at a glance.
Before implementing Invariant injection ensures the agent knows all constraints on affected files.