Product Roadmap — Semantic Model
1. Overview
A product-roadmap planning system. Product managers capture incoming feature requests, change requests, bugs, and tech-debt items as features, score them with RICE (reach × impact × confidence ÷ effort), align them to strategic objectives, and schedule the committed work into releases. Stakeholders contribute through feature_votes and comments; tags provide cross-cutting categorization.
2. Entity summary
| # | Table name | Singular label | Purpose |
|---|---|---|---|
| 1 | products | Product | Product or product area the roadmap covers |
| 2 | objectives | Objective | Strategic goals or themes that features roll up to |
| 3 | features | Feature | Central entity — anything on the roadmap (idea, enhancement, change request, bug, tech debt) |
| 4 | users | User | PMs, owners, requesters, voters |
| 5 | releases | Release | Planned release with target/actual ship dates |
| 6 | feature_votes | Feature Vote | Junction — users voting for features (M:N) |
| 7 | comments | Comment | Discussion thread on a feature |
| 8 | tags | Tag | Reusable labels for categorizing features |
| 9 | feature_tags | Feature Tag | Junction — features ↔ tags (M:N) |
Entity-relationship diagram
flowchart LR
products -->|has| features
products -->|has| releases
products -->|has| objectives
objectives -->|rolls up| features
releases -->|contains| features
users -->|owns| products
users -->|owns| objectives
users -->|owns| features
users -->|requests| features
users -->|authors| comments
features -->|has| comments
features --> feature_votes
users --> feature_votes
features --> feature_tags
tags --> feature_tags
3. Entities
3.1 products — Product
Plural label: Products
Label column: product_name
Audit log: no
Description: A product or product area the roadmap covers. Every feature, release, and (optionally) objective belongs to one product.
Fields
| Field name | Format | Required | Label | Reference / Notes |
|---|---|---|---|---|
product_name | string | yes | Name | label_column; unique |
product_code | string | no | Code | short abbreviation |
product_description | text | no | Description | |
product_status | enum | no | Status | values: active, sunset, archived |
product_owner_id | reference | no | Product Owner | → users (N:1) |
Relationships
- A
productmay have oneproduct_owner(N:1 →users). - A
producthas manyreleases(1:N, viareleases.product_id). - A
producthas manyfeatures(1:N, viafeatures.product_id). - A
productmay have manyobjectives(1:N, viaobjectives.product_id; objectives without a product are company-wide).
3.2 objectives — Objective
Plural label: Objectives
Label column: objective_name
Audit log: yes
Description: A strategic goal or theme that features roll up to (e.g. “Reduce churn by 10%”, “Mobile-first UX”). Optional product scope; objectives without a product apply company-wide.
Fields
| Field name | Format | Required | Label | Reference / Notes |
|---|---|---|---|---|
objective_name | string | yes | Name | label_column |
objective_description | text | no | Description | |
objective_period | string | no | Period | e.g. Q2 2026, FY26 |
objective_status | enum | no | Status | values: proposed, active, achieved, missed, cancelled |
target_metric | string | no | Target Metric | freeform target text |
product_id | reference | no | Product | → products (N:1); null = company-wide |
objective_owner_id | reference | no | Owner | → users (N:1) |
Relationships
- An
objectivemay belong to oneproduct(N:1, optional). - An
objectivemay have oneobjective_owner(N:1 →users). - An
objectiverolls up manyfeatures(1:N, viafeatures.objective_id).
3.3 features — Feature
Plural label: Features
Label column: feature_title
Audit log: yes
Description: The central roadmap entity. Anything that lands on the roadmap is a feature, distinguished by feature_type (new feature, enhancement, change request, bug, tech debt). Carries RICE scoring, status, source, and a target release once committed.
Fields
| Field name | Format | Required | Label | Reference / Notes |
|---|---|---|---|---|
feature_title | string | yes | Title | label_column |
feature_description | text | no | Description | |
feature_type | enum | yes | Type | values: new_feature, enhancement, change_request, bug, tech_debt |
feature_status | enum | yes | Status | values: new, under_review, planned, in_progress, shipped, declined, parked |
feature_priority | enum | no | Priority | values: critical, high, medium, low |
feature_source | enum | no | Source | values: customer, support, sales, internal, partner |
product_id | reference | yes | Product | → products (N:1) |
objective_id | reference | no | Objective | → objectives (N:1) |
release_id | reference | no | Release | → releases (N:1); null until scheduled |
requester_id | reference | no | Requester | → users (N:1) |
owner_id | reference | no | Owner (PM) | → users (N:1) |
submitted_at | date-time | no | Submitted At | |
target_start_date | date | no | Target Start | |
target_completion_date | date | no | Target Completion | |
reach_score | integer | no | Reach | RICE — # users/period reached |
impact_score | float | no | Impact | RICE — typical: 0.25, 0.5, 1, 2, 3 |
confidence_score | float | no | Confidence | RICE — percentage (0–100) |
effort_score | float | no | Effort | RICE — person-months |
rice_score | float | no | RICE Score | (reach × impact × confidence) / effort |
is_committed | boolean | no | Committed | soft idea/feature divide |
Relationships
- A
featurebelongs to oneproduct(N:1, required). - A
featuremay align with oneobjective(N:1, optional). - A
featuremay be scheduled into onerelease(N:1, optional — null until committed and scheduled). - A
featuremay have onerequesterand oneowner(each N:1 →users). - A
featurehas manycomments(1:N, viacomments.feature_id). features↔users(voting) is M:N through thefeature_votesjunction.features↔tagsis M:N through thefeature_tagsjunction.
3.4 users — User
Plural label: Users
Label column: user_full_name
Audit log: no
Description: People who participate in the roadmap process: product managers, owners, requesters, voters, and stakeholders.
Fields
| Field name | Format | Required | Label | Reference / Notes |
|---|---|---|---|---|
user_full_name | string | yes | Full Name | label_column |
user_email | email | yes | unique | |
user_role | enum | no | Role | values: admin, product_manager, engineering, stakeholder, viewer |
user_status | enum | no | Status | values: active, inactive |
Relationships
- A
usermay own manyproducts,objectives, andfeatures(1:N for each, via the respective*_owner_id/owner_idfield). - A
usermay request manyfeatures(1:N, viafeatures.requester_id). - A
usermay author manycomments(1:N, viacomments.author_id). users↔features(voting) is M:N through thefeature_votesjunction.
3.5 releases — Release
Plural label: Releases
Label column: release_name
Audit log: yes
Description: A planned release version for a product, with target and actual ship dates. Features are scheduled into a release once committed.
Fields
| Field name | Format | Required | Label | Reference / Notes |
|---|---|---|---|---|
release_name | string | yes | Name | label_column; e.g. v2.5, March 2026 Release |
release_description | text | no | Description | |
release_status | enum | yes | Status | values: planned, in_progress, released, cancelled |
target_release_date | date | no | Target Date | |
actual_release_date | date | no | Actual Date | |
product_id | reference | yes | Product | → products (N:1) |
release_notes | html | no | Release Notes | rich-text summary published with the release |
Relationships
- A
releasebelongs to oneproduct(N:1, required). - A
releasecontains manyfeatures(1:N, viafeatures.release_id).
3.6 feature_votes — Feature Vote
Plural label: Feature Votes
Label column: feature_vote_label
Audit log: no
Description: Junction table representing a user’s vote on a feature. Supports weighted votes for stakeholder tiers. The caller must populate feature_vote_label on creation (e.g. "{user_full_name} → {feature_title}").
Fields
| Field name | Format | Required | Label | Reference / Notes |
|---|---|---|---|---|
feature_vote_label | string | yes | Label | label_column; caller populates as {user_full_name} → {feature_title} |
feature_id | reference | yes | Feature | → features (N:1) |
user_id | reference | yes | User | → users (N:1) |
voted_at | date-time | no | Voted At | |
vote_weight | integer | no | Weight | default 1; higher = stronger signal |
Relationships
- A
feature_votebelongs to onefeature(N:1) and oneuser(N:1). - Acts as the junction for the M:N relationship between
featuresandusers.
3.7 comments — Comment
Plural label: Comments
Label column: comment_label
Audit log: no
Description: A discussion message posted by a user on a feature. The caller must populate comment_label on creation with a short snippet (e.g. first ~80 chars of body) for list display.
Fields
| Field name | Format | Required | Label | Reference / Notes |
|---|---|---|---|---|
comment_label | string | yes | Label | label_column; caller populates with first ~80 chars of body |
feature_id | reference | yes | Feature | → features (N:1) |
author_id | reference | yes | Author | → users (N:1) |
comment_body | text | yes | Body | |
posted_at | date-time | no | Posted At |
Relationships
- A
commentbelongs to onefeature(N:1, required). - A
commentis authored by oneuser(N:1, required).
3.8 tags — Tag
Plural label: Tags
Label column: tag_name
Audit log: no
Description: A reusable label for categorizing features (e.g. mobile, enterprise, platform).
Fields
| Field name | Format | Required | Label | Reference / Notes |
|---|---|---|---|---|
tag_name | string | yes | Name | label_column; unique |
tag_color | string | no | Color | hex color, e.g. #3b82f6 |
tag_description | text | no | Description |
Relationships
tags↔featuresis M:N through thefeature_tagsjunction.
3.9 feature_tags — Feature Tag
Plural label: Feature Tags
Label column: feature_tag_label
Audit log: no
Description: Junction table linking features to tags. The caller must populate feature_tag_label on creation (e.g. "{feature_title} / {tag_name}").
Fields
| Field name | Format | Required | Label | Reference / Notes |
|---|---|---|---|---|
feature_tag_label | string | yes | Label | label_column; caller populates as {feature_title} / {tag_name} |
feature_id | reference | yes | Feature | → features (N:1) |
tag_id | reference | yes | Tag | → tags (N:1) |
Relationships
- A
feature_tagbelongs to onefeature(N:1) and onetag(N:1). - Acts as the junction for the M:N relationship between
featuresandtags.
4. Relationship summary
| From | Field | To | Cardinality | Kind | Delete behavior |
|---|---|---|---|---|---|
products | product_owner_id | users | N:1 | reference | clear |
objectives | product_id | products | N:1 | reference | clear |
objectives | objective_owner_id | users | N:1 | reference | clear |
features | product_id | products | N:1 | reference | restrict |
features | objective_id | objectives | N:1 | reference | clear |
features | release_id | releases | N:1 | reference | clear |
features | requester_id | users | N:1 | reference | clear |
features | owner_id | users | N:1 | reference | clear |
releases | product_id | products | N:1 | reference | restrict |
feature_votes | feature_id | features | N:1 | parent (junction) | cascade |
feature_votes | user_id | users | N:1 | parent (junction) | cascade |
comments | feature_id | features | N:1 | parent | cascade |
comments | author_id | users | N:1 | reference | restrict |
feature_tags | feature_id | features | N:1 | parent (junction) | cascade |
feature_tags | tag_id | tags | N:1 | parent (junction) | cascade |
5. Enumerations
5.1 products.product_status
activesunsetarchived
5.2 objectives.objective_status
proposedactiveachievedmissedcancelled
5.3 features.feature_type
new_featureenhancementchange_requestbugtech_debt
5.4 features.feature_status
newunder_reviewplannedin_progressshippeddeclinedparked
5.5 features.feature_priority
criticalhighmediumlow
5.6 features.feature_source
customersupportsalesinternalpartner
5.7 users.user_role
adminproduct_managerengineeringstakeholderviewer
5.8 users.user_status
activeinactive
5.9 releases.release_status
plannedin_progressreleasedcancelled
6. Open questions
6.1 🔴 Decisions needed (blockers)
None.
6.2 🟡 Future considerations (deferred scope)
- Should a feature be splittable across multiple releases (e.g. phase 1 / phase 2) by promoting
features.release_idto afeature_releasesjunction with phase metadata? - Should RICE estimates be tracked as a separate
estimatesentity to retain history (who scored what when, and how the score evolved), instead of living as fields onfeatures? - Should features be linkable to specific customers/accounts (e.g. via a
customer_requestsentity) so the team can answer “which customers asked for this and how many”? - Should features support dependencies on other features (a
feature_dependenciesself-junction withpredecessor/successorcardinality)? - Should
feature_sourcebe promoted from an enum to its ownfeature_sourcesentity if sources need their own metadata (URL, channel, contact person)? - Should attachments (mockups, specs, screenshots) be modeled as a first-class
attachmentsentity linked tofeatures? - Should
releasescarry capacity data (team capacity vs. allocated effort) to support release-load planning? - Should a strategic tier above
objectives(e.g.initiatives) be introduced if the org begins planning multi-objective programs?
7. Implementation notes for the downstream agent
- Create one module named
product_roadmap(the module name must equal thesystem_slugfrom the front-matter — do not invent a different module slug here) and two baseline permissions (product_roadmap:read,product_roadmap:manage) before any entity. - Create entities in the order given in §2 — entities referenced by others first. Recommended order:
users,products,objectives,releases,tags,features,feature_votes,comments,feature_tags. - For each entity: set
label_columnto the snake_case field marked as label in §3, passmodule_id,view_permission,edit_permission. Do not manually createid,created_at,updated_at, or the auto-label field. - For each field in §3: pass
table_name,field_name,format,title(the Label column), and forreference/parentfields alsoreference_tableand areference_delete_modeconsistent with §4. - Fix up each entity’s auto-created label-column field title.
create_entityauto-creates a field whosefield_nameequals the entity’slabel_column, withtitledefaulting tosingular_label. Every entity in this model has a label_column whose §3 Label differs from itssingular_label, so every entity needs the fixup. After eachcreate_entity, follow up withupdate_field(id passed as a string in the form"{table_name}.{field_name}") to set the correct title:"products.product_name"→ title"Name""objectives.objective_name"→ title"Name""features.feature_title"→ title"Title""users.user_full_name"→ title"Full Name""releases.release_name"→ title"Name""feature_votes.feature_vote_label"→ title"Label""comments.comment_label"→ title"Label""tags.tag_name"→ title"Name""feature_tags.feature_tag_label"→ title"Label"
- Deduplicate against Semantius built-in tables. This model declares
usersas a self-contained entity. If Semantius ships a built-inuserstable, skip the create and reuse the built-in as thereference_tabletarget for every FK pointing tousers(product_owner_id,objective_owner_id,requester_id,owner_id,author_id,feature_votes.user_id). Only add missing fields (e.g.user_role,user_status) to the built-in if they are not already present and the addition is low-risk. - After creation, spot-check that
label_columnon each entity resolves to a real string field and that allreference_tabletargets exist. The junction tables (feature_votes,feature_tags) require their*_labelfield to be populated by the caller on every insert — flag this in any seed script or import flow.