tree

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func Load

func Load(ctx context.Context, root string) (*Tree, []LoadError, error)

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

type LoadError struct {
	Path string
	Err  error
}

LoadError is a per-file error encountered during loading. The loader collects these instead of aborting; checks surface them as findings.

func (*LoadError) Error

func (e *LoadError) Error() string

func (*LoadError) Unwrap

func (e *LoadError) Unwrap() error

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

func (t *Tree) ByID(id string) *entity.Entity

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

func (t *Tree) ByIDAll(id string) []*entity.Entity

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) ByKind

func (t *Tree) ByKind(k entity.Kind) []*entity.Entity

ByKind returns every entity of the given kind, in tree-walk order.

func (*Tree) ByPriorID

func (t *Tree) ByPriorID(id string) *entity.Entity

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

func (t *Tree) HasPlannedFile(path string) bool

HasPlannedFile reports whether path (forward-slash, repo-relative) appears in PlannedFiles. Safe to call when PlannedFiles is nil.

func (*Tree) Reaches

func (t *Tree) Reaches(from, to string) bool

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

func (t *Tree) ReachesAny(froms []string, to string) bool

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

func (t *Tree) ReferencedBy(id string) []string

ReferencedBy returns the ids of entities that reference id, in sorted order. Returns nil when no entity references id.

func (*Tree) ResolveByCurrentOrPriorID

func (t *Tree) ResolveByCurrentOrPriorID(id string) *entity.Entity

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

func (t *Tree) TrunkIDStrings() []string

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL