Skip to content

Instantly share code, notes, and snippets.

@sergeliatko
Created June 24, 2026 16:33
Show Gist options
  • Select an option

  • Save sergeliatko/9ecb023d013a85ac6407cac3a5faef5a to your computer and use it in GitHub Desktop.

Select an option

Save sergeliatko/9ecb023d013a85ac6407cac3a5faef5a to your computer and use it in GitHub Desktop.
Architecture schema

Architecture Schema

Carrier Files

Schema version 3 uses semantic architecture carrier names:

architecture.yaml
architecture.unit.yaml
architecture.environment.yaml
<file-name>.architecture.unit.yaml
<file-name>.architecture.environment.yaml

architecture.yaml may aggregate split carriers:

unit:
  $ref: ./architecture.unit.yaml
environment:
  $ref: ./architecture.environment.yaml

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/core
purpose: owns the normalized checker truth layer
essence:
  description: Core model and comparison modules.
  why: checker truth must stay independent from source-language plugins
  parts:
    src/core/model.ts:
      purpose: defines normalized architecture objects
behavior:
  provides:
    normalized-model:
      statement: exposes architecture graph types
  uses:
    src/evidence:
      target: src/evidence
      how: reads
      statement: consumes observed relationship facts
      expectation: static
context:
  environment: src
  obeys:
    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: src
purpose: governs source modules
why: source modules need stable layer rules
members:
  src/core:
    purpose: truth layer
rules:
  src.rule.core-no-plugin-imports:
    statement: core modules must not import plugin modules
    force: must
    propagation: inheritable
    appliesTo:
      src/core: {}
    forbids:
      from: src/core
      to: src/plugins
      how: 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 domain
    propagation: inheritable
    appliesTo:
      app/persistence: {}
    forbids:
      from: app/persistence
      to: app/domain
      how: 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 domain
    propagation: inheritable
    appliesTo:
      app/persistence: {}
    forbids:
      from: app/persistence
      to: app/domain
      how: any
  app.rule.domain-no-persistence:
    statement: domain must not depend on persistence
    propagation: inheritable
    appliesTo:
      app/domain: {}
    forbids:
      from: app/domain
      to: app/persistence
      how: any

Stops And Supersession

rulePropagation:
  stop:
    src.rule.core-no-plugin-imports:
      reason: plugins are the permitted parser-specific boundary
rules:
  src.plugins.rule.emit-facts-only:
    statement: plugins emit observed facts and do not create findings
    propagation: inheritable
    supersedes: src.rule.core-no-plugin-imports

Every stop requires a reason. A rule cannot supersede itself.

Evidence Boundary

Core comparison consumes observed relationship facts. Evidence plugins emit facts only:

interface EvidencePlugin {
  name: string;
  observes?: RelationshipKind[];
  analyze(input: { root: string; tree: SourceTree }): Promise<ObservedRelationship[]>;
}

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: planned
  draft:
    candidateUnits:
      src/app:
        id: src/app
        reason: app boundary needs review
  deferred:
    src/app/domain:
      target: src/app/domain
      reason: 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.

Current State

Purpose

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.

The primary evidence files are:

Evidence What It Proves
.architecture/architecture.json Normalized architecture snapshot, source inventory, observed relationships, resolved rules, and enabled plugins.
.architecture/findings.json Current drift and validation findings.
.architecture/summary.md Human-readable finding totals by category.
src/index.ts 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:

  1. Normalize the selected sourceRoot and checker mode.
  2. Resolve checker-owned output and logs paths that must be excluded from source walking.
  3. Walk the selected source root with the default source classification policy.
  4. Load schema v3 architecture carriers from the selected source root.
  5. Build the evidence plugin list from explicit plugins or from default options.
  6. Run evidence plugins and collect observed relationship facts.
  7. Check TypeScript comments unless the caller disables the comment lane.
  8. Compare normalized architecture with source facts and observed relationship facts.
  9. Run planner granularity checks against normalized units.
  10. Resolve applicable environment rules for every unit for the architecture snapshot.
  11. Create a run manifest for draft and plan modes.
  12. 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.

