use crate::findings::types::{Confidence, FindingCategory, Severity};
use crate::rules::metadata::RuleMetadata;
use crate::rules::{RuleLifecycle, SignalSource};
pub(super) static RULES: &[RuleMetadata] = &[
RuleMetadata {
rule_id: "architecture.large-file",
title: "File exceeds recommended size",
category: FindingCategory::Architecture,
default_severity: Severity::Medium,
max_severity: Severity::High,
default_confidence: Confidence::Medium,
lifecycle: RuleLifecycle::Experimental,
signal_source: SignalSource::TextHeuristic,
docs_url: None,
description: "This file has more lines of code than the configured threshold. Large files accumulate responsibilities over time and make navigation and testing harder.",
recommendation: Some(
"Split the file into smaller, focused modules. Use repopilot.toml `max_file_loc` to adjust the threshold.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.deep-directory-nesting",
title: "Deep directory nesting detected",
category: FindingCategory::Architecture,
default_severity: Severity::Low,
default_confidence: Confidence::Medium,
lifecycle: RuleLifecycle::Experimental,
signal_source: SignalSource::TextHeuristic,
docs_url: None,
description: "A file is nested deeply within the directory structure. Deep directory nesting makes the codebase harder to navigate, import from, and maintain.",
recommendation: Some(
"Simplify the directory structure or introduce path aliases to flatten the file layout. Adjust the max depth threshold using scan config.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.deep-relative-imports",
title: "Deep relative import found",
category: FindingCategory::Architecture,
default_severity: Severity::Low,
max_severity: Severity::Medium,
default_confidence: Confidence::Medium,
lifecycle: RuleLifecycle::Preview,
signal_source: SignalSource::TextHeuristic,
docs_url: None,
description: "A source file imports across three or more parent directories. Deep relative imports are fragile during refactors and often indicate missing module boundaries or aliases.",
recommendation: Some(
"Introduce a stable module boundary, path alias, or facade module instead of importing through multiple parent directories.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.barrel-file-risk",
title: "Risky barrel file detected",
category: FindingCategory::Architecture,
default_severity: Severity::Low,
default_confidence: Confidence::Low,
lifecycle: RuleLifecycle::Experimental,
signal_source: SignalSource::TextHeuristic,
docs_url: None,
description: "An index file re-exports many modules or relies heavily on wildcard exports. Large barrel files can become unstable module hubs and make dependency boundaries harder to understand.",
recommendation: Some(
"Split the barrel into smaller feature-level entrypoints or replace wildcard exports with explicit, stable public APIs.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.too-many-modules",
title: "Directory contains too many modules",
category: FindingCategory::Architecture,
default_severity: Severity::Medium,
default_confidence: Confidence::Medium,
lifecycle: RuleLifecycle::Experimental,
signal_source: SignalSource::TextHeuristic,
docs_url: None,
description: "A directory contains more files than the configured threshold, suggesting it may need to be broken into sub-packages.",
recommendation: Some(
"Group related files into sub-directories. Adjust `max_directory_modules` in repopilot.toml if the current threshold is too strict.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.circular-dependency",
title: "Circular import dependency detected",
category: FindingCategory::Architecture,
default_severity: Severity::High,
default_confidence: Confidence::High,
lifecycle: RuleLifecycle::Stable,
signal_source: SignalSource::ImportGraph,
docs_url: Some(
"https://github.com/MykytaStel/repopilot/blob/main/docs/rulesets.md#architecture",
),
description: "Two or more files import each other, forming a cycle. Circular dependencies make build order undefined, complicate testing, and prevent dead-code elimination.",
recommendation: Some(
"Extract the shared logic into a third module that both files can import without creating a cycle.",
),
false_positive_notes: Some(
"Generated, fixture, and test-only import cycles should stay out of default scans; production source cycles are treated as actionable.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.excessive-fan-out",
title: "File imports too many project-internal modules",
category: FindingCategory::Architecture,
default_severity: Severity::Medium,
default_confidence: Confidence::High,
lifecycle: RuleLifecycle::Stable,
signal_source: SignalSource::ImportGraph,
docs_url: Some(
"https://github.com/MykytaStel/repopilot/blob/main/docs/rulesets.md#architecture",
),
description: "This file depends on an unusually large number of other internal modules, making it a fragile integration point that breaks whenever any dependency changes.",
recommendation: Some(
"Introduce an abstraction layer or facade to reduce the number of direct imports. Consider splitting responsibilities across multiple files.",
),
false_positive_notes: Some(
"Aggregator entrypoints can be acceptable when intentionally reviewed; default thresholds should be raised locally only after that decision.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.high-instability-hub",
title: "High-instability hub: high fan-in and high fan-out",
category: FindingCategory::Architecture,
default_severity: Severity::High,
default_confidence: Confidence::High,
contextual_confidence: true,
lifecycle: RuleLifecycle::Stable,
signal_source: SignalSource::ImportGraph,
docs_url: Some(
"https://github.com/MykytaStel/repopilot/blob/main/docs/rulesets.md#architecture",
),
description: "This file is both widely imported (high fan-in) and depends on many other modules (high fan-out). Changes here ripple across the codebase with no stable upstream to absorb them.",
recommendation: Some(
"Separate the stable, widely-imported interface from the volatile implementation details to reduce coupling.",
),
false_positive_notes: Some(
"Framework entrypoints and generated hubs can be expected; production modules with high fan-in and fan-out should be reviewed. When the repository contains unresolved relative imports, fan-in is a lower bound and the finding is reported at Medium confidence.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.dead-module",
title: "Dead module detected",
category: FindingCategory::Architecture,
default_severity: Severity::Low,
default_confidence: Confidence::High,
contextual_confidence: true,
lifecycle: RuleLifecycle::Experimental,
signal_source: SignalSource::ImportGraph,
docs_url: None,
description: "This production file is not imported by any other project file and is not a known entrypoint. It may be dead code.",
recommendation: Some(
"Remove the file if it is no longer used, or ensure it is exported in the package's public API.",
),
false_positive_notes: Some(
"Dynamic imports, dependency injection, and build-tool aliases create importers the graph cannot see. When the repository contains unresolved relative imports the finding is reported at Medium confidence, and it is suppressed when an unresolved import matches the candidate file's name.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.layer-violation",
title: "Layer violation detected",
category: FindingCategory::Architecture,
default_severity: Severity::Medium,
default_confidence: Confidence::High,
lifecycle: RuleLifecycle::Experimental,
signal_source: SignalSource::ImportGraph,
docs_url: None,
description: "A module imports another module from a higher layer than its own, against the order declared in `[[architecture.layers]]`. Opt-in: emits nothing unless layers are configured.",
recommendation: Some(
"Invert the dependency using an interface, or move the shared logic into a layer at or below the importer.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.test-leak",
title: "Test code leaked into production",
category: FindingCategory::Architecture,
default_severity: Severity::Medium,
default_confidence: Confidence::High,
lifecycle: RuleLifecycle::Experimental,
signal_source: SignalSource::ImportGraph,
docs_url: None,
description: "A production module imports a test or fixture module.",
recommendation: Some(
"Remove the test dependency from production code or extract the shared logic into a production utility.",
),
..RuleMetadata::DEFAULT
},
RuleMetadata {
rule_id: "architecture.package-boundary-violation",
title: "Package boundary violation",
category: FindingCategory::Architecture,
default_severity: Severity::Medium,
default_confidence: Confidence::High,
lifecycle: RuleLifecycle::Experimental,
signal_source: SignalSource::ImportGraph,
docs_url: None,
description: "A file imports a private module from another package/feature instead of using its public API. Opt-in: emits nothing unless `[architecture] package_roots` is configured.",
recommendation: Some(
"Import from the package's public barrel file (e.g. index.ts, mod.rs) instead of reaching into its internal implementation.",
),
..RuleMetadata::DEFAULT
},
];