Documentation
¶
Overview ¶
CANARY: REQ=ENG-4317; FEATURE="ProjectConfig"; ASPECT=Storage; STATUS=IMPL; UPDATED=2026-08-30
Index ¶
Constants ¶
const DefaultStaleDays = 30
DefaultStaleDays is the staleness window (in days) used when verification.staleness_days is not configured. It lives here, in the single config type, so pkg/canaryscan can reference it without pkg/config having to import pkg/canaryscan.
Variables ¶
var SourceKeyPattern = regexp.MustCompile(`^[A-Z][A-Z0-9]*$`)
SourceKeyPattern is the required shape of a requirement-ID prefix: uppercase alphanumeric starting with a letter (e.g. "CBIN", "ENG", "GH2").
Functions ¶
func ValidateProjectKey ¶ added in v0.3.3
ValidateProjectKey enforces the project.key shape rule: empty (unset) is legal, a non-empty key must match SourceKeyPattern. It is the single implementation of that rule, called both by ProjectConfig.validate (the config.Load path) and by pkg/sources.FromProjectConfig, which validates a project.key of its own -- direct construction of a *ProjectConfig (used in tests and by any caller that builds one without going through Load) never runs validate(), so FromProjectConfig cannot rely on Load having already checked it.
func ValidateSources ¶ added in v0.3.3
func ValidateSources(specs []SourceSpec) error
ValidateSources enforces the source rules: every type must be known, every key well-formed and unique, and at most one source may be the ticket destination (which must not be a flatfile source).
Types ¶
type PeerConfig ¶ added in v0.3.1
type PeerConfig struct {
Name string `yaml:"name"`
// Root is the peer project's root directory, resolved relative to
// this project's own root when not absolute. Its status.json is read
// from <Root>/status.json.
Root string `yaml:"root"`
}
PeerConfig is one peer project this repo is inter-dependent with: a sibling repo whose own `canary scan --out status.json` this project reads (read-only, never written to) to resolve requirement ids that peer owns — including ids under a prefix this project's own `sources:` list doesn't recognize at all. See pkg/external's peer-resolution layer. CANARY: REQ=ENG-3961; FEATURE="PeerProjects"; ASPECT=Storage; STATUS=TESTED; TEST=TestCANARY_ENG_3961_LoadPeers,TestCANARY_ENG_3961_LoadPeers_AbsentIsEmpty; UPDATED=2026-08-29
type ProjectConfig ¶
type ProjectConfig struct {
Project struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Key string `yaml:"key"`
} `yaml:"project"`
Sources []SourceConfig `yaml:"sources"`
// Peers lists sibling projects consulted for requirement ids this
// project doesn't own itself. Optional; empty when unconfigured.
Peers []PeerConfig `yaml:"peers"`
Requirements struct {
IDPattern string `yaml:"id_pattern"`
} `yaml:"requirements"`
Verification struct {
StalenessDays int `yaml:"staleness_days"`
} `yaml:"verification"`
Agent struct {
DefaultModel string `yaml:"default_model"`
} `yaml:"agent"`
}
ProjectConfig represents the .canary/project.yaml configuration
func Load ¶
func Load(rootDir string) (*ProjectConfig, error)
CANARY: REQ=ENG-4317; FEATURE="StrictProjectConfig"; ASPECT=Storage; STATUS=TESTED; TEST=TestLoadRejectsUnknownField,TestLoadRejectsDuplicateKey,TestLoadRejectsNegativeStaleness,TestLoadRejectsBadSourceType,TestLoadRejectsMultiDocumentYAML,TestAuditF19; UPDATED=2026-08-30 Load reads, strictly parses and validates <rootDir>/.canary/project.yaml. Unknown fields, duplicate mapping keys, and invalid values are errors: a config that does not mean what it says must never be silently downgraded to defaults. A missing file is legal and yields an empty (unconfigured) config.
func (*ProjectConfig) ProjectID ¶ added in v0.3.3
func (c *ProjectConfig) ProjectID() string
ProjectID is the resolved project identifier: project.key when set, else "default".
func (*ProjectConfig) StalenessDays ¶ added in v0.3.3
func (c *ProjectConfig) StalenessDays() int
StalenessDays is the resolved staleness window in days: the configured verification.staleness_days when set, else DefaultStaleDays.
type SourceConfig ¶
type SourceConfig struct {
Name string `yaml:"name"`
Type string `yaml:"type"` // flatfile | jira | github | gitlab
Key string `yaml:"key"` // ID prefix, e.g. "CBIN", "PLAT", "GH"
URL string `yaml:"url,omitempty"`
// API is the REST base URL used by `canary ticket sync` when it differs
// from URL (which is the human browse-link template). Precedence is
// env > source.API: if JIRA_BASE_URL is set, it always wins; API is
// only consulted as a fallback when JIRA_BASE_URL is unset. Email and
// Token have no config-file fallback — they must come from
// JIRA_EMAIL/JIRA_API_TOKEN regardless of what this field holds.
API string `yaml:"api,omitempty"`
// StatusMap overrides the default CANARY-status -> remote-status-name
// mapping (STUB/IMPL/TESTED/BENCHED keys) for this source only.
StatusMap map[string]string `yaml:"status_map,omitempty"`
// Project is the ticket-system project key this source creates issues
// in and fetches remote status for (e.g. a JIRA project key). Optional;
// when unset, this source contributes no project of its own to `canary
// ticket sync`.
Project string `yaml:"project,omitempty"`
// Destination marks this source as the target for create_issue actions
// promoting flatfile requirements. At most one source may set this; see
// Registry.DestinationSource in pkg/sources for the resolution rule
// when no source is marked.
Destination bool `yaml:"destination,omitempty"`
}
SourceConfig describes one requirement-ID source: a flatfile prefix or an external ticket system (jira, github, gitlab) whose keys appear in REQ= fields. CANARY: REQ=ENG-4322; FEATURE="TicketSources"; ASPECT=Storage; STATUS=TESTED; TEST=TestCANARY_CBIN_201_LoadSources; UPDATED=2026-08-28 CANARY: REQ=CP-279; FEATURE="TicketSync"; ASPECT=Storage; STATUS=TESTED; TEST=TestCANARY_CBIN_306_LoadSources_TicketSyncFields; UPDATED=2026-08-29 CANARY: REQ=ENG-3958; FEATURE="TicketDestination"; ASPECT=Storage; STATUS=TESTED; TEST=TestCANARY_ENG_3958_LoadSources_ProjectDestinationFields; UPDATED=2026-08-29
type SourceSpec ¶ added in v0.3.3
SourceSpec is the minimal source shape ValidateSources checks. It exists so the rules have exactly one implementation, shared by config parsing and by pkg/sources' registry construction (which validates its own Source type).