Documentation
¶
Overview ¶
Package tree loads aiwf entities from a consumer repository's directory tree into an in-memory model.
The loader is deliberately tolerant: per-file parse errors are collected and returned as LoadErrors alongside the (possibly partial) tree, not folded into a single failure. This matches the framework's "errors are findings" principle — `aiwf check` reports inconsistent state, it does not refuse to start.
Index ¶
- func Load(ctx context.Context, root string) (*Tree, []LoadError, error)
- type LoadError
- type Tree
- func (t *Tree) ByID(id string) *entity.Entity
- func (t *Tree) ByIDAll(id string) []*entity.Entity
- func (t *Tree) ByKind(k entity.Kind) []*entity.Entity
- func (t *Tree) ByPriorID(id string) *entity.Entity
- func (t *Tree) HasPlannedFile(path string) bool
- func (t *Tree) Reaches(from, to string) bool
- func (t *Tree) ReachesAny(froms []string, to string) bool
- func (t *Tree) ReferencedBy(id string) []string
- func (t *Tree) ResolveByCurrentOrPriorID(id string) *entity.Entity
- func (t *Tree) TrunkIDStrings() []string
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Load ¶
Load walks the consumer repo's entity-bearing directories and parses every recognized entity file. Per-file errors are returned in the LoadError slice; the (*Tree) is always populated with whatever could be parsed, even if some files failed.
The third return is reserved for fatal errors that prevent the walk from completing (e.g., a permission error on a parent directory). A missing entity-bearing directory is not fatal — fresh repos may not yet have one.
Types ¶
type LoadError ¶
LoadError is a per-file error encountered during loading. The loader collects these instead of aborting; checks surface them as findings.
type Tree ¶
type Tree struct {
// Root is the absolute path to the consumer repo root the tree was
// loaded from.
Root string
// Entities holds every successfully-parsed entity. Order is the
// order encountered during the directory walk, which is stable
// across runs but not otherwise specified.
Entities []*entity.Entity
// Stubs holds entities whose source file failed to parse. Each
// carries only id (derived from the path), kind, and path; body
// fields are zero. Stubs are deliberately not in Entities so that
// frontmatter-shape, status-valid, and similar body-level checks
// don't emit spurious findings. They exist so reference resolution
// can still locate a target by id, preventing one file's parse
// failure from cascading into "unresolved reference" findings on
// every entity that links to it. The original parse failure is
// reported as a load-error finding.
Stubs []*entity.Entity
// PlannedFiles records repo-relative file paths (forward-slash form)
// that a verb plans to write but hasn't yet. Used by checks that
// otherwise consult disk so that validate-then-write verbs can
// validate the projected world, including files about to be created.
// Loaded trees leave this nil.
PlannedFiles map[string]struct{}
// ReverseRefs maps each entity id (and each composite id mentioned
// as a reference target) to the ids of entities that reference it.
// Built from entity.ForwardRefs at Load time; consumed by aiwf show's
// referenced_by field, by aiwf check audits ("ADR is unreferenced"),
// and by the I2.5 provenance scope-reachability check.
//
// Composite-id targets roll up to their parent: a gap with
// `addressed_by: M-007/AC-1` appears in the AC's referrer list AND
// in M-007's referrer list. Each value-slice is sorted ascending
// and de-duplicated for stable output.
ReverseRefs map[string][]string
// TrunkIDs is the entity-id set observed in the configured trunk
// ref's tree, used by AllocateID (so a new id can't collide with
// trunk) and by the ids-unique check (so a working-tree id that
// also exists at a different path on trunk surfaces as a finding
// before push).
//
// Tree.Load does not populate this field — the cmd dispatcher reads
// the trunk via the trunk package once per verb run and assigns
// here so the verb's projection check sees the same trunk view.
// Tests that build trees in-memory leave TrunkIDs nil, in which
// case the allocator and the check rule degrade to working-tree-
// only behavior (the previous default).
TrunkIDs []trunk.ID
// TrunkRef is the resolved trunk ref name (e.g.
// "refs/remotes/origin/main") or empty when no trunk read
// happened. The reallocate tiebreaker uses it as the second
// argument to `git merge-base --is-ancestor` when two entities
// collide on an id and the verb has to pick which side to
// renumber. Populated alongside TrunkIDs by the cmd dispatcher;
// empty in tests that don't set it (the verb falls back to
// today's "ambiguous, pass a path" error in that case).
TrunkRef string
// Strays holds repo-relative file paths (forward-slash form) that
// the loader walked under work/* but could not classify as a
// recognized entity file via entity.PathKind. The tree-discipline
// check (G40) reports each as `unexpected-tree-file`; without that
// field the loader's silent skip would let any LLM-written stray
// linger under work/ undetected.
//
// Strays are tracked only under work/*; docs/adr/ is conventionally
// permissive (READMEs, templates, etc.) and is left alone. Files
// inside a contract's directory (work/contracts/C-NNN-*/) are
// recorded here but filtered by the check rule, since contracts
// legitimately carry schema/fixture artifacts alongside contract.md.
Strays []string
}
Tree is the in-memory representation of every aiwf entity discovered in a consumer repository.
func (*Tree) ByID ¶
ByID returns the first entity matching the id, or nil if absent. In a tree with duplicate ids (which the ids-unique check reports), ByID returns one; iterate Entities to enumerate all.
func (*Tree) ByIDAll ¶
ByIDAll returns every entity matching the id, in tree-walk order. Used by `aiwf reallocate` to detect the duplicate-id case so the trunk-ancestry tiebreaker can run; ByID alone would silently pick one and obscure that there's a choice to make.
func (*Tree) ByPriorID ¶
ByPriorID returns the entity whose `prior_ids` lineage list includes id, or nil if no entity claims that id as a prior. When multiple entities claim the same prior id (a hand-edit accident or the rare lineage-broken case), ByPriorID returns the first match in tree-walk order; iterate Entities to enumerate all.
func (*Tree) HasPlannedFile ¶
HasPlannedFile reports whether path (forward-slash, repo-relative) appears in PlannedFiles. Safe to call when PlannedFiles is nil.
func (*Tree) Reaches ¶
Reaches reports whether `from` can reach `to` by walking forward references (parent, depends_on, addressed_by, relates_to, supersedes, discovered_in, linked_adrs, etc.) through the tree. Self-loop returns true (an entity reaches itself trivially).
Composite ids are resolved to their parent before traversal: an AC walks under the milestone's reference graph, and reaching the milestone counts as reaching one of its ACs. This matches the scope-reachability rule in docs/pocv3/design/provenance-model.md §"Scope check": "addressed_by: M-007/AC-1" makes the gap reach M-007 (and therefore anything M-007 reaches via parent etc.).
The walk is bounded by the existing entity set; an unresolved id (referenced but not in the tree) is a dead end. Used by the I2.5 allow-rule (verb.Allow) to gate non-human-actor verbs against an active scope's scope-entity.
func (*Tree) ReachesAny ¶
ReachesAny reports whether any of `froms` reaches `to`. Used by the allow-rule's creation-act branch: a new entity's outbound references are evaluated as a set against the scope-entity.
func (*Tree) ReferencedBy ¶
ReferencedBy returns the ids of entities that reference id, in sorted order. Returns nil when no entity references id.
func (*Tree) ResolveByCurrentOrPriorID ¶
ResolveByCurrentOrPriorID resolves id to an entity by trying ByID first (current id), then ByPriorID (lineage match). Returns nil when neither matches. Used by `aiwf history` so a query for an old id transparently resolves to the current entity, then the caller walks the chain via the entity's PriorIDs slice.
func (*Tree) TrunkIDStrings ¶
TrunkIDStrings returns the id strings from TrunkIDs. Convenience for AllocateID, which only needs id values; the full trunk.ID is kept on the tree so the ids-unique check can include the trunk-side path in its finding message.