Skip to content

feat(rules): config + ESLint-style rules engine with check command#42

Merged
bntvllnt merged 3 commits into
mainfrom
feat/config-rules-engine
Jun 10, 2026
Merged

feat(rules): config + ESLint-style rules engine with check command#42
bntvllnt merged 3 commits into
mainfrom
feat/config-rules-engine

Conversation

@bntvllnt

@bntvllnt bntvllnt commented Jun 3, 2026

Copy link
Copy Markdown
Owner

Implements specs/backlog/2026-06-02-config-rules-engine.md end-to-end.

What

  • Config (src/config) — discovery (codebase-intelligence.json + dotfile variants + package.json#codebaseIntelligence, walk-up), zod validation against the committed schema.json, CLI overrides, ConfigError → exit 2.
  • Rules engine (src/rules/engine.ts) — pluggable Rule contract, ESLint-style severities (off|warn|error or 0|1|2, [severity, options]), // ci-ignore-file / // ci-ignore-next-line <rules> suppressions, stable fingerprints, deterministic ordering.
  • Rulesno-comments (the requested one; configurable — keeps JSDoc, tool/compiler directives, and a file-leading license header by default; style: line|block|all), no-circular-deps, no-dead-exports.
  • Formatterstext, json, sarif.
  • CLI check — exit codes 0 clean / 1 findings ≥ failOn / 2 config or usage error; flags --format/--json/--config/--fail-on/--gate/--base/--quiet/--summary.
  • MCP check tool — returns {verdict, summary, findings}. Read-only — actions[] are advisory hints, never applied (per roadmap §3).
  • Fix: getGitChurn/getHeadHash no longer leak git's stderr on non-git dirs.

Tests (38 new, no internal mocks)

  • config-loader — discovery, package.json key, walk-up, invalid JSON / unknown-key → ConfigError, overrides.
  • rules-engine — real parser→graph→analyzer→engine pipeline: no-comments option matrix, suppressions, circular/dead-export rules, formatters, verdict gating.
  • cli-check.e2e — spawns the real built binary, asserts exit codes + stdout (json, sarif, suppression, config error, --config).
  • mcp-check — real in-memory MCP server + client.

Validation

  • pnpm lint clean · pnpm typecheck clean · pnpm build clean.
  • pnpm vitest run (parallel): 380 passed, 0 errors.
  • Coverage 95.71% lines / 86.8% branches (thresholds 80/70/80/80); src/rules 97.5%.

Note: the local --coverage --no-file-parallelism run hit vitest's known onTaskUpdate worker-RPC timeout under heavy serial load on a loaded dev box (suite 460s vs 35s parallel). All tests pass and coverage thresholds are met; CI's dedicated runner is the authoritative gate for the serial coverage step.

Not in scope (follow-ups)

Boundary rules engine, audit new-only diffing, more formatters (codeclimate/pr-comment), init/hooks/watch — speced, not built here.

Implements specs/backlog/2026-06-02-config-rules-engine.md.

- config: discovery (codebase-intelligence.json + variants + package.json key,
  walk-up) + zod validation + CLI overrides + ConfigError (exit 2)
- engine: pluggable Rule contract, severity resolution (off/warn/error|0/1/2),
  ci-ignore-file / ci-ignore-next-line suppressions, stable fingerprints
- rules: no-comments (configurable: keeps JSDoc/directives/header by default,
  style line|block|all), no-circular-deps, no-dead-exports
- formatters: text, json, sarif
- CLI 'check' command: exit 0/1/2, --format/--json/--config/--fail-on/--gate/--base
- MCP 'check' tool + setRoot/getRoot (read-only, advisory actions only)
- silence git stderr leak in getGitChurn/getHeadHash (non-git dirs)
- tests: config loader, rules engine (real pipeline), CLI e2e (spawned binary),
  MCP tool — 38 tests, no internal mocks
- docs: mcp-tools (16th tool), architecture + CLAUDE module maps
@bntvllnt bntvllnt self-assigned this Jun 4, 2026
bntvllnt added 2 commits June 4, 2026 18:29
Blocker:
- F1: implement file-level --gate new-only --base (git diff filtering) instead of
  shipping inert flags

Correctness/reliability:
- surface rule-throws to stderr (no silent CI false-negative)
- confine sourceOf reads to project root via realpath (blocks ../ and symlink-out)
- failOn:'never' now disables the maxWarnings gate too
- block-comment ci-ignore (/* ci-ignore-file */) suppressions now honored
- no-comments allow[] = comment-body prefix match (no 'a' over-matching 'bad')

Config:
- ConfigError messages use basename (no full-path leak via MCP)
- stop config walk-up at repo root (no ambient-config hijack)
- distinct read vs parse errors (EACCES != invalid JSON)

SARIF/output:
- emit shortDescription, partialFingerprints, endLine/endColumn
- MCP check uses canonical formatJson; extract shared formatSummaryLine
- quiet-first output ordering

Types/MCP:
- FindingAction.kind union; RuleSetting tuple rejects [off, opts]
- register check in codebase://setup; generic tool description; getRoot TSDoc
- example config gate: all (safe default)

Tests: new-only gate (real git repo), block suppression, allow precision, failOn:never+maxWarnings
- git diff --relative + forward-slash normalization so changed-file paths
  compare correctly against rootDir-relative findings (was: subdir target
  filtered out every finding -> silent pass; also fixes Windows path.sep)
- regression test: gate on a repo-subdirectory target keeps its findings
- pin --quiet-wins-over---summary contract with e2e tests (pass: silent,
  fail: summary line)
- hermetic git identity env in gate tests (CI without global git config)
@bntvllnt bntvllnt merged commit 2e952f9 into main Jun 10, 2026
2 checks passed
@bntvllnt bntvllnt deleted the feat/config-rules-engine branch June 10, 2026 13:18
bntvllnt added a commit that referenced this pull request Jun 10, 2026
#44)

## What

Docs only — brings `roadmap.md` back in line with shipped reality and
records the 2026-06-10 architecture sweep findings as a roadmap item.

- **Header**: `Last updated: 2026-06-10 · shipped through v2.4.1 + rules
engine (#42)`
- **§2 baseline sync**: `check` added to the command list; new *Shipped
since* block covering #36#42 (config + ESLint-style rules engine
foundation, `init` opt-in agent selection)
- **New §5.13 — Operation registry, one engine N surfaces (P1)**: the 15
analysis operations are exposed twice (CLI + MCP) with their lifecycle
(validation, error routing, hints, serialization, graph-load) smeared
across `cli.ts` / `mcp/index.ts` / `src/core`. Proposal: one
`Operation<TInput, TResult>` descriptor per op (zod schema feeding both
surfaces, result/error union, hints, formatter) with CLI/MCP as thin
adapters — the same registry shape `src/rules/` paved in #42.
- **§6 sequencing**: §5.13 slotted in P1 before §5.7, since output
formats (SARIF/CodeClimate/…) become formatters over the registry, and
the §5.12 LSP becomes a third thin adapter.

