Documentation
¶
Overview ¶
Package entity defines the six aiwf entity kinds, their status sets, id formats, and the in-memory frontmatter shape every entity carries.
The package is the data model. It deliberately knows nothing about the filesystem, git, or validation; the tree package loads entities, the check package validates them.
Index ¶
- Constants
- Variables
- func ActiveFormOf(relPath string) string
- func AllocateID(k Kind, entities []*Entity, trunkIDs []string) string
- func AllowedACStatuses() []string
- func AllowedStatuses(k Kind) []string
- func AllowedTDDPhases() []string
- func AllowedTDDPolicies() []string
- func AllowedTransitions(k Kind, from string) []string
- func BodyTemplate(k Kind) []byte
- func CancelTarget(k Kind) string
- func Canonicalize(id string) string
- func IDFormat(k Kind) string
- func IDFromPath(relPath string, k Kind) (string, bool)
- func IDGrepAlternation(id string) string
- func IsAllowedACStatus(s string) bool
- func IsAllowedStatus(k Kind, status string) bool
- func IsAllowedTDDPhase(p string) bool
- func IsAllowedTDDPolicy(p string) bool
- func IsArchivedPath(relPath string) bool
- func IsCompositeID(s string) bool
- func IsLegalACTransition(from, to string) bool
- func IsLegalTDDPhaseTransition(from, to string) bool
- func IsProseyTitle(title string) bool
- func IsTerminal(k Kind, status string) bool
- func MilestoneCanGoDone(m *Entity) (canGoDone bool, openACs []string)
- func ParseACSections(body []byte) map[string]string
- func ParseBodySections(body []byte) map[string]string
- func ParseCompositeID(s string) (parent, sub string, ok bool)
- func SectionSlug(heading string) string
- func Serialize(e *Entity, body []byte) ([]byte, error)
- func Slugify(title string) string
- func SlugifyDetailed(title string) (slug string, dropped []rune)
- func Split(content []byte) (frontmatter, body []byte, ok bool)
- func SubKindFromID(id string) (string, bool)
- func ValidateID(k Kind, id string) error
- func ValidateSlug(slug string, maxLength int) error
- func ValidateTitle(title string, maxLength int) error
- func ValidateTransition(k Kind, from, to string) error
- type AcceptanceCriterion
- type BodySection
- type Cardinality
- type Entity
- type ForwardRef
- type Kind
- type RefField
- type Schema
Constants ¶
const ( // Epic / shared. StatusProposed = "proposed" StatusActive = "active" StatusDone = "done" StatusCancelled = "cancelled" // Milestone-only. StatusDraft = "draft" StatusInProgress = "in_progress" // ADR / Decision. StatusAccepted = "accepted" StatusSuperseded = "superseded" StatusRejected = "rejected" // Gap. StatusOpen = "open" StatusAddressed = "addressed" StatusWontfix = "wontfix" // Contract. StatusDeprecated = "deprecated" StatusRetired = "retired" // Acceptance criterion (composite). StatusMet = "met" StatusDeferred = "deferred" )
Status constants for the closed sets. Hardcoded; see docs/pocv3/design/design-decisions.md and the schemas table for per-kind allowance. Use these constants instead of bare string literals so a future renumber / spelling shift lands in one place.
const ( TDDPhaseRed = "red" TDDPhaseGreen = "green" TDDPhaseRefactor = "refactor" TDDPhaseDone = "done" )
TDD-phase constants for the AC FSM.
const CanonicalPad = 4
CanonicalPad is the canonical zero-pad width for every entity kind's id format (E-NNNN, M-NNNN, ADR-NNNN, G-NNNN, D-NNNN, C-NNNN). Numbers exceeding 10^pad expand naturally; the pad is a *minimum*, not a maximum.
Per ADR-0008, the kernel emits a uniform 4-digit width across all kinds. Parsers (idPatterns, ParseCompositeID) keep accepting narrower legacy widths so pre-migration trees, branches, and commit trailers continue to validate without history rewrite. Display surfaces and new allocations always emit canonical width via Canonicalize.
Variables ¶
var ErrNoFrontmatter = errors.New("no YAML frontmatter found")
ErrNoFrontmatter is returned when the input does not contain a `---`-delimited YAML block at the top of the file.
Functions ¶
func ActiveFormOf ¶ added in v0.8.0
ActiveFormOf returns the active-shape repo-relative path for relPath, stripping a recognized per-kind `archive/` segment when present. Returns relPath unchanged when it is already active-shape (or doesn't classify as any ADR-0004 archive form). Idempotent.
Use this when comparing a branch path to a trunk path across a potential archive sweep: an entity at `work/gaps/archive/G-0010.md` on branch and `work/gaps/G-0010.md` on trunk shares the same active form, so the path divergence is a sweep rename, not a collision.
The path is forward-slash, repo-relative — the same shape stored on Entity.Path by tree.Load and emitted by tree.TrunkIDs.
func AllocateID ¶
AllocateID picks the next free id for the kind, scanning the union of (a) entities — the caller's working tree — and (b) trunkIDs — id strings already present in the configured trunk ref's tree. Computes max(parsed-id over both sources) + 1 and formats with the canonical pad width. trunkIDs may be nil when no trunk is in scope (e.g., a sandbox repo with no remotes); see package trunk for the policy that produces the slice.
ids in either source whose prefix does not match k are ignored (cheap-and-correct: they parse to 0); ids that match the prefix but fail strconv are also ignored. The bookkeeping error is surfaced by the frontmatter-shape and id-path-consistent checks; the allocator does not need to refuse to start.
Branch-to-branch collisions that survive both sources (two branches from the same trunk SHA both allocating the same id before either lands on trunk) are caught at merge time by the ids-unique check, which also reads the trunk ref, and resolved by `aiwf reallocate`.
func AllowedACStatuses ¶
func AllowedACStatuses() []string
AllowedACStatuses returns the closed status set for an acceptance criterion. The returned slice shares memory with the package-level constant; callers must not mutate it.
func AllowedStatuses ¶
AllowedStatuses returns the closed status set for the kind. Statuses outside this set are reported by the status-valid check. Delegates to the schemas table so there is a single source of truth.
func AllowedTDDPhases ¶
func AllowedTDDPhases() []string
AllowedTDDPhases returns the closed phase set for an acceptance criterion's `tdd_phase` field. Shares memory with the package-level constant; callers must not mutate it.
func AllowedTDDPolicies ¶
func AllowedTDDPolicies() []string
AllowedTDDPolicies returns the closed policy set for a milestone's `tdd:` field. Shares memory with the package-level constant; callers must not mutate it.
func AllowedTransitions ¶
AllowedTransitions returns the statuses reachable from `from` for the given kind. Returns nil if the kind or the source status is unknown.
func BodyTemplate ¶
BodyTemplate returns the per-kind starter body that `aiwf add` writes after the frontmatter. Sections are scaffolds; bodies are not validated by `aiwf check`.
func CancelTarget ¶
CancelTarget returns the kind's terminal-cancel status — the one `aiwf cancel` promotes any non-terminal entity to. Used by the cancel verb to know which terminal status maps to "discarded": cancelled for epic/milestone, rejected for adr/decision/contract, wontfix for gap.
func Canonicalize ¶
Canonicalize rewrites a recognizable entity-id string to canonical width: the numeric portion is left-zero-padded to CanonicalPad digits if it currently uses fewer. Numbers that already meet or exceed the canonical width are returned unchanged (so an id like `M-12345` keeps its natural width — CanonicalPad is a minimum, not a maximum).
Composite ids (`M-NNN/AC-N`) recurse on the parent portion; the `AC-N` sub-id is left alone (the AC's id has no minimum-digit requirement at the grammar layer; see the docstring on compositeIDPattern). An input that does not parse as one of the six aiwf id formats — and is not a composite — is returned verbatim. Empty input passes through unchanged.
This is the lookup-side complement to AllocateID's canonical emission. Callers comparing an externally-supplied id against an on-disk id (Tree.ByID, history-trailer matching, render canonicalization) run both sides through Canonicalize so a pre-migration narrow id (e.g. `E-22`) and a canonical id (`E-0022`) are treated as equivalent.
Pure function. No allocations beyond the formatted output.
func IDFormat ¶
IDFormat returns a human-readable description of the kind's id shape. Used in error messages produced by the frontmatter-shape check. Delegates to the schemas table so there is a single source of truth.
func IDFromPath ¶
IDFromPath extracts the entity id encoded in an entity-bearing path, for the given kind. The id is the leading "<kind>-<digits>" portion of the relevant path component (the parent directory for epic and contract; the filename for milestone, gap, decision, and adr); any trailing slug is ignored.
Returns false if the path does not match the kind's expected shape or the extracted id does not validate. Used by the tree loader to register stub entities for files that fail to parse.
Archive paths under the per-kind `archive/` subdirectory (per ADR-0004's storage table) are accepted alongside their active counterparts. The id-extraction shape is identical — only the path prefix differs — so consumers downstream of this function (loader, trunk reader, allocator) treat archived entities uniformly.
func IDGrepAlternation ¶
IDGrepAlternation returns a POSIX-extended regex alternation that matches both the canonical and narrow legacy widths of id, suitable for `git log --grep -E ...^aiwf-entity: <pattern>$` queries that must continue to find pre-migration commit trailers (per AC-2 and AC-4 in M-081).
For composite ids the parent recurses; the AC-N sub-id is anchored verbatim. For unrecognized ids the input is regex-quoted unchanged so callers always receive a syntactically-valid pattern.
Concretely, an input of `E-22` returns `(E-0*22)` (any width that equals 22 numerically); `E-0022` returns the same. `M-22/AC-1` returns `(M-0*22)/AC-1`. The pattern is intended to be embedded in a wider regex (anchors, prefix), so it is wrapped in a single capture group for unambiguous concatenation.
func IsAllowedACStatus ¶
IsAllowedACStatus reports whether s is a recognized AC status. Empty string returns false; the empty-string sentinel for "absent" is not itself a legal status value.
func IsAllowedStatus ¶
IsAllowedStatus reports whether status is in the kind's allowed set.
func IsAllowedTDDPhase ¶
IsAllowedTDDPhase reports whether p is a recognized TDD phase. Empty string returns false; phase absence (when the parent milestone is `tdd: none` or `tdd: advisory`) is checked separately by the caller.
func IsAllowedTDDPolicy ¶
IsAllowedTDDPolicy reports whether p is a recognized policy value. Empty string returns false; the absent-field default is `none`, but the caller is responsible for substituting that before consulting this predicate (the empty string is not itself a legal policy value).
func IsArchivedPath ¶ added in v0.8.0
IsArchivedPath reports whether relPath sits under a per-kind `archive/` subdirectory recognized by ADR-0004's storage table. Returns false for active paths and for paths that don't classify as any kind. The argument is a forward-slash, repo-relative path (the same shape stored on Entity.Path by tree.Load).
The archive marker is the presence of an `archive` segment at the per-kind position (immediately after `work/<kind>/` or `docs/adr/`). `stripArchiveSegment` carries the canonical recognition logic; IsArchivedPath compares before-vs-after to decide.
Used by the M-0086 archive-aware check rules to decide whether an entity should be linted under the active-set health rules or under the archive-specific drift rules.
func IsCompositeID ¶
IsCompositeID reports whether s matches the composite-id grammar `<entity-id>/AC-<digits>`. Bare ids and malformed inputs return false.
func IsLegalACTransition ¶
IsLegalACTransition reports whether (from, to) is a legal AC status transition under the FSM. Self-transitions, unknown `from`, and unknown `to` all return false. The verb-projection finding `acs-transition` (Step 6) consults this; `--force --reason` (Step 4) is what relaxes it.
func IsLegalTDDPhaseTransition ¶
IsLegalTDDPhaseTransition reports whether (from, to) is a legal transition along an AC's TDD phase FSM. Self-transitions, unknown `from`, and unknown `to` all return false.
func IsProseyTitle ¶
IsProseyTitle reports whether a title looks like prose rather than a short label. Used by `aiwf add ac` to refuse multi-sentence / markdown-formatted titles at verb time, and by `acs-title-prose` (a warning finding) to surface the same on standing trees.
Triggers: markdown formatting (`**`, `__`, backticks); link brackets (`[...](`); explicit newlines; >1 sentence (`.`/`?`/`!` followed by a space and a capital letter); length > 80 chars.
Pure function. No allocations beyond a single rune walk; safe to call in both verb projection and check.Run.
func IsTerminal ¶
IsTerminal reports whether (kind, status) names a terminal state in the kind's FSM — i.e., a state with no outgoing transitions. Returns false for unknown kinds and unknown statuses, so downstream checks keep firing on junk-status entities rather than silently exempting them.
Derives terminality from the FSM rather than a parallel hardcoded list to keep one source of truth: if the FSM grows or shrinks a state's outgoing edges, IsTerminal tracks it automatically.
func MilestoneCanGoDone ¶
MilestoneCanGoDone reports whether the milestone's ACs are in a state that permits the milestone itself to transition to `done`. Returns (true, nil) when no AC has `status: open`; returns (false, openACs) listing the bare AC ids (`AC-N`) that are still open.
This is the AC-level precondition; the per-status milestone FSM (`in_progress → done`) is a separate check that ValidateTransition already covers. Step 6's `milestone-done-incomplete-acs` finding surfaces this on every `aiwf check` pass; Step 7's promote verb wires it into the projection.
The function is milestone-specific by intent. Calling it on other kinds returns (true, nil) trivially — non-milestone Entities never carry ACs in the schema.
func ParseACSections ¶
ParseACSections walks `### AC-N` headings in body and returns a map from AC id to that section's prose, trimmed of leading and trailing whitespace. The heading line itself is not included; only the prose under it. A subsequent `### AC-N` heading or any `## ` / `# ` heading terminates the current section.
Unrecognized `### ` headings (anything that does not start with `AC-<digits>`) are skipped — the section's body is not captured. This keeps the parser predictable when authors add free-form `### ` notes outside the AC structure. Returns nil when no `### AC-N` heading is present.
func ParseBodySections ¶
ParseBodySections walks `## `-level headings in body and returns a map from slugified heading to the section's prose, trimmed of leading and trailing whitespace. Sub-headings (`### `, `#### `, …) and prose inside the section are returned verbatim under the parent `## `.
The slug lowercases the heading, replaces every run of non-alphanumeric runes with a single `_`, and trims leading/trailing `_`. So:
## Goal → "goal" ## Out of scope → "out_of_scope" ## What's missing → "what_s_missing"
Multiple `## ` sections with the same slug collapse to the last one (last write wins). Body content before the first `## ` is dropped — the templates always start with a `## ` heading. Returns nil when no `## ` heading is present.
This is the read-side companion to BodyTemplate. It is intentionally not a full markdown parser: only `## ` boundaries matter, and a `# ` or EOF terminates the current section.
func ParseCompositeID ¶
ParseCompositeID splits a composite id into its parent and sub portions. Returns ok=false (with both strings empty) when s does not match the composite grammar — including bare ids, malformed parents, missing sub-ids, and trailing garbage.
func SectionSlug ¶
SectionSlug derives the body-section key from a `## ` heading. See ParseBodySections for the rule.
func Serialize ¶
Serialize composes an entity file's bytes: the opening "---" line, the entity's YAML frontmatter, the closing "---" line, and the body bytes verbatim. Use Split's body output as the body argument when editing an existing file, or BodyTemplate(kind) for newly-created entities.
Field order in the YAML follows the Entity struct definition: id, title, status, then per-kind fields (which appear only when set, thanks to `omitempty`). This makes output deterministic across runs.
func Slugify ¶
Slugify converts a title into a kebab-case slug suitable for use in filenames and directory names. Lowercases ASCII letters, keeps digits, collapses runs of non-alphanumerics into single hyphens, and trims leading and trailing hyphens.
Non-ASCII characters are dropped (e.g., "Café" becomes "caf"). Use SlugifyDetailed if you need to know which characters were dropped so the user can be warned.
func SlugifyDetailed ¶
SlugifyDetailed is Slugify plus the list of input runes that were silently dropped because they were non-ASCII letters/digits. The dropped list is empty when the title is purely ASCII (or contained only ASCII alphanumerics + punctuation that legitimately collapses to hyphens). Callers in the verb dispatcher use the dropped list to surface a one-line notice to the user.
func Split ¶
Split returns the YAML frontmatter bytes and the markdown body bytes from a complete entity file. Returns (nil, nil, false) when the input has no frontmatter delimiter, mirroring Parse's tolerance.
Mutating verbs use Split to preserve body prose during frontmatter edits: read → split → modify entity → Serialize(entity, body) → write.
func SubKindFromID ¶
SubKindFromID returns the sub-kind label encoded in a composite id. Currently only `"ac"` is defined (acceptance criterion). Bare ids and malformed composites return ("", false).
func ValidateID ¶
ValidateID returns nil if id matches the kind's format, or an error describing the mismatch. Used by the frontmatter-shape check.
func ValidateSlug ¶ added in v0.8.0
ValidateSlug reports whether slug is within the configured length cap. Same cap as ValidateTitle — title and slug share a budget so filenames and frontmatter stay in sync (G-0102). Used by `aiwf rename`, where the operator supplies a slug directly with no title context.
A non-positive maxLength is a no-op. Validation runs on the post-slugify form (after `SlugifyDetailed`) because that's what ends up on disk; pre-slugify length may include characters (whitespace, punctuation) that get dropped.
func ValidateTitle ¶ added in v0.8.0
ValidateTitle reports whether title is within the consumer's configured length cap (`entities.title_max_length` in aiwf.yaml; kernel default 80, per G-0102). Returns nil when title is acceptable, or a typed error explaining the cap and pointing the operator at `--body-file` for elaboration that doesn't belong in the title.
A non-positive maxLength is a no-op (validation always passes), so callers in tests or paths that don't thread a config can pass 0.
The same cap also applies to slugs (see ValidateSlug) — title and slug share a length budget so on-disk filenames and frontmatter titles stay in sync. This is what makes the kernel surfaces (CLI tables, HTML render, git-log subjects, `aiwf history`, filesystem) all degrade uniformly rather than diverging.
func ValidateTransition ¶
ValidateTransition reports nil when (kind, from, to) is a legal step. Returns a descriptive error when from is unknown to the kind, when the kind itself is unknown, or when no transition from→to exists.
Types ¶
type AcceptanceCriterion ¶
type AcceptanceCriterion struct {
ID string `yaml:"id,omitempty"`
Title string `yaml:"title,omitempty"`
Status string `yaml:"status,omitempty"`
TDDPhase string `yaml:"tdd_phase,omitempty"`
}
AcceptanceCriterion is a milestone sub-element addressed by composite id `M-NNN/AC-N`. ACs are namespaced inside their parent milestone — they have no global allocator and cannot move between milestones (see docs/pocv3/design/design-decisions.md §"Acceptance criteria and TDD").
Every field is a plain string with `omitempty`; empty == absent. Closed-set membership (IsAllowedACStatus, IsAllowedTDDPhase) rules out "" as a legal value, so the empty-string sentinel is unambiguous.
AC ids are position-stable: `acs[i].ID == fmt.Sprintf("AC-%d", i+1)` for every index. Cancelled ACs stay in the slice at their original position; the allocator picks `max+1` over the full list including cancelled.
type BodySection ¶
BodySection is one `## ` section with its display heading preserved alongside the slugified key. Heading is what the markdown source actually wrote (no slug round-trip — apostrophes, spaces, and capitalization come back verbatim); Slug is the ParseBodySections key; Content is the section prose, trimmed.
Used by ParseBodySectionsOrdered when the caller cares about document order (the HTML renderer per G35/G36) instead of just the slug → content map.
func ParseBodySectionsOrdered ¶
func ParseBodySectionsOrdered(body []byte) []BodySection
ParseBodySectionsOrdered walks `## `-level headings in body and returns the sections in source order, with each section's display heading preserved alongside the slug. Same semantics as ParseBodySections for heading boundaries (`# ` or EOF terminates the current `## ` section; content before the first `## ` is dropped); duplicate slugs collapse to the last occurrence so callers can rely on slug uniqueness.
Returns nil when body is empty or has no `## ` heading.
type Cardinality ¶
type Cardinality string
Cardinality describes whether a reference field carries a single id or a list of ids.
const ( Single Cardinality = "single" Multi Cardinality = "multi" )
Cardinality values used by RefField.
type Entity ¶
type Entity struct {
// Common — present on every kind.
ID string `yaml:"id"`
Title string `yaml:"title"`
Status string `yaml:"status"`
// Lineage. PriorIDs lists the ids this entity has carried before
// the current one, oldest first. Populated by `aiwf reallocate`
// on every renumber; the resulting chain lets `aiwf history` walk
// the entity's full timeline under its current canonical id.
// Tree-only readers (HTML render, `aiwf show`, future projections)
// can resolve lineage without re-walking git log.
PriorIDs []string `yaml:"prior_ids,omitempty"`
// Milestone references.
Parent string `yaml:"parent,omitempty"`
DependsOn []string `yaml:"depends_on,omitempty"`
// Milestone — acceptance criteria and TDD policy (added in I2).
TDD string `yaml:"tdd,omitempty"`
ACs []AcceptanceCriterion `yaml:"acs,omitempty"`
// ADR chain.
Supersedes []string `yaml:"supersedes,omitempty"`
SupersededBy string `yaml:"superseded_by,omitempty"`
// Gap.
DiscoveredIn string `yaml:"discovered_in,omitempty"`
AddressedBy []string `yaml:"addressed_by,omitempty"`
AddressedByCommit []string `yaml:"addressed_by_commit,omitempty"`
// Decision.
RelatesTo []string `yaml:"relates_to,omitempty"`
// Contract.
LinkedADRs []string `yaml:"linked_adrs,omitempty"`
// Loader-set metadata, not part of YAML.
Kind Kind `yaml:"-"`
Path string `yaml:"-"`
}
Entity is the in-memory representation of a single aiwf entity, loaded from a markdown file's YAML frontmatter. The body prose is not parsed.
The struct is the union of all six kinds' frontmatter fields. Per-kind shape rules (which fields are required, which references point to which kinds) live in the check package; this struct is the data model.
func Parse ¶
Parse decodes a markdown file's YAML frontmatter into an Entity. The body (everything after the closing `---`) is discarded; aiwf does not validate body prose. Path is recorded on the returned entity for downstream finding-context.
Parse does not assign Entity.Kind. The tree loader resolves kind from the file's path before handing the entity to checks.
type ForwardRef ¶
ForwardRef describes one outbound reference from an entity. Field is the YAML key the reference came from (e.g., "parent", "addressed_by"); Target is the referenced id (bare or composite); AllowedKinds is the closed set of kinds the target may resolve to, taken from the kind's schema. An empty AllowedKinds means any kind is allowed (open-target fields like gap.addressed_by and decision.relates_to).
ForwardRef is the published shape used by both the validator (check package) and the reference-graph index (tree package). Callers that only need the (referrer, target) pair can ignore Field and AllowedKinds.
func ForwardRefs ¶
func ForwardRefs(e *Entity) []ForwardRef
ForwardRefs returns every outbound reference the entity carries, one ForwardRef per (field, target) pair. Multi-cardinality fields produce one entry per target. Empty / absent fields produce no entries.
The function is the single source of truth for "what references does an entity make" — consulted by check.refsResolve to validate them and by tree.Load to invert them into the reverse-ref index. A drift- detection test in the check package pins the result against the per- kind schema table.
type Kind ¶
type Kind string
Kind identifies one of the six aiwf entity kinds. The value is the canonical lowercase identifier used in path-discovery rules and in error messages.
const ( KindEpic Kind = "epic" KindMilestone Kind = "milestone" KindADR Kind = "adr" KindGap Kind = "gap" KindDecision Kind = "decision" KindContract Kind = "contract" )
The six aiwf entity kinds. Hardcoded; see docs/pocv3/design/design-decisions.md.
func AllKinds ¶
func AllKinds() []Kind
AllKinds returns the closed set of kinds in canonical order. Useful for iteration in checks that walk every kind.
func KindFromID ¶
KindFromID returns the kind matching the id's format. The second return is false if the id matches no kind's format. Useful for reverse-lookup when validating cross-kind references.
Composite ids (e.g. `M-007/AC-1`) resolve to their parent's kind (here, milestone). The sub-kind is reported separately by SubKindFromID.
func PathKind ¶
PathKind returns the kind implied by a file's path, relative to the consumer repo root. The second return is false if the path doesn't match any entity-bearing pattern; such files are skipped by the loader.
Recognized active patterns:
work/epics/<dir>/epic.md -> epic work/epics/<dir>/M-*.md -> milestone work/gaps/G-*.md -> gap work/decisions/D-*.md -> decision work/contracts/<dir>/contract.md -> contract docs/adr/ADR-*.md -> adr
Recognized archive patterns (per ADR-0004's storage table — terminal- status entities live in a per-kind `archive/` subdirectory). Path shape is uniform with active aside from the inserted `archive/` segment; downstream callers (tree loader, trunk reader, id resolver) treat both locations identically.
work/epics/archive/<dir>/epic.md -> epic work/epics/archive/<dir>/M-*.md -> milestone work/gaps/archive/G-*.md -> gap work/decisions/archive/D-*.md -> decision work/contracts/archive/<dir>/contract.md -> contract docs/adr/archive/ADR-*.md -> adr
type RefField ¶
type RefField struct {
Name string `json:"name"`
Cardinality Cardinality `json:"cardinality"`
AllowedKinds []Kind `json:"allowed_kinds,omitempty"`
Optional bool `json:"optional"`
}
RefField describes one reference field on an entity. AllowedKinds is the set of kinds the target id may resolve to; an empty slice means any kind is allowed (e.g. gap.addressed_by, decision.relates_to). Optional reports whether the field is allowed to be empty/absent.
type Schema ¶
type Schema struct {
Kind Kind `json:"kind"`
IDFormat string `json:"id_format"`
AllowedStatuses []string `json:"allowed_statuses"`
RequiredFields []string `json:"required_fields"`
OptionalFields []string `json:"optional_fields,omitempty"`
References []RefField `json:"references,omitempty"`
}
Schema describes the frontmatter contract for one kind. It is the single source of truth consulted by `aiwf schema` (the published surface for skill authors) and by check.refsResolve (the runtime enforcement). Drift between the two is pinned by a regression test in the check package.
func AllSchemas ¶
func AllSchemas() []Schema
AllSchemas returns one Schema per kind, in AllKinds() order.
func SchemaForKind ¶
SchemaForKind returns the Schema for k. The second return is false if k is not one of the six aiwf kinds. The returned Schema shares slice memory with the package-level table; callers must not mutate it.