SDS-020: CI Semantic Gates & Debt Selection

Type

Software Design Specification - Governance / CI

Status

Draft

Purpose

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.


1. Gate Architecture

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.

1.1. Gate Stages

| 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 |


2. Validation & Enforcement Rules

This matrix defines how the system reacts to failures based on the existence of a matching SemanticDebtItem in the Ledger.

2.1. Enforcement Matrix

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

2.2. Mitigation Rules

2.3. Proposal ID Traceability

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.


3. Relevant Debt Item Selection Algorithm

This deterministic algorithm identifies which Semantic Debt Items apply to the current CI context.

Match Logic

A Debt Item is “relevant” if:

  1. Scope Match: affected.conceptIds OR affected.policyNames intersect with the artifacts modified in the PR/Commit.
  2. Signal Match: detectedBy.signal matches the specific error code raised by the CI tool (e.g., policy_contradiction:pol-123).

Algorithm Pseudocode

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)

4. CLI Contracts (sea-gate)

4.1. sea-gate select

Identifies applicable debt items for a given context.

Usage:

1
sea-gate select --context ./dist --report-out selector-report.json

Artifact: SelectorReport (JSON)

1
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"]
    }
  ]
}

4.2. sea-gate evaluate

Decides 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)

1
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."
    }
  ]
}

5. Authority Enforcement

This section defines the CI-level enforcement hooks for privileged actions. All governance decisions require cryptographically verified approvals before the pipeline can proceed.

5.1. Required Authority Events

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

5.2. Event Schema (Authority Events)

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."
  }
}

5.3. CI Verification Algorithm

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

5.4. Capability Token Verification

Role claims are verified using signed capability tokens (JWTs with Ed25519 signatures).

Token Structure

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": "..."
}

Verification Steps

  1. Signature Check: Verify JWT signature against known authority signing keys
  2. Expiry Check: exp claim must be in the future
  3. Issuer Check: iss must be the governance authority service
  4. Scope Check: sea:scope must cover the artifact being approved
  5. Capability Check: sea:capabilities must include the action being performed
  6. Constraint Check: sea:constraints must permit the current environment/severity

Token Issuance

Capability tokens are issued by the Authority Service upon:

  1. Successful authentication (SSO/OAuth)
  2. Role binding lookup from IFL
  3. Scope intersection with current session context

Tokens are short-lived (default: 1 hour) and must be refreshed for long-running sessions.

5.5. Four-Eyes Rule (SoD) Enforcement

Invariant: No single actor can both propose AND approve the same semantic change for production.

CI Gate Check

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"

Enforcement Logic

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

5.6. Approval Expiry & Version Binding

All approvals have mandatory expiry and are bound to a specific proposal version.

Expiry Rules

| 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 |

Version Binding Schema

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:

5.7. CLI Command: sea-gate verify-authority

Usage:

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"
    }
  ]
}