Software Design Specification (SDS)
Specifies the Semantic Failure Management subsystem, the “Organizational Immune System” responsible for:
SemanticDebtItem entitiesSemanticIncident aggregates with root cause analysisThis specification consolidates the former SDS-016 (Semantic Incidents) into a unified view.
Defined as a JSON Schema (Draft-07).
Schema Source: schemas/events/governance/semantic-debt/v1/opened.schema.json (and related status schemas)
State transitions are governed by strict schema validation for each lifecycle event.
The following optional fields enable grouping under incidents (§7):
| Field | Type | Description |
|---|---|---|
incidentId |
string? |
FK to grouping SemanticIncident (nullable) |
incidentRelationship |
enum? |
primary, secondary, duplicate, superseded |
supersededBy |
string? |
If superseded, reference to superseding debtId |
All SEA™ events, including Semantic Debt events, must use the canonical envelope.
Schema Source: schemas/events/envelope-v1.schema.json
1
2
3
4
5
6
7
8
{
"specversion": "1.0",
"id": "uuid",
"type": "governance.semantic-debt.opened",
"source": "//core/semantic-ledger",
"time": "2024-01-01T12:00:00Z",
"data": { ... }
}
| Event Type | Schema | Trigger |
|---|---|---|
governance.semantic-debt.opened |
opened.schema.json |
New item detected |
governance.semantic-debt.updated |
updated.schema.json |
Fields changed |
governance.semantic-debt.status-changed |
status-changed.schema.json |
Lifecycle change |
governance.semantic-debt.superseded |
superseded.schema.json |
Merged/replaced |
To prevent “debt spam” from repeat executions:
_ separators<system>:<checkFamily>:<checkId>Construct canonical JSON object with: failureClass, boundedContext, conceptIds, policyNames, detectorSignal, policyEvalMode.
1
dedupeKey = "sha256:" + sha256(canonicalJson)
dedupeKey exists in open/accepted/mitigating → Update existing item (append evidence)sea.semanticDebt.*aggregated payload mode(SemanticDebtItem)-[:AFFECTS]->(Concept)Events can carry privacy.payloadMode = "aggregated" where sensitive details (specific PII in evidence) are omitted, allowing safe dashboarding.
Semantic Debt lifecycle transitions are governed by SDS-031: Authority & Ownership Boundaries.
| Transition | Required Role | Additional Constraints |
|---|---|---|
open → accepted |
Domain Steward (R-DS) or Architecture Governor (R-AG) |
Separation of duties: acceptor ≠ reporter |
accepted → mitigating |
Developer (R-DEV) with R-DS oversight |
Mitigation plan required for High/Critical |
accepted → resolved |
Developer (R-DEV) |
Evidence of resolution required |
Any → break-glass override |
Security Officer (R-SO) |
See SDS-031 §4.3 Break-glass Protocol |
All status transitions emit events to the IFL per SDS-031 §6 (Mandatory Audit Artifacts):
governance.authority.debt-acceptedgovernance.authority.debt-expiredSemantic Debt Items can be grouped under Semantic Incidents when multiple items share a common root cause. This addresses “debt spam” where the same underlying issue manifests as multiple items.
A SemanticIncident provides:
Sections 7-14 define the complete incident model (formerly SDS-016).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://schemas.newsea.io/governance/semantic-incident/v1.schema.json",
"title": "SemanticIncident",
"description": "Groups multiple SemanticDebtItems under a common root cause.",
"type": "object",
"required": ["incidentId", "title", "status", "rootCause", "debtItems", "detectedAt", "detectedBy"],
"properties": {
"incidentId": {
"type": "string",
"pattern": "^inc:[a-f0-9]{12}$",
"description": "Unique incident identifier"
},
"title": {
"type": "string",
"minLength": 10,
"description": "Human-readable incident title"
},
"description": { "type": "string" },
"status": {
"type": "string",
"enum": ["open", "triaging", "contained", "resolving", "resolved", "postmortem", "closed"]
},
"severity": {
"type": "string",
"enum": ["low", "medium", "high", "critical"]
},
"rootCause": { "$ref": "#/definitions/RootCause" },
"debtItems": {
"type": "array",
"items": { "$ref": "#/definitions/DebtItemReference" },
"minItems": 1
},
"affectedContexts": {
"type": "array",
"items": { "type": "string" }
},
"blastRadius": { "$ref": "#/definitions/BlastRadius" },
"correlationPatterns": { "$ref": "#/definitions/CorrelationPatterns" },
"timeline": {
"type": "array",
"items": { "$ref": "#/definitions/TimelineEntry" }
},
"owner": { "$ref": "#/definitions/Principal" },
"detectedAt": { "type": "string", "format": "date-time" },
"detectedBy": { "type": "string", "enum": ["automatic", "manual"] },
"containedAt": { "type": "string", "format": "date-time" },
"resolvedAt": { "type": "string", "format": "date-time" },
"postmortemUrl": { "type": "string", "format": "uri" }
},
"definitions": {
"RootCause": {
"type": "object",
"required": ["category", "summary"],
"properties": {
"category": {
"type": "string",
"enum": [
"policy_contradiction", "schema_drift", "concept_collision",
"translation_loss", "projection_mismatch", "external_dependency",
"configuration_error", "unknown"
]
},
"summary": { "type": "string" },
"technicalDetails": { "type": "string" },
"affectedArtifact": { "type": "string" }
}
},
"DebtItemReference": {
"type": "object",
"required": ["debtId", "relationship", "addedAt"],
"properties": {
"debtId": { "type": "string" },
"relationship": {
"type": "string",
"enum": ["primary", "secondary", "duplicate", "superseded"]
},
"addedAt": { "type": "string", "format": "date-time" },
"addedBy": { "type": "string" },
"supersededBy": { "type": "string" }
}
},
"BlastRadius": {
"type": "object",
"properties": {
"conceptsAffected": { "type": "integer" },
"policiesAffected": { "type": "integer" },
"contextsAffected": { "type": "integer" },
"consumersAffected": { "type": "integer" },
"ciRunsBlocked": { "type": "integer" },
"estimatedResolutionEffort": {
"type": "string",
"enum": ["trivial", "small", "medium", "large", "epic"]
}
}
},
"CorrelationPatterns": {
"type": "object",
"properties": {
"correlationIds": { "type": "array", "items": { "type": "string" } },
"nearKeyMatches": { "type": "array", "items": { "type": "string" } },
"sharedConcepts": { "type": "array", "items": { "type": "string" } },
"sharedPolicies": { "type": "array", "items": { "type": "string" } }
}
},
"TimelineEntry": {
"type": "object",
"required": ["timestamp", "action", "actor"],
"properties": {
"timestamp": { "type": "string", "format": "date-time" },
"action": { "type": "string" },
"actor": { "type": "string" },
"details": { "type": "string" }
}
},
"Principal": {
"type": "object",
"required": ["principalId", "role"],
"properties": {
"principalId": { "type": "string" },
"role": { "type": "string" }
}
}
}
}
An incident is automatically created when ANY of the following conditions are detected:
| Trigger | Detection Logic | Minimum Threshold |
|---|---|---|
| Same correlationId | Multiple debt items share correlationId from single CI run |
≥2 items in same run |
| Repeated nearKey matches | dedupeKey similarity ≥85% across items |
≥3 near-matches in 24h |
| Cross-context conflicts | Same conceptId or policyId appears in debt across ≥2 bounded contexts |
≥2 contexts |
| Policy contradiction cluster | Multiple policy_contradiction failures reference same policy |
≥3 items on same policy |
| Cascade pattern | Debt item X references concept that is dependsOn for concept in debt item Y |
≥2 dependency hops |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def detect_incidents(new_debt_item: SemanticDebtItem) -> Optional[SemanticIncident]:
"""Determines if a new debt item should be grouped into an existing or new incident."""
# Strategy 1: Same correlation ID
existing = find_incident_by_correlation_id(new_debt_item.correlationId)
if existing:
add_to_incident(existing, new_debt_item, relationship="secondary")
return existing
# Strategy 2: Near-key matching (fuzzy dedupe, Jaccard similarity ≥85%)
near_matches = find_near_key_matches(new_debt_item.dedupeKey, threshold=0.85)
if len(near_matches) >= 2:
incident = find_or_create_incident_for_near_keys(near_matches + [new_debt_item])
return incident
# Strategy 3: Cross-context conflict detection
cross_context_items = find_debt_items_by_concept(
new_debt_item.affectedConcepts,
exclude_context=new_debt_item.boundedContext
)
if len(cross_context_items) >= 1:
incident = create_cross_context_incident(new_debt_item, cross_context_items)
return incident
# Strategy 4: Cascade detection (dependency graph, depth=2)
dependent_debt = find_debt_on_dependencies(new_debt_item.affectedConcepts, depth=2)
if dependent_debt:
incident = create_cascade_incident(new_debt_item, dependent_debt)
return incident
# No incident detected - item remains standalone
return None
Users with role R-DS or R-AG may manually create incidents via:
sea-incident create --debt-items debt-001,debt-002 --title "..."POST /incidents with debt item IDs1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─────────────────────────────────────────────────────────────────────┐
│ INCIDENT-DEBT TOPOLOGY │
├─────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────┐ │
│ │ SemanticIncident │ │
│ │ (inc:abc123) │ │
│ └─────────┬───────────┘ │
│ ┌───────────────┼───────────────┬──────────────────┐ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ DebtItem│ │ DebtItem│ │ DebtItem│ ... │ DebtItem│ │
│ │ primary │ │secondary│ │duplicate│ │supersede│ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────────────┘
| Relationship | Description | CI Behavior |
|---|---|---|
primary |
Root cause debt item | Shown in dashboards; resolving this resolves incident |
secondary |
Related debt item with same root cause | Inherits resolution from primary |
duplicate |
Exact or near-exact copy of another item | Hidden from dashboards; auto-resolves with primary |
superseded |
Replaced by a newer, more accurate debt item | Marked read-only; shows superseding item link |
Every SemanticDebtItem MAY have an incidentId foreign key:
1
2
3
4
5
{
"debtId": "debt-12345",
"incidentId": "inc:abc123", // nullable - standalone if null
"incidentRelationship": "secondary"
}
Backward Compatibility: incidentId is OPTIONAL. Existing debt items without incidents continue to function. Migration is NOT required.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌──────────┐ triage() ┌───────────┐ contain() ┌────────────┐
│ open │───────────────►│ triaging │──────────────►│ contained │
└──────────┘ └───────────┘ └────────────┘
│ │ │
│ auto-resolve() │ reject() │ resolve_start()
▼ ▼ ▼
┌──────────┐ ┌───────────┐ ┌────────────┐
│ resolved │◄──────────────│ open │ │ resolving │
└──────────┘ escalate() └───────────┘ └────────────┘
│ │
│ postmortem() │ resolve_complete()
▼ ▼
┌────────────┐ ┌────────────┐
│ postmortem │◄─────────────────────────────────────────│ resolved │
└────────────┘ └────────────┘
│
│ close()
▼
┌──────────┐
│ closed │
└──────────┘
| State | Description | Entry Criteria | Exit Criteria |
|---|---|---|---|
open |
Incident detected, awaiting triage | Auto-detection or manual creation | Owner assigned + severity confirmed |
triaging |
Owner investigating root cause | Owner assigned | Root cause identified OR escalated |
contained |
Blast radius limited; workarounds in place | Mitigation deployed | All affected contexts stabilized |
resolving |
Permanent fix in progress | Resolution plan approved | All debt items resolved |
resolved |
All constituent debt items resolved | All primary/secondary items resolved |
Postmortem scheduled |
postmortem |
Lessons learned documented | Postmortem meeting held | Postmortem document approved |
closed |
Incident complete | Postmortem approved | N/A (terminal state) |
| Transition | Required Role | Additional Constraints |
|---|---|---|
open → triaging |
R-DS or R-AG | Owner assignment required |
triaging → contained |
R-DS | Mitigation plan documented |
contained → resolving |
R-DS with R-AG approval for cross-context | Resolution proposal approved |
resolving → resolved |
R-DEV (with R-DS verification) | All debt items resolved |
resolved → postmortem |
R-AG or R-SO for Critical | Postmortem scheduled |
postmortem → closed |
R-AG | Postmortem document linked |
Two debt items are automatically merged into one incident when:
| Condition | Action |
|---|---|
dedupeKey exact match |
Mark newer as duplicate; update evidence on original |
dedupeKey similarity ≥95% |
Prompt user to confirm merge; suggest duplicate |
Same conceptId + same failureClass within 1 hour |
Auto-group into incident if not already grouped |
A debt item is superseded when:
| Trigger | Behavior |
|---|---|
| User manually marks as superseded | Link to superseding item required |
| Same concept + higher severity detected | Old item auto-superseded by new |
| Resolution invalidated by new failure | Reopened item supersedes resolved item |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def merge_debt_items(target: SemanticDebtItem, source: SemanticDebtItem) -> SemanticDebtItem:
"""Merges source into target. Target becomes the surviving item."""
# Preserve highest severity
if severity_rank(source.severity) > severity_rank(target.severity):
target.severity = source.severity
# Append evidence
target.evidence.extend(source.evidence)
# Union affected concepts
target.affectedConcepts = list(set(target.affectedConcepts + source.affectedConcepts))
# Mark source as superseded
source.status = "superseded"
source.supersededBy = target.debtId
emit_event("governance.semantic-debt.superseded", source)
# If source was in an incident, transfer to target's incident
if source.incidentId and not target.incidentId:
target.incidentId = source.incidentId
emit_event("governance.semantic-debt.updated", target)
return target
| Event Type | Trigger | Required Fields |
|---|---|---|
governance.incident.created |
Incident created | incidentId, title, debtItems, detectedBy, timestamp |
governance.incident.debt-added |
Debt item added | incidentId, debtId, relationship, addedBy, timestamp |
governance.incident.debt-removed |
Debt item removed | incidentId, debtId, removedBy, reason, timestamp |
governance.incident.status-changed |
Lifecycle transition | incidentId, from, to, actor, timestamp |
governance.incident.owner-assigned |
Owner assigned/changed | incidentId, previousOwner, newOwner, timestamp |
governance.incident.postmortem-linked |
Postmortem attached | incidentId, postmortemUrl, timestamp |
governance.incident.closed |
Incident closed | incidentId, resolution, timestamp |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"specversion": "1.0",
"id": "evt-uuid-12345",
"type": "governance.incident.created",
"source": "//governance/incident-service",
"time": "2025-12-22T10:35:00Z",
"data": {
"incidentId": "inc:abc123def456",
"title": "Cross-context policy contradiction in Billing/Payments",
"severity": "high",
"rootCause": {
"category": "policy_contradiction",
"summary": "Tax calculation policy conflicts between Billing and Payments contexts"
},
"debtItems": [
{ "debtId": "debt-001", "relationship": "primary" },
{ "debtId": "debt-002", "relationship": "secondary" }
],
"detectedBy": "automatic",
"detectedAt": "2025-12-22T10:35:00Z"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
PREFIX sea-inc: <http://sea-forge.com/schema/incident#>
SELECT ?incident ?title ?severity (COUNT(?debt) AS ?debtCount) ?status
WHERE {
?incident a sea-inc:SemanticIncident ;
sea-inc:title ?title ;
sea-inc:severity ?severity ;
sea-inc:status ?status ;
sea-inc:hasDebtItem ?debt .
FILTER (?status IN ("open", "triaging", "contained", "resolving"))
}
GROUP BY ?incident ?title ?severity ?status
ORDER BY DESC(?severity) DESC(?debtCount)
1
2
3
4
5
6
7
8
SELECT ?incident ?title (GROUP_CONCAT(?context; separator=", ") AS ?contexts)
WHERE {
?incident a sea-inc:SemanticIncident ;
sea-inc:title ?title ;
sea-inc:affectedContext ?context .
}
GROUP BY ?incident ?title
HAVING (COUNT(?context) > 1)
Prefix: sea-inc: <http://sea-forge.com/schema/incident#>
| Class/Property | Type | Description |
|---|---|---|
sea-inc:SemanticIncident |
Class | Groups related debt items under common root cause |
sea-inc:RootCause |
Class | The underlying cause of the incident |
sea-inc:hasDebtItem |
Property | Links Incident to constituent DebtItems |
sea-inc:hasRootCause |
Property | Links Incident to RootCause |
sea-inc:affectedContext |
Property | Links Incident to affected BoundedContext |
sea-inc:owner |
Property | Links Incident to responsible Principal |
sea-inc:status |
Property | Current lifecycle state |
sea-inc:severity |
Property | Aggregate severity |
sea-inc:supersedes |
Property | Links DebtItem to superseded DebtItem |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Incident creation
<sea:incident:{incidentId}> a sea-inc:SemanticIncident ;
rdfs:label "{title}" ;
sea-inc:status "{status}" ;
sea-inc:severity "{severity}" ;
sea-inc:detectedAt "{detectedAt}"^^xsd:dateTime .
# Link to root cause
<sea:incident:{incidentId}> sea-inc:hasRootCause [
a sea-inc:RootCause ;
sea-inc:category "{rootCause.category}" ;
rdfs:comment "{rootCause.summary}"
] .
# Link to debt items
<sea:incident:{incidentId}> sea-inc:hasDebtItem <sea:debt:{debtId}> .
# Supersession relationship
<sea:debt:{newDebtId}> sea-inc:supersedes <sea:debt:{oldDebtId}> .
| Transaction Type | Description |
|---|---|
INCIDENT_CREATE |
Creates new SemanticIncident record |
INCIDENT_ADD_DEBT |
Links debt item to incident |
INCIDENT_REMOVE_DEBT |
Unlinks debt item from incident |
INCIDENT_STATUS_CHANGE |
Transitions incident lifecycle state |
DEBT_SUPERSEDE |
Marks debt item as superseded by another |
DEBT_MERGE |
Merges evidence from one item into another |
sea-incident create1
2
3
4
5
6
sea-incident create \
--title "Cross-context policy contradiction" \
--debt-items debt-001,debt-002,debt-003 \
--root-cause-category policy_contradiction \
--severity high \
--out incident.json
sea-incident add-debt1
2
3
4
sea-incident add-debt \
--incident inc:abc123 \
--debt-id debt-004 \
--relationship secondary
sea-incident status1
2
3
4
sea-incident status \
--incident inc:abc123 \
--to contained \
--mitigation "Feature flag deployed to disable conflicting calculation"
sea-incident query1
2
3
4
sea-incident query \
--status open,triaging \
--severity high,critical \
--format table
| Version | Date | Author | Change |
|---|---|---|---|
| 1.0.0 | 2025-12-31 | SEA-Forge™ | Consolidated SDS-016 (Semantic Incidents) into unified specification |
| 0.1.0 | 2024-01-01 | SEA-Forge™ | Initial draft (Semantic Debt Ledger only) |