## Evidence (self-analysis, graph-backed)

| Signal | Value |
|---|---|
| `cli.ts` | 1205 LOC, fan-in 0 / fan-out 13, coupling 0.93 (top
hotspot) |
| `mcp/index.ts` | 446 LOC, cohesion 0.17 (JUNK_DRAWER) |
| Validation | duplicated: `parseInt`+`isNaN` guards (CLI) vs zod (MCP)
|
| Graph-load pipeline | duplicated verbatim: `loadGraph` vs `runMcpMode`
in `cli.ts` |
| Precedent | `src/rules/` registry: cohesion 0.71, COHESIVE |

## Why

The roadmap predates #41/#42, so it didn't answer "where are we" — and
the sweep's keystone finding belongs on the direction file, not in a
chat log.
bntvllnt added a commit that referenced this pull request Jun 10, 2026
#46)

## Summary

Working-tree housekeeping batch:

**.gitignore** — three groups added:
- analysis + coverage artifacts (`.code-visualizer/`, `coverage/`)
- local agent state (`.claude/`)
- self-dogfood `init` outputs stay local, consistent with #35 being
closed unmerged (`.cursor/`, `.pi/`, `GEMINI.md`, `CONVENTIONS.md`,
`.github/copilot-instructions.md`)

**specs/** — archive shipped, commit backlog:
- `mcp-only` → shipped (2026-03-03, #3)
- `cli-mode` → shipped (2026-03-12, #16) — was untracked despite all 10
scope items done
- `config-rules-engine` → shipped (2026-06-10, #42)
- status frontmatter updated + 3 `history.log` entries (marked *archived
retroactively*; actuals unknown, left as `-`)
- 3 untracked 2026-04-29 backlog specs committed (module-depth,
find-seams, forces signals)

Docs/config only — no source changes.

## Not touched (follow-up decision)
7 remaining `specs/active/` entries from the pre-MCP-only UI era
(analytics-v2, nextjs-migration, e2e-playwright, group-metrics-legend,
agent-intelligence-roadmap, symbol-level-ui, group-spatial-clustering)
look obsolete (dropped, not shipped) — archiving them to
`specs/dropped/` needs a judgment call per spec.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant