Skip to content

Latest commit

 

History

History

README.md

AR-system: one engine, twelve components, three frontends

grund is one pipeline. A single tree walk reads every file once and produces Findings; the resolver completes the structural model; chapter-rule parsing and fact production meet only as ParsedRule and RuleFacts in the rules engine; checker rules turn that model into a Report; queries and writers answer from the same model; and thin frontends render or transport what the engine returns. Everything that decides lives in the engine crate grund-core, so the CLI, the LSP server and the planned bindings are the same verdicts behind different surfaces (§GOAL-multi-language, §FS-distribution, §FS-rules.11). Speed is set by the walk, which is why the walk happens once (§GOAL-fast-feedback). This page is the whole system, described once; each page in the index below describes one part and names its box here in a placement chapter (section 5).

1. The system

Twelve components in one crate, stacked in the order they may read each other, and the frontends above them:

             ┌───────────┐  ┌───────────┐  ┌──────────────────────┐
 frontends   │ grund-cli │  │ grund-lsp │  │ grund-node, grund-py │  planned
             └─────┬─────┘  └─────┬─────┘  └──────────┬───────────┘
                   └──────────────┼───────────────────┘
                                  ▼
             ┌────────────────────────────────────────────────────┐
 grund-core  │ 2.9  api                                           │
             ├─────────────────────────┬──────────────────────────┤
             │ 2.7  queries            │ 2.8  writers             │
             │      show, refs, list,  │      fmt, id, init,      │
             │      cover, completions,│      fetch, integrations │
             │      editor answers     │                          │
             ├─────────────────────────┴──────────────────────────┤
             │ 2.6  checker                                       │
             ├────────────────────────────────────────────────────┤
             │ 2.12 rules                                         │
             ├────────────────────────────────────────────────────┤
             │ 2.10 resolver                                      │
             ├────────────────────────────────────────────────────┤
             │ 2.5  scanner                                       │
             ├─────────────────────────┬──────────────────────────┤
             │ 2.4  workspace          │ 2.11 templates           │
             ├─────────────────────────┴──────────────────────────┤
             │ 2.3  config                                        │
             ├────────────────────────────────────────────────────┤
             │ 2.1  grammar                                       │
             ├────────────────────────────────────────────────────┤
             │ 2.2  model                                         │
             └────────────────────────────────────────────────────┘
             a component reads only what is below it (section 4)

The same components as the data moves through them:

               grund.toml ──► config ──► workspace ──┐
                                                     ▼
  tree ──► scanner ──► Findings ─────────────────► resolver ────────────────┐
                                                        │                  │
                             rule titles + vocabulary ──┤                  │
                                                        ▼                  ▼
                              ParsedRule + RuleFacts ─► rules ─► Diagnostic│
                                                                           ▼
                       loaded Findings ─────────────────► checker ─► Report ─┐
                              │                                             │
                              ├──► queries ──► data ────────────────────────┤
                              └──► writers ──► edits ───────────────────────┤
                                                                            ▼
                                                                           api
                                                                            │
                                                ┌──────────────┬────────────┴───────────┐
                                               cli            lsp           node, py (planned)

Data flows along the arrows and so does knowledge: a component knows only what the arrows into it carry (section 4).

2. Components

One subsection per box. Each says what the box consumes, what it produces, what it must not know, and where its design is written. A component whose subsection is its whole architecture has no page of its own; it gets one when it has invariants beyond its placement. Each subsection ends with the component's Rust module in crates/grund-core/src/, one directory per box, whose mod.rs re-exports all that crosses its boundary (§AR-core-module-layout.1, section 4).

2.1 grammar

Consumes text. Produces the lexical facts every other component shares: the ID grammar and its near-miss detection, comment-line and comment-block recognition, fenced-block boundaries, the number-only shorthand, inline-note layout, the never-rewrite predicates (§FS-fmt.2.3), and the formatter's own syntax — the --cross-refs link wrapper a shown body is flattened back from (§FS-fmt.6.2, §DF-show-cross-ref-flattening) and the two scopes a repository takes out of a rewrite's reach (§FS-fmt.2.5, §DF-fmt-suppression). Knows no file and no rule, and takes no Config: what configuration decides reaches it as the compiled Grammar and the one settings record built beside it — the marker, [reference] strict, the comment prefixes, the inline-note keys — so every reader here reads a decision and makes none (§FS-config.3.2, §FS-config.3.1, §FS-config.3.5). Module: crates/grund-core/src/grammar/.

2.2 model

Consumes nothing. Produces the data every component passes along: Findings, Declaration, Citation, Report, and the value records (§FS-values.2). Knows nothing else; it is types and tiny helpers. Module: crates/grund-core/src/model/.

2.3 config

Consumes grund.toml and the defaults. Produces one validated Config per project (§FS-config). Knows nothing of the tree it describes. Module: crates/grund-core/src/config/.

