Skip to content
4 changes: 4 additions & 0 deletions .github/github.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"workGraphReadModel": "docs/work-graph-read-model.md",
"mergeTrainPolicy": "docs/merge-train-policy.md",
"mergeReadiness": "docs/merge-readiness.md",
"governanceEvidence": "docs/governance-evidence.md",
"mergeTrainStructuralProvenance": "docs/merge-train-structural-provenance.md",
"productOwnerPolicy": "docs/product-owner-policy.md",
"ownerAcceptance": "docs/owner-acceptance.md",
"changeImpactPolicy": "docs/change-impact-policy.md",
"agentContextBoundary": "docs/agent-context-boundary.md",
"operations": "docs/operations.md",
"records": "docs/records.md",
Expand Down
15 changes: 8 additions & 7 deletions contracts/agent-operator-contract.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
},
"governance": {
"authorization_admission_and_landing_are_independent": true,
"shadow_evidence_authoritative": false,
"shadow_evidence_role": "advisory_only"
"engineering_review_advisory": true,
"github_projection_role": "routing_and_status_only",
"owner_acceptance_authoritative": true
},
"product_lifecycle": {
"active_automation_states": [
Expand Down Expand Up @@ -176,7 +177,7 @@
"503"
],
"reviewed_evidence": [],
"schema_fingerprint_sha256": "810ce55fdcb429797375cb8a624d6ee36dc20ec80bf3fb17ac0e882b174e9d18",
"schema_fingerprint_sha256": "5aaa63991adeec924cd6ad00b45e79f0ad9813bcc541dfe512d2ad78a5c34911",
"supported_surfaces": [
"agent_helper",
"read_only_service"
Expand Down Expand Up @@ -381,7 +382,7 @@
],
"operation_id": "read_governance_projection",
"path": "/v1/governance/projection",
"purpose": "Read governance evidence without granting shadow authority.",
"purpose": "Read governance evidence without granting mutation authority.",
"response_statuses": [
"200",
"400",
Expand All @@ -391,7 +392,7 @@
"503"
],
"reviewed_evidence": [],
"schema_fingerprint_sha256": "9140bde34ab5930422c2e9f654db3e7af1ede283a69bd8289c62a93931f2d11e",
"schema_fingerprint_sha256": "28daa2b5e8010b8460a67bc7e4ea45f12cc8be3fb3d3d76e075222eef9226b8c",
"supported_surfaces": [
"read_only_service",
"operator_ui"
Expand Down Expand Up @@ -422,8 +423,8 @@
},
"normalization_version": 1,
"provenance": {
"source_commit_sha": "ef953b66251d8dce8f87405e54e6150d8e4b19a1"
"source_commit_sha": "3f8e22ff1762be0b81d5eeb19f237a14b6a8bd4f"
},
"schema_version": 1,
"semantic_digest_sha256": "a3f2c6602376cd0cd8638283f186471fe4f8a53516f531d4ff613b9ed5203479"
"semantic_digest_sha256": "5ca368e08c9d1d094eba3ed5cf47a789b144bb81e6ff8722138440ebaff23b64"
}
7 changes: 4 additions & 3 deletions control_plane/advisory_check_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def write_advisory_check_projection(
body = {
"name": projection.name,
"status": "completed",
"conclusion": "neutral",
"conclusion": projection.conclusion,
"external_id": projection.external_id,
"details_url": projection.details_url,
"output": {"title": projection.title, "summary": projection.summary},
Expand Down Expand Up @@ -138,7 +138,7 @@ def _matches_projection(check_run: dict[str, object], projection: AdvisoryCheckP
output = check_run.get("output")
return (
str(check_run.get("status") or "") == "completed"
and str(check_run.get("conclusion") or "") == "neutral"
and str(check_run.get("conclusion") or "") == projection.conclusion
and str(check_run.get("external_id") or "") == projection.external_id
and str(check_run.get("details_url") or "") == projection.details_url
and isinstance(output, dict)
Expand Down Expand Up @@ -175,7 +175,7 @@ def _result(
!= projection.external_id
or _app_id(check_run.get("app")) != installation_token.app_id
or str(check_run.get("status") or "") != "completed"
or str(check_run.get("conclusion") or "") != "neutral"
or str(check_run.get("conclusion") or "") != projection.conclusion
or str(check_run.get("details_url") or "") != projection.details_url
):
raise AdvisoryCheckProjectionError(
Expand All @@ -193,6 +193,7 @@ def _result(
"GitHub advisory check run response requires id.",
error_type=AdvisoryCheckProjectionError,
),
conclusion=projection.conclusion,
)


Expand Down
7 changes: 4 additions & 3 deletions control_plane/agent_operator_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class OperationSpec:
OperationSpec(
"GET",
"/v1/governance/projection",
"Read governance evidence without granting shadow authority.",
"Read governance evidence without granting mutation authority.",
("read_only_service", "operator_ui"),
("read",),
"none",
Expand Down Expand Up @@ -236,8 +236,9 @@ class OperationSpec:
"landing_sha_source": "terminal_controller_result_only",
},
"governance": {
"shadow_evidence_authoritative": False,
"shadow_evidence_role": "advisory_only",
"owner_acceptance_authoritative": True,
"github_projection_role": "routing_and_status_only",
"engineering_review_advisory": True,
"authorization_admission_and_landing_are_independent": True,
},
"protected_workflow_policy": {
Expand Down
3 changes: 0 additions & 3 deletions control_plane/change_impact_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,6 @@ class ChangeImpactPolicyReadModel(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)

schema_version: int = Field(default=1, ge=1)
mode: Literal["shadow"] = "shadow"
authoritative: Literal[False] = False
enforcement_effect: Literal["none"] = "none"
repository_id: str
current_policy: ChangeImpactPolicyRecord | None = None
policy_history_count: int = Field(default=0, ge=0)
Expand Down
4 changes: 3 additions & 1 deletion control_plane/contracts/advisory_check_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
)

AdvisoryCheckProjectionStatus = Literal["projected", "updated", "replayed"]
AdvisoryCheckConclusion = Literal["neutral", "success", "failure", "action_required"]


class AdvisoryCheckProjection(BaseModel):
Expand All @@ -33,6 +34,7 @@ class AdvisoryCheckProjection(BaseModel):
details_url: str
title: str = Field(min_length=1, max_length=255)
summary: str = Field(min_length=1, max_length=65535)
conclusion: AdvisoryCheckConclusion = "neutral"

@model_validator(mode="after")
def _validate_projection(self) -> "AdvisoryCheckProjection":
Expand Down Expand Up @@ -80,7 +82,7 @@ class AdvisoryCheckProjectionResult(BaseModel):
app_id: int = Field(ge=1)
installation_id: int = Field(ge=1)
check_run_id: int = Field(ge=1)
conclusion: Literal["neutral"] = "neutral"
conclusion: AdvisoryCheckConclusion


def is_launchplane_projected_check(name: str) -> bool:
Expand Down
18 changes: 9 additions & 9 deletions control_plane/contracts/change_impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,14 @@ def _normalize_timestamp(value: str, field_name: str) -> str:
raise ValueError(f"{field_name} must be an ISO-8601 timestamp") from error
if parsed.tzinfo is None:
raise ValueError(f"{field_name} must include a timezone")
return parsed.astimezone(timezone.utc).replace(microsecond=0).isoformat().replace(
"+00:00",
"Z",
return (
parsed.astimezone(timezone.utc)
.replace(microsecond=0)
.isoformat()
.replace(
"+00:00",
"Z",
)
)


Expand Down Expand Up @@ -375,9 +380,7 @@ class ChangeImpactEvaluationRequest(BaseModel):

schema_version: int = Field(default=1, ge=1)
target: ChangeImpactTargetReference
metadata: ChangeImpactEvaluationMetadata = Field(
default_factory=ChangeImpactEvaluationMetadata
)
metadata: ChangeImpactEvaluationMetadata = Field(default_factory=ChangeImpactEvaluationMetadata)

@model_validator(mode="after")
def _validate_request(self) -> "ChangeImpactEvaluationRequest":
Expand Down Expand Up @@ -506,9 +509,6 @@ class ChangeImpactEvaluation(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)

schema_version: int = Field(default=1, ge=1)
mode: Literal["shadow"] = "shadow"
authoritative: Literal[False] = False
enforcement_effect: Literal["none"] = "none"
status: ChangeImpactDecisionStatus
reason_code: str
target: ChangeImpactTarget
Expand Down
11 changes: 2 additions & 9 deletions control_plane/contracts/governance_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,9 @@ class GovernanceOwnerHistoryEntry(BaseModel):
human_action_semantics: OwnerAcceptanceHumanActionSemantics
target_status: Literal["current", "historical"]
decision_relationship: Literal["current", "historical"]
authorizes: tuple[str, ...] = ()

@model_validator(mode="after")
def _validate_entry(self) -> "GovernanceOwnerHistoryEntry":
if self.authorizes:
raise ValueError("Historical Owner product review authorizes no effect")
expected_semantics = owner_acceptance_human_action_semantics(self.record.action)
if self.human_action_semantics != expected_semantics:
raise ValueError("Owner history semantics must match the stored event")
Expand All @@ -54,16 +51,13 @@ class GovernanceOwnerJudgmentFacet(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)

level: Literal[1] = 1
mode: Literal["historical_product_judgment"] = "historical_product_judgment"
authoritative: Literal[False] = False
authorizes: tuple[str, ...] = ()
mode: Literal["owner_acceptance"] = "owner_acceptance"
authoritative: Literal[True] = True
current: OwnerAcceptanceDecision
history: tuple[GovernanceOwnerHistoryEntry, ...] = ()

@model_validator(mode="after")
def _validate_facet(self) -> "GovernanceOwnerJudgmentFacet":
if self.authorizes:
raise ValueError("Level 1 Owner product judgment authorizes no effect")
ordered = tuple(
sorted(
self.history,
Expand Down Expand Up @@ -166,7 +160,6 @@ class GovernanceAdvisoryObservation(BaseModel):

observation_scope: GovernanceAdvisoryObservationScope
observation: MergeReadinessAdvisoryObservation
neutral: Literal[True] = True
authoritative: Literal[False] = False
authorizes: tuple[str, ...] = ()

Expand Down
25 changes: 8 additions & 17 deletions control_plane/contracts/owner_acceptance.py
Original file line number Diff line number Diff line change
Expand Up @@ -748,27 +748,24 @@ def _validate_event(self) -> "OwnerAcceptanceEventRecord":
def owner_acceptance_human_action_semantics(
action: OwnerAcceptanceAction | None,
) -> OwnerAcceptanceHumanActionSemantics:
"""Project a stored human action into machine-readable non-authority semantics.
"""Project a stored human action into machine-readable review semantics.

The stored enum never changes. This projection exists so an API or UI client
cannot read L1 ``accepted`` as merge readiness, landed state, or production
authorization.
The stored enum never changes. Owner acceptance is an authoritative prerequisite
for merge admission, but remains distinct from technical readiness, landing, and
production authorization.
"""
if action is None:
return "none"
return _HUMAN_ACTION_SEMANTICS[action]


def _validate_non_authority(
def _validate_decision(
*,
admissible: bool,
status: OwnerAcceptanceDecisionStatus,
authorizes: tuple[str, ...],
current_event: "OwnerAcceptanceEventRecord | None",
human_action_semantics: OwnerAcceptanceHumanActionSemantics,
) -> None:
if authorizes:
raise ValueError("Owner product review never authorizes merge, release, or production")
if admissible and status != "accepted":
raise ValueError("Only a currently accepted Owner product review can be admissible")
expected_semantics = owner_acceptance_human_action_semantics(
Expand All @@ -792,16 +789,14 @@ class OwnerAcceptanceProductDecision(BaseModel):
current_event: OwnerAcceptanceEventRecord | None = None
admissible: bool = False
human_action_semantics: OwnerAcceptanceHumanActionSemantics = "none"
authorizes: tuple[str, ...] = ()

@model_validator(mode="after")
def _validate_product_decision(self) -> "OwnerAcceptanceProductDecision":
if self.schema_version != 1:
raise ValueError("Unsupported Owner acceptance product decision schema version.")
_validate_non_authority(
_validate_decision(
admissible=self.admissible,
status=self.status,
authorizes=self.authorizes,
current_event=self.current_event,
human_action_semantics=self.human_action_semantics,
)
Expand Down Expand Up @@ -883,27 +878,22 @@ class OwnerAcceptanceDecision(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)

schema_version: int = Field(default=1, ge=1)
mode: Literal["shadow"] = "shadow"
authoritative: Literal[False] = False
enforcement_effect: Literal["none"] = "none"
status: OwnerAcceptanceDecisionStatus
reason_code: OwnerAcceptanceReasonCode
binding: OwnerAcceptanceBinding | None = None
current_event: OwnerAcceptanceEventRecord | None = None
admissible: bool = False
human_action_semantics: OwnerAcceptanceHumanActionSemantics = "none"
authorizes: tuple[str, ...] = ()
products: tuple[OwnerAcceptanceProductDecision, ...] = ()
evaluated_at: str

@model_validator(mode="after")
def _validate_decision(self) -> "OwnerAcceptanceDecision":
if self.schema_version != 1:
raise ValueError("Unsupported Owner acceptance decision schema version.")
_validate_non_authority(
_validate_decision(
admissible=self.admissible,
status=self.status,
authorizes=self.authorizes,
current_event=self.current_event,
human_action_semantics=self.human_action_semantics,
)
Expand Down Expand Up @@ -990,6 +980,7 @@ def owner_acceptance_event_replay_digest(record: OwnerAcceptanceEventRecord) ->
authorization = payload.get("authorization")
if isinstance(authorization, dict):
authorization.pop("authorized_at", None)
authorization.pop("owner_login", None)
return _canonical_sha256(payload)


Expand Down
11 changes: 3 additions & 8 deletions control_plane/contracts/product_owner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
PRODUCT_OWNER_REQUIREMENT_WRITE_ACTION = "product_owner_requirement.write"
PRODUCT_OWNER_ROUTING_READ_ACTION = "product_owner_routing.read"
PRODUCT_OWNER_ROUTING_WRITE_ACTION = "product_owner_routing.write"
PRODUCT_OWNER_SHADOW_READ_ACTION = "product_owner_shadow.read"

ProductOwnerRecordStatus = Literal["active", "superseded"]
ProductOwnerShadowDecision = Literal[
ProductOwnerAuthorityDecision = Literal[
"authorized",
"denied",
"not_required",
Expand Down Expand Up @@ -465,7 +464,6 @@ class ProductOwnerRequirementRecord(BaseModel):
system: str
requirement_revision: int = Field(ge=1)
requirements: tuple[ProductOwnerRequirement, ...] = ()
enforcement_mode: Literal["shadow"] = "shadow"
effective_at: str
source: str
reason: str
Expand Down Expand Up @@ -608,14 +606,11 @@ def _validate_context(self) -> "ProductOwnerActionContext":
return self


class ProductOwnerShadowEvaluation(BaseModel):
class ProductOwnerAuthorityEvaluation(BaseModel):
model_config = ConfigDict(extra="forbid")

schema_version: int = Field(default=1, ge=1)
mode: Literal["shadow"] = "shadow"
authoritative: Literal[False] = False
enforcement_effect: Literal["none"] = "none"
decision: ProductOwnerShadowDecision
decision: ProductOwnerAuthorityDecision
reason_code: str
context: ProductOwnerActionContext
actor_identity_id: str
Expand Down
Loading
Loading