model
model::Alt model::ArchRule One architectural-principle lint rule (LANG.md §9 — the C4 facade/boundary principles). `code` is its stable identifier (`PDS-ARCH-001`); `slug` is the filename of the article documenting it under the published principles base URL. Every firing is a `Warning` — a violation advises rather than invalidates — so the rule carries no severity of its own.
model::Architecture LANG.md §9 — architectural-principle lints over the resolved graph. Beyond §8.2 visibility (which a `public` component still satisfies), these advise on C4 structure: a cross-module call SHOULD reach a container/system's published face, not an internal `component` (Facade/Gateway); the module dependency graph SHOULD stay acyclic; a cross-system call SHOULD couple to the other system's boundary, not its containers. Each firing is a `Warning` stamped with the rule's `code` and `codeDescription` (its article URL); the model stays valid.
Parent
- for model::Model
Inbound
- call model::Checks · check
Outbound
- from → syntax::Diagnostic
- from → syntax::Diagnostic
- from → syntax::Diagnostic
- from → syntax::Diagnostic
model::Branch model::Builder Projects the parsed, resolved workspace into the architecture graph: structural nodes plus edges from `for` parents, body calls, triggers, and `from` provenance, with a sequence trace recorded per disclosed body. A pure projection — no I/O — so the emit crate and a future salsa/LSP layer can both adopt it.
Parent
- for model::Model
Inbound
- call model::Model · graph
- call model::Checks · graphOf
Outbound
- from → model::Graph
- call → model::Workspace · build
- from → model::Graph
Scenarios
One resolved graph feeds every view (§9).
- given a parsed, resolved workspace
- when the graph is built
- then every structural node and callable becomes a graph node
- and for-parent, call, trigger, and provenance edges are recorded
- and each disclosed body carries an ordered sequence trace
model::Call model::CaretResolve Caret resolution — the engine every cursor feature shares (`lsp_core` hover, definition, references, rename; the IDE's symbol actions). Tokenizes the active source, finds the identifier under the offset, and resolves it across the workspace and its externals: a node or data FQN, a `self.`/member access, or a local reference. Token-level, so it survives a partial parse.
Parent
- for model::Model
model::Checks Static analysis (LANG.md §2.2, §2.3, §2.4, §3.3, §3.4, §3.5, §4, §5.1, §5.2, §6, §7, §8): builds the symbol tables, then runs the well-formedness checks. Every entry returns a `syntax::Diagnostic[]`; well-formed input yields an empty one. Checks are conservative — each rule fires only when the violation is certain.
Parent
- for model::Model
Inbound
- call model::Model · check
- call model::Model · checkWorkspace
- call model::Model · checkWorkspaceModules
Outbound
- call → syntax::Syntax · parse
- from → syntax::Diagnostic
- call → model::Resolver · build
- from → syntax::Diagnostic
- call → model::Workspace · buildWithExternals
- from → syntax::Diagnostic
- call → model::VariantCollision · check
- call → model::FeatureCheck · check
- from → syntax::Diagnostic
- call → model::ReservedNames · check
- call → model::TypeRefs · check
- from → syntax::Diagnostic
- from → syntax::Diagnostic
- call → model::CrossModule · check
- call → model::Builder · graphOf
- call → model::Architecture · check
Scenarios
Well-formed input yields no diagnostics: a check returns data, never throws.
- given a well-formed module
- when it is checked
- then the returned diagnostic list is empty
- but the check never throws or aborts
A cross-module visibility error is attributed to the referrer (§8.2).
- given module B referencing a private node in module A
- when the workspace is checked per module
- then the diagnostic lands in module B, where the reference is written
- but module A, the declarer, stays clean
model::Commit model::Completion Context-aware completion (the model-native producer `lsp_core` maps to LSP items): candidates at a caret offset resolved across the workspace and its externals — members after `.`, a module's public symbols after `::`, the built-in macros after `#[`, types in type position, else keywords and symbols.
Parent
- for model::Model
model::ConstantType One `constant`'s FQN paired with its declared primitive type name (§3.6, ADR-039), so inference resolves a `module::NAME` reference to its type.
model::CrossModule §8.2 — cross-module visibility resolution. Walks each module's qualified references — `for` parents, type annotations (field, parameter, return, and generic-argument types), feature targets, body call targets, value-position union-variant references, and the member named after a call target's FQN — and against the global FQN index enforces: a reference from module A to a node in module B resolves only if that node is `public`. A private target or a dangling cross-module FQN is a diagnostic. Same-module references are the single-module checks' business and are skipped here.
Scenarios
Cross-module references obey visibility (§8.2).
- given a workspace whose modules reference each other by FQN
- when the workspace is checked
- then a cross-module reference to a public node resolves
- but a reference to a private node, or a dangling FQN, is rejected
model::DataField One field of a `data` record or union-variant record: its name and rendered type.
model::DataShape The disclosed shape of a `data` node (§3.4, §3.5): a record of fields, a discriminated union of variants, or an undisclosed black box. Carried on a `data` `GraphNode` so the entity (ER) view draws the rows without re-reading source.
model::DataVariant One variant of a `data` union (§3.5): its name and, for a record variant, its fields.
model::DepError Why parsing a manifest's `[dependencies]` table or a `pds.lock` failed: the offending entry and a human-readable message. Malformed TOML, an unknown key, or more than one of tag/rev/branch on one dependency (§8.3).
model::DepFile One dependency module's in-memory form: its FQN *within* the dependency workspace (path relative to that workspace root) and its source. The resolver prefixes it with the dependency name.
model::DepSpec A `[dependencies]` entry as written in `pds.toml` (§8.3): a git URL with at most one of tag/rev/branch and an in-repo sub-path, or a local sibling path. Empty strings stand for the absent options.
model::Deps §8.3 — the dependency resolver. Parses a manifest's `[dependencies]` and a `pds.lock`, then maps each direct dependency to its dependency-name-prefixed modules — the externals the checker binds for cross-workspace resolution. The single source of the slug→name→`dep::module` mapping, shared by the native loader (`project`) and the WASM bridge (`ide`). Pure: fetching and disk are the caller's job.
Parent
- for model::Model
Scenarios
Dependencies resolve to prefixed externals, by one shared rule (§8.3).
- given a manifest declaring git and local dependencies, with a pds.lock
- when the dependency modules are resolved
- then each direct dependency's modules load under its dependency-name prefix
- and a vendored git file is selected by matching its slug to a lock edge
- but the same rule serves both the native loader and the WASM bridge, so they never drift
model::Edge A typed, directed relationship between two graph node FQNs. `source` is the wire field `from` — `from` is a PseudoScript keyword, so the model renames it. Either endpoint may name a node not present as a `GraphNode` — a synthesised trigger initiator, or a target that does not resolve. `label` is the method name for `Call`, otherwise empty. `span` is the source range that produced the edge — the call site for a `Call` — so an architectural lint can point its diagnostic at the offending call; the empty span when the edge is synthesised.
model::EdgeKind What relationship a graph edge expresses (LANG.md §9.1): a `for` parent link, a body call (label = method), a synthesised trigger initiator edge, or a `from` provenance link.
model::FeatureCheck §5.2 / §8.1 — feature checks. A feature's `for` target MUST resolve to a node, not a type or module; a target this single-module model cannot see is left to cross-module resolution. Feature names occupy their own module namespace, so a repeated name is a collision (checked once per module).
Scenarios
Bindings are single-assignment, with no shadowing (§7.1, ADR-002).
- given a callable body that binds a name already in scope
- when the body is checked
- then the re-binding is rejected
- and a binding introduced in a nested block still shadows nothing
A feature's target must be a node, and feature names are unique (§5.2, §8.1).
- given a feature whose `for` target names a `data` type
- when the module is checked
- then the feature is rejected because its target is not a node
- and a second feature reusing an existing feature name is a collision
model::FieldlessVariants A union's fieldless variant names, keyed by the union's bare name (§3.5). Fieldless variants do not hoist a symbol — they are addressed `module::Union::Variant` (ADR-032) — so they index under their union.
model::Folding The foldable regions of a module: every multi-line disclosed declaration and statement block, and doc-comment runs.
Parent
- for model::Model
model::Git model::Graph The resolved architecture graph: every node and typed edge across all C4 levels, plus feature scenarios. The single source every view projects from — enough to extract any view without re-reading source. Per-callable sequence traces (`Step[]`) are held alongside, keyed by the owning callable's FQN.
Inbound
- from model::Model
- from model::Builder
- from model::Builder
- from model::Model
model::GraphNode One node in the resolved graph — a structural declaration, a `data` type, or a callable. Carries its FQN, simple name, kind, enclosing-node FQN (`for` parent, owning node, or empty at top level), declaring module, visibility, name span, any trigger macros (callables only), the call signature (callables only), the disclosed shape (`data` only), and lifted `///` documentation.
Inbound
- from doc::Pages
- from universe::FlowTracer
model::Inference Local-binding type inference, for inlay hints and hover. Assignments are untyped (`x = expr`); this infers each binding's type from its right-hand side — a call's return type, a field access, a `from` expression, or a literal. Best-effort: an un-typeable expression yields nothing rather than a guess.
Parent
- for model::Model
model::Local model::Lock A parsed `pds.lock` (§8.4): the pinned dependency graph. A black box here — its shape is the lockfile's concern, not the model's.
model::Loop model::MacroTargeting §2.4 / ADR-015 — macro targeting. Every built-in macro (`onevent`, `schedule`, `http`, `manual`) targets callables; on any structural declaration it is a wrong-target error, and an unrecognised macro is unknown. An `#[onevent(E)]` handler MUST have exactly one parameter whose type matches the event type.
Parent
- for model::Model
Scenarios
A built-in macro on a structural declaration is a wrong-target error (§2.4).
- given a `#[http(...)]` macro written on a container declaration
- when the declaration is checked
- then the macro is rejected as targeting the wrong kind
- but the same macro on a callable is accepted
An onevent handler's parameter type must match the triggered event (§2.4).
- given a callable tagged `#[onevent(OrderPlaced)]`
- when its single parameter type is checked
- then a parameter of type OrderPlaced is accepted
- but a mismatched parameter type is rejected
model::Member A member reachable through `.` from a node or `data` owner: its name, kind, definition span, one-line signature detail (`run(name: string): uuid`), value type (a field's type or a callable's return type), the ordered parameter types (empty for a field — their count is the call arity, ADR-022), the `///` summary (absent for a field — the grammar has no field doc), and whether it is `public` (callables only; gates cross-module member completion, §8.2). Powers member completion, arity checks, and go-to-definition.
model::MemberKind What kind of member is reachable through `.` from an owner: a node's callable (§5.1) or a `data` record field (§3.4).
model::Model `crates/pseudoscript-model`. AST to one resolved graph; static checks (resolution, visibility, Result flow, return coverage); the §8.3 dependency resolver; and the LSP analysis primitives (completion, folding, semantic tokens, inference). WASM-safe.
Parent
Inbound
- call cli::DocCmd · checkWorkspaceModules
- call cli::DocCmd · graph
- call cli::CheckCmd · checkWorkspace
- call cli::CheckCmd · check
- call cli::EvalCmd · check
- call cli::OutlineCmd · graph
- call cli::SvgCmd · graph
- call ide::LanguageSession · check
- call ide::Docs · checkWorkspaceModules
- call ide::Universe · graph
- call lsp_core::Analysis · check
- call universe::Adapter · graph
Outbound
- from → model::ModuleDiagnostics
- from → cli::Graph
- from → syntax::Diagnostic
- from → syntax::Diagnostic
- from → syntax::Diagnostic
- from → cli::Graph
- from → cli::Graph
- from → model::ModuleDiagnostics
- from → model::Graph
- call → model::Builder · graph
- call → model::Checks · check
- call → model::Checks · checkWorkspace
- call → model::Checks · checkWorkspaceModules
- from → model::Graph
model::ModuleDiagnostics One workspace module's diagnostics, attributed to its FQN. Every span lies in that module's own source, so a tool with the FQN-to-file mapping publishes each list against the right file.
Inbound
- from model::Model
- from model::Model
- from ide::LanguageSession
- from lsp::Store
model::ModuleEntry One module's parsed AST, resolved symbol table, and FQN, as held by a `Workspace`.
Inbound
- from model::Workspace
- from model::Workspace
- from model::Workspace
- from model::Workspace
model::NodeDoc A node's documentation, lifted from its `///` block (LANG.md §2.1): the summary (compact-diagram text), the extended description (tooltip text), and its tags (` `, including the `#`), in source order.
model::NodeKind What kind of model element a `GraphNode` is. Adds `Callable` to the structural kinds: structural declarations, `data` types (including hoisted union variants), and callables all become nodes.
model::OnEvent model::PackageId A content-addressable package identity: its source, resolved revision, and in-repo path. Drives the `pds_modules/` slug a git package materialises into.
model::ParentKind §4 / ADR-010 — C4 parent-kind well-formedness. A `for` parent links a node to its enclosing level, and the levels MUST nest: a `container` parents a `system`, a `component` parents a `container`. A parent of the wrong kind is a diagnostic (`container `X` parent `Y` is not a system`). A bare parent that names no same-module node is unresolved (§8.1); a qualified parent this single-module model cannot see is left to cross-module resolution.
Parent
- for model::Model
Scenarios
C4 levels must nest: a container parents a system, a component a container (§4).
- given a container whose `for` parent names a component, not a system
- when the declaration is checked
- then the container is rejected because its parent is the wrong kind
- but a container parenting a system, and a component parenting a container, are accepted
model::Private model::Public model::Record model::ReservedNames §2.3 / ADR-012 — reserved-word identifiers. A declared identifier MUST NOT be a reserved word: a keyword, a primitive type name, or `Result`/`Option`. Covers every name a module declares — `data`, node, callable, field, parameter, variant, and feature names — so `data string` or a parameter named `Result` is a diagnostic.
model::Resolution The outcome of resolving a cross-module reference (LANG.md §8.2): the FQN names a public symbol, a private one reached from another module, or nothing.
model::ResolveHit One resolved caret hit: the clicked identifier's span, the defining module and the definition's span there, the resolved graph FQN (a member resolves to `owner::member` — the key `emit`'s symbol projection uses), a Markdown title line, and the doc summary or signature detail shown under it.
model::Resolver Resolves a module's declarations into a symbol table (LANG.md §8). FQNs are file-derived: a single `.pds` file is one module, named by its `//!` path. Holds two namespaces — type names and node names share the symbol table, while feature names live in a separate namespace the `Checks` enforce — plus the member, fieldless-variant, and constant-type indexes. The LSP reuses the table for hover and go-to-definition.
Scenarios
References resolve regardless of declaration order (§8.1).
- given a node referenced before its declaration appears in the module
- when the symbol table is built
- then the reference resolves to the later declaration
model::ResultFlow §6 — flow-sensitive `Result` / `Option` typestate. Each binding carries a state (`Unknown`, `Ok`/`Err` for a `Result`, `Some`/`None` for an `Option`), narrowed on entering the branches of `if (r.isErr)` / `if (r.isOk)` / `if (o.isNone)` / `if (o.isSome)`. Reading `.value` while known-`Err` or known-`None`, or `.error` while known-`Ok`, is a model error. The env is cloned per branch so narrowing does not leak; an `if` whose guarded arm diverges narrows the fall-through to the inverse state (`Ok`<->`Err`, `Some`<->`None`).
Parent
- for model::Model
Scenarios
Result and Option typestate narrows accessor reads to the right branch (§6).
- given a binding `r` of type Result inside `if (r.isErr)`
- and a binding `o` of type Option inside `if (o.isNone)`
- when the body is checked
- then reading `r.value` on the Err branch is rejected
- and reading `r.error` on the Ok branch is rejected
- and reading `o.value` on the None branch is rejected
- but the matching accessor on each branch is accepted
A diverging guarded arm narrows the fall-through to the inverse state (§6).
- given `if (r.isErr) { return Err(r.error) }` with no else
- when the statements after the `if` are checked
- then `r` is treated as Ok in the fall-through
- and a later `r.value` read is accepted
model::Return model::ReturnCoverage ADR-016 — return coverage. A disclosed non-void callable MUST return on every path. A `return` makes a block diverge; an `if`/`else` diverges only when both arms do; `for`/`while` bodies and bare expressions never guarantee a return. An explicit `void` and a parse-recovered missing return type (ADR-040) both mean void.
Parent
- for model::Model
Scenarios
A disclosed non-void callable must return on every path (ADR-016).
- given a disclosed callable with a non-void return type
- when its body is checked
- then a body returning on every path is accepted
- but a path that falls through without returning is rejected
model::Rev A resolved revision selector — at most one of these (§8.3); `Default` is the remote's default-branch HEAD.
model::Scenario A `feature` BDD scenario attached to its target node (LANG.md §5.2, §9.3): its name, the canonicalised target FQN (as written if it does not resolve), the declaring module's FQN, the span of the feature name (a host's go-to-definition target), lifted `///` documentation, and the ordered given/when/then steps. Steps are prose, not resolved, so a scenario adds no edges; it renders as a card on its target node's doc page.
Inbound
- from doc::Scenarios
model::ScenarioStep One step of a `Scenario` (LANG.md §5.2): a keyword (`given`/`when`/`then`/ `and`/`but`) and its prose, with the string literal's quotes stripped.
model::SelfCall model::Semantic AST-aware semantic colouring: each identifier classified by its declared role (node→namespace, `data`→class, callable→method, parameter, call segment), the spans `lsp_core` delta-encodes for the editor.
Parent
- for model::Model
model::SigParam One parameter of a callable signature: its name and rendered type.
model::Signature A callable node's signature (§5.1): its ordered parameters and rendered return type. Carried on a callable `GraphNode` so the sequence and symbol views can show the call shape without re-reading source.
model::Source The resolved source of a dependency (ADR-026): a fetched git repository at a revision and in-repo sub-path, or a local sibling workspace read from disk.
model::Step One step in a callable's ordered sequence trace (LANG.md §7, §9.2). Calls in a chained expression are emitted left-to-right; control flow becomes `Alt`/ `Loop` frames whose bodies are nested step lists, in §7 evaluation order.
model::Symbol One declared, addressable node in a module namespace: the bare name, its FQN (`module::Ledger`), its declaration kind, whether it is `public` (§8.2), and the span of its name.
model::SymbolKind The lowercase declaration keyword a `Symbol` names. Drives the parent-kind and macro-target diagnostics, which embed it as prose. `Constant` is a top-level primitive value (§3.6, ADR-039).
model::SymbolTable One module's resolved symbol table (LANG.md §8): its FQN plus the declared nodes, the members reachable through `.`, the fieldless-variant index (ADR-032), and the constant-type index (ADR-039). The `Resolver` produces it; the `Workspace` indexes and the `Checks` consume it.
Inbound
- from model::Resolver
model::Tag model::Trigger A trigger macro on a callable (LANG.md §2.4): the macro that makes the callable a diagram entry point and synthesises an inbound initiator edge. Each variant maps to a synthesised initiator node (`event:<FQN>`, `scheduler`, `client`, `caller`).
model::TypeRefs §3.3 / §8.1 — type-reference resolution. Every named type in a declaration — a field, parameter, or return type, and each generic argument — MUST resolve. A bare name that resolves to nothing is reported as unresolved; a name that resolves to a declared type or node MUST be written by its full FQN (`module::Name`), so an unqualified declared-type annotation is rejected. Built-in types (the primitives, `Result`, `Option`) stay bare.
model::Union model::VariantCollision §3.5 / ADR-006 — union variant collision. A top-level `data Name` followed by an inline variant `| Name { ... }` hoisting the same name is a collision. Bare variants reference existing data and never collide.
Scenarios
An inline union variant colliding with a prior data name is rejected (§3.5).
- given a top-level `data Name` and an inline variant `| Name { ... }`
- when the module is checked
- then the colliding inline variant is rejected
- but a bare `| Name` referencing the existing data is accepted
model::Visibility Whether a node is `public` (cross-module addressable) or module-private (LANG.md §8.2).
model::Workspace The resolved set of modules keyed by FQN (LANG.md §8). Built once per workspace from `(fqn, parsed module)` pairs; owns the per-module models and a global FQN-to-`Symbol` index for cross-module resolution.
Parent
- for model::Model
Inbound
- from lsp::Store
- call model::Checks · buildWithExternals
- call model::Builder · build
Outbound
- from → model::ModuleEntry
- from → model::ModuleEntry
- from → model::Option
- from → model::ModuleEntry
- from → model::ModuleEntry
Scenarios
A same-module reference resolves regardless of visibility (§8.2).
- given a reference whose target is declared in the same module
- when the reference is resolved
- then it resolves whether the target is public or private
model::WorkspaceModule One `.pds` module of a workspace: its caller-supplied FQN (the loader derives it from the file path relative to `pds.toml`, LANG.md §8.1) and its source text. This crate stays pure over in-memory modules; it never touches the filesystem.
Inbound
- from ide::FsAccessAdapter
- from ide::FsAccessAdapter
- from lsp::Store