2.4 workspace

Consumes configs. Produces the multi-project scope — member expansion, claims, scope narrowing, boundary roots (§FS-workspace). Knows no rule, no rendering and no scan: loading the projects it names is the resolver's, one box up (section 2.10). Its own invariants are §AR-workspace. Module: crates/grund-core/src/workspace/.

2.5 scanner

Consumes the scope and the grammar. Produces Findings: every declaration, section, citation, value binding and grounding unit in the tree, from one walk (§FS-check.1) — and the one probe over the tree that is no part of that walk, which agent entrypoint files a repository has, because init and check both ask it and must not disagree (§FS-init.2.1, §FS-check.3.5). Knows no rule and no frontend, and never asks whether it is in a workspace. Design: §AR-scanner. Module: crates/grund-core/src/scanner/.

2.6 checker

Consumes the resolver's loaded Findings and diagnostics from the rules component. Produces the Report: errors, warnings and suggestions, each check one pass over its owned input (§FS-check, §FS-rules.11). It orchestrates rule parsing, fact production and evaluation without owning any of their grammar or relational meaning. Reads no file except in the two checks that must, and knows no frontend. Design: §AR-checker. Module: crates/grund-core/src/checker/.

2.7 queries

Consume Findings. Produce data for one question each: a declaration body (§FS-show), the citers of an ID (§FS-refs), the catalog (§FS-list), per-file coverage (§FS-cover), shell completions (§FS-completions), and the editor's snapshot, hover and on-type answers (§FS-lsp). Know no rendering; the text and JSON shapes belong to the frontends. Module: crates/grund-core/src/queries/.

2.8 writers

Consume Findings and the tree. Produce edits: citation normalization and cross-reference links (§FS-fmt), a proposed ID (§FS-id), the init scaffold and the managed agent-entrypoint block — which files a run writes, the splice that puts the block in one, and the walk-up the block's workspace section needs, the block text being the templates' (section 2.11) (§FS-init) — an external fact snapshot (§FS-fetch), and the clickable-citation client artifacts (§FS-integrations). The only components that write to the tree, and each writes only what its spec names (§REQ-no-data-loss). Module: crates/grund-core/src/writers/. The grund CLI calls these data-returning operations and owns their argv, rendered bytes and exit codes; for example, integrations is implemented by crates/grund-cli/src/cli_integrations*.rs over the pub block of writers/mod.rs (§FS-integrations.1, §AR-bindings.3).

2.9 api

Consumes everything above. Produces the embedding surface: data-returning functions and the public types (§AR-bindings.2, §FS-distribution.3). Writes to no stream, exits no process and knows no frontend. Module: crates/grund-core/src/api/, one file per surface beside the private adapters that fill it. There is no process-frontend exception (section 2.9.1).

2.9.1 No process frontend lives in the engine

No engine component parses argv, renders to a stream, or decides a process exit status. The former grund_core::main_entry() process frontend and its compat/ module left after the deprecation path of §REQ-backwards-compatibility.2 completed; lib.rs exports no replacement renderer (§FS-distribution.3.1.1, §DA-engine-renders-nothing). Run-level cautions, including the four [workspace] warnings that were last to move, travel as Diagnostic data for each frontend to render (§FS-check.4.7, §FS-check.3.29, §FS-check.4.10, §FS-workspace.6.1). tests/integration/test_engine_boundary.py holds the absence of the renderer and process-entry export; the direction ledger in section 4 covers only the twelve engine components.

2.10 resolver

Consumes the project map from workspace and every project's Findings from the scanner. Produces the loaded project set a run operates on — each project scanned, its off-grammar citations and its cross-namespace number-only shorthands reconciled across the whole set — and four answers that are functions of what a run loaded: which project a citation resolves against, a declaration's body sliced by the spans the scan recorded, the link target its ID resolves to, and which declaration a shorthand token names in whichever project's catalog answers for it (§AR-resolver.4) (§FS-workspace.8, §FS-show.2, §FS-fmt.6.2, §FS-fmt.2.4). Knows no rule and no rendering; it runs scans, which is what puts it above the scanner while the config half of the workspace stays below it. Design: §AR-resolver. Module: crates/grund-core/src/resolver/.

2.11 templates

Consumes config and the managed-block markers the grammar recognizes. Produces the text a managed block should say as a function of that config: the AGENTS.md block of §FS-init.2.3 with its config-derived sections, the generated grund.toml, and the embedded scaffold payload (§FS-init.2.1, §FS-init.2.4, §FS-init.5). Rendering is deterministic, so a fresh render is the hash: init writes it and check re-renders its ### Citation directions and ### Clickable citations sections and byte-compares them for drift (§FS-check.3.5, §AR-checker.2.7) — two commands asking one component for the same answer from opposite directions, which is why it is a box rather than a corner of the writers. Knows no file, no rule and no rendering of a report: it writes nothing and reads no tree, and the one block section that needs a walk arrives already rendered from the run that walked for it (§FS-init.2.3.4.15). Module: crates/grund-core/src/templates/.

