Event Types¶
The Tech Strategy Tool processes 29 event types across 5 entity categories plus 1 system event. All events are submitted via POST /api/events and processed by the event processor.
Quick Reference¶
| Event | Entity | Description |
|---|---|---|
create_team |
Team | Create a new team |
update_team_name |
Team | Rename a team |
update_team_color |
Team | Change a team's color |
delete_team |
Team | Delete a team and all its contents |
create_group |
Group | Create a group within a team |
update_group_name |
Group | Rename a group |
update_group_description |
Group | Update a group's description |
delete_group |
Group | Delete a group (objectives become ungrouped) |
reorder_group |
Group | Change a group's position within its team |
create_principle |
Principle | Create a principle within a team |
update_principle_title |
Principle | Update a principle's title |
update_principle_description |
Principle | Update a principle's description |
delete_principle |
Principle | Delete a principle (removed from all objectives) |
reorder_principle |
Principle | Change a principle's position within its team |
create_objective |
Objective | Create an objective within a team |
update_objective_title |
Objective | Update an objective's title |
assign_objective_to_group |
Objective | Assign an objective to a group |
remove_objective_from_group |
Objective | Remove an objective from its group |
assign_principle_to_objective |
Objective | Link a principle to an objective |
remove_principle_from_objective |
Objective | Remove a principle link from an objective |
delete_objective |
Objective | Delete an objective and its initiatives |
reorder_objective |
Objective | Change an objective's position within its group |
create_initiative |
Initiative | Create an initiative within an objective |
update_initiative_name |
Initiative | Update an initiative's name |
update_initiative_progress |
Initiative | Set an initiative's completion percentage |
set_initiative_jira_key |
Initiative | Set or update the Jira issue key |
remove_initiative_jira_key |
Initiative | Clear the Jira issue key |
delete_initiative |
Initiative | Delete an initiative |
reorder_initiative |
Initiative | Change an initiative's position within its objective |
restore_history |
System | Restore the strategy to a previous point in time |
Event Structure¶
Every event submission includes:
| Field | Type | Description |
|---|---|---|
eventType |
string | One of the 29 event types listed below |
targetId |
string (GUID) | The entity being acted upon (see conventions below) |
data |
object | Key-value pairs specific to the event type |
lastSeenSequence |
long (optional) | For conflict detection on supported field edits |
lastSeenSequence is a top-level field
lastSeenSequence is a top-level field on the SubmitEventRequest, not a key inside the data dictionary. It is only meaningful for the 7 event types that support conflict detection.
TargetId conventions¶
| Event Category | TargetId Meaning |
|---|---|
| Create events | Parent entity ID (the team or objective that owns the new entity) |
| Update/delete events | The entity being modified or deleted |
create_team |
(not set — teams have no parent) |
restore_history |
(not set) |
For create events, the new entity's ID is always in data.id.
Field Limits¶
All text fields are validated server-side against maximum lengths. Exceeding a limit causes the event to be rejected.
| Field | Max Length |
|---|---|
| Team name | 100 |
| Team color | 7 (# + 6 hex digits) |
| Group name | 100 |
| Group description | 200 |
| Principle title | 300 |
| Principle description | 2000 |
| Objective title | 300 |
| Initiative name | 200 |
| Jira issue key | 50 |
Team Events¶
Team events require the admin role. Non-admin users receive a 403 response.
create_team¶
Creates a new team.
| Data Field | Required | Description |
|---|---|---|
id |
Yes | GUID for the new team |
name |
Yes | Team display name (max 100) |
color |
No | Hex color code (e.g., #3498db). Defaults to #000000 if omitted |
targetId is null — teams have no parent.
{
"eventType": "create_team",
"targetId": null,
"data": {
"id": "a1b2c3d4-...",
"name": "Platform Team",
"color": "#3498db"
}
}
update_team_name¶
Updates a team's display name. No conflict detection.
| Data Field | Required | Description |
|---|---|---|
name |
Yes | New team name (max 100) |
update_team_color¶
Updates a team's color. Validated via regex ^#[0-9A-Fa-f]{6}$. No conflict detection.
| Data Field | Required | Description |
|---|---|---|
color |
Yes | New hex color code (e.g., #3498db) |
delete_team¶
Deletes a team and all of its contents (groups, principles, objectives, initiatives).
No data fields required. targetId is the team to delete.
Group Events¶
create_group¶
Creates a new group within a team.
| Data Field | Required | Description |
|---|---|---|
id |
Yes | GUID for the new group |
name |
Yes | Group display name (max 100) |
targetId is the parent team ID.
update_group_name¶
Updates a group's name. Supports conflict detection.
| Data Field | Required | Description |
|---|---|---|
name |
Yes | New group name (max 100) |
update_group_description¶
Updates a group's description. Supports conflict detection.
| Data Field | Required | Description |
|---|---|---|
description |
Yes | New group description (max 200) |
delete_group¶
Deletes a group. Objectives assigned to this group become ungrouped (they are not deleted).
No data fields required. targetId is the group to delete.
reorder_group¶
Changes the position of a group within its team.
| Data Field | Required | Description |
|---|---|---|
index |
Yes | New zero-based target position (clamped to valid range) |
No-op if the group is already at the requested position.
Principle Events¶
create_principle¶
Creates a new principle within a team.
| Data Field | Required | Description |
|---|---|---|
id |
Yes | GUID for the new principle |
title |
Yes | Principle title (max 300, must be non-empty) |
description |
No | Principle description (max 2000) |
targetId is the parent team ID.
update_principle_title¶
Updates a principle's title. Supports conflict detection. Titles support *highlight* syntax for emphasizing key terms in the team color.
| Data Field | Required | Description |
|---|---|---|
title |
Yes | New title (max 300) |
update_principle_description¶
Updates a principle's description. Supports conflict detection.
| Data Field | Required | Description |
|---|---|---|
description |
Yes | New description (max 2000) |
delete_principle¶
Deletes a principle and removes it from all objectives that reference it — across all teams. Emits cascade card-changed notifications for every affected objective.
No data fields required. targetId is the principle to delete.
reorder_principle¶
Changes the position of a principle within its owning team's list.
| Data Field | Required | Description |
|---|---|---|
index |
Yes | New zero-based target position (clamped to valid range) |
Objective Events¶
create_objective¶
Creates a new objective within a team.
| Data Field | Required | Description |
|---|---|---|
id |
Yes | GUID for the new objective |
title |
Yes | Objective title (max 300, must be non-empty) |
groupId |
No | GUID of the group to place the objective in |
targetId is the parent team ID. The objective is inserted at the end of the group's slice (or the ungrouped section if no groupId is provided).
update_objective_title¶
Updates an objective's title. Supports conflict detection.
| Data Field | Required | Description |
|---|---|---|
title |
Yes | New title (max 300) |
assign_objective_to_group¶
Assigns an objective to a group. The group must exist in the same team. The objective is relocated to the end of the target group's slice.
| Data Field | Required | Description |
|---|---|---|
groupId |
Yes | GUID of the target group |
remove_objective_from_group¶
Removes an objective from its current group, moving it to the ungrouped section.
No data fields required. targetId is the objective.
assign_principle_to_objective¶
Links a principle to an objective.
| Data Field | Required | Description |
|---|---|---|
principleId |
Yes | GUID of the principle to link |
targetId is the objective.
Cross-team decomposition
If the principle belongs to a different team than the objective, the processor decomposes this into 2 derived events:
create_principle— creates a copy in the objective's team withtitle,description, andsourceTeamIdin dataassign_principle_to_objective— links the copy to the objective
Each derived event is stored separately in the event log. The sourceTeamId in the first derived event enables the entity history to show "Created principle (copied from 'Team Name')" for cross-team copies. For same-team principles, the assignment is direct with no decomposition.
remove_principle_from_objective¶
Removes a principle reference from an objective. The principle itself is not deleted.
| Data Field | Required | Description |
|---|---|---|
principleId |
Yes | GUID of the principle to unlink |
delete_objective¶
Deletes an objective and all of its initiatives.
No data fields required. targetId is the objective to delete.
reorder_objective¶
Changes the position of an objective within its current group's slice.
| Data Field | Required | Description |
|---|---|---|
index |
Yes | New zero-based index within the group slice |
Initiative Events¶
create_initiative¶
Creates a new initiative within an objective.
| Data Field | Required | Description |
|---|---|---|
id |
Yes | GUID for the new initiative |
name |
Yes | Initiative name (max 200) |
targetId is the parent objective ID.
update_initiative_name¶
Updates an initiative's name. Supports conflict detection.
| Data Field | Required | Description |
|---|---|---|
name |
Yes | New initiative name (max 200) |
update_initiative_progress¶
Updates an initiative's progress percentage. Supports conflict detection. The objective's total progress bar recalculates automatically.
| Data Field | Required | Description |
|---|---|---|
progress |
Yes | Completion percentage (0-100 inclusive, integer) |
set_initiative_jira_key¶
Sets or updates the Jira issue key for an initiative. No conflict detection.
| Data Field | Required | Description |
|---|---|---|
jiraKey |
Yes | Jira issue key (e.g., PROJ-123, max 50) |
remove_initiative_jira_key¶
Clears the Jira issue key from an initiative. No-op if the key is already null.
No data fields required. targetId is the initiative.
delete_initiative¶
Deletes an initiative.
No data fields required. targetId is the initiative to delete.
reorder_initiative¶
Changes the position of an initiative within its parent objective's initiative list.
| Data Field | Required | Description |
|---|---|---|
index |
Yes | New zero-based index position |
System Events¶
restore_history¶
Restores the system state to a previous point in time. Requires the admin role.
| Data Field | Required | Description |
|---|---|---|
restoreToSequence |
Yes | Sequence number to restore to |
targetId is null. This event is always recorded as applied in the log (it serves as a marker). The actual state restoration is handled by the API layer after the event is logged.
Conflict Detection¶
Seven field-edit event types support conflict detection via lastSeenSequence. The processor uses the HasConflict helper to compare the request's lastSeenSequence against the field's current sequence in the FieldSequences dictionary. If the field was modified since the client's last read, the event is rejected.
| Event Type | Field Checked |
|---|---|
update_group_name |
name |
update_group_description |
description |
update_principle_title |
title |
update_principle_description |
description |
update_objective_title |
title |
update_initiative_name |
name |
update_initiative_progress |
progress |
All other event types ignore lastSeenSequence even if provided.
On conflict rejection, the response includes:
| Field | Value |
|---|---|
status |
"rejected" |
rejectionReason |
Description of the conflict |
conflictingServerValue |
The current server value the client is out of sync with |
On successful application of a field-edit event, the response includes:
| Field | Value |
|---|---|
status |
"applied" |
previousValue |
The field's value before the change (for history) |
See Conflict Resolution for the full design rationale and client-side handling.
No-Op Detection¶
All update events compare the new value against the current value (trimmed). If they are identical, the processor returns a no-op result — no event is persisted and no SSE notification is broadcast. The API returns "no_change" as the status.
Envelope Validation¶
Before reaching the event processor, the API layer validates the request envelope:
eventTypemust be non-empty and one of the 29 recognized types (fromEventTypes.All)targetIdis required for all event types exceptcreate_teamandrestore_history(listed inEventTypes.NoTargetIdRequired)
Invalid envelopes receive a 400 Bad Request response. This is distinct from domain rejections (200 OK with status: "rejected"), which indicate valid requests rejected by business rules.