Skip to main content

Governance rules

Architecture decisions die when they only live in a wiki. ovecc lets you declare them — language-neutral, in .ovecc/config.toml — and enforces them at index time. Violations surface in violations, advise, the gate CI check, and review.

Boundary rules

Forbid (or explicitly allow) a module-to-module dependency:

[[rules.boundaries]]
name = "core must stay foundational"
source = "core"
target = "services"
allowed = false
severity = "high"

When someone adds import { recordUsage } from "../services/billing" inside src/core/db.ts:

$ ovecc violations --severity high
[High] core -> services
Rule: core must stay foundational
Type: CrossDomainDependency
Evidence: src/core/db.ts:2 (../services/billing)

And because the rule is checked per-change too, the offending PR fails gate with 1 new boundary violation(s) — the backlog never grows silently.

source and target match module names (modules are inferred from directory structure — run ovecc query "module <name>" or ovecc export graph to see yours).

Banned imports

Ban dependencies by specifier pattern — exact, prefix*, *suffix, or *infix*:

[[rules.banned_imports]]
name = "no-lodash"
pattern = "lodash*"
message = "use es-toolkit instead"
severity = "medium"
[Medium] Banned import: lodash*
Rule: banned-import/no-lodash
Type: ForbiddenImport
Evidence: src/utils/helpers.ts:1 (lodash/groupBy)

The pattern matches the import specifier, so lodash* catches both lodash and deep imports like lodash/groupBy. Typical uses: deprecating a library, forbidding direct database-driver imports outside the data layer, banning internal packages from leaking into public modules.

Inline suppressions

Silence one specific finding on one specific line:

const digest = createHash("md5").update(cacheKey).digest("hex"); // ovecc-ignore
// ovecc-ignore-next-line
const legacy = eval(trustedMigrationSnippet);
result = subprocess.run(cmd, shell=True) # ovecc-ignore

The finding is dropped at index time. Suppressions are themselves audited: an ovecc-ignore that no longer suppresses anything is reported as stale-suppression (low) — because a dead ignore would silently swallow the next real finding on that line. ovecc fix deletes stale suppressions for you.

The baseline ratchet

For findings you can't fix yet, prefer the baseline over sprinkling ignores:

ovecc violations --write-baseline # accept the current findings (.ovecc/baseline.json)
ovecc violations --baseline # report only findings NOT in the baseline

Combined with --fail-on any in CI, this is a one-way ratchet: existing debt is tolerated, new debt fails the build. Re-run --write-baseline after a cleanup to shrink the accepted set.

Scoping what gets indexed

Also in config.toml (or as flags on index):

[index]
# Extra paths to skip, on top of the built-ins (node_modules, target, dist, .venv, ...)
exclude = ["vendored", "generated"]
# Flag manifest dependencies that no indexed file imports (off by default)
detect_unused_deps = true

The full file, key by key, is documented in the configuration reference.