You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
No other carrier names are supported. Files using non-v3 carrier names are not loaded as architecture.
Unit Schema
Units describe architectural identity, intent, contracts, relationships, and parent environment:
id: src/corepurpose: owns the normalized checker truth layeressence:
description: Core model and comparison modules.why: checker truth must stay independent from source-language pluginsparts:
src/core/model.ts:
purpose: defines normalized architecture objectsbehavior:
provides:
normalized-model:
statement: exposes architecture graph typesuses:
src/evidence:
target: src/evidencehow: readsstatement: consumes observed relationship factsexpectation: staticcontext:
environment: srcobeys:
src.rule.core-no-plugin-imports:
statement: core does not import plugin modules
parts, provides, requires, maintains, uses, and obeys are keyed objects. Keys are stable clause or address identities. Arrays are invalid for these fields.
Environment Schema
Environments describe member units and rules:
id: srcpurpose: governs source moduleswhy: source modules need stable layer rulesmembers:
src/core:
purpose: truth layerrules:
src.rule.core-no-plugin-imports:
statement: core modules must not import plugin modulesforce: mustpropagation: inheritableappliesTo:
src/core: {}forbids:
from: src/coreto: src/pluginshow: imports
members and rules may be empty while the architecture is being built. An environment still has value without rules because its purpose, why, and optional description explain how child units operate together from inside the parent boundary.
Rule propagation is local or inheritable. local applies to direct members only. inheritable carries down descendant addresses until stopped or superseded.
Rules may include severity, requiresDescription, and prompts when planner workflows need rule-specific architect questions or description requirements.
Forbidden Relationships
Environment rules may include one forbids relationship constraint. The constraint matches declared and observed relationships by address subtree:
rules:
app.rule.persistence-independent:
statement: persistence must not depend on the task domainpropagation: inheritableappliesTo:
app/persistence: {}forbids:
from: app/persistenceto: app/domainhow: any
how may be imports, calls, reads, sends, or any. Declaring a forbidden edge does not silence the finding because the comparison engine checks declared relationships and observed relationships together.
forbids.from and forbids.to define the enforced source and target subtrees. appliesTo still controls which units must acknowledge the rule, but it does not narrow the forbidden edge match.
Forbidden dynamic edges such as reads and sends are enforced against observed relationships only when an enabled evidence plugin reports those relationship kinds. Without that evidence, the checker can still catch a declared forbidden dynamic edge, but it cannot observe a hidden runtime edge.
A rule has one forbids constraint. Use separate rule ids for separate constraints, including bidirectional boundaries. This keeps each finding tied to one named architectural law.
rules:
app.rule.persistence-no-domain:
statement: persistence must not depend on domainpropagation: inheritableappliesTo:
app/persistence: {}forbids:
from: app/persistenceto: app/domainhow: anyapp.rule.domain-no-persistence:
statement: domain must not depend on persistencepropagation: inheritableappliesTo:
app/domain: {}forbids:
from: app/domainto: app/persistencehow: any
Stops And Supersession
rulePropagation:
stop:
src.rule.core-no-plugin-imports:
reason: plugins are the permitted parser-specific boundaryrules:
src.plugins.rule.emit-facts-only:
statement: plugins emit observed facts and do not create findingspropagation: inheritablesupersedes: src.rule.core-no-plugin-imports
Every stop requires a reason. A rule cannot supersede itself.
observes declares which relationship kinds the plugin can observe. The checker uses this metadata to report unenforceable_forbidden_relationship when a forbids constraint has no enabled observer for its relationship kind.
If any enabled plugin omits observes, coverage is incomplete. The checker still uses that plugin's relationship facts, but it does not report unenforceable_forbidden_relationship from an incomplete coverage set.
TypeScript import evidence is the default profile. When API callers provide routeTargets and do not override plugins, runChecker also enables JavaScript fetch evidence for matching sends relationships. Other repository-specific dynamic evidence belongs in optional plugins, not in the core checker.
Planner Schema
Planner maps are keyed objects. planning.draft.candidateUnits, planning.draft.candidateRelationships, planning.draft.boundaryQuestions, planning.deferred, planning.questions, planning.assumptions, and planning.evidence must not be arrays.
planning:
status: planneddraft:
candidateUnits:
src/app:
id: src/appreason: app boundary needs reviewdeferred:
src/app/domain:
target: src/app/domainreason: inspect after app boundary is chosen
planning.status uses draft, planned, implemented, deprecated, or retired. A unit with draft or planned status is stale when the unit source exists and every declared essence.parts address exists in the source tree. The checker reports stale_planning_status; it does not rewrite the carrier. Change the status to implemented when the unit is built.
deprecated and retired are enforced lifecycle labels. A deprecated unit whose source address still exists reports deprecated_unit_still_present as a warning. A retired unit whose source address still exists reports retired_unit_still_present as an error.
The stale-status source-present check is based on source tree addresses. Folder and file parts must exist before a draft or planned unit is stale. Method-level parts use # addresses from comment checks and do not block stale status until a future source walker reports method addresses as source facts.
Planner And Session Boundary
Planner fields such as planning.draft, planning.deferred, planning.granularity, assumptions, and questions guide architecture-building workflows. They do not weaken core drift detection.
Run reports write schema version 3, enabled plugin names, normalized units, normalized environments, observed relationships, and resolved inherited rules.
This repository contains a local TypeScript architecture checker and planning assistant. The tool makes repository architecture explicit, validates the authored architecture contract, compares it with observed source facts, and writes reports that can be read by humans and automation.
The solution is stand-alone. Understanding, running, and extending it requires the repository, Node.js dependencies, and the documented commands; no external orchestration framework is required.
Operating Contract
Schema version 3 is the only accepted architecture language. The checker loads schema v3 carrier names, normalizes keyed v3 objects, validates current shapes, runs evidence plugins, compares declared intent with observed facts, and writes reports.
Non-v3 carrier names are unsupported and are not loaded as architecture. Array-shaped authoring maps are invalid for schema identity fields.
Verified State
The latest self-check report in .architecture records the following state:
Field
Value
Schema version
3
Normalized units
53
Normalized environments
18
Source files
35
Source folders
18
Observed relationships
113
Resolved rule entries
53
Enabled plugins
typescript-imports
Current findings
0
The latest full verification command is npm run check. It builds the project and runs the full test suite. The current run passed with 34 test files and 121 tests.
The latest repository self-check command is node dist/cli.js check --root . --source-root src --output .architecture. It completed with zero findings, and .architecture/findings.json contains [].
The latest architecture-only self-check command is node dist/cli.js check --root . --source-root src --output .architecture --no-comments. It completed with zero findings and verifies the comment lane can be gated independently.
The planning command also works without schema selection: node dist/cli.js plan --root . --source-root src --logs .logs --label v3-cli-default completed with zero findings.
Auditor Review Scope
This document records the checker as observed from the current generated report and the current implementation. It is intended as a factual starting point for a separate audit, not as a substitute for running the verification commands.
Runtime orchestration for source walking, carrier loading, evidence, comparison, and reporting.
src/core/compare.ts
Core comparison policy for structure, relationships, planning lifecycle, and rule findings.
src/session/run.ts
Output directory, logs directory, run manifest, and source exclusion behavior.
Observed Architecture Snapshot
The self-check currently models src as 53 normalized units inside 18 normalized environments. The source walker reports 35 source files and 18 source folders after excluding architecture carriers and generated output locations.
The default self-check evidence profile enables typescript-imports only. The current report contains 113 observed relationships, all emitted as observed facts before the core comparison step evaluates drift.
The current findings report is empty. This means the authored self architecture, discovered source tree, observed relationships, resolved rules, planning status, and comment checks agree for the current src self-check.
Top-Level Shape
The implementation is split into small modules with clear ownership:
src/core
normalized model, validation, rule resolution, and comparison
src/carriers
carrier naming, YAML loading, ref handling, and unsupported carrier warnings
src/source
source tree walking
src/repository
source file classification policy
src/evidence
evidence plugin contracts and registry
src/plugins
default TypeScript import evidence plugin
src/analyzers
reusable relationship analyzers
src/comments
TypeScript docblock and file-header checks
src/planner
architect questions, draft helpers, plan helpers, and granularity checks
src/session
run manifests and finding comparison
src/report
JSON, YAML, summary, question, and feedback report writing
src/cli
argument parsing and command handlers
Stable import paths under src/compare, src/descriptions, src/questions, src/granularity, and src/schema remain as direct paths over the v3 engine. They do not accept non-v3 schema shapes.
Runtime Flow
The public API entrypoint is runChecker(root, options) in src/index.ts. It orchestrates the checker without embedding repository-specific assumptions.
The runtime flow is:
Normalize the selected sourceRoot and checker mode.
Resolve checker-owned output and logs paths that must be excluded from source walking.
Walk the selected source root with the default source classification policy.
Load schema v3 architecture carriers from the selected source root.
Build the evidence plugin list from explicit plugins or from default options.
Run evidence plugins and collect observed relationship facts.
Check TypeScript comments unless the caller disables the comment lane.
Compare normalized architecture with source facts and observed relationship facts.
Run planner granularity checks against normalized units.
Resolve applicable environment rules for every unit for the architecture snapshot.
Create a run manifest for draft and plan modes.
Write architecture, findings, run, question, feedback, and summary reports.
Plan and draft modes also create a timestamped run manifest under the selected logs directory.
Implementation Trace
runChecker first calls outputExclusionsForSource so generated checker artifacts cannot become source facts. In plan and draft modes, the default .logs directory is treated as active even when logsDirectory is not set.
walkSource receives the active exclusions through excludedUnitIds. The walker returns repository-relative folder and file ids that become the source facts used by later comparison steps.
loadArchitectureCarriers scans only schema v3 carrier names. It normalizes units and environments, follows aggregate $ref entries, and returns findings instead of throwing for repairable carrier drift.
The default evidence list always includes typescript-imports. When API callers pass routeTargets and do not pass a custom plugins list, runChecker adds javascript-fetch evidence for matching runtime sends relationships.
Evidence plugins produce observed relationships only. They do not create findings, mutate carriers, or decide whether a relationship is valid.
writeReports serializes the final CheckResult to JSON, YAML, Markdown summary, questions, feedback, and run files. Reports are written after comparison, not during evidence collection.
Audit Checkpoints
Checkpoint
Current Behavior
Audit Location
Source pollution control
Active output and logs paths are excluded before walking.
architecture.yaml may point to split carriers with repository-local relative $ref values.
The v3 unit model uses keyed maps for essence.parts, behavior clauses, behavior.uses, context.obeys, and planner maps. The v3 environment model uses keyed maps for members, rules, rule application targets, and rule propagation stops.
Core Model
The normalized model lives in src/core/model.ts. It defines repository-neutral addresses, clauses, units, environments, rules, relationships, and planning metadata.
The key runtime objects are:
Type
Role
Unit
Declares an addressable architecture unit.
Environment
Declares the local operating context for member units.
EnvironmentRule
Declares local or inheritable rules for members.
RelationshipClause
Declares expected relationships to other addresses.
UnitPlanning
Stores draft, plan, granularity, assumption, question, and evidence metadata.
Finding
Reports validation, drift, or quality issues.
ArchitectureSnapshot
Serializes the normalized checked state.
The public src/model.ts keeps operational types such as findings, source trees, run manifests, checker options, and snapshots. It re-exports current v3 model types from src/core/model.ts.
Carrier Loading
src/carriers/naming.ts classifies only v3 carrier names. It also detects unsupported architecture-looking carrier names so stale files can produce visible warnings.
src/carriers/load.ts recursively scans the selected source root. It parses YAML, resolves aggregate $ref values, normalizes units and environments, and returns normalized v3 objects plus findings.
Unsupported carrier files are not parsed as architecture. The loader reports unsupported_architecture_carrier warnings for stale non-v3 architecture filenames.
Normalization And Validation
src/core/normalize.ts converts parsed YAML into normalized v3 Unit and Environment objects. It accepts records for schema identity maps and reports invalid_architecture_shape when arrays or non-record values are used for v3 keyed fields.
src/core/validate.ts checks normalized structures that require semantic validation. It verifies part and member purposes, rule propagation values, propagation stop reasons, and invalid self-supersession.
src/schema/validate.ts is a v3-only stable import path. It calls normalizeUnit and normalizeEnvironment and returns the first finding for callers that want a simple validation result.
Source Discovery
src/source/walk.ts walks the selected source root and returns source folders and files as repository-relative unit addresses.
src/repository/classify.ts keeps architecture carrier files out of source units. Code files are included by default, resource files are included only when policy requires all files or a matching v3 file-unit carrier declares them.
Unsupported architecture-looking carrier names are ordinary source files from the source walk perspective. They are not v3 architecture artifacts.
Evidence Boundary
Evidence plugins emit observed relationship facts only. They do not create findings and do not edit architecture.
The default plugin is typescript-imports. It observes TypeScript static imports and conservative imported calls, declares observes: ["imports", "calls"], then returns ObservedRelationship entries.
When API callers pass routeTargets and do not override plugins, runChecker also enables JavaScript fetch evidence for matching sends relationships and declares observes: ["sends"]. Optional analyzers also exist for HTML asset references. They are reusable analyzer code and test-covered, but other dynamic evidence is not enabled by default in runChecker.
Comparison Capabilities
src/core/compare.ts compares normalized architecture with source discovery and observed relationships.
It can report missing architecture for folders and files, orphan architecture, missing unit parts, missing environment members, stale parts, stale members, unresolved why references, reference-only whys, missing rule acknowledgements, unknown rule acknowledgements, undeclared relationships, unobserved static relationships, unconfirmed dynamic relationships, forbidden relationships, unenforceable forbidden relationships, and missing descriptions required by rules.
The comparison engine remains repository-agnostic. It consumes source facts, normalized architecture, and observed relationship facts.
Current Implementation State
This section records the implementation approach for external audit.
Functionality
Implementation approach
Primary audit locations
Finding categories
Finding.category is required in the shared model. Core, validation, carrier, planner, and comment producers assign structure, relationships, planning, or comments. Run manifests and summaries count by category, and run comparison identity includes category so lanes do not collapse.
CheckerOptions.includeComments controls comment-contract execution. runChecker skips checkTypeScriptComments when this option is false, and the CLI maps --no-comments to includeComments: false. This disables only comment findings; source walking, carrier loading, evidence plugins, core comparison, and planning checks still run.
EnvironmentRule.forbids accepts one constraint with from, to, and optional how. Validation rejects empty endpoints or invalid how. Core comparison evaluates forbidden rules against declared behavior.uses relationships and observed plugin relationships, matches by address subtree and how, prefers observed child evidence over duplicate declared parent coverage, keeps distinct concrete edge findings separate, and emits forbidden_relationship even when incomplete membership metadata also emits structure findings. appliesTo controls acknowledgement, not the edge match. Enabled plugin observes metadata lets core emit unenforceable_forbidden_relationship when a forbidden relationship kind has no enabled observer and coverage metadata is complete. The one-constraint-per-rule shape remains intentional; schema expansion should wait until repeated real projects show unacceptable authoring noise.
UnitStatus is validated against the known vocabulary. Core comparison reports stale_planning_status with category planning when a unit is draft or planned, its own address exists in the source tree, and every declared source-address essence.parts address exists. Method-level parts use # addresses and do not block stale status. A source-present deprecated unit reports deprecated_unit_still_present as a warning. A source-present retired unit reports retired_unit_still_present as an error. The checker reports only; it does not rewrite carrier status.
outputExclusionsForSource resolves the active output and logs directories against the selected source root. Plan and draft modes treat the default .logs directory as active even when logsDirectory is omitted. Child paths are passed to walkSource as excludedUnitIds; paths equal to or above the source root throw before walking. The walker excludes matching folders and files before they become source facts.
Acceptance fixtures prove forbidden boundary detection, stale planning, includeComments: false, output/log exclusion, grouped summaries, and the clean path that clears relationship and planning findings.
test
Runtime evidence acceptance
Acceptance fixtures prove default logs exclusion, boundary enforcement under membership drift, routeTargets fetch evidence, retired lifecycle findings, and unenforceable dynamic sends boundaries without route evidence.
test
Rule Resolution
src/core/rule-resolution.ts resolves environment rules for each unit. It supports direct local rule application, inherited rule propagation, propagation stops, and supersession.
Rule acknowledgement uses stable rule ids. A descendant can acknowledge an inherited rule by the inherited rule id without needing to know the declaring environment path.
src/planner/granularity.ts checks whether a parent unit with direct-children granularity has declared deeper implementation detail without an allowed or deferred reference.
src/planner/draft.ts and src/planner/plan.ts expose planning helpers through stable module paths.
Session And Report Capabilities
src/session/run.ts creates run manifests for draft and plan modes. It records mode, root, source root, output directory, enabled plugins, command, report paths, finding counts, and timestamps.
src/session/compare-runs.ts compares finding sets from two runs. It reports introduced, resolved, and unchanged findings by stable finding identity.
src/report/write.ts writes machine-readable reports. It writes architecture, findings, questions, summary, and feedback artifacts in the selected output directory.
CLI Capabilities
The CLI entrypoint is dist/cli.js after build. The package binary name is arch-check.
The CLI does not accept a schema version flag. Reports still serialize schemaVersion: "3" so generated artifacts identify the current report format.
check writes stable reports. draft and plan write timestamped run folders. questions prints architect prompts. feedback ensures the run feedback template exists. compare summarizes finding movement between two logged runs.
API Capabilities
The primary API is runChecker(root, options). It returns a CheckResult containing the architecture snapshot, findings, and an optional run manifest.
The main caller options are sourceRoot, outputDirectory, mode, label, logsDirectory, command, includeComments, routeTargets, and plugins.
Callers can pass plugins: [] to run without evidence plugins. Callers can pass custom evidence plugins that implement the EvidencePlugin interface, including optional observes metadata when coverage should prove forbidden-rule enforceability.
The enabledPlugins option is run and report metadata plumbing used when creating manifests. Normal callers should configure evidence through plugins or routeTargets.
Report Shape
The architecture report serializes:
schemaVersion
normalized units
normalized environments
source files
source folders
observed relationships
resolved rules
enabled plugins
The findings report serializes every finding with code, category, message, severity, unit id, optional path, and optional details.
Extension Points
Add evidence by implementing EvidencePlugin and passing it through CheckerOptions.plugins. Set observes?: RelationshipKind[] when the plugin can observe relationship kinds such as imports, calls, reads, or sends.
Add source classification behavior by extending SourceClassificationPolicy and passing it to source walking code where needed.
Add comparison rules in src/core/compare.ts only when the rule is repository-agnostic and consumes existing normalized inputs.
Add planner behavior in src/planner when the behavior guides architecture authoring without changing checker truth.
Current Boundaries
The checker does not auto-fix code or architecture files. It reports findings and writes planning artifacts.
The checker does not load unsupported carrier names as architecture. Stale non-v3 architecture carrier files are warnings during carrier discovery.
The checker does not infer rules just to satisfy the schema. Empty rules maps are valid.
The checker does not treat optional dynamic analyzers as core behavior. The default run uses the TypeScript import plugin, and API runs with routeTargets add JavaScript fetch evidence unless the caller supplies a custom plugin list.
The checker does not hard-code this repository's source root or output paths. Callers choose those paths through CLI flags or API options.
This project is a TypeScript CLI architecture checker and planning assistant. It reads schema v3 architecture carriers from a selected source root, walks source units, runs configured evidence plugins, compares declared architecture with observed facts, and writes machine-readable reports plus planning logs.
The core checker is repository-agnostic. Callers choose --root, --source-root, output directories, and plugin profile. Test fixtures are data, not baked-in project assumptions.
Current Architecture
src/core
normalized model, validation, rule resolution, and comparison
src/carriers
carrier naming, loading, refs, and unsupported carrier warnings
src/repository
source classification policy
src/evidence
plugin contracts and registry
src/plugins
default TypeScript import evidence plugin
src/planner
questions, draft/plan helpers, and granularity checks above core truth
src/session
run manifests and finding comparison
src/report
schema v3 reports, summaries, questions, and feedback templates
src/cli
parser and command handlers
Stable import paths that remain call the schema v3 engine directly. They do not accept non-v3 authoring shapes.
Units and environments use keyed objects for parts, members, clauses, relationships, obeyed rules, and environment rules. Environments may have empty rules; they still document how child units operate together. Rule propagation supports local and inheritable, with explicit stops and supersession.
Schema version 3 is the only accepted authoring contract. Non-v3 carrier names and array-shaped identity maps are not loaded as architecture.
Reports include:
schemaVersion: "3"
normalized units
normalized environments
observed relationships from configured plugins only
Evidence plugins emit observed relationship facts only. They do not create findings and do not modify architecture. The default TypeScript plugin observes static imports and conservative imported calls. API callers that pass routeTargets without overriding plugins also enable JavaScript fetch evidence for matching sends relationships. Other evidence belongs in optional plugins configured by the caller.
API Examples
Enable fetch evidence for runtime route sends by passing routeTargets to runChecker:
Custom evidence plugins can declare coverage with observes:
awaitrunChecker(root,{plugins: [{name: "custom-runtime",observes: ["sends"],asyncanalyze({ tree }){returntree.files.map((file)=>({source: file.id,target: "app/api",how: "sends",analyzer: "custom-runtime"}));}}]});
Current Behavior
Findings carry category values: structure, relationships, planning, or comments. Reports include category counts and summary.md groups findings by category.
Environment rules may define one forbids constraint over source and target address subtrees. Core comparison checks the union of declared relationships and observed relationships and emits forbidden_relationship when an edge matches, even when imperfect membership metadata is also producing structure findings. Dynamic reads and sends constraints need enabled evidence that observes those relationship kinds to catch hidden runtime edges.
Forbidden rules also report unenforceable_forbidden_relationship when their relationship kind has no enabled observer, so dynamic boundaries cannot look enforced when the run has no evidence source for them.
Planning status is enforced as drift for lifecycle labels. A unit marked draft or planned reports stale_planning_status when the unit address and all source-address parts are present in source; method-level parts use # addresses and do not block stale status. A source-present deprecated unit reports deprecated_unit_still_present as a warning, and a source-present retired unit reports retired_unit_still_present as an error.
The --no-comments CLI flag and includeComments: false API option skip only comment-contract findings. Architecture structure, relationships, planning, and evidence checks still run.
Active output and logs directories under the selected source root are excluded from source walking. Paths that would hide the source root are rejected before walking.
Planning Boundary
Planner metadata helps agents ask questions, record draft candidates, defer deeper design, and flag granularity issues. Planner output consumes normalized architecture and findings; it does not change checker truth.
The self-check report should contain schema version 3, normalized source architecture, resolved rules, enabled plugin names, and observed relationships from configured plugins only.