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.