// Package core holds the pure domain logic of spec.sr.ht: owner and space name
// validation, safe document paths, the globally-unique document ID grammar, the
// frontmatter schema contract, `.spec.yml` space policy, and the proposal state
// machine.
//
// It depends on nothing but the standard library and gopkg.in/yaml.v3, never
// touches the network or the filesystem, and knows nothing about git, Postgres
// or HTTP. Dependency direction is strictly downward: gitx, db, service, api and
// web import core; core imports none of them. That is what keeps the three
// agent-facing surfaces (REST, MCP, GraphQL) behaviourally identical — they
// share these rules rather than each re-deriving them.
package core
import "errors"
// Sentinel errors, one per failure class. Callers compare with errors.Is;
// wrapping with %w adds the offending value without losing the class, which is
// what lets the API layer map a failure to a status code (422 for a schema
// violation, 400 for a malformed name) without string matching.
var (
// ErrInvalidName is returned for a malformed owner or space name.
ErrInvalidName = errors.New("invalid name")
// ErrReservedName is returned for a name that is well-formed but not the
// caller's to claim. The one instance today is the meta-project: it is an
// address that resolves to a filter excluding nothing, so a stored project
// of that name could only shadow it.
ErrReservedName = errors.New("reserved name")
// ErrInvalidPath is returned for a path that is not a safe relative path
// inside a space: absolute, traversing, or carrying bytes that would be
// unsafe in a git tree or misleading in the review UI.
ErrInvalidPath = errors.New("invalid path")
// ErrInvalidDocID is returned for a document ID that does not match the
// PREFIX-DIGITS grammar. IDs are globally unique and are the anchor for
// cross-space links, comments and staleness checks, so shape is enforced
// at every door.
ErrInvalidDocID = errors.New("invalid document id")
// ErrMalformedFrontmatter marks a document whose YAML frontmatter block is
// missing, unterminated, not a mapping, or not parseable. It is distinct
// from ErrMissingField: this is "cannot read", not "read but incomplete".
ErrMalformedFrontmatter = errors.New("malformed frontmatter")
// ErrMissingField marks frontmatter that parsed but omits a key the space's
// schema requires. This is the 422 an agent gets for forgetting `status:`.
ErrMissingField = errors.New("missing required frontmatter field")
// ErrInvalidStatus is returned for a `status:` value outside the allowed
// enum. Note "approved" is deliberately not a status — approval is a
// property of the branch a document is reachable from, never of authored
// metadata.
ErrInvalidStatus = errors.New("invalid status")
// ErrInvalidPolicy is returned for a `.spec.yml` that parses as YAML but
// does not describe a usable policy.
ErrInvalidPolicy = errors.New("invalid .spec.yml policy")
// ErrInvalidPattern is returned for a malformed auto_merge path pattern.
// A pattern that silently matches nothing would quietly turn the bimodal
// cadence back into "everything waits for a human", so it is rejected.
ErrInvalidPattern = errors.New("invalid path pattern")
// ErrInvalidState is returned for a proposal state string outside the
// open/merged/rejected set, typically read back from Postgres.
ErrInvalidState = errors.New("invalid proposal state")
// ErrInvalidTransition is returned for a proposal state change the machine
// does not allow, such as re-merging an already rejected proposal.
ErrInvalidTransition = errors.New("invalid proposal state transition")
// ErrInvalidApproval is returned for an approval kind outside human/policy.
ErrInvalidApproval = errors.New("invalid approval kind")
// ErrInvalidProposalID is returned for a proposal id that cannot name a
// proposal: ids are Postgres sequence values starting at 1, so anything
// else is an unwritten row or an unset field rather than a proposal.
ErrInvalidProposalID = errors.New("invalid proposal id")
)