Documentation
¶
Overview ¶
Package planstore resolves the one authoritative Plan namespace for a SpecScore project. Routing is portable repository identity; local checkout paths are a separate, machine-owned concern.
Index ¶
Constants ¶
const ( UserConfigFile = ".specscore.yaml" OrgConfigFile = ".specscore.yaml" RepoConfigFile = "specscore.yaml" LocalConfigFile = "specscore.local.yaml" )
Variables ¶
var ErrNoRoute = errors.New("no plans repository is configured")
ErrNoRoute is the sentinel wrapped (via %w, errors.Is-detectable) by Resolve when NO Plan route is configured for the source project at all — neither plans_repo nor plan_repos names it in any layer. It is deliberately distinct from every other resolution failure (a configured-but-broken route: missing checkout, wrong origin, nested path, ambiguous plan_repos mapping, a misplaced key) so a caller with a legitimate no-route default — `spec lint`, which is not itself one of the dedicated Plan verbs repo-config#req:plan-route-required binds to — can fall back to the historical same-repository default ONLY for "nothing configured", and must never do so for "something is configured and it's broken" (that shape must fail closed instead of silently reading or writing the wrong spec/plans tree). The wrapped message text is unchanged from before this sentinel existed.
var ErrRouteUnresolved = errors.New("plan routing is configured but could not be resolved")
ErrRouteUnresolved is the sentinel wrapped (errors.Is-detectable, via routeUnresolvedError.Unwrap) by every Resolve failure that occurs once a route destination has actually been found or a routing key has actually been read — an ambiguous plan_repos mapping, a misplaced routing key (plan_repos in project/local config, plans_repo in organization/user config), a committed repo_checkouts, a missing or malformed repo_checkouts entry, a checkout with the wrong origin or that names a nested path, or a Plan namespace path that escapes its checkout through a symlink. These are exactly the shapes the reviewer's reproduction named (repo-config#req:plan-route-required's "route configured but checkout missing / wrong origin / nested / ambiguous plan_repos").
Deliberately NOT wrapped: repositoryRoots/repositoryIdentity/UserHomeDir failures for the SOURCE project — these happen before any config layer is even reached (e.g. the source isn't a git repository at all, or its origin remote can't be parsed) and are unrelated to whether a route is configured, so a caller like `spec lint` must keep treating them the same permissive way it always has (same as ErrNoRoute), not as "route configured but broken" — many of this CLI's own tests lint a bare tmpdir with no .git at all and rely on that leniency.
IS wrapped: a local/repo/org/user layer file that EXISTS but fails to be read or parsed. Before this sentinel existed, such a failure returned a plain error indistinguishable from ErrNoRoute, so a caller with a legitimate no-route default (`spec lint`) silently fell back to reading — and, under --fix, writing — the local spec/plans tree, exactly as if nothing were configured. That is wrong whenever the broken file could plausibly have configured routing: the reviewer's PR #199 adversarial re-review reproduced a malformed user ~/.specscore.yaml, a malformed organization .specscore.yaml (beside a repository whose OWN committed specscore.yaml carried a valid plans_repo that was never even reached), and a malformed committed specscore.yaml, each independently defeating routing while a stale local Plan got linted — and, under --fix, rewritten — as though routing were absent rather than broken (finding 1). A layer file that simply does not exist (os.ErrNotExist) is NOT an error — readLayer returns a nil error for that case — so the no-route default is preserved for every project that has not adopted any config layer at all.
Functions ¶
func OrgConfigPath ¶
OrgConfigPath returns the organization layer associated with a source checkout, anchored beside its canonical clone for linked worktrees.
Types ¶
type Resolution ¶
type Resolution struct {
SourceRoot string
SourceRepo string
PlansRepo string
PlansCheckout string
PlansDir string
External bool
RouteConfigPath string
CheckoutConfigPath string
}
Resolution names both halves of a Plan operation. SourceRoot continues to own Features and ACs; PlansDir is the only editable Plan namespace.
func Resolve ¶
func Resolve(sourceRoot string, mode AccessMode) (Resolution, error)
Resolve finds the Plan namespace for sourceRoot. Plan operations fail when routing is absent or ambiguous; there is deliberately no implicit local fallback.