Software Design Specification - Governance Protocol
Draft
Defines the Authority Boundaries for SEA-Forge™, the Internal Federated Ledger (IFL), and the Semantic Core. This specification establishes:
This spec is the authoritative source for “who can do what” across the SEA™ ecosystem.
| Role ID | Role Name | Scope | Description |
|---|---|---|---|
R-DS |
Domain Steward | Bounded Context | Semantic owner of a domain’s concepts, policies, and business rules. |
R-AG |
Architecture Governor | System-wide | CALM/architecture compliance owner. Approves structural changes. |
R-LC |
Ledger Custodian | IFL Cluster | IFL operator, key custodian, node manager. |
R-SO |
Security Officer | System-wide | Approves break-glass actions, key rotation, security exceptions. |
R-RM |
Release Manager | Deployment Pipeline | Deployment authority, controls promotion gates. |
R-DEV |
Developer | Feature/PR scope | Proposal author, implements changes. Cannot self-approve in production. |
R-AA |
Automated Agent | Task scope | Recommends actions, executes approved automation. Cannot approve. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────────────────────────────────┐
│ GOVERNANCE AUTHORITY │
├─────────────────────────────────────────────────────────────────┤
│ Security Officer (R-SO) │
│ ├── Can delegate: Break-glass review (to R-AG) │
│ └── Cannot delegate: Key rotation approval │
├─────────────────────────────────────────────────────────────────┤
│ Architecture Governor (R-AG) │
│ ├── Can delegate: Semantic review (to R-DS) │
│ └── Cannot delegate: CALM compliance decisions │
├─────────────────────────────────────────────────────────────────┤
│ Domain Steward (R-DS) │
│ ├── Can delegate: Policy drafting (to R-DEV) │
│ └── Cannot delegate: Semantic meaning decisions │
├─────────────────────────────────────────────────────────────────┤
│ Ledger Custodian (R-LC) │
│ ├── Can delegate: Operational monitoring (to R-AA) │
│ └── Cannot delegate: Key custody, node admission │
├─────────────────────────────────────────────────────────────────┤
│ Release Manager (R-RM) │
│ ├── Can delegate: Staging deployment (to R-AA) │
│ └── Cannot delegate: Production promotion │
└─────────────────────────────────────────────────────────────────┘
| Constraint ID | Rule | Rationale |
|---|---|---|
C-01 |
A single person MUST NOT hold both R-DS and R-AG for the same bounded context |
Prevents semantic-structural conflict of interest |
C-02 |
R-LC MUST NOT also be R-SO |
Separation of operational and security authority |
C-03 |
R-DEV cannot approve their own proposals in production environment |
Four-eyes principle |
C-04 |
R-AA MUST have an R-* sponsor for any privileged action |
Traceability to human accountability |
Legend: R = Responsible, A = Accountable, C = Consulted, I = Informed
| Decision | R-DS | R-AG | R-LC | R-SO | R-RM | R-DEV | R-AA |
|---|---|---|---|---|---|---|---|
| Propose Semantic Change | R | C | I | I | I | R | R |
| Approve Semantic Change (Non-Breaking) | A | C | I | I | I | - | - |
| Approve Semantic Change (Breaking) | R | A | I | C | C | - | - |
| Accept Semantic Debt | A | C | I | C | I | R | - |
| Resolve Semantic Debt | A | I | I | I | I | R | R |
| Invoke Break-glass | R | C | I | A | C | R | - |
| Decision | R-DS | R-AG | R-LC | R-SO | R-RM | R-DEV | R-AA |
|---|---|---|---|---|---|---|---|
| Mint Identity Token | I | C | R | I | A | I | R |
| Rotate Signing Key | I | I | R | A | I | - | - |
| Add/Remove IFL Node | I | C | R | A | I | - | - |
| Revoke Compromised Key | I | I | R | A | I | - | - |
| Decision | R-DS | R-AG | R-LC | R-SO | R-RM | R-DEV | R-AA |
|---|---|---|---|---|---|---|---|
| Approve Schema Evolution (Minor) | A | C | I | I | I | R | - |
| Approve Schema Evolution (Major) | C | A | I | C | C | R | - |
| Change Canonical Normalizer Algorithm | C | A | I | C | I | R | - |
| Promote to Production | I | C | I | I | A | I | R |
| Emergency Rollback | C | C | R | A | R | I | R |
| SoD ID | Actor 1 | Actor 2 | Transaction | Enforcement |
|---|---|---|---|---|
SOD-01 |
Proposer | Approver | SemanticChangeProposal (production) |
CI Gate blocks if proposer == approver |
SOD-02 |
Debt Requester | Debt Acceptor | SemanticDebt.accept |
Ledger rejects if same principal |
SOD-03 |
Break-glass Requester | Break-glass Approver | BreakGlass.activate |
Security Officer MUST be different from requester |
SOD-04 |
Key Generator | Key Approver | KeyRotation.execute |
Two-party control for production keys |
SOD-05 |
Minter | Semantic Owner | Mint.execute for governance entities |
Domain Steward cannot mint their own definitions |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Policy: SoD Enforcement (evaluated at transaction commit)
Policy SeparationOfDutiesEnforcement:
applies_to: [SemanticChangeProposal, SemanticDebtAcceptance, BreakGlassActivation, MintTransaction, KeyRotation]
rule EnforceTwoPartyControl:
when:
- environment == "production"
- action.requires_approval == true
then:
- REQUIRE action.approver != action.proposer
- REQUIRE action.approver.role IN [R-DS, R-AG, R-SO, R-RM] # Excludes R-DEV, R-AA
- EMIT AuditEvent(type: "sod.enforced", actors: [action.proposer, action.approver])
else:
- REJECT with "SoD Violation: Self-approval not permitted in production"
Complete Workflow: For the full
SemanticChangeProposalschema, option generation policy, impact analysis algorithm, and anti-rubber-stamp guardrails, see §5. Decision Execution below.
States: draft → submitted → under_review → approved |
rejected → enacted |
withdrawn |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────┐ submit() ┌───────────┐ assign_reviewer() ┌──────────────┐
│ draft │ ─────────────► │ submitted │ ────────────────────► │ under_review │
└─────────┘ └───────────┘ └──────────────┘
│ │ │
│ withdraw() │ withdraw() │
▼ ▼ │
┌───────────┐ ┌───────────┐ │
│ withdrawn │ │ withdrawn │ │
└───────────┘ └───────────┘ │
┌─────────────┴─────────────┐
│ │
approve() reject()
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ approved │ │ rejected │
└──────────┘ └──────────┘
│
enact()
│
▼
┌─────────┐
│ enacted │
└─────────┘
Protocol Fields:
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
{
"proposalId": "ifl:hash:<sha256>",
"type": "semantic_change",
"scope": {
"boundedContext": "string",
"affectedConcepts": ["ConceptId"],
"breakingChange": "boolean"
},
"author": {
"principalId": "string",
"role": "R-DEV | R-DS"
},
"reviewers": [
{
"principalId": "string",
"role": "R-DS | R-AG",
"assignedAt": "ISO8601"
}
],
"state": "draft | submitted | under_review | approved | rejected | enacted | withdrawn",
"stateHistory": [
{
"from": "string",
"to": "string",
"actor": "principalId",
"timestamp": "ISO8601",
"rationale": "string"
}
],
"createdAt": "ISO8601",
"updatedAt": "ISO8601"
}
Invariants:
I-SCP-01: breakingChange == true ⇒ REQUIRES R-AG approvalI-SCP-02: Transition to enacted REQUIRES binding to IFL ledger entryI-SCP-03: stateHistory is append-only; no state can be removedI-SCP-04: Four-Eyes Rule: author.principalId != approver.principalId for production environmentI-SCP-05: Approval expires after TTL; re-approval required if expired before enactmentApproval Expiry & Version Binding:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"approval": {
"approvalId": "uuid",
"proposalId": "ifl:hash:<sha256>",
"proposalVersion": "v2.3.1",
"approver": {
"principalId": "string",
"role": "R-DS | R-AG",
"capabilityToken": "jwt-token"
},
"approvedAt": "ISO8601",
"expiresAt": "ISO8601",
"boundTo": {
"commitSha": "string",
"artifactHash": "sha256:...",
"boundAt": "ISO8601"
},
"signature": {
"algorithm": "Ed25519",
"publicKey": "hex",
"value": "hex"
}
}
}
Expiry Rules: | Proposal Type | Environment | Default TTL | Maximum TTL | |—————|————-|————-|————-| | Non-breaking change | staging | 7 days | 14 days | | Non-breaking change | production | 24 hours | 7 days | | Breaking change | staging | 24 hours | 7 days | | Breaking change | production | 4 hours | 24 hours |
Binding Invariants:
artifactHash changes after bindingcommitSha does not match deployment targetStates: open → accepted |
rejected → mitigating → resolved |
expired |
Acceptance Constraints:
| Field | Requirement |
|——-|————-|
| acceptor | MUST be R-DS or R-AG (not R-DEV, not R-AA) |
| expiration | MANDATORY. Maximum 90 days for High severity, 30 days for Critical |
| mitigationPlan | MANDATORY for High and Critical severity |
| reviewCadence | MANDATORY. Defines revalidation schedule (default: 14 days) |
Protocol Fields:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"debtId": "ifl:hash:<sha256>",
"status": "open | accepted | rejected | mitigating | resolved | expired",
"severity": "low | medium | high | critical",
"acceptedBy": {
"principalId": "string",
"role": "R-DS | R-AG",
"timestamp": "ISO8601"
},
"expiration": "ISO8601",
"reviewCadence": "P14D", // ISO8601 Duration
"nextReviewDate": "ISO8601",
"mitigationPlan": {
"summary": "string",
"milestones": [{ "description": "string", "dueDate": "ISO8601" }]
},
"acceptanceRationale": "string"
}
Invariants:
I-SDA-01: severity == "critical" ⇒ acceptance REQUIRES R-SO consultationI-SDA-02: If currentDate > expiration ⇒ Status auto-transitions to expiredI-SDA-03: expired debt BLOCKS CI pipeline until renewed or resolvedPurpose: Emergency override of standard governance controls when business continuity requires immediate action.
States: requested → approved |
denied → active → expired |
revoked |
Protocol Fields:
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
{
"breakGlassId": "uuid",
"requestedBy": {
"principalId": "string",
"role": "R-DS | R-RM | R-DEV",
"timestamp": "ISO8601"
},
"approvedBy": {
"principalId": "string",
"role": "R-SO", // MUST be Security Officer
"timestamp": "ISO8601"
},
"scope": {
"bypassedControls": ["control_id"],
"affectedArtifacts": ["artifact_id"],
"boundedContexts": ["context_id"]
},
"reason": {
"category": "production_outage | security_incident | regulatory_deadline | data_corruption",
"description": "string",
"ticketReference": "string" // Link to incident ticket
},
"constraints": {
"maxDuration": "PT4H", // ISO8601 Duration, Maximum 4 hours default
"expiresAt": "ISO8601",
"renewalLimit": 2,
"renewalCount": 0
},
"status": "requested | approved | denied | active | expired | revoked",
"auditTrail": [
{
"action": "string",
"actor": "principalId",
"timestamp": "ISO8601",
"details": "object"
}
]
}
Invariants:
I-BG-01: approvedBy.role MUST be R-SO (non-delegable)I-BG-02: maxDuration ≤ 4 hours for initial approvalI-BG-03: Renewal requires new R-SO approval; max 2 renewalsI-BG-04: All actions during active window MUST be logged to auditTrailI-BG-05: expired |
revoked ⇒ immediate restoration of bypassed controls |
Audit Event (emitted on every state transition):
1
2
3
4
5
6
7
8
{
"type": "governance.authority.break-glass-state-changed",
"breakGlassId": "uuid",
"from": "requested",
"to": "approved",
"actor": "principalId",
"timestamp": "ISO8601"
}
Purpose: Controls the transition from pre-mint (ifl:hash) to attested (ifl:token) identity.
States: pending_validation → validated → pending_signature → signed → committed
Protocol Fields:
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
{
"attestRequestId": "uuid",
"sourceHash": "ifl:hash:<sha256>",
"targetArtifact": {
"type": "Concept | Policy | Entity | Resource | Flow",
"namespace": "string",
"name": "string"
},
"validationResult": {
"schemaValid": "boolean",
"semanticValid": "boolean",
"driftCheckPassed": "boolean",
"validatedAt": "ISO8601",
"validator": "R-AA | R-DEV"
},
"signature": {
"algorithm": "Ed25519",
"publicKey": "hex",
"signatureValue": "hex",
"signedAt": "ISO8601",
"signedBy": {
"principalId": "string",
"role": "R-RM | R-LC" // Release Manager or Ledger Custodian
}
},
"bindingRecord": {
"targetToken": "ifl:token:<chain>:<contract>:<tokenId>",
"committedAt": "ISO8601",
"ledgerEntryId": "string"
},
"status": "pending_validation | validated | pending_signature | signed | committed | failed"
}
Invariants:
I-MA-01: signature.signedBy.role MUST be R-RM or R-LCI-MA-02: validationResult.* all true before transition to pending_signatureI-MA-03: committed state REQUIRES successful IFL consensusI-MA-04: sourceHash MUST match SHA-256 of canonical normalized artifact (see SDS-018)This chapter defines the complete Semantic Change Proposal Workflow that implements the forced-choice governance mechanism required by PRD-018. The workflow provides the technical enforcement of the authority roles defined in §1-4.
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://schemas.newsea.io/governance/semantic-change-proposal/v1.schema.json",
"title": "SemanticChangeProposal",
"description": "A structured proposal for changing canonical semantics with forced-choice options.",
"type": "object",
"required": [
"proposalId",
"version",
"status",
"changeType",
"author",
"affectedArtifacts",
"options",
"createdAt"
],
"properties": {
"proposalId": {
"type": "string",
"pattern": "^ifl:hash:[a-f0-9]{64}$",
"description": "Deterministic hash of proposal canonical form"
},
"version": {
"type": "string",
"pattern": "^v\\d+\\.\\d+\\.\\d+$",
"description": "Semantic version of the proposal"
},
"status": {
"type": "string",
"enum": [
"draft",
"options_generated",
"under_review",
"approved",
"rejected",
"enacted",
"withdrawn"
],
"description": "Current lifecycle state"
},
"changeType": {
"type": "string",
"enum": [
"concept_definition",
"policy_update",
"normalizer_rule",
"identity_schema",
"resource_definition",
"flow_definition"
],
"description": "Category of semantic change"
},
"breakingChange": {
"type": "boolean",
"description": "True if change breaks backward compatibility"
},
"author": {
"$ref": "#/definitions/Principal"
},
"affectedArtifacts": {
"type": "array",
"items": {
"$ref": "#/definitions/ArtifactReference"
},
"minItems": 1
},
"options": {
"type": "array",
"items": {
"$ref": "#/definitions/ChangeOption"
},
"minItems": 1,
"maxItems": 3,
"description": "Generated options with trade-offs"
},
"selectedOption": {
"$ref": "#/definitions/OptionSelection",
"description": "The option chosen by the approver (null until approved)"
},
"reviewers": {
"type": "array",
"items": {
"$ref": "#/definitions/ReviewerAssignment"
}
},
"stateHistory": {
"type": "array",
"items": {
"$ref": "#/definitions/StateTransition"
}
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"updatedAt": {
"type": "string",
"format": "date-time"
}
},
"definitions": {
"Principal": {
"type": "object",
"required": ["principalId", "role"],
"properties": {
"principalId": { "type": "string" },
"role": {
"type": "string",
"enum": ["R-DS", "R-AG", "R-LC", "R-SO", "R-RM", "R-DEV", "R-AA"]
}
}
},
"ArtifactReference": {
"type": "object",
"required": ["artifactId", "artifactType", "namespace"],
"properties": {
"artifactId": { "type": "string" },
"artifactType": {
"type": "string",
"enum": [
"Concept",
"Policy",
"Entity",
"Resource",
"Flow",
"NormalizerRule",
"IdentitySchema"
]
},
"namespace": { "type": "string" },
"currentVersion": { "type": "string" }
}
},
"ChangeOption": {
"type": "object",
"required": [
"optionId",
"category",
"summary",
"impactAnalysis",
"riskRating",
"requiredApproverRoles",
"invariantsPreserved",
"invariantsViolated"
],
"properties": {
"optionId": {
"type": "string",
"pattern": "^opt-(conservative|balanced|aggressive)$"
},
"category": {
"type": "string",
"enum": ["conservative", "balanced", "aggressive"]
},
"summary": {
"type": "string",
"minLength": 50,
"description": "Human-readable summary of this option"
},
"detailedDescription": {
"type": "string",
"description": "Extended explanation of the option"
},
"impactAnalysis": {
"$ref": "#/definitions/ImpactAnalysis"
},
"riskRating": {
"type": "string",
"enum": ["low", "medium", "high", "critical"]
},
"requiredApproverRoles": {
"type": "array",
"items": { "type": "string", "enum": ["R-DS", "R-AG", "R-SO"] },
"minItems": 1
},
"invariantsPreserved": {
"type": "array",
"items": { "type": "string" },
"description": "List of invariant IDs that remain intact"
},
"invariantsViolated": {
"type": "array",
"items": { "type": "string" },
"description": "List of invariant IDs that would be violated"
},
"tradeOffExplanation": {
"type": "string",
"description": "Explicit explanation of what is gained and lost"
},
"recommendedFor": {
"type": "string",
"description": "Scenario where this option is most appropriate"
}
}
},
"ImpactAnalysis": {
"type": "object",
"required": [
"debtCreated",
"debtRetired",
"projectionsAffected",
"compatibility",
"blastRadius"
],
"properties": {
"debtCreated": {
"type": "array",
"items": {
"type": "object",
"properties": {
"debtType": { "type": "string" },
"severity": {
"type": "string",
"enum": ["low", "medium", "high", "critical"]
},
"description": { "type": "string" }
}
}
},
"debtRetired": {
"type": "array",
"items": { "type": "string" },
"description": "IDs of SemanticDebtItems resolved by this option"
},
"projectionsAffected": {
"type": "array",
"items": {
"type": "object",
"properties": {
"projectionTarget": { "type": "string" },
"regenerationRequired": { "type": "boolean" },
"estimatedEffort": { "type": "string" }
}
}
},
"compatibility": {
"type": "object",
"properties": {
"backward": { "type": "boolean" },
"forward": { "type": "boolean" },
"migrationPath": { "type": "string" }
}
},
"blastRadius": {
"type": "object",
"properties": {
"conceptsAffected": { "type": "integer", "minimum": 0 },
"policiesAffected": { "type": "integer", "minimum": 0 },
"consumersAffected": { "type": "integer", "minimum": 0 },
"servicesAffected": {
"type": "array",
"items": { "type": "string" }
}
}
}
}
},
"OptionSelection": {
"type": "object",
"required": [
"selectedOptionId",
"selectedBy",
"justification",
"selectedAt",
"signature"
],
"properties": {
"selectedOptionId": {
"type": "string",
"pattern": "^opt-(conservative|balanced|aggressive)$"
},
"selectedBy": {
"$ref": "#/definitions/Principal"
},
"justification": {
"type": "string",
"minLength": 20,
"description": "Required explanation for why this option was chosen"
},
"alternativesConsidered": {
"type": "string",
"description": "Optional: why other options were rejected"
},
"selectedAt": {
"type": "string",
"format": "date-time"
},
"expiresAt": {
"type": "string",
"format": "date-time"
},
"signature": {
"$ref": "#/definitions/Signature"
}
}
},
"Signature": {
"type": "object",
"required": ["algorithm", "publicKey", "value"],
"properties": {
"algorithm": { "type": "string", "const": "Ed25519" },
"publicKey": { "type": "string" },
"value": { "type": "string" }
}
},
"ReviewerAssignment": {
"type": "object",
"required": ["principalId", "role", "assignedAt"],
"properties": {
"principalId": { "type": "string" },
"role": { "type": "string" },
"assignedAt": { "type": "string", "format": "date-time" },
"reviewedAt": { "type": "string", "format": "date-time" }
}
},
"StateTransition": {
"type": "object",
"required": ["from", "to", "actor", "timestamp"],
"properties": {
"from": { "type": "string" },
"to": { "type": "string" },
"actor": { "type": "string" },
"timestamp": { "type": "string", "format": "date-time" },
"reason": { "type": "string" }
}
}
}
}
| Change Type | Min Options | Max Options | Policy |
|---|---|---|---|
| Concept Definition (new) | 1 | 2 | Conservative (minimal scope) + Balanced (full scope) |
| Concept Definition (modify) | 2 | 3 | Conservative + Balanced + Aggressive (if breaking change possible) |
| Policy Update | 2 | 3 | Always generate trade-offs for enforcement level |
| Normalizer Rule | 1 | 3 | Single option if purely additive; 3 if affects existing hashes |
| Identity Schema | 2 | 3 | Always 2+ due to migration implications |
| Resource/Flow Definition | 1 | 3 | Based on coupling analysis |
Conservative Option (opt-conservative):
Balanced Option (opt-balanced):
Aggressive Option (opt-aggressive):
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
def generate_options(change_request: ChangeRequest) -> List[ChangeOption]:
"""Generates 1-3 options based on change characteristics."""
options = []
# Step 1: Always generate Conservative option
conservative = generate_conservative_option(change_request)
options.append(conservative)
# Step 2: Generate Balanced option if change has meaningful scope
if change_has_meaningful_scope(change_request):
balanced = generate_balanced_option(change_request)
options.append(balanced)
# Step 3: Generate Aggressive option if breaking change is feasible
if breaking_change_feasible(change_request):
aggressive = generate_aggressive_option(change_request)
options.append(aggressive)
# Step 4: Ensure we have at least 1, at most 3
assert 1 <= len(options) <= 3
# Step 5: Compute impact analysis for each
for option in options:
option.impactAnalysis = compute_impact_analysis(change_request, option)
option.riskRating = compute_risk_rating(option)
option.requiredApproverRoles = determine_required_approvers(option)
return options
| Event Type | Trigger | Required Fields |
|---|---|---|
sea.proposal.created |
Proposal enters draft |
proposalId, author, changeType, affectedArtifacts, timestamp |
sea.proposal.options_generated |
Options generated | proposalId, optionCount, optionSummaries, timestamp |
sea.proposal.reviewer_assigned |
Reviewer assigned | proposalId, reviewer, requiredRole, timestamp |
sea.proposal.option_selected |
Approver selects option | proposalId, selectedOptionId, approver, justification, signature, timestamp |
sea.proposal.approved |
Proposal approved | proposalId, selectedOptionId, approver, expiresAt, signature, timestamp |
sea.proposal.rejected |
Proposal rejected | proposalId, rejector, reason, timestamp |
sea.proposal.enacted |
Change applied to IFL | proposalId, ledgerEntryId, enactor, timestamp |
sea.proposal.withdrawn |
Proposal withdrawn | proposalId, withdrawer, reason, timestamp |
Option Selection Event Example:
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
{
"specversion": "1.0",
"id": "evt-uuid-12345",
"type": "sea.proposal.option_selected",
"source": "//governance/proposal-service",
"time": "2025-12-22T10:30:00Z",
"datacontenttype": "application/json",
"data": {
"proposalId": "ifl:hash:abc123...",
"proposalVersion": "v1.0.0",
"selectedOptionId": "opt-balanced",
"optionCategory": "balanced",
"approver": {
"principalId": "principal:human:github:architect01",
"role": "R-AG"
},
"justification": "Balanced option provides the capability we need while maintaining acceptable blast radius.",
"signature": {
"algorithm": "Ed25519",
"publicKey": "0x1234abcd...",
"value": "0xsig..."
},
"expiresAt": "2025-12-23T10:30:00Z"
}
}
| Guardrail ID | Rule | Enforcement |
|---|---|---|
GR-01 |
System cannot auto-select an option | Option selection requires human click + signature |
GR-02 |
Justification minimum length | justification.length >= 20 characters |
GR-03 |
Justification cannot be boilerplate | ML classifier rejects “looks good”, “approved”, “LGTM” |
GR-04 |
Four-eyes rule | proposer != approver for production |
GR-05 |
Option comparison required | UI must display all options before selection enabled |
GR-06 |
Risk acknowledgment for High/Critical | Explicit checkbox: “I understand this option violates invariants: [list]” |
GR-07 |
Time-bound review | Proposals expire after 7 days without review |
GR-08 |
No retroactive approval | Options cannot be selected after proposal enacted |
Boilerplate Detection:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
BOILERPLATE_PATTERNS = [
r"^(lgtm|looks good|approved|ok|fine|ship it)\.?$",
r"^(no comments?|no issues?)\.?$",
r"^(accept(ed)?|confirm(ed)?)\.?$",
r"^(rubber stamp(ed)?|auto[- ]?approv(e|ed))\.?$"
]
def validate_justification(justification: str) -> ValidationResult:
"""Validates that justification is meaningful, not boilerplate."""
normalized = justification.strip().lower()
if len(justification) < 20:
return ValidationResult.REJECT(
"Justification too short. Please explain your decision rationale (min 20 chars)."
)
for pattern in BOILERPLATE_PATTERNS:
if re.match(pattern, normalized, re.IGNORECASE):
return ValidationResult.REJECT(
"Justification appears to be boilerplate. Please provide specific reasoning."
)
return ValidationResult.PASS
Risk Acknowledgment (for High/Critical options):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"riskAcknowledgment": {
"acknowledgedAt": "2025-12-22T10:35:00Z",
"acknowledgedBy": "principal:human:github:architect01",
"invariantsAcknowledged": [
"I-COMPAT-01: Backward compatibility with v1 consumers",
"I-SCHEMA-03: Canonical hash stability"
],
"mitigationPlan": "Consumer migration scheduled for Q1; hash migration script prepared",
"signature": {
"algorithm": "Ed25519",
"publicKey": "0x1234abcd...",
"value": "0xsig..."
}
}
}
| Command | Description |
|---|---|
sea-proposal create --change-type <type> --artifact <path> --out <file> |
Create new proposal from change manifest |
sea-proposal generate-options --proposal <file> --out <file> |
Generate options for a proposal |
sea-proposal review --proposal <file> --approver <principal> |
Open interactive review UI |
sea-proposal approve --proposal <file> --option <id> --justification <text> --signing-key <path> |
Approve by selecting an option |
sea-proposal query --status <status> --since <date> --format json |
Query proposal history |
The following decisions MUST be made by humans. Automated Agents (R-AA) may recommend but CANNOT approve or execute these actions.
| Decision Category | Specific Actions | Required Role | Automation Limit |
|---|---|---|---|
| Semantic Meaning | Define business term meaning | R-DS | Recommend only |
| Semantic Meaning | Resolve conflicting interpretations | R-DS + R-AG | Recommend only |
| Regulatory Interpretation | Accept compliance exception | R-SO | Cannot participate |
| Ethical Trade-offs | Approve conflicting values | R-DS + R-SO | Cannot participate |
| Key Custody | Approve key rotation | R-SO | Cannot participate |
| Key Custody | Revoke compromised key | R-SO + R-LC | Cannot participate |
| Break-glass | Approve emergency override | R-SO | Cannot participate |
| Production Promotion | Final go/no-go for release | R-RM | Recommend only |
| Architecture Breaking Change | Approve cross-boundary change | R-AG | Recommend only |
Enforcement Policy:
1
2
3
4
5
6
7
8
9
10
Policy NonDelegableDecisionEnforcement:
applies_to: [all_transactions]
rule BlockAutomatedApproval:
when:
- transaction.type IN NonDelegableDecisionTypes
- transaction.approver.role == "R-AA"
then:
- REJECT with "Non-delegable decision: Human approval required"
- EMIT AuditEvent(type: "governance.authority.automated-approval-blocked")
This section defines the Human-in-the-Loop (HITL) escalation protocol for AI agents (R-AA) performing privileged actions.
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
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "AgentEscalationRequest",
"description": "Request for human approval of agent-initiated privileged action.",
"type": "object",
"required": [
"escalationId",
"agentId",
"action",
"requiredApproverRole",
"context",
"createdAt"
],
"properties": {
"escalationId": {
"type": "string",
"format": "uuid",
"description": "Unique identifier for this escalation request"
},
"agentId": {
"type": "string",
"pattern": "^principal:agent:[a-z-]+:[a-f0-9-]+$",
"description": "Agent principal requesting escalation"
},
"sponsorId": {
"type": "string",
"description": "Human principal who authorized the agent's task"
},
"action": {
"type": "object",
"required": ["type", "target", "description"],
"properties": {
"type": {
"type": "string",
"enum": [
"file_write",
"file_delete",
"command_execute",
"external_request",
"state_mutation",
"deployment",
"secret_access"
]
},
"target": {
"type": "string",
"description": "Resource being affected"
},
"description": {
"type": "string",
"description": "Human-readable description of the action"
},
"risk": {
"type": "string",
"enum": ["low", "medium", "high", "critical"]
},
"reversible": {
"type": "boolean"
}
}
},
"requiredApproverRole": {
"type": "string",
"enum": ["R-DS", "R-AG", "R-LC", "R-SO", "R-RM", "R-DEV"],
"description": "Minimum role required to approve this action"
},
"context": {
"type": "object",
"properties": {
"taskId": { "type": "string" },
"conversationId": { "type": "string" },
"boundedContext": { "type": "string" },
"relatedArtifacts": {
"type": "array",
"items": { "type": "string" }
},
"rationale": { "type": "string" }
}
},
"status": {
"type": "string",
"enum": ["pending", "approved", "rejected", "expired", "cancelled"]
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"expiresAt": {
"type": "string",
"format": "date-time"
},
"decision": {
"type": "object",
"properties": {
"approver": { "type": "string" },
"approverRole": { "type": "string" },
"decidedAt": { "type": "string", "format": "date-time" },
"rationale": { "type": "string" }
}
}
}
}
| Action Type | Trigger Condition | Required Approver |
|---|---|---|
file_write |
Writing to protected paths (e.g., libs/*/src/lib/**) |
R-DEV |
file_delete |
Any file deletion | R-DEV |
command_execute |
Commands with side effects (not marked SafeToAutoRun) |
R-DEV |
external_request |
Non-whitelisted external API calls | R-SO |
state_mutation |
Database or state store modifications | R-DS |
deployment |
Any deployment action | R-RM |
secret_access |
Accessing secrets or credentials | R-SO |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──────────────┐ create_escalation() ┌─────────────┐
│ Agent (R-AA) │ ──────────────────────────► │ pending │
└──────────────┘ └─────────────┘
│
┌─────────────┼─────────────┐
│ │ │
approve() reject() timeout()
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ approved │ │ rejected │ │ expired │
└──────────┘ └──────────┘ └──────────┘
│
execute_action()
│
▼
┌───────────────┐
│ action_logged │
└───────────────┘
| INV-ID | Invariant | Enforcement |
|---|---|---|
| INV-HITL-01 | Agents CANNOT self-approve escalations | approver.principalId != agentId |
| INV-HITL-02 | Escalation sponsor SHOULD NOT be the approver | Warning if sponsorId == approver |
| INV-HITL-03 | Expired escalations CANNOT be approved | Status check before approval |
| INV-HITL-04 | All approved actions MUST be logged | Audit event emission required |
| INV-HITL-05 | High-risk actions require explicit acknowledgment | UI checkbox for risk > medium |
| Event Type | Trigger | Required Fields |
|---|---|---|
governance.agent.escalation-created |
Escalation request created | escalationId, agentId, action, timestamp |
governance.agent.escalation-approved |
Human approves escalation | escalationId, approver, role, rationale, timestamp |
governance.agent.escalation-rejected |
Human rejects escalation | escalationId, rejector, role, reason, timestamp |
governance.agent.escalation-expired |
Escalation times out | escalationId, expiresAt, timestamp |
governance.agent.action-executed |
Approved action executed | escalationId, actionType, target, result, timestamp |
The HITL escalation protocol integrates with the MCP tool surface defined in SDS-052:
get_hover, get_diagnostics): No escalation required| Risk Level | Default Timeout | Maximum Extension |
|---|---|---|
| low | 30 minutes | 2 hours |
| medium | 15 minutes | 1 hour |
| high | 5 minutes | 30 minutes |
| critical | 2 minutes | 10 minutes |
Every privileged action MUST emit an audit event to the IFL. The following event types and required fields are normative.
| Event Type | Trigger | Required Fields |
|---|---|---|
governance.authority.proposal-submitted |
SemanticChangeProposal enters submitted |
proposalId, author, type, scope, timestamp |
governance.authority.proposal-approved |
Proposal transitions to approved |
proposalId, approver, role, rationale, timestamp |
governance.authority.proposal-rejected |
Proposal transitions to rejected |
proposalId, rejector, role, reason, timestamp |
governance.authority.proposal-enacted |
Proposal transitions to enacted |
proposalId, enactor, ledgerEntryId, timestamp |
governance.authority.debt-accepted |
SemanticDebt transitions to accepted |
debtId, acceptor, role, severity, expiration, rationale, timestamp |
governance.authority.debt-expired |
SemanticDebt auto-expires | debtId, originalAcceptor, expirationDate, timestamp |
governance.authority.break-glass-requested |
Break-glass request created | breakGlassId, requester, reason, scope, timestamp |
governance.authority.break-glass-approved |
Break-glass approved | breakGlassId, approver, expiresAt, timestamp |
governance.authority.break-glass-expired |
Break-glass window closed | breakGlassId, duration, actionsPerformed, timestamp |
governance.authority.break-glass-revoked |
Break-glass manually revoked | breakGlassId, revoker, reason, timestamp |
governance.authority.mint-requested |
Mint request created | mintRequestId, sourceHash, artifactType, requester, timestamp |
governance.authority.mint-signed |
Mint signature applied | mintRequestId, sourceHash, signer, role, timestamp |
governance.authority.mint-committed |
Binding record committed to IFL | mintRequestId, sourceHash, targetToken, ledgerEntryId, timestamp |
governance.authority.key-rotated |
Signing key rotated | keyId, oldKeyHash, newKeyHash, operator, approver, reason, timestamp |
governance.authority.key-revoked |
Signing key revoked (emergency) | keyId, keyHash, revoker, reason, timestamp |
governance.authority.node-added |
IFL node added | nodeId, operator, approver, timestamp |
governance.authority.node-removed |
IFL node removed | nodeId, operator, approver, reason, timestamp |
governance.authority.sod-violation-blocked |
SoD rule blocked a transaction | transactionId, violatedRule, actor, timestamp |
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
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "GovernanceAuthorityAuditEvent",
"type": "object",
"properties": {
"specversion": { "const": "1.0" },
"id": { "type": "string", "format": "uuid" },
"type": {
"type": "string",
"pattern": "^governance\\.authority\\.[a-z-]+$"
},
"source": { "type": "string", "const": "//core/authority-service" },
"time": { "type": "string", "format": "date-time" },
"data": {
"type": "object",
"required": ["actor", "action"],
"properties": {
"actor": {
"type": "object",
"required": ["principalId", "role"],
"properties": {
"principalId": { "type": "string" },
"role": {
"type": "string",
"enum": ["R-DS", "R-AG", "R-LC", "R-SO", "R-RM", "R-DEV", "R-AA"]
}
}
},
"action": { "type": "string" }
},
"additionalProperties": true
}
},
"required": ["specversion", "id", "type", "source", "time", "data"]
}
| Event Category | Retention Period | Storage Tier |
|---|---|---|
| Break-glass events | 7 years | Cold (compliance) |
| Key management events | 7 years | Cold (compliance) |
| Proposal lifecycle | 3 years | Warm |
| Debt acceptance | 3 years | Warm |
| Mint/attest | Indefinite | Hot (provenance) |
| SoD violations | 7 years | Cold (compliance) |
Trigger: Suspicion or confirmation that a signing key has been compromised.
Playbook:
Immediate (0-15 min):
R-SO + R-LC jointly revoke the compromised key via KeyRevoked transactiongovernance.authority.key-revoked eventR-RM and R-AG principalsShort-term (15 min - 4 hours):
Recovery (4-24 hours):
R-SO approves new key introductionR-LC executes key rotation via KeyRotated transactionPost-Incident (24-72 hours):
governance.authority.incident-closed eventTrigger: Detected divergence between IFL nodes or hash mismatch in Merkle proof.
Playbook:
Immediate (0-15 min):
R-LC halts write transactions to affected partitiongovernance.authority.ledger-inconsistency-detected eventDiagnosis (15 min - 2 hours):
Resolution:
R-SO approval to restoreRestoration:
R-LC + R-SO jointly approve return to normal operationsgovernance.authority.ledger-restored eventTrigger: Detection of intentional policy violation or unauthorized privileged action.
Playbook:
Immediate (0-5 min):
governance.authority.actor-quarantined eventContainment (5-30 min):
R-SO, R-AG, and affected R-DS principalsRemediation:
SemanticDebtItem for affected conceptsClosure:
R-SO closes incident with formal reportTrigger: Critical defect in production requiring immediate version rollback.
Constraints:
SemanticChangeProposal with type: "rollback"R-RM + R-AG (two-party, but can be concurrent)Playbook:
Initiation:
R-RM creates rollback proposal referencing target versionR-AG validates structural compatibilityExecution:
Post-Rollback:
SemanticDebtItem created for the defectPrincipals are identified by:
principal:human:<identity_provider>:<user_id>principal:service:<service_name>:<instance_id>principal:agent:<agent_type>:<agent_id>Role bindings are stored in the IFL as RoleBinding entities:
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"bindingId": "uuid",
"principalId": "principal:human:github:user123",
"role": "R-DS",
"scope": {
"type": "bounded_context",
"value": "com.example.billing"
},
"grantedBy": "principal:human:github:admin456",
"grantedAt": "ISO8601",
"expiresAt": "ISO8601 | null",
"revoked": false
}
| System | Integration |
|---|---|
| sea-gate (CI) | Enforces SoD rules, checks role permissions before proposal approval |
| IFL Service (SDS-050) | Stores role bindings, emits audit events, enforces transaction signatures |
| Identity Service (SDS-050) | Maps principals to public keys for signature verification |
| Semantic Debt Ledger (SDS-016) | Applies acceptance constraints from this spec |
| Delivery Pipeline (SDS-021) | Enforces promotion authority via R-RM role check |
Note: SDS-031 (Semantic Change Workflow) has been consolidated into §5 of this specification.
| Version | Date | Author | Change |
|---|---|---|---|
| 0.1.0 | 2025-12-22 | SEA-Forge™ | Initial draft |
| 0.2.0 | 2025-12-22 | SEA-Forge™ | Added: 4-eyes rule invariants (I-SCP-04, I-SCP-05), approval expiry & version binding schema, capability token integration |
| 0.3.0 | 2025-12-31 | SEA-Forge™ | Consolidated SDS-031 (Semantic Change Workflow) into §5 Decision Execution; supersedes SDS-031 |
| 0.4.0 | 2026-01-02 | SEA-Forge™ | Added §7 Agent HITL Escalation Protocol; fixed duplicate §6 numbering; renumbered sections 7-11 to 8-12 |