Software Design Specification - Governance / CI
Draft
Defines the CI/CD Gating Logic and Semantic Debt Selection Algorithm for SEA-Forge™. This system ensures that no code or artifact is promoted unless it is semantically sound or has explicit, governed exemptions (Semantic Debt).
It connects the Semantic Debt Ledger (SDS-016) to the Delivery Pipeline (ADR-012) via the sea-gate CLI.
The CI pipeline enforces quality through 5 sequential gates. Failure at any gate halts the pipeline unless a specific Semantic Debt Item exists to waive the failure.
| Stage | Description | Tools |
|—|—|—|
| 1. Parse & Schema | structural validity, JSON schema compliance | sea-gate validate-schema |
| 2. Identity & Naming | Canonical ID (SDS-050), naming conventions | sea-gate validate-identity |
| 3. Policy Semantics | SEA™ DSL / SBVR logic consistency, contradictions | sea-gate validate-policy |
| 4. Integrity & Drift | Hash mismatches, unauthorized manual edits | sea-gate validate-drift |
| 5. Compliance | CALM architecture rules, regulatory controls | calm validate |
This matrix defines how the system reacts to failures based on the existence of a matching SemanticDebtItem in the Ledger.
| Failure Severity | No Debt Item | Item Status: open |
Item Status: accepted |
Item Status: mitigating |
Item Status: resolved |
|---|---|---|---|---|---|
| Low | WARN | WARN | PASS | PASS | WARN (Regression) |
| Medium | BLOCK | BLOCK | PASS | PASS | BLOCK |
| High | BLOCK | BLOCK | PASS (Requires VP Approval) | PASS | BLOCK |
| Critical | BLOCK | BLOCK | BLOCK (Cannot accept critical) | PASS (Strict Mitigation) | BLOCK |
Critical failures, a specialized emergency_override token is required to bypass, triggering an immediate audit event. See SDS-031: Authority & Ownership Boundaries §4.3 for the complete break-glass protocol, approval requirements (Security Officer R-SO), and constraints.All waivers and overrides MUST reference a governing artifact for audit traceability:
| Waiver Type | Required Reference | Validation |
|---|---|---|
| Accepted Debt | SemanticDebtItem.debtId |
Must be in accepted or mitigating status |
| Break-glass | BreakGlass.breakGlassId |
Must be in active status with valid TTL |
| Semantic Change | SemanticChangeProposal.proposalId |
Must be in approved status with unexpired approval |
The sea-gate evaluate command MUST include these references in the GateReport:
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"results": [
{
"violationId": "err-001",
"status": "WAIVED",
"waivedBy": "debt-uuid-001",
"waiverType": "accepted_debt",
"governingProposal": "ifl:hash:abc123...",
"approvalExpiresAt": "2025-12-23T10:00:00Z",
"message": "Pricing contradiction known and accepted via proposal PRO-2024-1234"
}
]
}
See SDS-031: Semantic Change Workflow for the complete SemanticChangeProposal schema and approval requirements.
This deterministic algorithm identifies which Semantic Debt Items apply to the current CI context.
A Debt Item is “relevant” if:
affected.conceptIds OR affected.policyNames intersect with the artifacts modified in the PR/Commit.detectedBy.signal matches the specific error code raised by the CI tool (e.g., policy_contradiction:pol-123).1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def select_relevant_debt(ledger, current_changes, ci_errors):
relevant_items = []
for error in ci_errors:
matches = ledger.find(
affected_concepts__in=current_changes.concepts,
failure_class=error.class,
signal=error.code
)
# Exact match preferred
exact = [m for m in matches if m.detectedBy.signal == error.code]
if exact:
relevant_items.extend(exact)
else:
# Fallback to broader concept match if signal is specific
relevant_items.extend(matches)
return unique(relevant_items)
sea-gate)sea-gate selectIdentifies applicable debt items for a given context.
Usage:
1
sea-gate select --context ./dist --report-out selector-report.json
Artifact: SelectorReport (JSON)
schemas/reports/governance/gates/v1/gate-selector-report.schema.jsonexamples/ci-gates/selector-report-example.json1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"$schema": "https://schemas.newsea.io/reports/governance/gates/v1/gate-selector-report.schema.json",
"contextId": "commit:sha123",
"timestamp": "2025-12-21T12:00:00Z",
"relevantItems": [
{
"id": "debt-uuid-001",
"status": "accepted",
"severity": "medium",
"failureClass": "policy_contradiction",
"appliesTo": ["policy:pricing_rule_v2"]
}
]
}
sea-gate evaluateDecides whether to Pass or Block the pipeline based on the SelectorReport and the Validation Matrix.
Usage:
1
sea-gate evaluate --selector-report selector-report.json --violations ci-violations.json --out gate-report.json
Artifact: GateReport (JSON)
schemas/reports/governance/gates/v1/gate-report.schema.jsonexamples/ci-gates/gate-report-example.json1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"$schema": "https://schemas.newsea.io/reports/governance/gates/v1/gate-report.schema.json",
"decision": "PASS", // or BLOCK, WARN
"matrixApplied": "standard_v1",
"results": [
{
"violationId": "err-001",
"status": "WAIVED",
"waivedBy": "debt-uuid-001",
"message": "Pricing contradiction known and accepted."
},
{
"violationId": "err-002",
"status": "BLOCKING",
"message": "New critical naming conflict with no associated debt item."
}
]
}
This section defines the CI-level enforcement hooks for privileged actions. All governance decisions require cryptographically verified approvals before the pipeline can proceed.
The following events MUST be present in the IFL before the corresponding CI action can proceed. Events are verified via sea-gate verify-authority.
| CI Action | Required Event Type | Verification |
|---|---|---|
| Promote semantic change to prod | sea.authority.proposal.approved |
Signature + Role + SoD |
| Accept semantic debt | sea.authority.debt.accepted |
Signature + Role + Expiry |
| Apply break-glass override | sea.authority.breakGlass.approved |
Signature + R-SO role + TTL |
| Mint identity token | ifl.attestation.signed |
Signature + R-RM/R-LC role |
| Rotate signing key | ifl.key.rotation.approved |
Dual signature (R-SO + R-LC) |
| Deploy to production | sea.authority.release.approved |
Signature + R-RM role |
All authority events follow the CloudEvents envelope with SEA™ extensions:
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
{
"specversion": "1.0",
"id": "evt-uuid-12345",
"type": "sea.authority.proposal.approved",
"source": "//governance/authority-service",
"time": "2025-12-22T10:00:00Z",
"datacontenttype": "application/json",
"seaauthority": {
"proposalId": "ifl:hash:abc123...",
"proposalVersion": "v2.3.1",
"approver": {
"principalId": "principal:human:github:architect01",
"role": "R-AG",
"capabilityToken": "eyJhbGciOiJFZDI1NTE5..."
},
"signature": {
"algorithm": "Ed25519",
"publicKey": "0x1234abcd...",
"value": "0xsig..."
},
"expiresAt": "2025-12-22T14:00:00Z",
"boundTo": {
"proposalId": "ifl:hash:abc123...",
"version": "v2.3.1",
"commitSha": "abc123def456"
}
},
"data": {
"action": "approve",
"rationale": "Architecture review complete. No breaking changes detected."
}
}
The sea-gate verify-authority command performs the following checks:
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
def verify_authority(action_type: str, context: CIContext) -> VerifyResult:
"""
Verifies that required authority events exist and are valid.
Returns BLOCK if any check fails.
"""
# Step 1: Identify required events for this action
required_events = get_required_events(action_type)
for req in required_events:
# Step 2: Query IFL for matching event
event = ifl.query_event(
type=req.event_type,
bound_to=context.proposal_id,
bound_version=context.version
)
if not event:
return VerifyResult.BLOCK(
f"Missing required authority event: {req.event_type}"
)
# Step 3: Verify cryptographic signature
if not verify_signature(event.seaauthority.signature, event):
return VerifyResult.BLOCK(
f"Invalid signature on {req.event_type}"
)
# Step 4: Verify capability token (role claim)
if not verify_capability_token(event.seaauthority.approver.capabilityToken):
return VerifyResult.BLOCK(
f"Invalid or expired capability token for {req.event_type}"
)
# Step 5: Verify role authorization
if event.seaauthority.approver.role not in req.allowed_roles:
return VerifyResult.BLOCK(
f"Role {event.approver.role} not authorized for {req.event_type}"
)
# Step 6: Check expiry
if event.seaauthority.expiresAt < now():
return VerifyResult.BLOCK(
f"Authority event {req.event_type} has expired"
)
# Step 7: Verify version binding (prevents replay)
if event.seaauthority.boundTo.version != context.version:
return VerifyResult.BLOCK(
f"Authority event bound to different version"
)
# Step 8: Enforce 4-eyes rule (SoD)
if req.requires_sod:
if event.seaauthority.approver.principalId == context.proposer_id:
return VerifyResult.BLOCK(
"SoD Violation: Proposer cannot approve their own change (4-eyes rule)"
)
return VerifyResult.PASS
Role claims are verified using signed capability tokens (JWTs with Ed25519 signatures).
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
{
"header": {
"alg": "EdDSA",
"typ": "JWT",
"kid": "ifl:key:authority-signer-001"
},
"payload": {
"sub": "principal:human:github:user123",
"iss": "//governance/authority-service",
"iat": 1703246400,
"exp": 1703250000,
"sea:role": "R-DS",
"sea:scope": {
"type": "bounded_context",
"value": "com.example.billing"
},
"sea:capabilities": [
"proposal.approve",
"debt.accept",
"mint.request"
],
"sea:constraints": {
"environment": ["staging", "production"],
"maxSeverity": "high"
}
},
"signature": "..."
}
exp claim must be in the futureiss must be the governance authority servicesea:scope must cover the artifact being approvedsea:capabilities must include the action being performedsea:constraints must permit the current environment/severityCapability tokens are issued by the Authority Service upon:
Tokens are short-lived (default: 1 hour) and must be refreshed for long-running sessions.
Invariant: No single actor can both propose AND approve the same semantic change for production.
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
# sea-gate.yaml - SoD enforcement configuration
sod_rules:
- id: SOD-01
name: "Four-Eyes Proposal Approval"
applies_to: ["semantic_change", "breaking_change"]
environment: ["production"]
rule: "proposer != approver"
violation_action: "BLOCK"
audit_event: "governance.authority.sod-violation-blocked"
- id: SOD-02
name: "Debt Acceptance Separation"
applies_to: ["debt_acceptance"]
environment: ["production", "staging"]
rule: "requester != acceptor"
violation_action: "BLOCK"
audit_event: "governance.authority.sod-violation-blocked"
- id: SOD-03
name: "Break-glass Separation"
applies_to: ["break_glass"]
environment: ["*"]
rule: "requester != approver AND approver.role == 'R-SO'"
violation_action: "BLOCK"
audit_event: "governance.authority.sod-violation-blocked"
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
def enforce_sod(proposal: Proposal, approval: ApprovalEvent) -> SoDResult:
"""
Enforces separation of duties (4-eyes rule).
"""
# Rule 1: Different principals
if proposal.author.principalId == approval.approver.principalId:
return SoDResult.VIOLATION(
rule="SOD-01",
message=f"Self-approval blocked: {proposal.author.principalId} cannot approve their own proposal",
remediation="Request approval from a different authorized principal"
)
# Rule 2: Approver must have appropriate role
required_roles = get_required_approver_roles(proposal.type, proposal.environment)
if approval.approver.role not in required_roles:
return SoDResult.VIOLATION(
rule="ROLE-CHECK",
message=f"Role {approval.approver.role} cannot approve {proposal.type} in {proposal.environment}",
remediation=f"Require approval from: {required_roles}"
)
# Rule 3: Breaking changes require elevated approval
if proposal.scope.breakingChange and proposal.environment == "production":
if approval.approver.role not in ["R-AG", "R-SO"]:
return SoDResult.VIOLATION(
rule="BREAK-CHANGE",
message="Breaking changes in production require R-AG or R-SO approval",
remediation="Request approval from Architecture Governor or Security Officer"
)
return SoDResult.PASS
All approvals have mandatory expiry and are bound to a specific proposal version.
| Approval Type | Default TTL | Maximum TTL | Renewal Allowed | |—————|————-|————-|—————–| | Semantic Change (non-breaking) | 24 hours | 7 days | Yes (same approver) | | Semantic Change (breaking) | 4 hours | 24 hours | Yes (requires re-review) | | Debt Acceptance | 90 days | 90 days | Yes (requires review) | | Break-glass | 4 hours | 4 hours | Yes (max 2 renewals) | | Production Release | 2 hours | 8 hours | No |
1
2
3
4
5
6
7
8
9
{
"boundTo": {
"proposalId": "ifl:hash:abc123...",
"version": "v2.3.1",
"commitSha": "abc123def456789...",
"artifactHash": "sha256:def789...",
"boundAt": "2025-12-22T10:00:00Z"
}
}
Invariants:
I-AE-01: Approval is invalid if artifactHash changes after bindingI-AE-02: Approval is invalid if version does not match current deployment targetI-AE-03: Expired approvals are automatically rejected; no grace periodI-AE-04: Version binding prevents replay attacks; each version requires fresh approvalsea-gate verify-authorityUsage:
1
2
3
4
5
6
sea-gate verify-authority \
--action semantic_change \
--proposal-id ifl:hash:abc123... \
--version v2.3.1 \
--environment production \
--out authority-report.json
Output Artifact: AuthorityVerificationReport
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
{
"$schema": "https://schemas.newsea.io/reports/governance/gates/v1/authority-report.schema.json",
"timestamp": "2025-12-22T10:15:00Z",
"action": "semantic_change",
"proposalId": "ifl:hash:abc123...",
"version": "v2.3.1",
"environment": "production",
"decision": "PASS",
"checks": [
{
"check": "event_present",
"eventType": "sea.authority.proposal.approved",
"status": "PASS",
"eventId": "evt-uuid-12345"
},
{
"check": "signature_valid",
"status": "PASS",
"algorithm": "Ed25519",
"publicKey": "0x1234abcd..."
},
{
"check": "capability_token_valid",
"status": "PASS",
"role": "R-AG",
"scope": "com.example.billing"
},
{
"check": "sod_four_eyes",
"status": "PASS",
"proposer": "principal:human:github:dev01",
"approver": "principal:human:github:architect01"
},
{
"check": "expiry_valid",
"status": "PASS",
"expiresAt": "2025-12-22T14:00:00Z",
"remainingMinutes": 225
},
{
"check": "version_bound",
"status": "PASS",
"boundVersion": "v2.3.1"
}
]
}