Skip to content

RFC 105: oven architect rule engine for design, safety, idiom, maintainability, and risk findings

  • Status: Planned
  • Created: 2026-05-24
  • Author(s): Danny Meijer (@dannymeijer)
  • Related:
    • RFC 006 (generators)
    • RFC 048 (contract-backed models emit and tooling)
    • RFC 070 (Result combinators)
    • RFC 088 (iterator adapter surface)
    • RFC 096 (declaration metadata blocks)
    • RFC 106 (compiler-backed agent context graph)
    • RFC 117 (loaf.toml and Oven's language-neutral project model)
    • RFC 118 (Incan and Oven command-line surfaces)
    • RFC 119 (Oven-native Rust build facets and Cargo interoperation)
    • RFC 120 (canonical source symbol identity)
    • RFC 127 (Incan lint catalog, policy levels, suppression, and safe fixes)
  • Issue: #663
  • RFC PR: —
  • Written against: ~~v0.3~~ ~~v0.5~~ v0.7
  • Shipped in: —

Summary

This RFC proposes oven architect as a deterministic code-advice command for direct Incan source and for Oven-managed projects authored in Incan, Rust, or both. The command reports evidence-backed findings across architecture, safety, idiom usage, maintainability, and deterministic risk signals by running maintainable rules over the shared compiler-backed codegraph defined by RFC 106. The central goal is not to create a broad subjective linter, but to create a durable rule authoring surface where new advice can be added cheaply, tested precisely, calibrated against real projects, and consumed by humans, agents, editors, and CI without relying on model inference for core detection.

Core model

  1. Oven selects project scope: Inside a Loaf, oven architect obtains the selected workspace members, source facets, dependencies, targets, generated inputs, and source roles from Oven. It must not recreate project discovery or infer a project graph from nearby files.
  2. Language frontends own semantic facts: Incan's compiler owns checked Incan facts. Rust inspection and compiler infrastructure own checked or resolved Rust facts. Both enter the shared RFC 106 graph as ordinary declarations, references, calls, modules, and relationships carrying a language attribute.
  3. Provenance is preserved: Every source-backed fact and finding retains whether the source is authored, generated, external, or projected, plus its source facet and selected project authority. Generated Rust is not an independently authored implementation merely because it is visible to the graph.
  4. Rules interpret facts: Each rule consumes typed fact views and emits findings with qualified rule names, priorities, categories, confidence, evidence, suggestions, and risks. A rule declares the fact capabilities and languages it supports rather than assuming every source owner is Incan.
  5. Findings are advisory by default: Architect findings are not compiler errors. They describe design pressure or code-shape opportunities with enough evidence for a human or agent to decide whether to act. RFC 127 assigns catalog levels (allow, warn, deny, and forbid) that may make selected findings fail oven architect; that enforcement remains tooling policy and never turns the finding into a compiler error.
  6. Categories are explicit: Architecture findings, safety findings, idiom findings, maintainability findings, and risk findings remain separate in qualified rule names and profiles even when they share one command.
  7. Broad collection, precise classification: The command should scan the requested scope broadly and emit evidence-backed findings across categories. Confidence, category, profile, baseline, and priority decide presentation and action pressure; they should not erase valid findings up front merely because the finding is local, optional, or not architectural.
  8. Rule authoring is a product surface: The feature is only maintainable if adding a rule means using stable typed facts and reusable queries, not hand-parsing raw graph nodes or reimplementing per-language syntax walks.
  9. Peer context is explicit: Projects may declare locally meaningful peer groups such as command handlers, lowering passes, adapters, registries, or generators. A broad category may contain several valid peer groups; names and filesystem position are useful signals, not sufficient proof of membership or intent.
  10. Exploration stays subordinate to rules: Baseline or similarity analysis may identify review candidates from comparable peers, but it must remain explainable, provenance-aware, and non-authoritative. It cannot silently create an architectural contract or replace deterministic rule detection.

Motivation

Incan and Rust already have syntax checks, semantic checks, formatters, tests, and language-specific diagnostics. Oven adds one language-neutral project, dependency, target, artifact, and receipt model over authored Incan and Rust facets. Those tools answer whether a selected project parses, typechecks, compiles, formats, and runs. They do not answer whether the project is accumulating design pressure: repeated dispatch over the same domain, public boundaries that can panic on recoverable input, competing source authorities, duplicated contracts across language facets, cache identities that omit consumed inputs, old-shaped Incan control flow that should now use language features, or small helper functions that add indirection without carrying domain meaning.

The first experiments with an architecture-advice command showed that deterministic rules can surface useful pressure when they report concrete source evidence and classify action pressure separately from proof strength. Repeated match dispatch can reveal a growing operation boundary. Fail-fast calls inside public APIs can reveal recoverability problems. Body-shape facts can also support maintainability findings such as compound-assignment candidates, single-use trivial helpers, append-only list builders that could become comprehensions, or Result matches that could use RFC 070 combinators.

Without a formal rule engine, each new check risks becoming a one-off command-private or language-private syntax walk with custom parsing, inconsistent output, and ad hoc severity. That path does not scale, especially when the same project can contain authored Incan and Rust. The value is in a shared substrate: one Oven-selected project graph, one bilingual codegraph, one typed query layer, one finding model, one de-duplication path, and many small rules that are easy to review and calibrate.

This feature also matters for agent workflows. Agents can already make broad refactoring suggestions, but those suggestions are often expensive to verify and easy to overfit. oven architect should provide deterministic evidence that an agent can use as grounding: exact files, lines, matched domains, shared patterns, call sites, usage counts, and counterexample risks. A model may summarize or prioritize findings, but the core detection should remain inspectable and reproducible.

Goals

  • Define oven architect as the umbrella command for deterministic design, safety, idiom, maintainability, and risk-signal advice.
  • Provide a stable finding model with qualified rule name, category, priority, confidence, evidence, pressure, suggestions, risks, and machine-readable output.
  • Provide direct Incan file and directory scanning outside a Loaf, and Oven-selected package or workspace scanning inside a Loaf, with deterministic source, module, and finding de-duplication across Incan and Rust facets. The same finding model should support PR-diff and graph-snapshot scopes.
  • Treat Rust-only, Incan-only, and mixed Loaves as first-class project analysis scopes.
  • Preserve language, source role, source facet, generation provenance, and selected Oven project authority in graph facts and finding evidence.
  • Establish rule categories and profiles so users can run architecture-only, safety-only, idiom-only, maintainability-only, risk-only, or all-rule scans.
  • Establish a maintainable rule authoring surface based on typed facts and reusable queries over codegraph data.
  • Extend codegraph body facts as needed for rule families such as match dispatch, call sites, references, assignment/update shapes, helper usage, loop-builder shapes, and result-match shapes.
  • Include maintainability findings in scope when they have clear evidence, useful counterexamples, and calibrated confidence.
  • Include deterministic risk findings when local inputs such as coverage, churn, ownership, co-change, or decision records are available, while keeping risk evidence separate from architecture recommendations.
  • Keep detection deterministic; no language model is required for core finding generation.
  • Support text output for humans and stable JSON output for tools, agents, editors, and CI.
  • Make suppression and baselining part of the product model so mature codebases can adopt the command incrementally.
  • Define a metadata and fact-model path for project-specific peer context, including peer-group hierarchy, declared boundary constraints, exceptions, and membership provenance.
  • Provide an explicit experimental profile for explainable peer-baseline outliers, built on the same facts and the same reviewed baseline artifact.

Non-Goals

  • This RFC does not make architect findings compiler errors.
  • This RFC does not replace formatter rules, language diagnostics, Clippy, generated-source validation, or project tests. In particular, Architect should not reproduce local Rust lint rules that Clippy already owns without adding project-wide evidence.
  • This RFC does not treat generated Rust as authored Rust or report duplicate authored implementations merely because an Incan declaration has a generated Rust projection.
  • This RFC does not make Oven the owner of Incan or Rust semantic facts. Oven selects project scope; language frontends remain semantic authorities.
  • This RFC does not add arbitrary ambient source languages to a Loaf. Incan and Rust are the built-in authored facets defined by RFCs 117 and 119; other language integrations require their own explicit contracts.
  • This RFC does not require a small language model or remote AI service for rule detection.
  • This RFC does not attempt to infer developer intent from names alone.
  • This RFC does not make a similarity score, inferred peer membership, or outlier classification an architectural finding by itself.
  • This RFC does not allow an exploratory baseline to fail CI by default, silently update itself from a scan, or promote an observed pattern into a deterministic rule automatically.
  • This RFC does not require every possible maintainability rule or risk signal.
  • This RFC does not itself define automatic rewrites or apply fixes. RFC 127 defines the separate oven architect --fix mode over catalog findings.
  • This RFC does not itself decide whether a reported finding should be fixed in the current change. RFC 127 preserves that separation through explicit --fix invocation and per-rule fix policy; other findings remain decisions for the user, CI, or a fix loop.
  • This RFC does not define a public plugin ABI for third-party binary rule packages.
  • This RFC does not require every codegraph fact to be part of a permanently stable external schema; the public typed finding contract, its documented projections and command behavior must be stable; the exact serialization schema is not selected here.

Guide-level explanation

Users run oven architect on direct Incan source or on an Oven-managed project. In a Loaf, the default scope is the selected project graph, including every authored Incan and Rust facet in that selection.

oven architect .
oven architect src/lib.incn --format json
oven architect . --profile architecture
oven architect . --profile maintainability

Oven owns the canonical command and selected analysis scope. incan architect may remain an Incan-only convenience that delegates to the same Oven operation; it does not accept Rust input or own a second planner. Language frontends remain the authorities for checked facts. This is an explicit command-ownership amendment to RFC 118. A Rust-only Loaf is still a valid Architect scope. A mixed Loaf is analyzed as one project rather than as unrelated per-language scans.

Rules may be language-neutral, language-specific, or cross-language:

  • language-neutral rules operate over normalized facts such as declarations, calls, references, visibility, reachability, identities, caches, registries, and boundaries;
  • language-specific rules use a fact capability owned by one frontend, such as an Incan Result combinator candidate; and
  • cross-language rules reason about relationships between authored Incan and Rust, such as duplicated contracts, divergent registries, bypassed interop authority, or an authored implementation that is disconnected from the public facet that claims to expose it.

Generated projections remain visible as evidence but do not become authored peers. For example, Architect must not report an Incan function and its generated Rust body as duplicate implementations. Their source_role and generation relationship make them one authored declaration and one derived projection.

The command prints findings grouped by priority and grounded in source evidence.

[P3] Repeated match dispatch over `source_kind`
Pressure: 2 match expressions dispatch over `source_kind` and share 3/3 explicit arms: SourceKind.Arrow(...), SourceKind.Csv(...), SourceKind.Parquet(...)
Suggestions:
  - Decide whether this is intentionally exhaustive local logic or a growing operation boundary.
  - If it is a growing operation boundary, prefer an adapter or registry outside the domain type when the operation belongs to another subsystem.
Risks:
  - Keep local exhaustive matches when they are clearer than an abstraction and the case set changes rarely.
Evidence:
  - src/backend.incn:160:5 in register_one (explicit arms: 3/3; fallback: no)
  - src/schema.incn:322:5 in schema_columns_for_source (explicit arms: 3/3; fallback: no)

The architecture value is not merely that two matches are textually similar. The useful signal is that separate subsystems are making parallel decisions over the same closed domain. For example, an ingestion package might register execution backends in one module and infer schemas in another module, with both operations matching every SourceKind variant.

def register_backend(kind: SourceKind, registry: BackendRegistry) -> None:
    match kind:
        SourceKind.Csv(_) => registry.add("csv", csv_backend())
        SourceKind.Json(_) => registry.add("json", json_backend())
        SourceKind.Parquet(_) => registry.add("parquet", parquet_backend())


def infer_columns(source: Source) -> Result[list[Column], SchemaError]:
    match source.kind:
        SourceKind.Csv(_) => return infer_csv_columns(source)
        SourceKind.Json(_) => return infer_json_columns(source)
        SourceKind.Parquet(_) => return infer_parquet_columns(source)

The recommendation should not be "put backend registration and schema inference methods on SourceKind." That would move subsystem responsibilities onto the enum. The more architectural advice is to ask whether this is a growing operation boundary. If every new source format requires coordinated edits to backend registration, schema inference, validation, documentation, and test fixtures, the code may want a format-handler registry or adapter table where each format owns its related operations.

[P3] Repeated match dispatch over `source.kind`
Pressure: backend registration and schema inference both dispatch over all source formats.
Suggestion: Consider a format-handler registry if adding one format requires shotgun edits across subsystems.
Risk: Keep exhaustive local matches if the format set is closed, the operations are genuinely local, and cross-format registration would obscure control flow.

Architect findings use categories. Architecture findings describe design pressure. Safety findings describe failure or recoverability risk. Idiom findings describe opportunities to use Incan features more directly. Maintainability findings describe local readability, cleanup, or code-shape pressure. Risk findings describe deterministic prioritization evidence such as churn, weak coverage, co-change, or ownership spread.

safety.fail_fast_boundary_call
idiom.result_combinator_candidate
maintainability.single_use_trivial_helper
risk.untested_hotspot
arch.repeated_match_dispatch

Maintainability findings are allowed when they are precise and humble. A trivial helper rule can identify a private helper that is used once and only returns a pure expression.

def add(left: int, right: int) -> int:
    return left + right

The finding should not say that the helper is definitely wrong. It should say that the helper may be unnecessary unless its name carries useful domain meaning.

[P3] Private helper only wraps one expression
Pressure: `add` is private, used once, and only returns `left + right`.
Suggestion: Inline the expression if the helper does not name a useful domain concept.
Risk: Keep the helper if it documents intent, preserves API shape, acts as a callback, or is expected to grow.

A comprehension candidate should likewise report a specific body shape, not a broad preference.

def positive_scores(scores: list[int]) -> list[int]:
    out = []
    for score in scores:
        if score > 0:
            out.append(score)
    return out

The corresponding advice is useful only because the shape is append-only, the accumulator is returned, and no other mutation or side effect participates in the loop.

[P3] Append-only list builder can be a comprehension
Pressure: `positive_scores` builds and returns a list with one append-only loop.
Suggestion: Use `[score for score in scores if score > 0]` if the eager list is the intended result.
Risk: Keep the loop if additional statements, logging, early exits, or mutation are part of the real workflow.

For RFC 070 Result combinators, architect can identify obvious match shapes and suggest the equivalent method only when the transformation is mechanically recognizable.

match parsed:
    Ok(value) => Ok(clean(value))
    Err(err) => Err(err)

The finding can suggest parsed.map(clean) because one branch transforms the Ok payload and the Err branch passes through unchanged.

Reference-level explanation

Command behavior

oven architect [PATH] [OPTIONS] must accept a source file or directory. When PATH is omitted, the command should analyze the current directory.

When PATH is an Incan file outside a Loaf, the command must scan the file and the modules needed to resolve its imports according to ordinary Incan module rules.

When PATH is within a discovered Loaf, the command must delegate project and workspace selection to Oven and analyze the selected source graph. That graph may contain authored Incan, authored Rust, or both. The command must use Oven's selected members, facets, dependencies, generated inputs, and source roles rather than recursively treating every nearby source file as part of the project.

Direct Incan and Rust files or directories must be admitted through oven architect, using the owning frontend and explicit source selection. Outside a Loaf, the selection must not discover a Cargo project or infer dependency authority from nearby manifests. Required project facts that cannot be established from the explicit input must be reported as unavailable, with guidance to select a Loaf when necessary. The same capability gates apply to both languages.

Every scan must be deterministic. A source owner imported through multiple roots or visible through both an authored declaration and a generated projection must contribute according to its graph identity and provenance, not as duplicated text.

Default scope must follow documented Oven member, environment, target and source-role selection rules. Project customization belongs in loaf.toml under checked tooling metadata; the exact table grammar must be documented by its owning project contract. A scan must report its effective selection and exclusions. It must not silently broaden its closure because of the current working directory, a neighboring manifest or an undeclared environment value.

The requested scope is the scan boundary. Maintainability findings are not limited to code that happens to be near another edit. Profiles, baselines, suppressions, and output filters decide which findings are shown or acted on.

The report must have one typed Incan model contract independent of its rendering. The command must provide text and JSON renderers; JSON is one projection, not the semantic authority or the required representation for every consumer. Other explicit renderers may use YAML or TOML without changing findings, rule identities or coverage. This RFC defines required information and compatibility semantics, not an exact serialization schema.

The command should provide --profile with at least architecture, safety, idioms, maintainability, risk, experimental, and all. The default profile must include all stable supported rule categories, with category, profile and priority filters explicit. Experimental rules remain opt-in, including when all is selected. A missing frontend capability must be visible as missing coverage rather than silently dropping the affected rule family. experimental is opt-in and may contain peer-baseline review candidates; all excludes it unless the user requests it explicitly.

Finding model

Every finding must have a qualified rule name, namespaced by category. A catalog entry additionally has RFC 127's stable diagnostic code, which does not change when a rule moves between categories or groups. Published catalog rules must have numbered diagnostic identities allocated by RFC 127's catalog, alongside their descriptive names. Renaming or regrouping a rule must not silently reassign its number; this RFC does not allocate a competing numbering system.

arch.repeated_match_dispatch
safety.fail_fast_boundary_call
idiom.result_combinator_candidate
maintainability.single_use_trivial_helper
risk.untested_hotspot

Every finding must include a category, priority, confidence, title, pressure, evidence, suggestions, and risks. Source-backed evidence must also identify its language and source role. Findings emitted for a Loaf scope must identify the selected project authority or graph snapshot from which their evidence was derived.

Extension RFCs may define catalog metadata, policy levels, suppression, or safe transformation modes over Architect findings. They must preserve this RFC's project scope, provenance, evidence, typed-fact, de-duplication, and rule-authoring contracts. RFC 127 extends the finding envelope with its bare rule name, stable diagnostic code, group, effective level, level source, fix mode, and applied state; it does not redefine the engine or the selected project graph.

Priority must describe expected action pressure, not proof certainty.

P1: likely correctness, reliability, or public-boundary risk that should be reviewed before release
P2: meaningful design or maintainability pressure that should be tracked or scheduled
P3: low-risk local maintainability, idiom, or watchlist finding that is valid within the requested scan scope but optional to act on
Info: low-pressure educational or style-level advice

Confidence must describe how mechanically strong the rule match is.

High: the rule found a narrow, mechanically recognizable shape
Medium: the rule found a useful pattern with plausible counterexamples
Low: the rule is exploratory and should normally be hidden outside explicit profiles

Evidence must identify source file, line, column, owner declaration, language, source role, and source facet when available, plus rule-specific context. Rule-specific context may include matched arms, overlap counts, fallback/default-arm presence, callee labels, usage counts, body-shape summaries, generation relationships, selected project authority, or suggested replacement text.

Suggestions must be phrased as advice, not certainty. Risks must name the common counterexamples that would make the suggestion wrong.

Category, confidence, priority, profile, suppression, and baseline state shape presentation and policy. They must not be used as hidden reasons to drop evidence-backed findings before output unless the user selected a profile, suppression, or baseline that explicitly hides them.

Findings must be de-duplicated before output. Identical findings produced through multiple import roots must appear once.

Investigation consumers

RFC 126 proposes evidence-backed investigations that may reference Architect findings when forming and testing hypotheses. A finding remains advisory evidence about a rule match; its priority and confidence do not establish the cause of a runtime failure. An investigation must retain the finding's source snapshot, rule identity, evidence, and counterexample risks separately from its execution observations. Architect does not acquire process-control, experiment-execution, or automatic-repair responsibilities through this integration.

Rust diagnostic integration

Architect should reuse available Clippy information and Rust tooling capabilities through the selected Rust analysis path rather than duplicate Rust-local lint logic. Reused diagnostics or observations must retain their producing tool and version, source identity, selected target and relevant analysis configuration, with honest capability and coverage information. An unavailable integration must be reported as unavailable; it must not turn a skipped analysis into a clean result. Consuming Clippy information must not silently start a Cargo resolver, add a Cargo-compatibility mode, or turn generated Rust projections into independently authored peers. Equivalent findings must preserve their source identities and avoid duplicate reports. Project-wide Architect advice remains distinct from the Rust-local diagnostic it cites.

Rule categories

Architecture rules describe design pressure across declarations, modules, domains, language facets, or boundaries. Repeated match dispatch, growing literal domains, registry source-of-truth drift, competing source authorities, cache-key omissions, wrong-layer behavior, cross-language contract drift, disconnected implementations, and operation-boundary pressure belong here.

Safety rules describe recoverability, fail-fast behavior, partial handling, unchecked assumptions, or public-boundary hazards. A public function that can panic on caller-provided data belongs here.

Idiom rules describe opportunities to use a language or standard-library feature more directly. The first stable idiom rules are Incan-specific: Result combinator candidates, iterator adapter candidates, generator/comprehension candidates, and compound assignment candidates. Rust-local style and expression advice remains with Rust tooling unless Architect can add project-wide context that the language linter does not have.

Maintainability rules describe local readability, cleanup, or code-shape pressure. Single-use trivial helpers, repeated literals, unnecessary wrappers, long branch-heavy functions, broad type plumbing, parallel fixture lists, and append-only builders belong here when detected with concrete evidence.

Risk rules describe deterministic prioritization evidence rather than recommendations by themselves. Untested hotspots, churn, ownership spread, co-change without structural edges, decision staleness, and code-age volatility belong here when the required local inputs are available. Risk findings must expose raw contributing measures and caveats rather than only a composite score.

Rules must not be categorized as architecture findings merely because they are emitted by oven architect.

Every rule must declare its applicability independently of category:

  • language-neutral: requires normalized facts and may match any authored facet;
  • language-specific: requires one or more named languages or frontend capabilities; or
  • cross-language: requires relationships whose endpoints have different authored languages.

Applicability is a precondition, not a confidence adjustment. A rule that requires checked Incan body facts must not guess from Rust syntax, and a cross-language rule must not compare authored source with its generated projection as though they were independent implementations.

Peer context and experimental baselines

Project architecture often has distinctions that compiler facts cannot establish alone. A repository may contain several kinds of command handler, adapter, generator, registry, or lowering pass that share a broad category but have different responsibilities and dependency directions. Architect must therefore support project-specific peer context without treating a filename, directory, declaration name, or one aggregate metric as architectural truth.

A peer group is a named set of comparable source owners, such as files, modules, declarations, or other codegraph-backed scopes. Peer context may describe a hierarchy from broad category to local archetype, declared dependency boundaries, known exceptions, and the provenance of every membership decision. The provenance states are:

  • declared: project metadata explicitly assigns the member or boundary;
  • derived: a deterministic rule or compiler-backed query establishes the membership; and
  • experimental: an exploratory process proposes a comparable peer set but has not established a project contract.

Project metadata must be able to represent multiple valid peer groups below one broad role. It must not force a source owner into a single global category merely because its path or name resembles another group.

Peer signatures must expose versioned component facts and their evidence tiers. They may combine compiler-backed declaration kind, parameter and type shape, imports, resolved calls, references, ownership position, body summaries, and source topology. Names and paths may contribute weak context, but the signature must retain the provenance and strength of every fact so a consumer can distinguish checked identity from syntax-only or project-declared context. Unsupported resolution must remain unknown; similarity does not establish semantic equivalence or a forbidden dependency. Authored membership and boundary declarations belong in checked project or declaration metadata selected by the applicable authority. Cached or inferred observations belong in tool state and must not redefine authored intent.

An experimental baseline may compare a changed owner with its peer group and report a review candidate only when it explains the comparison: the selected peers, observed differences, relevant source locations, baseline version, and known counterexamples. For example:

[Info] Experimental peer-context outlier in `sync_user`
Pressure: `sync_user` imports a web boundary and calls two HTTP-facing APIs; none of its 31 declared worker peers do.
Evidence:
  - peer group: `background-workers` (membership: declared)
  - src/workers/sync_user.incn:4:1 imports `std.web`
  - baseline: codegraph schema 1, compiler 0.5.0, project revision abc123
Risk: the worker may intentionally own a web ingress boundary; record that exception or move the boundary if the dependency is accidental.

The candidate is not a deterministic arch.* violation unless a declared boundary or independently testable Architect rule establishes that the dependency is forbidden. Experimental baselines must be opt-in, advisory, and unable to alter their own committed baseline from the scan being evaluated. A baseline snapshot must record the Codegraph schema version, compiler version, project identity, and source revision or equivalent content identity.

Rule authoring contract

Rules must declare metadata: qualified rule name, category, default priority, default confidence, profile membership, applicability (language-neutral, language-specific, or cross-language), supported languages or frontend capabilities, required fact kinds, and a short explanation. Catalog entries additionally receive RFC 127's stable diagnostic code and catalog metadata.

Rules must consume typed fact views rather than raw serialized facts. A rule that needs match dispatch sites, call sites, assignment shapes, helper usage counts, or loop-builder shapes should ask for those views directly.

Rules should be small and independently testable. Each rule should have positive and negative fixtures. Negative fixtures are required for common counterexamples named in the rule's risk text.

Rules must not require typechecked metadata when a syntactic fact is sufficient. Rules may use type facts when precision depends on type information, such as recognizing Result[T, E] match shapes.

Trusted-constant versus caller-input advice must use shared checked origin and dataflow facts. A proved literal invariant may lower action pressure; a public input or unknown origin must retain its actual classification. Names, comments and source execution must not serve as trust evidence. Unknown origin analysis must be exposed as missing coverage, not a zero-risk score.

Stable maintainability rules must start from narrow shapes with positive fixtures, observable counterexamples and corpus calibration. A single append-only eager list accumulator is a candidate; a superficially similar loop with logging or other work is not an equivalent mechanical rewrite. Broad style judgments remain experimental until their evidence supports a stronger contract.

Rules should prefer narrow body-shape facts over broad textual heuristics. For example, a comprehension candidate should be based on an append-only list-builder shape, not the mere presence of a for loop and append.

Rules must not emit authored-source findings for generated, projected, or known external code unless the rule explicitly owns that source role or the user explicitly scans it. Generated facts may support a finding about their authored owner or a broken generation relationship, but they must not silently become peer authored declarations.

Codegraph fact requirements

The codegraph exporter must provide enough source facts for rules to avoid command-private or language-private syntax walks. The fact families include declarations, imports, public API metadata, match dispatches, call sites, references, assignment/update shapes, function body summaries, usage counts, loop-builder shapes, result-match shapes, declaration signatures, source topology, and stable identity/provenance fields.

Every source-backed fact must carry the RFC 106 language attribute and enough provenance to distinguish authored, generated, projected, and external source. For a Loaf-selected scan, facts must also retain the source facet and selected Oven project or graph authority. Generation and projection relationships must be explicit edges rather than inferred from filenames or output directories.

Normalized declaration, reference, call, containment, visibility, dependency, and reachability views must work across Incan and Rust. A query asking what a declaration depends on must not require its caller to choose a language-specific edge kind first.

Frontend-specific body facts may remain capability-gated. Incan idiom rules can require checked Incan body summaries; Rust-aware architecture rules can require Rust declaration and reference facts without claiming that every Incan body-shape query has an equivalent Rust implementation. Missing capability must disable the rule for that source owner rather than produce a low-confidence guess.

Cross-language facts must preserve the checked interop identity that connects an Incan declaration to the Rust declaration it names, plus generated-from, projected-from, implements, exports, and public-contract relationships where available. This is what lets Architect distinguish a legitimate generated projection from two authored implementations that can drift.

Match dispatch facts must include the matched domain, explicit pattern labels, explicit pattern count, source arm count, and wildcard/default-arm context.

Call-site facts must include callee key, callee label, receiver shape when available, source location, and owner declaration.

Reference facts must support usage counting for private declarations and helper functions.

Assignment/update facts must make compound-assignment candidates expressible without string matching.

Function body summary facts should identify simple shapes such as single-return expression, pure expression wrapper, append-only list builder, and short result-match transform. These summaries must be conservative.

Result-match facts should identify branch-preserving transformations only when the matched expression is known to be a Result[T, E] or the syntactic shape is unambiguous enough for an idiom finding with appropriate confidence.

Risk rules may consume optional process facts such as git churn, ownership, co-change, coverage reports, and decision records when available. Missing optional inputs must be represented as absent evidence, not as zero risk.

Suppression and baselining

Local suppression of catalog findings is defined by RFC 127: @allow("rule", reason="...") on declarations and in module position, plus [incan.lints.per-file-allow] for paths.

The command should support project baselines so existing findings can be recorded and new findings can fail CI or be highlighted separately. Accepted baselines must be separate versioned, reviewable artifacts, not entries in oven.lock or hidden mutable cache state. The artifact path must be configured through typed project tooling metadata. Entries must bind rule and rule-configuration identity, applicable project authority, source/graph evidence identity and relevant schema/compiler versions, plus dispositions, accepted scope, rationale and retirement record. A scan must not accept or rewrite its own baseline. Changed evidence must make an entry stale rather than suppress an unrelated finding.

Suppressions and baselines must preserve qualified rule name and evidence identity; catalog-backed records also preserve their stable diagnostic code. A future change that moves or changes the evidence should not silently suppress an unrelated finding.

Design details

Profiles

Profiles let users choose the kind of advice they want. architecture should include cross-cutting design pressure. safety should include fail-fast and recoverability risk. idioms should include feature-usage opportunities. maintainability should include local readability, cleanup, and code-shape findings. risk should include deterministic prioritization evidence. all should include every non-experimental rule.

Rules may belong to more than one profile only when that does not blur the category. For example, a public fail-fast boundary call is a safety finding even if it also has architecture implications.

Exploratory rules may exist behind an explicit experimental profile, but they must not be enabled by default.

Priority and confidence calibration

Priority should be calibrated against likely impact and the cost of ignoring the finding; confidence should be calibrated against the mechanical strength of the evidence. Public API failures are generally higher priority than private helper maintainability findings. Repeated design pressure across files is generally higher priority than a local expression-level cleanup. Idiom suggestions are generally P3 or Info unless the shape creates repeated complexity or risk.

Rules should downrank, lower confidence, or route known low-action cases to explicit profiles instead of mislabeling them as urgent. Rules should suppress a match only when counterexample evidence makes it invalid, or when user-selected profiles, suppressions, or baselines hide it. For example, fail-fast calls around trusted constants may be lower priority than fail-fast calls around caller-provided input. Exhaustive matches over a closed domain may be preferable to abstraction when the matched operation is local and the domain changes rarely.

Example rules

arch.repeated_match_dispatch reports repeated match expressions that dispatch over the same domain and share multiple explicit arms. The rule should report overlap counts and wildcard/default context.

safety.fail_fast_boundary_call reports unwrap, expect, panic, todo, and unreachable inside public or internal boundaries. Public API boundaries should generally be P1. Internal boundaries should generally be P2 unless evidence shows trusted constants or invariant setup.

idiom.result_combinator_candidate reports mechanically recognizable map_err, and_then, or_else, inspect, and inspect_err shapes. RFC 127's manual_result_map catalog entry owns the map shape, which must not be emitted under both identities.

RFC 127's assign_op_pattern catalog entry reports assignments such as i = i + 1 when resolved operator facts establish equivalence with i += 1.

RFC 127's comprehension_over_loop catalog entry reports append-only list builders that can be represented as eager list comprehensions.

maintainability.single_use_trivial_helper reports private, undocumented, undecorated helpers that are used once and only return a simple pure expression. The rule must mention that domain vocabulary can justify keeping the helper.

maintainability.repeated_literal_domain reports repeated raw string or scalar literal domains used as branch keys or dispatch keys across multiple sites.

arch.competing_source_authority reports two reachable paths that independently select project source, dependency, registry, or implementation authority where the project declares one canonical owner. This rule is language-neutral and may cite Incan, Rust, Oven, or mixed evidence.

arch.cross_facet_contract_drift reports an authored Incan/Rust boundary whose independently maintained declarations or registry entries disagree with the checked interop or public-contract relationship. Generated projections are explicitly excluded from peer comparison.

maintainability.unreachable_public_surface reports a public declaration with no production reachability when the selected project graph is complete enough to support that claim. Unresolved Rust dynamic dispatch must remain affected or unknown according to RFC 106's lower-bound reachability rule; absence of a statically resolved caller is not sufficient by itself.

risk.untested_hotspot reports files, modules, declarations, or architecture findings with high change frequency and weak test or coverage evidence when those process facts are available. The rule must expose the contributing churn and coverage facts rather than only a score.

Alternatives considered

Keep architect as architecture-only

This would preserve a narrow name, but it would force closely related idiom, maintainability, and risk findings into separate commands even though they need the same project-wide codegraph, evidence model, de-duplication, profiles, suppressions, and JSON output. The better boundary is category namespace, not separate infrastructure.

Build a general linter instead

A general linter would fit small syntax-level advice, but it would understate the project-wide design-pressure use case. The command should remain broader than a linter while still identifying local maintainability findings as one category.

Use a language model for rule detection

Model-based detection may be useful for summarization, clustering, or explaining findings in pull requests. It is not the right foundation for rule detection because findings need to be reproducible, testable, source-grounded, and suitable for CI.

Let every rule walk the AST directly

This is the fastest way to add a first rule and the worst way to maintain many rules. It duplicates traversal logic, fragments fact extraction, and makes rule behavior harder to share with agents, editors, and other code-intelligence tools.

Make findings auto-fixable in the engine

Some findings support safe rewrites, such as compound assignment candidates. This RFC keeps those rewrites outside the engine's base contract. RFC 127 defines fixes as a separate mode with explicit applicability, policy, formatting, re-evaluation, and reporting contracts.

Drawbacks

This feature adds a new advisory surface that can become noisy if rule quality is poor. The command must earn trust by showing evidence, classifying findings precisely, calibrating confidence and priority, and naming counterexamples.

The codegraph fact model will grow. If facts are added without a typed query layer, rules will become stringly and brittle. If facts are over-designed too early, implementation will slow down before the rule set proves itself.

Some maintainability findings are subjective. A helper that looks unnecessary may carry important domain meaning. A loop that could be a comprehension may be clearer as a loop when side effects are about to be added. The finding model must make room for this uncertainty through confidence and risk text.

Project-wide scanning may be slower than entry-point scanning, especially for mixed Loaves whose Rust facts require compiler-grade inspection. The implementation should reuse Oven and codegraph identities where possible and leave room for caching, but correctness and evidence take priority over premature optimization.

Implementation architecture

This section is non-normative.

The recommended internal shape is a layered pipeline: direct-source or Oven project selection, Incan and Rust semantic fact extraction into the shared RFC 106 graph, provenance-preserving typed fact views, query indexes, project peer context and boundary metadata, independent rule modules, finding normalization, de-duplication, profile filtering, and text/JSON rendering.

Oven should supply project selection and operational authority, not semantic interpretation. The Incan and Rust frontend infrastructure should produce source facts through the codegraph layer. Architect should own neither parsing, typechecking, Rust project reconstruction, nor Loaf resolution. Architect rules should operate over typed views such as declarations, cross-language identities, reachability, match dispatch sites, call sites, references, assignment/update candidates, usage counts, loop-builder shapes, and result-match shapes.

The rule engine should provide a small metadata contract for rule authors. A rule should declare its code, category, default priority, confidence, profiles, applicability, supported languages or capabilities, required facts, accepted source roles, and explanation. A rule should receive a query context and emit findings. Project peer context is separate from rule metadata: it describes locally meaningful comparable scopes and declared boundary intent, and must preserve whether each claim is declared, derived, or experimental.

The report layer should be shared by all rules. Sorting, de-duplication, JSON serialization, text formatting, suppression matching, and baseline matching should not be implemented per rule.

The rule set should be small and calibrated rather than a large catalog. New rules should be added only when they have clear positive fixtures, negative fixtures, and calibration evidence from real source.

Experimental peer-baseline analysis is a consumer of the same fact and query layers. It may construct explainable signatures and compare declared or derived peer groups, and it stays outside the deterministic rule path.

Layers affected

  • Oven project model: Inside a Loaf, Architect must consume Oven's selected workspace members, authored source facets, dependencies, generated inputs, source roles, and graph authority without becoming a second resolver.
  • Incan parser / AST: No new user syntax is required, but Incan source traversal must expose enough body shapes for codegraph facts.
  • Incan typechecker / Symbol resolution: Rules may need checked public API metadata, resolved imports, type facts for Result shapes, and symbol usage information.
  • Rust inspection / compiler facts: Rust-only and mixed Loaves require declaration, reference, call, visibility, reachability, public-contract, and provenance facts in the same RFC 106 identities and relationships used for Incan. Dynamic Rust reachability must retain RFC 106's lower-bound semantics.
  • IR Lowering: No required impact.
  • Emission: No required impact.
  • Stdlib / Runtime (incan_stdlib): No required runtime impact, though stdlib feature surfaces such as Result combinators and iterator adapters inform idiom rules.
  • Formatter: RFC 105 itself requires no formatter changes. RFC 127 requires the formatter to remain callable as a library after Architect applies safe fixes; the formatter still owns canonical representation rather than lint evaluation.
  • LSP / Tooling: The JSON findings format should be usable by editors, agents, CI, and other diagnostics-style surfaces. Editor consumers must see the same selected source authority and language provenance as the CLI.
  • CLI / Project tooling: oven architect needs direct-source and Oven-selected scope handling, profiles, stable text/JSON output, suppression support, and baseline support. Peer-baseline tooling needs opt-in invocation and schema-, compiler-, Oven-plan-, and graph-versioned snapshots.
  • Documentation: The CLI reference must document direct-source versus Loaf-selected behavior, Incan/Rust/mixed coverage, source-role provenance, profiles, categories, priorities, confidence, suppressions, peer-context provenance, and examples.

Design Decisions

  • The default includes all stable supported rules, with explicit category/profile/priority filtering. Experimental rules remain opt-in. Missing required frontend capabilities are visible coverage gaps.
  • Clippy information and capabilities may support Rust analysis through the selected frontend path, preserving tool/source provenance, configuration and honest coverage. This neither duplicates local lint ownership nor introduces Cargo mode.
  • Accepted baselines are separate versioned review artifacts carrying scope, source/graph identity, reasons and retirement records. Ordinary scans never approve or rewrite them. Authored peer intent belongs in checked metadata; observations and caches do not redefine it.
  • Peer signatures expose versioned components with explicit evidence tiers. Checked declaration/type/call/body/ownership facts, declared roles and weak naming/path hints remain distinguishable; unknown resolution and similarity do not become semantic authority.
  • Peer-baseline outliers run only under --profile experimental, are advisory and never fail CI by default. Snapshots use the same reviewed baseline artifact as other findings. Calibrating what counts as an outlier is implementation work, as it is for risk rules.
  • Rule applicability names required languages and frontend fact capabilities. Missing required facts disable the affected evaluation with a coverage diagnostic rather than a low-confidence guess.
  • RFC 118's no-Cargo boundary applies. Direct-source analysis and the Oven-selected Loaf graph remain the supported authorities; no separate Cargo project discovery mode is introduced.
  • Selecting and calibrating concrete risk rules is implementation work. The evidence and coverage requirements remain binding; missing inputs are not zero risk.
  • Rule registration is repository/catalog-owned. External rule-package loading is not part of this design; typed rule interfaces remain extensible.

  • Oven owns oven architect; the Incan-only convenience delegates. Direct Incan and Rust input use the same capability and explicit-selection rules.

  • Project scope follows documented Oven selection and checked loaf.toml customization, with effective exclusions visible and no ambient Cargo discovery.
  • Findings are typed Incan records with numbered RFC 127 catalog identities. JSON is one renderer; exact serialization layout is not a language-design vote.
  • Stable local rules require calibrated narrow shapes and counterexamples; broad judgments stay experimental.
  • Input-origin facts distinguish proved constants from caller input, retaining unknown origins and missing coverage honestly.