---
artifact: semantic-model
system_name: Product Roadmap
system_slug: product_roadmap
domain: Product Management
naming_mode: agent-optimized
created_at: 2026-04-28
entities:
  - products
  - objectives
  - features
  - users
  - releases
  - feature_votes
  - comments
  - tags
  - feature_tags
departments:
  - Product
initial_request: |
  I need to plan the product roadmap. I have ideas/change requests, which need to be estimated and prioritized, I've to plan releases
---

# 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

```mermaid
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 `product` may have one `product_owner` (N:1 → `users`).
- A `product` has many `releases` (1:N, via `releases.product_id`).
- A `product` has many `features` (1:N, via `features.product_id`).
- A `product` may have many `objectives` (1:N, via `objectives.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 `objective` may belong to one `product` (N:1, optional).
- An `objective` may have one `objective_owner` (N:1 → `users`).
- An `objective` rolls up many `features` (1:N, via `features.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 `feature` belongs to one `product` (N:1, required).
- A `feature` may align with one `objective` (N:1, optional).
- A `feature` may be scheduled into one `release` (N:1, optional — null until committed and scheduled).
- A `feature` may have one `requester` and one `owner` (each N:1 → `users`).
- A `feature` has many `comments` (1:N, via `comments.feature_id`).
- `features` ↔ `users` (voting) is M:N through the `feature_votes` junction.
- `features` ↔ `tags` is M:N through the `feature_tags` junction.

---

### 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 | Email | unique |
| `user_role` | `enum` | no | Role | values: `admin`, `product_manager`, `engineering`, `stakeholder`, `viewer` |
| `user_status` | `enum` | no | Status | values: `active`, `inactive` |

**Relationships**

- A `user` may own many `products`, `objectives`, and `features` (1:N for each, via the respective `*_owner_id` / `owner_id` field).
- A `user` may request many `features` (1:N, via `features.requester_id`).
- A `user` may author many `comments` (1:N, via `comments.author_id`).
- `users` ↔ `features` (voting) is M:N through the `feature_votes` junction.

---

### 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 `release` belongs to one `product` (N:1, required).
- A `release` contains many `features` (1:N, via `features.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_vote` belongs to one `feature` (N:1) and one `user` (N:1).
- Acts as the junction for the M:N relationship between `features` and `users`.

---

### 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 `comment` belongs to one `feature` (N:1, required).
- A `comment` is authored by one `user` (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` ↔ `features` is M:N through the `feature_tags` junction.

---

### 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_tag` belongs to one `feature` (N:1) and one `tag` (N:1).
- Acts as the junction for the M:N relationship between `features` and `tags`.

## 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`
- `active`
- `sunset`
- `archived`

### 5.2 `objectives.objective_status`
- `proposed`
- `active`
- `achieved`
- `missed`
- `cancelled`

### 5.3 `features.feature_type`
- `new_feature`
- `enhancement`
- `change_request`
- `bug`
- `tech_debt`

### 5.4 `features.feature_status`
- `new`
- `under_review`
- `planned`
- `in_progress`
- `shipped`
- `declined`
- `parked`

### 5.5 `features.feature_priority`
- `critical`
- `high`
- `medium`
- `low`

### 5.6 `features.feature_source`
- `customer`
- `support`
- `sales`
- `internal`
- `partner`

### 5.7 `users.user_role`
- `admin`
- `product_manager`
- `engineering`
- `stakeholder`
- `viewer`

### 5.8 `users.user_status`
- `active`
- `inactive`

### 5.9 `releases.release_status`
- `planned`
- `in_progress`
- `released`
- `cancelled`

## 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_id` to a `feature_releases` junction with phase metadata?
- Should RICE estimates be tracked as a separate `estimates` entity to retain history (who scored what when, and how the score evolved), instead of living as fields on `features`?
- Should features be linkable to specific customers/accounts (e.g. via a `customer_requests` entity) so the team can answer "which customers asked for this and how many"?
- Should features support dependencies on other features (a `feature_dependencies` self-junction with `predecessor` / `successor` cardinality)?
- Should `feature_source` be promoted from an enum to its own `feature_sources` entity if sources need their own metadata (URL, channel, contact person)?
- Should attachments (mockups, specs, screenshots) be modeled as a first-class `attachments` entity linked to `features`?
- Should `releases` carry 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

1. Create one module named `product_roadmap` (the module name **must** equal the `system_slug` from the front-matter — do not invent a different module slug here) and two baseline permissions (`product_roadmap:read`, `product_roadmap:manage`) before any entity.
2. 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`.
3. For each entity: set `label_column` to the snake_case field marked as label in §3, pass `module_id`, `view_permission`, `edit_permission`. Do **not** manually create `id`, `created_at`, `updated_at`, or the auto-label field.
4. For each field in §3: pass `table_name`, `field_name`, `format`, `title` (the Label column), and for `reference`/`parent` fields also `reference_table` and a `reference_delete_mode` consistent with §4.
5. **Fix up each entity's auto-created label-column field title.** `create_entity` auto-creates a field whose `field_name` equals the entity's `label_column`, with `title` defaulting to `singular_label`. Every entity in this model has a label_column whose §3 Label differs from its `singular_label`, so every entity needs the fixup. After each `create_entity`, follow up with `update_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"`
6. **Deduplicate against Semantius built-in tables.** This model declares `users` as a self-contained entity. If Semantius ships a built-in `users` table, **skip the create** and reuse the built-in as the `reference_table` target for every FK pointing to `users` (`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.
7. After creation, spot-check that `label_column` on each entity resolves to a real string field and that all `reference_table` targets exist. The junction tables (`feature_votes`, `feature_tags`) require their `*_label` field to be populated by the caller on every insert — flag this in any seed script or import flow.
