Skip to content

feat: add shadow module and repeated field pattern audit rules (#635, #441)#943

Merged
chubes4 merged 2 commits intomainfrom
feat/shadow-module-detection
Mar 23, 2026
Merged

feat: add shadow module and repeated field pattern audit rules (#635, #441)#943
chubes4 merged 2 commits intomainfrom
feat/shadow-module-detection

Conversation

@chubes4
Copy link
Member

@chubes4 chubes4 commented Mar 23, 2026

Summary

Two new audit rules, both language-agnostic:

Shadow Module Detection (#635)

Detects directories that are near-copies — overlapping file names with high content similarity. Stronger signal than individual DuplicateFunction findings because it indicates systemic duplication (an entire module was copy-pasted).

  • Groups fingerprints by parent directory
  • Compares all pairs for ≥50% file name overlap AND ≥50% method/structural hash overlap
  • Filters out test directories and parent/child relationships
  • New finding kind: ShadowModule

Repeated Struct Field Pattern (#441)

Detects groups of fields that appear together in 3+ struct/class/interface definitions. Candidates for extraction into a shared type.

  • Walks source files, extracts struct definitions using brace-depth tracking
  • Parses field declarations (name + type) from line content
  • Finds co-occurring field groups across multiple types
  • New finding kind: RepeatedFieldPattern

Also

Addresses #635, #441

chubes4 added 2 commits March 23, 2026 03:37
Detect directories that are near-copies of each other — overlapping file
names with high content similarity. Stronger signal than individual
DuplicateFunction findings because it indicates systemic duplication
(an entire module was copy-pasted instead of properly refactored).

Algorithm is language-agnostic:
- Groups fingerprints by parent directory
- Compares all directory pairs for file name overlap (≥50%)
- Verifies content similarity via method/structural hash overlap (≥50%)
- Filters out test directories and parent/child relationships
- Emits ShadowModule findings for both directories in a pair

Wired into the audit pipeline as Phase 4n.
Detect groups of fields that appear together in 3+ struct definitions.
When the same name+type fields co-occur across multiple types, they're
candidates for extraction into a shared struct with flatten/embed.

Algorithm is language-agnostic:
- Walks source files, extracts struct/class/interface definitions
- Parses field declarations using line-level pattern matching
- Groups fields by signature, finds co-occurring groups
- Reports when MIN_GROUP_SIZE (2) fields appear in MIN_OCCURRENCES (3+) structs

Handles Rust (struct), PHP (class), TS/JS (interface), Go (struct).
Wired into the audit pipeline as Phase 4o.
@chubes4 chubes4 merged commit 1182d92 into main Mar 23, 2026
1 check passed
@homeboy-ci
Copy link
Contributor

homeboy-ci bot commented Mar 23, 2026

Homeboy Results — homeboy

Audit

Failure Digest

Audit Failure Digest

  • Alignment score: 0.921
  • Severity counts: info: 18, unknown: 3, warning: 44
  • Outliers in current run: 3
  • Parsed outlier entries: 3
  • Drift increased: yes
  • New findings since baseline: 27
    1. intra-method-duplication — Duplicated block in extract_type_name — 5 identical lines at line 254 and line 264 (intra-method-duplication::src/core/code_audit/field_patterns.rs::IntraMethodDuplicate)
    2. intra-method-duplication — Duplicated block in run_fix_iteration — 12 identical lines at line 445 and line 475 (intra-method-duplication::src/core/refactor/plan/verify.rs::IntraMethodDuplicate)
    3. dead_code — Public function 'diff_changed_files' is not referenced by any other file (dead_code::src/core/code_audit/impact.rs::UnreferencedExport)
    4. dead_code — Public function 'expand_scope' is not referenced by any other file (dead_code::src/core/code_audit/impact.rs::UnreferencedExport)
    5. dead_code — Public function 'find_affected_files' is not referenced by any other file (dead_code::src/core/code_audit/impact.rs::UnreferencedExport)
  • Top actionable findings:
    1. src/core/code_audit/conventions.rs — god_file — File has 1537 lines (threshold: 1000)
    2. src/core/code_audit/mod.rs — god_file — File has 1119 lines (threshold: 1000)
    3. src/core/refactor/plan/planner.rs — god_file — File has 1183 lines (threshold: 1000)
    4. src/core/refactor/plan/planner.rs — high_item_count — File has 28 top-level items (threshold: 15)
    5. src/core/code_audit/conventions.rs — intra_method_duplicate — Duplicated block in check_signature_consistency — 5 identical lines at line 638 and line 696
All parsed audit findings (65)
1. **src/core/code_audit/conventions.rs** — god_file — File has 1537 lines (threshold: 1000)
2. **src/core/code_audit/mod.rs** — god_file — File has 1119 lines (threshold: 1000)
3. **src/core/refactor/plan/planner.rs** — god_file — File has 1183 lines (threshold: 1000)
4. **src/core/refactor/plan/planner.rs** — high_item_count — File has 28 top-level items (threshold: 15)
5. **src/core/code_audit/conventions.rs** — intra_method_duplicate — Duplicated block in `check_signature_consistency` — 5 identical lines at line 638 and line 696
6. **src/core/code_audit/conventions.rs** — intra_method_duplicate — Duplicated block in `discover_conventions` — 5 identical lines at line 412 and line 429
7. **src/core/code_audit/field_patterns.rs** — intra_method_duplicate — Duplicated block in `extract_type_name` — 5 identical lines at line 254 and line 264
8. **src/core/code_audit/impact.rs** — intra_method_duplicate — Duplicated block in `find_affected_files` — 5 identical lines at line 408 and line 418
9. **src/core/code_audit/impact.rs** — intra_method_duplicate — Duplicated block in `test_diff_fingerprints_detects_removed_export` — 6 identical lines at line 594 and line 603
10. **src/core/code_audit/impact.rs** — intra_method_duplicate — Duplicated block in `test_diff_fingerprints_detects_removed_hook` — 6 identical lines at line 649 and line 658
11. **src/core/code_audit/impact.rs** — intra_method_duplicate — Duplicated block in `test_find_affected_calls_removed_function` — 5 identical lines at line 689 and line 698
12. **src/core/refactor/plan/verify.rs** — intra_method_duplicate — Duplicated block in `run_fix_iteration` — 12 identical lines at line 445 and line 475
13. **src/core/code_audit/impact.rs** — unreferenced_export — Public function 'diff_changed_files' is not referenced by any other file
14. **src/core/code_audit/impact.rs** — unreferenced_export — Public function 'expand_scope' is not referenced by any other file
15. **src/core/code_audit/impact.rs** — unreferenced_export — Public function 'find_affected_files' is not referenced by any other file
16. **src/core/code_audit/impact.rs** — unreferenced_export — Public function 'fingerprint_from_git_ref' is not referenced by any other file
17. **src/core/code_audit/run.rs** — unreferenced_export — Public function 'default_audit_exit_code' is not referenced by any other file
18. **src/core/refactor/plan/planner.rs** — unreferenced_export — Public function 'analyze_stage_overlaps' is not referenced by any other file
19. **src/core/refactor/plan/planner.rs** — unreferenced_export — Public function 'normalize_sources' is not referenced by any other file
20. **src/core/refactor/plan/planner.rs** — unreferenced_export — Public function 'run_lint_refactor' is not referenced by any other file
21. **src/core/refactor/plan/planner.rs** — unreferenced_export — Public function 'run_test_refactor' is not referenced by any other file
22. **src/core/refactor/plan/planner.rs** — unreferenced_export — Public function 'summarize_plan_totals' is not referenced by any other file
23. **src/core/refactor/plan/planner.rs** — unreferenced_export — Public function 'test_refactor_request' is not referenced by any other file
24. **src/core/code_audit/conventions.rs** — missing_test_method — Method 'all_names' has no corresponding test (expected 'test_all_names')
25. **src/core/code_audit/conventions.rs** — missing_test_method — Method 'check_signature_consistency' has no corresponding test (expected 'test_check_signature_consistency')
26. **src/core/code_audit/conventions.rs** — missing_test_method — Method 'discover_conventions' has no corresponding test (expected 'test_discover_conventions')
27. **src/core/code_audit/conventions.rs** — missing_test_method — Method 'from_extension' has no corresponding test (expected 'test_from_extension')
28. **src/core/code_audit/conventions.rs** — missing_test_method — Method 'from_path' has no corresponding test (expected 'test_from_path')
29. **src/core/code_audit/field_patterns.rs** — missing_test_method — Method 'run' has no corresponding test (expected 'test_run')
30. **src/core/code_audit/field_patterns.rs** — orphaned_test — Test method 'test_new' references 'new' which no longer exists in the source
31. **src/core/code_audit/impact.rs** — missing_test_method — Method 'diff_changed_files' has no corresponding test (expected 'test_diff_changed_files')
32. **src/core/code_audit/impact.rs** — missing_test_method — Method 'expand_scope' has no corresponding test (expected 'test_expand_scope')
33. **src/core/code_audit/impact.rs** — missing_test_method — Method 'find_affected_files' has no corresponding test (expected 'test_find_affected_files')
34. **src/core/code_audit/impact.rs** — missing_test_method — Method 'fingerprint_from_git_ref' has no corresponding test (expected 'test_fingerprint_from_git_ref')
35. **src/core/code_audit/impact.rs** — orphaned_test — Test method 'test_find_affected_calls_removed_function' references 'find_affected_calls_removed_function' which no longer exists in the source
36. **src/core/code_audit/impact.rs** — orphaned_test — Test method 'test_find_affected_extends_renamed_class' references 'find_affected_extends_renamed_class' which no longer exists in the source
37. **src/core/code_audit/impact.rs** — orphaned_test — Test method 'test_find_affected_hooks_removed_action' references 'find_affected_hooks_removed_action' which no longer exists in the source
38. **src/core/code_audit/impact.rs** — orphaned_test — Test method 'test_find_affected_imports_renamed_type' references 'find_affected_imports_renamed_type' which no longer exists in the source
39. **src/core/code_audit/impact.rs** — orphaned_test — Test method 'test_find_affected_renamed_function_in_calls' references 'find_affected_renamed_function_in_calls' which no longer exists in the source
40. **src/core/code_audit/impact.rs** — orphaned_test — Test method 'test_find_affected_skips_changed_files' references 'find_affected_skips_changed_files' which no longer exists in the source
41. **src/core/code_audit/impact.rs** — orphaned_test — Test method 'test_make_fingerprint' references 'make_fingerprint' which no longer exists in the source
42. **src/core/code_audit/run.rs** — missing_test_method — Method 'default_audit_exit_code' has no corresponding test (expected 'test_default_audit_exit_code')
43. **src/core/code_audit/run.rs** — missing_test_method — Method 'run_main_audit_workflow' has no corresponding test (expected 'test_run_main_audit_workflow')
44. **src/core/code_audit/shadow_modules.rs** — missing_test_method — Method 'run' has no corresponding test (expected 'test_run')
45. **src/core/code_audit/shadow_modules.rs** — orphaned_test — Test method 'test_make_fp' references 'make_fp' which no longer exists in the source
46. **src/core/refactor/plan/planner.rs** — missing_test_method — Method 'analyze_stage_overlaps' has no corresponding test (expected 'test_analyze_stage_overlaps')
47. **src/core/refactor/plan/planner.rs** — missing_test_method — Method 'build_refactor_plan' has no corresponding test (expected 'test_build_refactor_plan')
48. **src/core/refactor/plan/planner.rs** — missing_test_method — Method 'lint_refactor_request' has no corresponding test (expected 'test_lint_refactor_request')
49. **src/core/refactor/plan/planner.rs** — missing_test_method — Method 'normalize_sources' has no corresponding test (expected 'test_normalize_sources')
50. **src/core/refactor/plan/planner.rs** — missing_test_method — Method 'run_lint_refactor' has no corresponding test (expected 'test_run_lint_refactor')
51. **src/core/refactor/plan/planner.rs** — missing_test_method — Method 'run_test_refactor' has no corresponding test (expected 'test_run_test_refactor')
52. **src/core/refactor/plan/planner.rs** — missing_test_method — Method 'summarize_plan_totals' has no corresponding test (expected 'test_summarize_plan_totals')
53. **src/core/refactor/plan/planner.rs** — orphaned_test — Test method 'test_component' references 'component' which no longer exists in the source
54. **src/core/refactor/plan/planner.rs** — orphaned_test — Test method 'test_refactor_request' references 'refactor_request' which no longer exists in the source
55. **src/core/refactor/plan/planner.rs** — orphaned_test — Test method 'test_tmp_dir' references 'tmp_dir' which no longer exists in the source
56. **src/core/refactor/plan/verify.rs** — missing_test_file — No test file found (expected 'tests/core/refactor/plan/verify_test.rs') and no inline tests
57. **src/core/code_audit/conventions.rs** — repeated_field_pattern — Repeated field group [expected_methods, confidence, expected_registrations] appears in 3 structs: src/core/code_audit/conventions.rs::Convention, src/core/code_audit/mod.rs::ConventionReport, src/core/code_audit/mod.rs::DirectoryConvention
58. **src/core/code_audit/conventions.rs** — repeated_field_pattern — Repeated field group [suggestion, kind] appears in 3 structs: src/core/code_audit/conventions.rs::Deviation, src/core/code_audit/findings.rs::Finding, src/core/code_audit/report.rs::AuditSummaryFinding
59. **src/core/code_audit/mod.rs** — repeated_field_pattern — Repeated field group [expected_methods, confidence, expected_registrations] appears in 3 structs: src/core/code_audit/conventions.rs::Convention, src/core/code_audit/mod.rs::ConventionReport, src/core/code_audit/mod.rs::DirectoryConvention
60. **src/core/code_audit/run.rs** — repeated_field_pattern — Repeated field group [ignore_baseline, baseline] appears in 3 structs: src/commands/utils/args.rs::BaselineArgs, src/core/code_audit/run.rs::AuditRunWorkflowArgs, src/core/extension/lint/run.rs::LintRunWorkflowArgs
61. **src/core/code_audit/run.rs** — repeated_field_pattern — Repeated field group [ratchet, json_summary] appears in 3 structs: src/commands/audit.rs::AuditArgs, src/commands/test.rs::TestArgs, src/core/code_audit/run.rs::AuditRunWorkflowArgs
62. **src/core/refactor/plan/planner.rs** — repeated_field_pattern — Repeated field group [sniffs, category, glob, file, exclude_sniffs, errors_only] appears in 3 structs: src/commands/lint.rs::LintArgs, src/core/extension/lint/run.rs::LintRunWorkflowArgs, src/core/refactor/plan/planner.rs::LintSourceOptions
63. **src/commands/docs.rs** — outlier — (outlier)
64. **tests/commands/deploy_test.rs** — outlier — (outlier)
65. **src/core/engine/undo/rollback.rs** — outlier — (outlier)
- Full audit log: https://github.com/Extra-Chill/homeboy/actions/runs/23420394848

Autofixability classification

  • Overall: auto_fixable
  • Autofix enabled: yes
  • Autofix attempted this run: no
  • Auto-fixable failed commands:
    • audit
  • Failed commands with available automated fixes:
    • audit

Machine-readable artifacts

  • homeboy-lint-summary.json
  • homeboy-test-failures.json
  • homeboy-audit-summary.json
  • homeboy-autofixability.json

⚡ Scope: changed files only

audit (changed files only)

Auto-refactor

ℹ️ Autofix enabled, but no fixable file changes were produced

Failure Digest

Autofixability classification

  • Overall: human_needed
  • Autofix enabled: yes
  • Autofix attempted this run: no
  • Human-needed failed commands:
    • refactor --from all

Machine-readable artifacts

  • homeboy-lint-summary.json
  • homeboy-test-failures.json
  • homeboy-audit-summary.json
  • homeboy-autofixability.json

⚡ Scope: changed files only

refactor --from all

Tooling versions
  • Homeboy CLI: homeboy 0.85.3+304f343
  • Extension: rust from https://github.com/Extra-Chill/homeboy-extensions
  • Extension revision: unknown
  • Action: Extra-Chill/homeboy-action@v2

Homeboy Action v1

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.

Audit rule: enforce architecture/layer ownership contracts

1 participant