Skip to content

engine: RuleContext shim is a hand-maintained copy that nothing verifies #511

Description

@rhuanbarreto

Follow-up from review of #497. Deferred there because replacing the shim's generation mechanism is a design change, not a fix to the feature under review.

RuleContext is declared twice: once as a real interface in src/formats/rules.ts, and once as text inside a template literal in src/helpers/rules-shim.ts. That second copy is what generateRulesDts() writes to .archgate/rules.d.ts in every governed project, and it is what a rule author's editor uses for autocomplete and type errors while writing .rules.ts.

The two copies are kept in sync entirely by hand, and nothing in bun run validate verifies the second one.

Reproduction

Plant a flatly wrong signature in the shim's template literal — a wrong parameter type plus two types that do not exist anywhere in the codebase:

readYAML(path: number, bogus: Nonexistent): Promise<WrongType>;

Then run the full gate:

Gate Result
bun run typecheck (tsc --build) passes
bun run lint (oxlint --deny-warnings) passes
bun run check (incl. ARCH-022/rulecontext-shim-parity) passes, 5/5
bun run validate exit 0

Nothing fails. tsc type-checks code, and this interface is a string — so to the compiler it is inert text.

Why the existing rule does not cover it

ARCH-022/rulecontext-shim-parity (added in #497) compares member names only. readYAML is present in both surfaces, so the planted signature passes. The rule's own description and the ADR both say full signature/JSDoc parity remains a manual review item; this issue is about how wide that manual surface actually is.

Empirically it does not hold: three separate findings in #497's review were this exact drift, after readYAML's content was narrowed from unknown to YamlValue mid-review:

The surface

One-line type change; six hand-maintained descriptions of it:

Surface Audience Machine-checked
src/formats/rules.ts the compiler yes
src/helpers/rules-shim.ts rule authors' editors, via .archgate/rules.d.ts member names only
docs/src/content/docs/reference/rule-api.mdx English readers no
docs/src/content/docs/nb/reference/rule-api.mdx Norwegian readers page parity only (GEN-002)
docs/src/content/docs/pt-br/reference/rule-api.mdx Portuguese readers page parity only (GEN-002)
docs/public/llms-full.txt LLM consumers generated, content unverified

Suggested approach

Derive the shim from the real interface instead of transcribing it. src/formats/rules.ts is already the single source of truth for these types; generateRulesDts() should read them from there and emit the ambient form (strip export, prepend declare) rather than restating them. Drift then becomes structurally impossible rather than review-dependent.

Two things to settle when implementing:

  1. Extraction mechanism. The ambient shim needs the type declarations plus their JSDoc, with export removed. Note ctx.ast() cannot parse these out of the source: Bun.Transpiler erases type-only declarations before the tree is built, which is why rulecontext-shim-parity uses a regex over raw text. A generator has the same constraint and would need the TypeScript AST or a text-level extractor.
  2. Which types travel. The shim currently mirrors a specific set (RuleContext, RuleConfig, RuleSet, and the types they reference). Deriving it needs an explicit rule for what is in scope, or the ambient file grows to everything exported from rules.ts.

Smaller alternative if that is too invasive: extend rulecontext-shim-parity to compare normalized signature text per member, not just names. Cheaper, and it would have caught every finding above — but it stays a text comparison, so it constrains drift rather than preventing it, and JSDoc prose stays unchecked.

Scope and severity

This is not a security or runtime-behavior issue. The engine's behavior comes from src/formats/rules.ts and the actual implementations; the shim only affects the type hints rule authors see in their editor. The realistic worst case is that Archgate ships .archgate/rules.d.ts files that disagree with the runtime, and a rule author gets misleading autocomplete or a spurious type error with nothing failing anywhere.

Not a blocker for #497, which is green and approved.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions