Software Design Specification - Governance Protocol
Draft
Defines the Event Evolution Governance model for SEA-Forge™. This specification transforms event schemas from aspirational documentation into enforceable contracts with strict forward/backward compatibility rules. It establishes the Schema Registry structure, Consumer Contract format, and the Regime Attribution logic for translation loss.
Every event emitted in the system must be governed by a Producer Schema. Every consumer must declare its expectations via a Consumer Contract. The CI/CD pipeline enforces compatibility between Producers and Consumers before deployment.
Events are explicit projections of an Invariant Regime (see REF-012).
regimeId to trace the semantic authority.The source of truth for all event schemas is the SEA™ Monorepo:
docs/schemas/events/<domain>/<event-type>/<version>.json
Example Path:
docs/schemas/events/semantic-core/sea.policy.eval/v1.json
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
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PolicyEvaluationEvent",
"description": "Emitted when a SEA™ DSL policy is evaluated.",
"type": "object",
"properties": {
"specs": {
"regimeId": { "type": "string", "description": "The Invariant Regime governing this evaluation." },
"schemaId": { "type": "string", "const": "sea.policy.eval.v1" }
},
"payload": {
"type": "object",
"properties": {
"policyId": { "type": "string" },
"result": { "type": "string", "enum": ["compliant", "non-compliant", "unknown"] },
"translationLoss": {
"type": "array",
"items": { "type": "string" },
"description": "List of semantic nuances lost in this projection."
}
},
"required": ["policyId", "result"]
}
},
"required": ["specs", "payload"]
}
Consumers must declare what fields they rely on. This allows the system to determine if a Producer change (e.g., removing a field) actually breaks any active consumers.
Located in: apps/<consumer-app>/contracts/<event-type>.contract.yaml
Example Contract:
1
2
3
4
5
6
7
8
9
10
consumer: "governance-dashboard"
event: "sea.policy.eval.v1"
expectations:
- field: "payload.result"
assertion: "must be one of [compliant, non-compliant, unknown]"
- field: "specs.regimeId"
assertion: "present"
# Note: Consumer explicitly does NOT rely on 'translationLoss', so removing it is safe for this consumer.
compatibility:
mode: "forward" # Consumer must handle new fields gracefully
Producer schema changes are semantic changes that affect API contracts and require formal governance.
All producer schema changes MUST follow the SemanticChangeProposal workflow per SDS-031: Semantic Change Workflow:
| Change Type | changeType Value | Required Approver | Typical Options |
|---|---|---|---|
| Add optional field | policy_update |
R-DS | Usually single option (additive) |
| Add required field (new version) | policy_update |
R-DS + R-AG | Conservative (optional), Balanced (required in v2) |
| Remove field | policy_update |
R-AG | Conservative (deprecate), Balanced (remove in v2), Aggressive (immediate) |
| Change field type | policy_update |
R-AG | Always requires migration path in proposal |
| Major version bump | policy_update |
R-AG | Requires all 3 options with consumer impact analysis |
The SemanticChangeProposal.impactAnalysis for schema changes MUST include:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"impactAnalysis": {
"projectionsAffected": [
{
"projectionTarget": "consumer:governance-dashboard",
"contractPath": "apps/governance-dashboard/contracts/policy-eval.contract.yaml",
"breakingForConsumer": true,
"regenerationRequired": true
}
],
"compatibility": {
"backward": false,
"forward": true,
"migrationPath": "Consumers upgrade to v2 schema within 30 days"
},
"blastRadius": {
"consumersAffected": 3
}
}
}
The Schema Evolution Guard runs in CI to check changes to Producer Schemas against registered Consumer Contracts.
required fields used by any Consumer.required fields (breaks existing Consumers who don’t send it? No, breaks Consumers who validate structure strictness, or strictly, purely additive changes are fine for flexible consumers). Correction: Adding a required field in a new version is fine, but editing an existing version tag is forbidden.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def check_compatibility(producer_schema, consumer_contracts):
for contract in consumer_contracts:
if contract.event != producer_schema.schemaId:
continue
for expectation in contract.expectations:
field_path = expectation.field
# Check 1: Existence
if not schema_has_field(producer_schema, field_path):
return Result.BLOCK(f"Breaking Change: Field '{field_path}' removed but required by {contract.consumer}")
# Check 2: Type Safety
schema_type = get_schema_type(producer_schema, field_path)
# Logic to verify type compatibility...
# Check 3: Enum Coverage
if expectation.assertion.startswith("must be one of"):
required_values = parse_enum_values(expectation.assertion)
schema_values = get_schema_enum(producer_schema, field_path)
if not set(required_values).issubset(set(schema_values)):
return Result.WARN(f"Consumer {contract.consumer} handles specific enums that might have changed.")
return Result.PASS
When projecting complex semantic states (like “Unknown due to missing data”) into simpler systems (like a boolean “is_valid”), the loss must be logged.
Use a translationLoss array in the payload or metadata key.
Example:
Unknown (3-valued logic).false (Fail-safe default).1
2
3
4
5
6
{
"payload": {
"valid": false,
"translationLoss": ["Semantics 'Unknown' projected to 'false' for legacy compatibility"]
}
}
If the loss is critical (risk of semantic failure), a Semantic Debt Item must be created (see SDS-016) referencing the event ID.
| Scenario | Check Result | Action |
|---|---|---|
| Producer removes unused field | PASS | Allow merge. |
| Producer adds optional field | PASS | Allow merge. |
| Producer removes field used by Consumer | BLOCK | Blocking Error. Producer must restore field or Consumer must be updated first. |
| Producer changes enum (adds value) | WARN | Warning to Consumers to check exhaustiveness. |
| Breaking Schema Change (v1 -> v2) | MANUAL | Requires Major Version bump and new Schema file. |