2.12 rules

Consumes authored rule titles with config vocabulary and the resolver's complete structural model. Produces located rule Diagnostics for the checker through exactly two internal data boundaries: ParsedRule from the sentence front end and RuleFacts from the Markdown adapter. The logic engine evaluates only those values and owns semantic deduplication; the scanner remains rule-blind (§FS-rules.11). Knows no frontend, renderer or filesystem beyond repository-relative fact anchors. Design: §AR-rules. Module: crates/grund-core/src/rules/ (created with the implementation).

3. Frontends

Two today, two planned, and none has engine logic (§AR-bindings). grund-cli parses arguments, renders text and JSON, and maps exit codes (§AR-bindings.3, §FS-cli). grund-lsp speaks LSP over stdio and translates every request into an api call (§AR-lsp). grund-node and grund-py will marshal the same functions (§AR-bindings.5, §AR-bindings.6). Each depends on grund-core and on nothing of the others, so the CLI carries no JSON-RPC and the server no terminal renderer (§DA-lsp-optional).

4. Dependency direction

One rule: no component reads one above it. The stack in section 1 is the rule drawn: the frontends sit above api; api above the queries and the writers, which are siblings and read nothing of each other; those above the checker; the checker above rules; rules above resolver; resolver above scanner; scanner above workspace and templates, which are siblings and read nothing of each other; both above config; config above grammar; and grammar above model, which reads nothing but std. Two things hold it. The compiler holds a component's privacy — nothing outside a module directory can name what its mod.rs does not re-export (§AR-core-module-layout.1.1) — and tests/integration/test_dependency_direction.py holds the order across those directories, with every read that still runs the other way listed one by one and marked at its import, so the list can only shrink. The rules component's internal direction and stronger parser/engine prohibitions are held by tests/integration/test_rules_architecture.py (§AR-rules.6). Three consequences are held by tests of their own:

  • The engine writes no stream and exits no process; the frontends render (tests/integration/test_engine_boundary.py).
  • The engine names no frontend's protocol: no LSP types in grund-core, no CLI in grund-lsp (tests/integration/test_frontend_isolation.py).
  • A frontend re-implements nothing: every regex, walk and rule is in the engine (§AR-bindings.2).

5. What holds the shape

  • Placement. Every page in the index below opens with a ## placement: chapter — a named section, so grund <ID>.placement is the question — that opens with a diagram in the notation of section 1 — what feeds the component on the left, its box in the middle, what it feeds on the right — and then says, in four facts, its box in section 2 or 3, what it takes and from whom, what it gives and to whom, and what it must not know. Anything wider than that belongs on this page. tests/integration/test_architecture_placement.py holds it: every page but this one has the chapter, the chapter opens with a fenced diagram, and it cites this page.
  • Files. How the engine's files are named, owned and sized is §AR-core-module-layout; two tests hold its module directories and the direction across them (§AR-core-module-layout.1.4), and fissile holds the size.
  • Assurance is not a component. §AR-ci, §AR-benchmarks and §AR-goal-measurement measure the system rather than sit in it, and are listed apart below; their placement chapters say what each measures.

Index

One file per page; each H1 declares an AR-<slug> ID and the body is its contract, and §AR-<slug>.<section> from anywhere in the tree resolves into it. A page may live inline in the doc-comment of the file it describes: its canonical link in this index enrolls it directly, with no stub file (§FS-check.3.18), and grund <ID> resolves the source declaration and strips its comment markers (§AR-scanner.4 lists the doc-comment forms). §AR-checker is the worked example: its only declaration is the doc-comment of fn check in crates/grund-core/src/checker/report.rs.

The system:

ID Subject
§AR-system one engine, twelve components, three frontends — this page

The components and frontends:

ID Subject
§AR-scanner how grund discovers declarations and citations
§AR-checker how grund validates the scanner's findings — declared and enrolled directly from crates/grund-core/src/checker/report.rs
§AR-workspace how the config-time workspace layer composes with the config loader and the scanner
§AR-resolver how a run loads every project and resolves a citation to one of them
§AR-rules how sentence parsing and relational evaluation meet only through ParsedRule and RuleFacts
§AR-bindings the engine's contract with its frontends, and the shape of the planned ones
§AR-lsp how the LSP server is built
§AR-core-module-layout how the engine's files are named, owned and sized

What measures the system:

ID Subject
§AR-ci CI mirrors the local pre-commit gate
§AR-benchmarks instruction-counting benchmarks for the hot CLI commands
§AR-goal-measurement goal and requirement meters live outside goals

The index is navigational: cite a page's ID, or AR-system for the whole, never this file by path.