Skip to content

02 — The schema contract: canonical spine + Rust-native leaves #11

Description

@rahlk

Part of #9. Phase 0 — foundations.

Learning goals

Structs and enums as data modeling (TRPL Using Structs / Enums and Pattern Matching), serde
derive in anger (rename_all, defaults, skip_serializing_if), Option discipline,
BTreeMap vs HashMap (determinism!), newtypes.

Task

Evolve src/entities/ into the canonical CLDK contract while keeping its excellent Rust-native
leaves
(is_async/is_unsafe/is_const/extern_abi, lifetimes, generics, SafetyAnalysis
these are the "expand at the leaves" exemplars). Add the missing spine:

  • Root: RustApplication { symbol_table: BTreeMap<String, RustModule>, call_graph: Vec<RustCallEdge>, entrypoints, external_symbols }.
  • signature: String on every declaration node; Callsite.callee_signature: Option<String>
    (None at record time, backfilled by issue 06); identity-only RustCallEdge { source, target, type, weight, provenance }; cache metadata (content_hash, last_modified, file_size) on
    Module; serde derives everywhere, snake_case JSON.
  • Design signature_of() — the linchpin. One canonicalizer, used for declarations AND
    backfilled callees. Recommended shape: crate::module::path::func(param_types) for free fns,
    crate::module::Type::method(...) for inherent methods, <Type as Trait>::method(...) for
    trait impls. One entity per definition (not per generic instantiation). Rust has no
    constructors — Type::new is an ordinary associated fn; decide (and record) whether struct-
    literal expressions count as construction callsites.
  • Work the anchor → differentiate → decide → define loop per node
    (references/schema-design-loop.md), recording every decision in .claude/SCHEMA_DECISIONS.md
    (watch the global-gitignore trap: !.claude/ + !.claude/SCHEMA_DECISIONS.md in .gitignore).

Teacher's notes

  • BTreeMap for symbol_table and method maps buys deterministic JSON for free — the sibling
    analyzers fight for this with sort calls.
  • Traits/impls/enums-with-data are your new node kinds; trait bounds land in base_classes.
    Decide macros now: analyze source as written (recommended MVP) vs expanded — record it.
  • Compare against codeanalyzer-clang/codeanalyzer_clang/schema/clang_schema.py field by field:
    the spine must match; the leaves are yours.

Gate

  • A golden-file test: a hand-built RustApplication serializes to committed JSON (snake_case,
    stable key order) and round-trips back equal.
  • .claude/SCHEMA_DECISIONS.md covers: signature grammar, trait/impl/enum modeling, macro
    stance, constructor stance, generics stance.

Metadata

Metadata

Assignees

Labels

learning-ladderThe escalating-complexity curriculum issues

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions