Overview
See what deserves a refactor.
See the evidence first.
Reforge analyzes the structure of a codebase, explains every finding, and makes gaps in analysis visible.
reforge analyze . --output html --output-file reforge-report.html
From signal to decision
Reforge does not hide structural observations behind a score. It gives reviewers the context needed to decide whether a change is worthwhile.
Find the pressure points
Surface duplication, oversized responsibilities, dependency tangles, naming drift, and difficult value paths.
Inspect the evidence
Trace each finding back to its rule, measurements, source locations, and exact value-flow witness when available.
Know the limits
See partial and unsupported analysis explicitly. No findings never means more coverage than Reforge actually observed.
What Codebase looks for
Responsibilities
Find oversized files, functions, types, public surfaces, and directories that may own too much.
Duplication and drift
Inspect repeated implementations, overlapping shapes, generic buckets, naming drift, and dependency tangles.
See how Codebase analysis works →
Designed for review, not scoring
Findings are inspection prompts—not severity labels, priorities, or defect predictions. Reforge runs locally, uploads no source code, and collects no telemetry. Use it interactively, generate a standalone HTML report, or compare reviewed JSON baselines in CI. An advanced Dataflow analysis is available when exact value-path inspection is needed, but it is not required for normal Codebase use.
Agent-code Playground
See three bad habits that coding agents can leave behind even when a patch works: bypassing an existing boundary, copying a local workaround instead of the shared abstraction, and reusing a helper from the wrong side of the dependency graph.
Each scenario compares a real before/after repository fixture, connects the agent’s local decision to the wider codebase, and presents the single Issue generated from the after state.
The documentation build verifies that every before state has zero Issues and every after state has exactly one expected Issue. The Playground has no backend, uploads no code, and does not scan or evaluate third-party repositories.
User guide
Install
The verified release installer chooses the supported asset for the current OS and CPU, validates it against the release SHA256SUMS, checks reforge --version, and atomically installs the binary. It also installs the reforge-analyze Codex skill unless disabled.
Unix:
curl -fsSL https://raw.githubusercontent.com/LyleMi/Reforge/main/scripts/install.sh | sh
# Pin a release or choose a destination:
curl -fsSL https://raw.githubusercontent.com/LyleMi/Reforge/main/scripts/install.sh | \
sh -s -- --version v0.2.0 --bin-dir "$HOME/.local/bin"
The Unix default is ${REFORGE_INSTALL_DIR:-$HOME/.local/bin}. Supported assets are Linux x86_64 and macOS x86_64/aarch64.
PowerShell:
$installer = Join-Path $env:TEMP "install-reforge.ps1"
irm https://raw.githubusercontent.com/LyleMi/Reforge/main/scripts/install.ps1 -OutFile $installer
& $installer
# Pin a release or choose a destination:
& $installer -Version v0.2.0 -BinDir C:\Tools\Reforge
Windows x86_64 defaults to %LOCALAPPDATA%\Reforge\bin. Use --skip-skill or -SkipSkill to install only the binary. Neither installer edits PATH; when necessary it prints the exact command to add the selected directory. Re-running an installer safely replaces the same version or upgrades it.
From a source checkout, the existing scripts/install-reforge.sh, scripts/install-reforge.ps1, and .bat wrapper remain available for cargo install --path development workflows.
Analyze
Run the default Codebase analysis:
reforge analyze . --reproducible
Without a configuration file, the CLI enables a small starter set of preview
advisories for large files, long functions, dependency cycles, and similar
functions. They produce review prompts but cannot fail a policy gate. Run
reforge init to write the same starter selection to reforge.toml, then tune
or disable it for the repository.
Dataflow is explicit. Run it alone or combine both core analyses over one workspace index:
reforge analyze . --analysis dataflow --output json --reproducible
reforge analyze . --analysis codebase --analysis dataflow --reproducible
Use --output and --output-file for human, HTML, JSON, YAML, or SARIF reports. Raw Codebase metrics and the complete Flow IR are opt-in debug sidecars:
reforge analyze . --analysis codebase --metrics-output metrics.json
reforge analyze . --analysis dataflow --flow-ir-output flow-ir.json
Pick and clean comments
reforge comments pick . --candidates
reforge comments clean . --text 'your code here'
reforge comments clean . --text 'your code here' --apply
Pick extracts Rust/JS/TS comments with context. Clean previews selected removals
unless --apply is supplied, and retains protected comments. Use
reforge comments clean . --all --apply to remove every comment in scope,
including protected comments. The
comment guide covers JSON plans, selectors and write guarantees.
Read a report
Treat issues as the only decision units. Each Issue owns one typed subject and one or more Evidence records. Evidence identifies the rule and may include measurements, locations, and an ordered Dataflow witness.
Read Coverage before interpreting absence. Check the selected analysis status, every language receipt, capability limitation, rule execution, and suppression count. An empty Issue list is an observed zero only where Coverage is observable. Dataflow never represents a partial or unresolved path as exact.
Reforge intentionally emits no health score, severity, priority, or defect probability.
Baselines and CI gates
A baseline must use a compatible report format with the same producer, identity scheme, and workspace identity. Producer versions and unrelated analysis sets may differ. Coverage, scope, configuration, policy, or rule-semantic changes produce an unknown baseline state instead of claiming that an Issue is new or resolved.
After reviewing and storing a baseline report, gate new, updated, or unknown policy Issues with:
reforge analyze . --output json --output-file current.json \
--baseline reforge-baseline.json --gate new --reproducible
--gate all fails on every current policy Issue. Most rules remain preview/off;
enable selected preview rules as advisories in versioned reforge.toml. Only
stable rules can be enforced as policy.
Configuration and rules
reforge init writes a versioned configuration. Use reforge config validate, reforge config show, and reforge rules --output json to inspect effective settings and rule contracts. Durable settings belong in reforge.toml; temporary overrides use --set key=value.
Troubleshooting
Use Coverage and its capability limitations when zero Issues are reported. Regenerate incompatible older reports rather than editing them. If a Dataflow policy is rejected, verify that it names one supported language and that every source and sink path/symbol names exactly one frontend declaration.
Pick and clean comments
Use reforge comments pick to review comments, and reforge comments clean to
preview or apply explicitly selected removals. Both commands run locally.
They support Rust, JavaScript/JSX and TypeScript/TSX, including .mjs, .cjs,
.mts and .cts. Vue, other languages and files with syntax/encoding errors
are not editable and appear in the skipped-file receipts when discovered.
Pick a review set
reforge comments pick .
reforge comments pick . --candidates
reforge comments pick src --contains 'legacy' --kind line
reforge comments pick . --output json --output-file comments.json
The inventory contains the original comment text, path, line range, enclosing or following symbol when available, nearby source, protection reasons and review hints. Consecutive standalone ordinary line comments are one group: selecting a matching line selects the whole group. This preserves continuation lines of directives, licenses and rationale. Inspect the group before cleaning. Inline comments and block comments are separate items.
Review hints cover empty/separator comments, repeated text within one file, known template placeholders and possible commented-out code. They are heuristics, not proof of uselessness or obsolescence. Reforge does not infer whether an arbitrary explanation is stale and does not use a model to rewrite comments.
Selection flags:
| Flag | Meaning |
|---|---|
--contains TEXT | Case-sensitive literal substring of the original comment |
--text TEXT | Exact trimmed body without comment delimiters; multiline bodies retain line breaks |
--id ID | Exact ID from the inventory |
--kind line|block|documentation | Limit comment kind |
--candidates | Limit to comments with review hints |
Repeated values within one selector are ORed. Different selectors are ANDed. Empty text selectors are rejected. IDs include the file content hash: any change to that file makes its old IDs stale, which produces an error instead of silently selecting another comment. JSON inventory/plan output is deterministic for an unchanged root and configuration. Byte ranges refer to decoded UTF-8 without its BOM, not byte offsets in a UTF-16 file.
The commands discover reforge.toml and honor its [scope] settings. The scope
flags --config, --include-hidden, --include-generated, --no-gitignore,
--exclude-tests and repeatable --ignore-path are also available. Generated
folders such as target, node_modules, dist and build are excluded by
default. --ignore-path matches relative paths/directory prefixes, not globs.
Excluded files are outside the inventory; discovered but unsupported or invalid
files are listed as skipped. Other analyzer configuration is validated but does
not enable or disable comment inventory or cleanup.
Preview and apply
# Preview: source files stay unchanged.
reforge comments clean . --text 'your code here'
# Apply the selected ordinary comment removals in one command.
reforge comments clean . --text 'your code here' --apply
# Or select an exact comment group from pick.
reforge comments clean . --id comment-<full-id>
Clean requires --all, --id, --text or --contains. --candidates alone does not
authorize deletion. Documentation comments, license notices, recognized tool
instructions (including reforge:, @ts-ignore, ESLint and source-map markers),
TODO/FIXME debt, generated notices and recognized safety/rationale statements
are protected in text/ID selection mode, even if the selector matches them.
Protection is conservative and marker-based; a plain comment with important
meaning but no recognized marker still requires human judgment.
The default output is a unified diff on stdout. A summary and skipped-file
receipts go to stderr. A zero-match or fully protected selection produces no
changes. Applying a selection skips protected comments and reports their count.
To see their specific reasons, use pick with the same selectors.
To remove every comment, including protected documentation, licenses, TODO/FIXME and tool directives:
# Preview every removal in the configured scope.
reforge comments clean . --all
# One-command application.
reforge comments clean . --all --apply
# Or save and replay the full-removal plan.
reforge comments clean . --all --output json --output-file all-comments.json
reforge comments clean . --plan all-comments.json --apply
--all is mutually exclusive with text/ID/kind/candidate selectors and --plan.
It still honors scope exclusions and supported languages. Unsupported or invalid
files are reported as skipped; strings containing comment delimiters remain
unchanged. Hash checks, syntax checks and write recovery still apply. Deleting
documentation or tool directives can change generated docs, lint/type-check
results or other tooling behavior even when non-comment syntax is unchanged.
The summary explicitly identifies all-comments mode, including when replaying a
saved plan. protected_comments counts comments retained by protection, so it
is zero in this mode.
Save a plan when review and application happen separately:
reforge comments clean . --text 'your code here' \
--output json --output-file cleanup.json
reforge comments clean . --plan cleanup.json
reforge comments clean . --plan cleanup.json --apply
Pass the original workspace path when replaying a plan from a different working
directory. A plan cannot be combined with new selection or scope flags. Plans
use version 1 for protected text/ID selection and version 2 with
removal_mode: "all" for full removal. Existing version 1 plans retain their
original protection behavior. These are not schema 27 reports or workflow artifacts. The plan records selected IDs, file hashes, original edit text and
replacement ranges. Application validates the workspace root, hashes, selection,
the recorded removal mode and recomputed edits; arbitrary modified replacements are rejected.
To narrow a plan, generate another plan with the desired IDs rather than editing
its ranges or replacement text.
Output files must be new files with .json, .diff, .patch or .txt
extensions. Existing output files are never overwritten. Choose another name
or remove an obsolete artifact yourself.
Write behavior and limits
Whole standalone comment lines are removed. Inline comments are replaced with whitespace that separates neighboring tokens and preserves embedded line terminators. UTF-8, UTF-8 BOM, UTF-16 LE/BE BOM, retained CRLF/LF and file permissions are preserved. The result must parse and retain the same non-comment syntax tree, including punctuation. Ambiguous JavaScript automatic semicolon insertion changes are rejected. This syntax check does not establish that deleting an explanation is useful or preserve every external tool’s interpretation of unknown comment directives.
Every edited file is validated and staged before writing. Files changed after selection, symlink paths, hard-linked files and read-only files are rejected. Ordinary commit failures attempt to restore earlier writes and report any rollback failure. Multi-file application is not crash-atomic; avoid concurrent writers while applying. Use source control to revert an applied cleanup.
Diffs display decoded text. Use --apply or plan replay to preserve BOM and
UTF-16 encoding; external patch tools are not an encoding-preserving substitute.
Codebase report integration
Enable the advisory rule to include comment review hints in the regular Codebase report:
reforge analyze . --analysis codebase \
--set "rules.enable=['reforge.codebase.comment_hygiene']"
Or add reforge.codebase.comment_hygiene to [rules].enable in reforge.toml.
It is a preview, default-off rule for Rust, JavaScript and TypeScript/TSX. Evidence
includes the comment location and hint; protected comments do not produce hints.
It belongs to the documentation-integrity family and uses the shared parsed
workspace sources. Dataflow-only execution does not run it. The rule cannot be
enforced as policy while it is preview. Analysis never modifies source files.
Configuration
reforge.toml is versioned with version = 2. Generate it with reforge init.
version = 2
[analysis]
enabled = ["codebase"]
[scope]
include-hidden = false
include-generated = false
no-gitignore = false
exclude-tests = false
ignore-paths = []
[rules]
enable = [
"reforge.codebase.large_file",
"reforge.codebase.long_function",
"reforge.codebase.dependency_cycle",
"reforge.codebase.similar_functions",
]
disable = []
enforce = []
[codebase]
preset = "balanced"
churn = "auto"
max-file-lines = 600
[dataflow.search]
max-path-steps = 24
max-function-hops = 8
max-module-hops = 8
max-paths-per-source = 100
max-sinks-per-source = 100
work-budget = 100000
[dataflow.relay]
min-function-hops = 4
min-module-hops = 2
min-relay-percent = 90
[dataflow.fan-out]
min-sinks = 4
min-modules = 3
Rule arrays require complete IDs. Duplicate, conflicting, and unknown IDs are
errors. enforce implies enable and accepts only stable rules. Experimental
rules remain internal observations; preview rules are off unless enabled and
can only produce advisory Issues. The CLI starter configuration enables four
preview rules explicitly. Only enforced stable rules produce policy Issues or
participate in a gate.
Each Dataflow policy is single-language and names exact sink declarations:
[[dataflow.policies]]
name = "http-client"
language = "typescript"
protected-paths = ["src/domain/**"]
adapter-paths = ["src/adapters/http/**"]
exempt-paths = ["src/bin/**"]
[[dataflow.policies.sinks]]
path = "src/transport.ts"
symbol = "send"
A policy is rejected when its language is unsupported or a sink does not match exactly one public source symbol. Adapter bypass evidence requires a complete policy and an all-exact, value-preserving witness. Search budgets limit exploration and are not smell thresholds.
The versioned file is parsed as optional typed fields. Reforge then creates one
complete effective configuration by applying built-in defaults, preset,
configuration file, --set, and CLI scope overrides in that order. reforge config show prints every effective leaf together with its source.
low_module_cohesion uses preset-specific minimums:
| Preset | Module functions | Clustered functions |
|---|---|---|
strict | 16 | 40% |
balanced | 20 | 50% |
relaxed | 30 | 60% |
Override them with the positive-integer
codebase.min-module-functions and the 0–100
codebase.min-clustered-function-percent. The same dotted keys work with
--set; config show materializes preset-derived values and records their
source.
Codebase analysis
Codebase is Reforge’s default analysis. It reviews repository structure and produces evidence-backed findings for maintainers to inspect before deciding on a refactor.
reforge analyze .
What it examines
Codebase builds one project-wide index before applying any rule. That index contains files, directories, declared functions and types, imports, local dependencies, naming patterns, repeated syntax, test structure, and optional Git history.
Rules use that shared view to look for four broad kinds of pressure:
| Area | Typical findings | Review question |
|---|---|---|
| Responsibilities | Large files and types, long or complex functions, broad directories | Does this unit own more than one reason to change? |
| Duplication | Similar functions, repeated literals, repeated setup, overlapping type shapes | Is the repetition intentional, or is a shared concept missing? |
| Architecture | Dependency cycles and hubs, parallel implementations, boundary bypasses | Is ownership clear, and do dependencies point in the intended direction? |
| Consistency | Naming drift, generic buckets, stale compatibility paths, debt markers | Has a temporary or local convention spread beyond its original purpose? |
The complete list is in the Rule Reference.
Enable the rules you want to review
Rules start as opt-in previews. Running Codebase still records its coverage, but
a rule produces findings only after it is enabled in reforge.toml:
version = 2
[analysis]
enabled = ["codebase"]
[rules]
enable = [
"reforge.codebase.large_file",
"reforge.codebase.long_function",
"reforge.codebase.dependency_cycle",
"reforge.codebase.similar_functions",
]
[codebase]
max-file-lines = 600
max-function-lines = 80
Start with a small set whose meaning is easy to review in your repository. Adjust a threshold when the evidence is consistently too broad or too narrow; do not tune it merely to force a clean report.
Read a finding
A finding is the unit to review. It names one file, symbol, repository, or related group and contains one or more Evidence records. Evidence answers three questions:
- Which rule made the observation?
- Where in the source was it observed?
- Which measurement crossed the configured threshold?
Legitimate exceptions are expected. Generated facades, protocol signatures, composition roots, test builders, and deliberate compatibility layers can all look unusual for good reasons. Keep those decisions visible with a documented suppression instead of weakening a useful rule globally.
Check Coverage before trusting an empty result
Coverage records the files and languages seen by Codebase, the rules that ran, and any limitations. An empty findings list means only that the enabled rules found nothing within the observed surface. It does not prove that the codebase is healthy or defect-free.
Generate a report
Use the terminal output for quick review or create a standalone HTML file for a larger repository:
reforge analyze .
reforge analyze . --output html --output-file reforge-report.html
reforge analyze . --output json --output-file reforge-report.json --reproducible
JSON is the appropriate format for reviewed baselines and CI. See the User Guide for the baseline workflow and Configuration for scope, thresholds, and suppressions.
Advanced value-path analysis
Codebase is sufficient for normal structural review. Reforge also offers an opt-in Dataflow analysis for teams that need conservative, source-to-sink value paths or explicit adapter-boundary policies.
Rule cards
Every core rule is a refactoring-inspection claim, not a defect prediction, health score, generic priority, or automatic architecture inference. All cards inherit these non-goals. A finding’s identity comes from its typed subject and the rule-specific semantic anchor, not prose, ordering, checkout location, or line numbers. Measurement, threshold, evidence-set, or witness changes update the finding’s content fingerprint.
All rules below are currently preview, default_enabled = false,
validation_basis = fixture, semantic version 1.0.0, and ineligible for
enforcement. A language can become stable only through the audited calibration
protocol in calibration/README.md; other languages remain preview.
The CLI’s starter configuration explicitly enables four of these rules as
advisories; this does not change their manifest maturity or default-enabled
state.
| Rule | Claim / inspection question | Capability | Positive and negative fixtures | Legitimate exceptions |
|---|---|---|---|---|
reforge.codebase.large_file | A file exceeds the configured line boundary; is responsibility ownership too broad? | file inventory | over/under threshold | generated facades, declarative tables |
reforge.codebase.large_directory | A directory owns more direct source files than configured. | directory inventory | wide/narrow directories | flat packages with explicit ownership |
reforge.codebase.comment_hygiene | An ordinary comment has an empty, repeated, template or possible-code review hint; is it still useful? | Rust/JS/TS parsed comments | hint/protected/string fixtures; preview/apply and stale-plan tests | intentional repetition, examples, explanations without recognized protection markers |
reforge.codebase.debt_marker | A source comment explicitly declares TODO/FIXME debt. | source text | comment/non-comment markers | generated or externally tracked markers |
reforge.codebase.similar_functions | Multiple normalized bodies are structurally similar enough to inspect together. | parsed syntax similarity | cloned/distinct bodies | protocol implementations, tests |
reforge.codebase.long_function | A declared function exceeds the configured line span. | syntax and symbols | long/short functions | generated parsers, linear tables |
reforge.codebase.complex_function | Estimated branch complexity exceeds the configured bound. | parsed control syntax | branch-heavy/linear functions | explicit state machines |
reforge.codebase.deep_nesting | Lexical control nesting exceeds the configured bound. | parsed control syntax | nested/guard-clause fixtures | recursive walkers |
reforge.codebase.many_parameters | A function declares more parameters than configured. | symbol parameters | over/under arity | serialization and FFI boundaries |
reforge.codebase.large_type | A type exceeds configured span or member count. | type observations | large/small declarations | generated schemas |
reforge.codebase.large_public_surface | A file exports more items than configured. | export syntax | broad/narrow modules | deliberate prelude or facade |
reforge.codebase.import_heavy_file | A file imports more dependencies than configured. | import syntax | over/under import count | composition roots |
reforge.codebase.function_proliferation | A file combines high function count, density, and small-function ratio. | function inventory | dense/sparse files | parser combinators |
reforge.codebase.low_module_cohesion | Multiple exact call-connected function clusters suggest separable module responsibilities. | module function call graph | monolith/split modules | routers, registries, controllers |
reforge.codebase.unused_function | A private symbol has no supported project-local reference. | symbols and references | referenced/unreferenced symbols | reflection, callbacks, macros |
reforge.codebase.repeated_literal | A literal repeats enough to inspect ownership. | parsed literals | repeated/unique literals | protocol constants and test data |
reforge.codebase.repeated_error_pattern | Error-handling syntax repeats across sites. | parsed error syntax | repeated/distinct handlers | intentionally local recovery |
reforge.codebase.test_duplication | Test setup patterns repeat across tests. | parsed test syntax | duplicated/distinct setup | readability-focused local setup |
reforge.codebase.happy_path_only_tests | A test group has assertions without detected failure/boundary cases. | test syntax | positive-only/mixed tests | behavior proven elsewhere |
reforge.codebase.file_naming_drift | A directory mixes file naming conventions. | path inventory | mixed/uniform names | language-required names |
reforge.codebase.directory_drift | Directory concepts exceed the configured ownership bound. | paths and syntax names | mixed/cohesive fixtures | plugin registries |
reforge.codebase.data_clump | The same parameter combination recurs across functions. | symbol parameters | recurring/distinct sets | stable protocol signatures |
reforge.codebase.parallel_implementation | Similarly named capabilities are implemented independently. | symbol concepts | parallel/unrelated names | platform-specific variants |
reforge.codebase.shadowed_abstraction | Local helpers overlap a shared abstraction. | symbols and concepts | local/shared overlap | deliberate compatibility shims |
reforge.codebase.duplicate_type_shape | Type field shapes substantially overlap. | type fields | overlapping/distinct shapes | boundary DTOs |
reforge.codebase.config_key_drift | Configuration-like keys repeat or drift. | literal concepts | repeated/distinct keys | external protocol keys |
reforge.codebase.fixture_factory_drift | Test fixture/factory concepts repeat independently. | test symbols | duplicated/distinct factories | domain-specific builders |
reforge.codebase.generic_bucket_drift | A generic directory or file accumulates unrelated concepts. | typed file/directory subjects | generic/cohesive buckets | intentionally tiny shared kernels |
reforge.codebase.adapter_boundary_bypass | Naming/syntax suggests direct access around an adapter. | heuristic concepts | bypass/non-bypass fixtures | migration and bootstrap code |
reforge.codebase.stale_compatibility_path | Compatibility markers lack an explicit retirement boundary. | parsed compatibility syntax | stale/owned paths | supported long-term compatibility |
reforge.codebase.dependency_cycle | Resolved project-local dependencies form a cycle. | dependency graph | cyclic/acyclic graphs | mutually recursive generated modules |
reforge.codebase.dependency_hub | A file has unusually broad/deep resolved dependency topology. | dependency graph | hub/leaf graphs | composition roots and public facades |
reforge.dataflow.adapter_flow_bypass | An exact, value-preserving path violates a complete single-language adapter policy. | exact local/interprocedural flow | exact bypass/conforming and unsupported fixtures | explicit exemptions |
reforge.dataflow.excessive_relay | An exact path contains configured forwarding depth; inspect ownership only. | exact direct-call flow | long/short relay paths | pipelines, middleware, telemetry |
reforge.dataflow.flow_fan_out | One exact source reaches many supported sinks/modules. | exact direct-call flow | fan-out/narrow paths | orchestrators and event distribution |
Similarity, literal, generic-bucket, unused-function, adapter, relay, and fan-out heuristics remain preview/off until each language independently meets the calibration gates. Self-scan is regression data only and cannot promote a rule or select a threshold.
Dataflow
Dataflow builds a language-neutral Flow IR for Rust, JavaScript/TypeScript/TSX, and Python. Selecting Dataflow records internal observations and capability receipts; enabled preview rules can surface advisories, while configured policies add exact bypass evaluation.
Coverage retains every language discovered in the shared workspace index.
Rust, JavaScript, TypeScript, TSX, and Python receive rule observations;
other languages are explicitly unsupported. Parse failures, unresolved
edges, path truncation, and missing policy configuration use stable
language/rule limitation codes and explicit capability receipts.
Preview rules
reforge.dataflow.excessive_relay, when enabled, requires an exact complete path meeting all three inclusive relay minima: function hops, module hops, and relay percent.reforge.dataflow.flow_fan_out, when enabled, groups by source symbol and requires both the distinct sink-symbol and module minima.reforge.dataflow.adapter_flow_bypass, when enabled, requires an explicit policy and an exact complete witness that bypasses its adapter.
All three are preview, default off, and advisory-only. Same-module
forwarding, modeled or unresolved paths, unsupported semantics, generated or
test sources, and truncated searches do not produce these Issues.
Search and signal thresholds
Search budgets bound deterministic traversal under [dataflow.search]:
max-path-steps, max-function-hops, max-module-hops,
max-paths-per-source, max-sinks-per-source, and work-budget.
Signal thresholds live separately under [dataflow.relay] and
[dataflow.fan-out]. Changing a search budget never changes the rule claim.
Treat zero Issues together with coverage. partial, unsupported, and stable
limitation codes identify where absence is not evidence.
Measurements and evidence
Measurements are typed values attached to Evidence. Each records a stable name, numeric value, optional numeric threshold, and unit. Evidence adds a rule, message, locations, and an optional typed Dataflow witness.
A measurement is evidence for a detector decision, not a quality score. Reforge does not combine measurements into grades, normalized health scores, or cross-rule rankings.
Issues are the baseline, gate, and SARIF decision unit. Evidence explains why an Issue exists. Prose and ordering do not change identity; measurements, thresholds, evidence-set changes, and substantive witnesses update the content fingerprint while the same typed subject keeps its Issue ID.
Coverage records the observed denominator, rule activation and maturity, and language capability limitations. An unsupported or unresolved semantic surface is never inferred as an exact edge.
The compact report does not contain the raw Codebase metric inventory. Use
--metrics-output PATH for detector development or calibration. That sidecar
is deliberately outside the stable report contract.
HTML report
The offline React app renders a Reforge report: Issues, nested Evidence and measurements, typed Dataflow witnesses, per-analysis coverage, suppression totals, and optional baseline comparison. It does not render raw metrics, Flow IR, arbitrary JSON extensions, or internal ontology fields.
After frontend changes run:
cd web/report-app
npm ci
npm test
npm run test:e2e
npm run build
Commit the source together with regenerated assets/report-app.js and
assets/report-app.css plus their synchronized crates/reforge-output/assets
copies; the HTML renderer embeds the package-local assets and requires no
server or network.
Report format
The current report format uses schema_version = 27. reforge_schema::Report
contains schema_version, producer, target,
provenance, summary, suppression, coverage, issues, and optional
baseline_comparison. Unknown fields are rejected.
Provenance records identity scheme reforge-identity-v7, the evaluated scope
digest, per-analysis configuration and policy digests, and each evaluated
rule’s semantic version and evaluation digest.
An Issue contains kind = advisory | policy, explicit analysis and family,
typed Subject, readable prose, Evidence, an ri7-* ID, and a versioned
content_fingerprint (rc7-*). Subject entities contain independent key,
path, and optional
symbol fields; groups contain structured entity members. Symbol keys use
language, qualified owner, declaration kind, name, and signature or stable
disambiguator. Prose, ordering, checkout location, comments, and line numbers
do not define identity.
Evidence has an re7-* ID derived from rule and semantic anchor. Measurements,
thresholds, evidence-set changes, and substantive witness changes update the
Issue content fingerprint. Flow witnesses expose typed source/sink symbols,
ordered steps, hop counts, and exact, modeled, unresolved, or
unsupported resolution. Only all-exact, value-preserving paths can be policy
witnesses.
Coverage is keyed by analysis and language. Language entries include capability receipts for syntax, symbols, lexical scopes, local def-use, direct calls, call/return composition, field flow, and dynamic dispatch. Rule entries print once with maturity, activation source, status, observations, and limitations. Zero Evidence never erases the observed denominator.
Baseline comparison maps every current or previous Issue ID to new,
unchanged, updated, absent, or unknown, with an optional reason. A
matching ID with a changed content fingerprint is updated. Scope, relevant
configuration/policy, rule semantics/evaluation, analysis availability, or
coverage changes make otherwise unprovable additions/disappearances unknown.
Workspace identity mismatch is an error. Producer name and identity scheme must
match, but producer versions and unrelated analysis sets may differ.
Older report formats are rejected rather than silently converted. Regenerate them with the current analyzer so their findings and Coverage describe the same analysis behavior.
Architecture
tools/reforgeis a thin CLI and configuration boundary.crates/reforge-engineowns workspace indexing, execution planning, Codebase and Dataflow analysis, evidence aggregation, and report creation.crates/reforge-schemaowns the publicReport, stable identities, typed witnesses, coverage, and baseline comparison.crates/reforge-outputowns human, JSON, YAML, SARIF, and embedded HTML rendering.web/report-appowns the offline HTML interface.
The engine builds one shared workspace index. Each selected source is walked,
read, language-classified, and parsed once; Codebase and Dataflow consume the
same indexed sources. The typed Config selects either or both analyses and
owns scope, thresholds, policies, and suppressions.
The public model starts at the report. An analysis is an execution selection and a Coverage key, not a wrapper around the report:
Report
├── Coverage by analysis
│ ├── language counts
│ ├── rule execution
│ └── limitations
└── Issue
└── Evidence
├── Measurement
├── Location
└── optional Flow witness
Detectors produce DetectedEvidence with a semantic anchor and no internal
report ID. One static RuleSpec registry supplies analysis ownership,
aggregation family, output subject kind, input observation source, language
support, measurements, and a rule-specific description. Families
are an aggregation and identity mechanism, not an additional user workflow:
after suppression, the engine groups Evidence by family and Subject into
Issues; schema projection alone creates stable Evidence IDs.
The engine returns the public Report directly. Debug metrics and Flow IR take
separate explicit sidecar paths and never enter the report. Flow IR is only
materialized when --flow-ir-output is requested.
Comment inventory and cleanup live behind reforge_engine::api::comments.
They reuse source-scope discovery and language parsers, with separate versioned
inventory/patch structures rather than changing the analysis report schema.
The optional Codebase comment_hygiene rule shares the extractor over indexed
trees. Only the explicit comment-apply API writes source files; normal analysis
and comment preview remain read-only.
Contributing
Core user-facing work starts at tools/reforge and an explicit
the typed Config; do not add another peer analyzer CLI for a core rule. Every new
rule must declare exactly one Codebase or Dataflow owner, a namespaced family,
description, supported languages, default state, measurements, and focused
positive/negative tests in the rule registry.
Dataflow frontends emit the language-neutral Flow IR. Add exact edges only for semantics the frontend can prove, record dynamic/unsupported behavior as coverage limitations, and test positive, negative, partial, and unsupported cases. Stable path detectors require ordered source-to-sink witnesses, budget/cycle tests, at least five positive and five negative microfixtures, and documented real-project calibration before maturity changes.
Run cargo test --workspace --all-targets --all-features, all-target Clippy with warnings denied, both analysis self-checks, report-app unit/browser/build checks, installer tests, and docs build before review. Frontend changes must regenerate the committed embedded assets.
This project follows the repository guidelines in AGENTS.md. Keep changes
small, behavior-focused, and covered by targeted tests.
Setup
Install Rust 1.85 or newer, then run:
cargo build
cargo test
For a quick end-to-end smoke test:
cargo run -p reforge-cli -- analyze . --reproducible
For reproducible machine-readable output:
cargo run -p reforge-cli -- analyze . --analysis codebase --set codebase.churn=off --reproducible --output json
Development Workflow
Use cargo fmt before review:
cargo fmt
Run tests:
cargo test
Run Clippy before larger changes:
cargo clippy --all-targets --all-features
Before pushing, run the same Linux-side gate used by CI:
scripts/check-ci.sh
Install the repository-managed pre-push hook once per checkout to run that gate automatically:
scripts/install-git-hooks.sh
The gate includes the full-rule self audit and enforces zero Codebase issues,
byte-identical repeated reports, and isolated/combined coverage parity. CI calls
the same scripts/check-self-audit.sh implementation, so its policy cannot
silently drift from local validation.
When report formatting or schema behavior changes, include sample human, HTML, JSON, YAML, or SARIF output in the pull request description.
Report App Development
The React report app requires Node.js ^22.12.0, ^24.0.0, or >=26.0.0 and
npm; CI uses Node.js 22. Vite 8 is installed from the locked frontend
dependencies, so use the package scripts instead of a global Vite installation:
cd web\report-app
npm ci
npm run test
npm run build
npx playwright install chromium
npm run test:e2e
The build refreshes assets/report-app.js and assets/report-app.css, then
synchronizes them into crates/reforge-output/assets. Rust
embeds those files in offline HTML reports, so commit both generated asset sets
with the frontend source change.
The Playwright suite generates a report with deliberately strict thresholds
and opens the final self-contained HTML file in Chromium. It covers browser
rendering, report interactions, and desktop/mobile layout. Failure screenshots,
traces, and videos are written below target/playwright; the HTML test report
is written to web/report-app/playwright-report in CI.
Documentation Site
The documentation site uses mdBook 0.5.4. Install that exact version before building or serving the site locally:
cargo install mdbook --version 0.5.4 --locked
On Windows, generate the Codebase example report and serve the site with:
.\scripts\serve-docs.ps1
Build static files into target/docs-site without starting a server:
.\scripts\build-docs.ps1
On macOS or Linux, use the matching shell scripts:
sh scripts/serve-docs.sh
sh scripts/build-docs.sh
The published documentation root is
https://lylemi.github.io/Reforge/; the generated Codebase example is published at
https://lylemi.github.io/Reforge/sample/. Repository administrators must set
Settings > Pages > Build and deployment > Source to GitHub Actions before
the Pages workflow can deploy for the first time. Keep the github-pages
environment restricted to the main branch; the workflow also enforces that
branch boundary for manual runs.
The sample is a real, reproducible Codebase analysis of the Reforge repository. Its advisory findings are refactoring signals to review, not defects or a quality score. The Pages workflow validates a bounded issue count, evidence-rule variety, nonzero scan coverage, and the absence of generated report bundles before it can publish. These checks keep the sample useful as the codebase and detector output evolve; update the sample thresholds deliberately when legitimate repository changes move it outside those quality bounds.
Tests
Unit tests live next to the modules they exercise under #[cfg(test)] or in
module-specific test files included from the module. There is currently no
separate tests/ directory.
Add tests for:
- CLI parsing and default values when flags change.
- Config precedence and discovery when configuration changes.
- Source collection exclusions, thresholds, ordering, and report fields.
- Detector behavior, including false-positive guards.
- Output stability for human, HTML, JSON, YAML, and SARIF report changes.
Name tests by behavior, such as parses_output_format or
groups_similar_functions.
Style
Use idiomatic Rust formatted by cargo fmt. Prefer the existing module split:
cli, scan, model, detectors, evidence_analysis, workflow, and output.
Use snake_case for functions, variables, modules, and test names. Use
PascalCase for structs, enums, and traits. Keep CLI flags long,
descriptive, and kebab-case.
Avoid unrelated refactors in behavior changes. If a refactor is needed to make a feature safe, keep it scoped and covered by tests.
Report Compatibility
JSON, YAML, and SARIF reports are external interfaces. When fields are added, removed, or renamed:
- Update
reforge_schema::REPORT_SCHEMA_VERSION. - Update
docs/report-schema.md. - Update output tests.
- Mention the compatibility impact in the pull request.
Consumers should rely on stable Issue and Evidence IDs, typed measurements, Coverage, and typed Dataflow witnesses. The report format does not emit priority, confidence, severity, or hotspot ranking.
Commits and Pull Requests
Use Conventional Commits:
feat(codebase): detect directories with many source files
fix(report): keep JSON output stable
docs: add report schema reference
Keep descriptions imperative, lowercase, and without a trailing period. Keep commits scoped to one behavior change.
Pull requests should describe:
- User-visible effect.
- Validation commands run.
- Related issues.
- Sample human, HTML, JSON, YAML, or SARIF output when report formatting changes.
Do not commit generated outputs, dependency directories, build artifacts, or
local analysis artifacts. The checked-in assets/report-app.js and
assets/report-app.css bundles and their crates/reforge-output/assets copies
are the sole generated-output exception because
the Rust HTML renderer embeds them.