Canonical Types
All canonical types live in the provider package. The design contract:
fields common across source tools are typed; anything provider-specific
lives in a namespaced Metadata map[string]any passthrough (e.g.
"aha.progress_source"), so conversion is lossy-in-typing but
lossless-in-aggregate.
Item
The canonical roadmap unit — a feature, epic, initiative, objective, or
key result (Kind ItemKind discriminates).
Key field groups:
| Group | Fields | Notes |
|---|---|---|
| Identity/provenance | ID, Provider, SourceID, SourceRef, SourceURL |
ID is globally unique: "<provider>:<source id>". SourceRef is the tool's human-facing reference (e.g. Aha's SE-2) |
| Content | Kind, Name, Description |
|
| State | Status *Status, Progress *float64 |
Progress normalized 0–100; nil when untracked |
| Hierarchy | ParentID, ReleaseID |
Canonical IDs of related records |
| Dates | StartDate, DueDate, CreatedAt, UpdatedAt |
All *time.Time; nil = not provided |
| People/labels | Owner *Person, Tags []string |
|
| Custom data | CustomFields []CustomField |
Per-record values: {Key, Name, Value any, Type} |
| Prioritization | MoSCoW string, Kano string, RICE *RICE |
Never populated by adapters — see below |
| Extras | Links []Link, Metadata map[string]any |
Prioritization fields
No PM tool exposes MoSCoW/Kano/RICE as first-class API fields, so
adapters never set Item.MoSCoW, Item.Kano, or Item.RICE. They exist
on the canonical type because many tenants store prioritization as
custom fields — a downstream mapping layer (omniroadmap's fieldmap
package) populates them per tenant configuration, and omniroadmap's
augment layer overlays hand-authored values that survive re-syncs.
MoSCoW uses the must_have|should_have|could_have|wont_have vocabulary
with "" = unset; Kano uses prism-roadmap's
must-be|performance|attractive|indifferent|reverse|questionable
categories; RICE holds raw numeric components (Reach, Impact,
Confidence, Effort, Score, all *float64).
Status
type Status struct {
ID, Name string
Category StatusCategory // todo | in_progress | done | canceled
Complete bool
Color string
Position int64
}
Category normalizes provider-specific workflow statuses into four
tool-agnostic buckets while preserving the original name/ID/color for
display. How reliable the normalization is depends on the source: Jira has
authoritative server-side categories, Aha has a Complete flag plus
heuristics, ProductBoard exposes nothing (adapters leave Category unset
rather than guess).
Release
Same provenance fields as Item, plus Name, StartDate,
ReleaseDate, Released bool, Status, Progress, Metadata.
Capabilities
type Capabilities struct {
Kinds []ItemKind
SupportsReleases bool
SupportsObjectives bool
SupportsCustomFields bool
SupportsWrite bool
}
Callers branch on capabilities at runtime instead of relying on silently
empty results; unsupported operations return ErrUnsupportedOperation.
Errors
Sentinel errors (ErrNotFound, ErrUnsupportedOperation,
ErrUnsupportedProvider, ...) with Is* helpers, plus a structured
APIError{StatusCode, Message, Type, Code, Provider} for provider API
failures.