Skip to content

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.