Documentation
¶
Overview ¶
Package candywalk is the SHARED entity-discovery walk over a charly repo: the superproject plus every box/<distro> submodule, reading every candy/ + box/ charly.yml and returning one Entity per top-level node. It is the ONE discovery abstraction (R3) both out-of-process generator plugins use — candy/plugin-docs (the docs site generator) and candy/plugin-marketplace (the harness/marketplace generator) — so the two never fork their own directory conventions.
Discovery is PURE FILESYSTEM walking + os.ReadFile — it never shells out to git, never runs charly commands. "Defined, not default-active": it returns every DEFINED entity (what an author wrote down), never the resolved/enabled set (the resolver's surfaces are wrong for a catalog — `charly box list boxes` resolves through main's import: closure and omits every debian.* and ubuntu.* box). Each repo (the superproject + each checked-out box/<distro> submodule) is walked as its own root and the results unioned; an uninitialised submodule (no charly.yml) is skipped.
The candy de-submodule cutover (Phases 1-2) moved every candy to a standalone kind-prefixed repo (layer-*, pod-*, vm-*, plugin-*). Phase 4 deletes the in-repo candy/ dirs, after which the local FS walk alone finds only the residual fixtures. CollectEntitiesRemote extends discovery with a REMOTE-REF-AWARE mode: it also collects every @github.com/opencharly/... ref from the walked files' require:/candy: lists, resolves each repo+tag via a CALLER-SUPPLIED resolver seam (out-of-process generators use spec/refs.DownloadRepo; an in-proc caller could supply the loader's EnsureRepoDownloaded), and walks each fetched repo's candy dir (or repo-root charly.yml for config candies) — fix-pointing until no new refs surface, the same transitive closure the runtime scan materialises. The seam keeps this kit import-free: candywalk itself still carries NO charly or sdk import — only the stdlib + yaml.v3.
The package carries only the CONTRACT module + the pure kit (github.com/opencharly/spec for ParseRemoteRef (F4.3), github.com/opencharly/sdk/kit for RepoLayoutDirs (F4.4)) — never charly, never a mechanism kit with registry/host coupling. It returns the raw per-node kind discriminator + value node; the caller owns the projection into its own types (the docs plugin decodes `candy:` into its candyView/boxView; the marketplace plugin decodes `skill:`/`hook:`/ `marketplace:` into the generated spec types).
Index ¶
Constants ¶
const UnifiedFileName = "charly.yml"
UnifiedFileName is the one entity filename (the project rulebook's "one filename charly.yml").
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Entity ¶
type Entity struct {
Name string // the top-level node name (as authored)
Namespace string // "" for the superproject, else the box/<distro> submodule name
Dir string // directory holding this charly.yml, relative to the repo root
Kind string // the node's kind discriminator ("candy", "skill", "hook", "marketplace", …)
Value yaml.Node // the kind value body
SourceRoot string // absolute repo root ("" = local root; else the fetched remote cache dir)
}
Entity is one top-level node of a discovered charly.yml, regardless of kind. The value node is the node's kind value (the body under `<name>: {<kind>: …}`).
Value is a VALUE yaml.Node, not a pointer: yaml.v3's decode into `map[string]map[string]*yaml.Node` silently drops nested structs when the caller then Decodes the leaf (a plugin-fleet candy's `plugin:` block came back nil), while the value-node decode round-trips faithfully. The value-node form is what the original plugin-docs walk used.
SourceRoot is the ABSOLUTE path of the repo root this entity was read from: "" for a local superproject/box root (the caller knows the local root it passed in), or the fetched repo cache dir for a REMOTE entity from CollectEntitiesRemote. Schema reads and other per-entity file lookups must resolve relative to SourceRoot (when non-empty) rather than the local project root.
func CollectEntities ¶
CollectEntities reads every entity node across every repo root, for the two discovery directories the loader uses (candy/ and box/). Discovery is by directory convention (candy/<name>/charly.yml, box/<name>/charly.yml) — the same two the loader uses — so the result is exactly what an author has written down.
func CollectEntitiesRemote ¶ added in v0.2026237.1646
func CollectEntitiesRemote(roots []Root, resolve RemoteResolver) ([]Entity, error)
CollectEntitiesRemote extends CollectEntities with the candy de-submodule cutover's remote-ref-aware discovery (Phase 3): every @github.com/opencharly/... ref found in the walked files' require:/candy: lists is resolved through the caller-supplied seam and the fetched repo's own candy entities (candy/<name>/charly.yml or the repo-root charly.yml for config candies) are unioned into the result, TRANSITIVELY — a fetched repo's own require:/candy: lists may surface new refs, which are fetched in turn, until closure (the same fix-point the runtime scan materialises). Entity.SourceRoot carries the fetched repo dir for remote entities so schema reads resolve against the fetched tree, not the local project root.
func ReadEntityFile ¶
ReadEntityFile decodes one charly.yml into its top-level nodes. The file is a map of entity NAME to a single kind key (name-first shape); each (name, kind, value) triple is one Entity. A charly.yml that does not fit the name-first shape (a multi-doc bed, a template) is skipped rather than failing the whole discovery. Scalar top-level keys (the standalone repos' prepended `version: <calver>` schema header) are tolerated and ignored — they are not entities.
type RemoteResolver ¶ added in v0.2026237.1646
RemoteResolver resolves one remote candy ref to a LOCALLY FETCHED repo directory. It is the caller-supplied seam that keeps this kit import-free: an out-of-process generator supplies spec/refs.DownloadRepo (or a wrapper), an in-proc caller supplies the loader's EnsureRepoDownloaded. The returned dir must hold the fetched repo export (the same shape the runtime's repo cache produces) so the walker can read its candy/ + root charly.yml.
type Root ¶
type Root struct {
Namespace string // "" for the superproject, else the box/<distro> submodule name
Dir string // absolute path
}
Root is one project tree to walk: the superproject, or a box/<distro> submodule.