compareNormalizedArchitecture owns drift policy. It compares source facts, normalized units, normalized environments, declared behavior, observed relationships, resolved rules, and planning status.

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. src/session/run.ts, src/index.ts, test/output-exclusion.test.ts
Carrier contract Only schema v3 carrier names load as architecture. src/carriers/naming.ts, src/carriers/load.ts
Evidence boundary Plugins emit facts and do not emit findings. Plugin observes metadata declares relationship coverage for forbidden-rule enforceability checks. src/evidence/types.ts, src/evidence/registry.ts, src/plugins/typescript-imports.ts, src/index.ts
Dynamic route evidence routeTargets enables javascript-fetch unless plugins are overridden. src/index.ts, test/dynamic-forbidden-evidence.test.ts
Forbidden boundaries Declared and observed forbidden edges emit forbidden_relationship; forbidden rules with no enabled observer emit unenforceable_forbidden_relationship. src/core/compare.ts, test/forbidden-relationships.test.ts, test/dynamic-forbidden-evidence.test.ts
Self core boundary src.rule.core-no-plugin-imports and src.rule.core-no-analyzer-imports keep src/core independent from plugin wrappers and source-language analyzers. src/architecture.environment.yaml, src/core/*.architecture.unit.yaml
Membership drift hardening Missing member metadata does not suppress forbidden boundary findings. src/core/compare.ts, acceptance tests
Lifecycle labels Deprecated source is a warning and retired source is an error. src/core/compare.ts, test/planning-status.test.ts
Comment gate includeComments: false disables only comment findings. src/index.ts, src/comments/typescript-docblocks.ts
Report shape Reports serialize schema v3 architecture and finding categories. src/report/write.ts, .architecture/summary.md

Schema Version 3

Carrier names are restricted to this set:

architecture.yaml
architecture.unit.yaml
architecture.environment.yaml
<file-name>.architecture.unit.yaml
<file-name>.architecture.environment.yaml

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. src/model.ts, src/core/compare.ts, src/core/normalize.ts, src/core/validate.ts, src/comments/typescript-docblocks.ts, src/session/compare-runs.ts, src/report/write.ts
Comment gate 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. src/index.ts, src/cli/parse.ts, src/cli/commands.ts, src/comments/typescript-docblocks.ts
Forbidden relationships 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. src/core/model.ts, src/core/validate.ts, src/core/compare.ts, src/evidence/types.ts, test/forbidden-relationships.test.ts, test/dynamic-forbidden-evidence.test.ts
Stale and lifecycle planning status 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. src/core/model.ts, src/core/validate.ts, src/core/compare.ts, test/planning-status.test.ts
Output and logs exclusion 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. src/session/run.ts, src/source/walk.ts, src/repository/classify.ts, src/index.ts, test/output-exclusion.test.ts
Boundary and lifecycle acceptance 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.

Planning Capabilities

src/planner/questions.ts generates architect questions from normalized units, environments, resolved rule prompts, weak whys, missing descriptions, missing dynamic evidence, and granularity findings.

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 supported command set is:

node dist/cli.js check --profile typescript --root <repo-root> --source-root <source-root> --output <output-dir>
node dist/cli.js check --no-plugins --root <repo-root> --source-root <source-root>
node dist/cli.js draft --root <repo-root> --source-root <source-root> --logs <logs-dir> --label <label>
node dist/cli.js plan --root <repo-root> --source-root <source-root> --logs <logs-dir> --label <label>
node dist/cli.js questions --root <repo-root> --source-root <source-root> --unit <unit-id>
node dist/cli.js feedback --logs <logs-dir> --run <run-id>
node dist/cli.js compare --logs <logs-dir> --from <baseline-run> --to <latest-run>

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.

Verification Commands

Run the full build and test suite:

npm run check

Run the repository self-check:

node dist/cli.js check --root . --source-root src --output .architecture

Inspect the self-check findings:

Get-Content .architecture\findings.json

The expected self-check findings content for the current state is:

[]

Related Documents

Use docs/architecture-schema.md for authoring syntax.

Use docs/architecture-checker-agent-guide.md for command workflow and finding interpretation.

Use docs/architecture-checker-summary.md for a shorter overview.

Architecture Checker Summary

What Was Built

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.

Schema Version 3

Carrier names:

architecture.yaml
architecture.unit.yaml
architecture.environment.yaml
<file-name>.architecture.unit.yaml
<file-name>.architecture.environment.yaml

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
  • resolved inherited rules
  • enabled plugin names

CLI Modes

node dist/cli.js check --profile typescript --root <repo-root> --source-root <source-root> --output <output-dir>
node dist/cli.js check --no-plugins --root <repo-root> --source-root <source-root>
node dist/cli.js check --no-comments --root <repo-root> --source-root <source-root>
node dist/cli.js draft --root <repo-root> --source-root <source-root> --logs <logs-dir> --label unit-inventory
node dist/cli.js plan --root <repo-root> --source-root <source-root> --logs <logs-dir> --label first-workflow
node dist/cli.js questions --root <repo-root> --source-root <source-root> --unit <unit-id>
node dist/cli.js feedback --logs .logs --run <timestamp>
node dist/cli.js compare --logs .logs --from <timestamp> --to <timestamp>

Evidence Boundary

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:

await runChecker(root, {
  sourceRoot: "app",
  routeTargets: [{ routePrefix: "/api", target: "app/api" }]
});

Custom evidence plugins can declare coverage with observes:

await runChecker(root, {
  plugins: [
    {
      name: "custom-runtime",
      observes: ["sends"],
      async analyze({ tree }) {
        return tree.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.

Verification State

Expected verification:

npm run check
node dist/cli.js check --root . --source-root src --output .architecture

The self-check report should contain schema version 3, normalized source architecture, resolved rules, enabled plugin names, and observed relationships from configured plugins only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment