Skip to content

symfonic.agent.cutover.governance_contract

governance_contract

What "a C1-K governance surface" is, and the two names its rows refuse by.

The shape and the vocabulary. The three rows themselves live in :mod:~symfonic.agent.cutover.governance_surfaces and the contract that explains why each is a REPLACE lives in :mod:~symfonic.agent.cutover.authority -- the same contract / data / rule split :mod:~symfonic.agent.cutover.settings_contract, :mod:~symfonic.agent.cutover.retired_settings and :mod:~symfonic.agent.cutover.config_retirement already make, and made for the same two reasons: the published surface reads end to end without the raise sites in the way, and each piece stays inside the repository's module budget.

Why a fourth surface dataclass. LifecycleRow, AdmissionSurface and PauseSurface each exist because a lane's rows did not fit the one before it, and C1-K's do not fit any of the three. AdmissionSurface requires an override_argument on a row it does not admit, which is right for a lane that declined its triage and wrong for one that applied it -- these three rows are refused because the recommendation was applied, not overridden. PauseSurface has the right shape and the wrong name: it partitions the entry points and demands a break beside every replacement, which is exactly this lane's obligation, but a governance row filed under a pause type would be a row whose home says something false about it. So :class:GovernanceSurface restates PauseSurface's two invariants rather than importing them, and the restating is deliberate: they are the invariants this lane's acceptance names, and a lane that inherited them from a module about pauses would lose the reason it has them. It adds one of its own -- kind -- because C1-K is the first subgroup whose rows refuse through two different mechanisms, and a table that could not say which would make the reader guess.

GovernanceSurface dataclass

GovernanceSurface(path: str, subgroup: str, owner: str, kind: str, disposition: str, served_by: tuple[str, ...], refused_by: tuple[str, ...], inert_on: tuple[str, ...], consumer: str, observable: str, replaced_by: str | None = None, adopter_break: str | None = None, override_argument: str | None = None)

One C1-K row, and what 11.0 does with it on each entry point.

served_by / refused_by / inert_on partition :data:~symfonic.agent.cutover.lifecycle_contract.PUBLIC_ENTRY_POINTS exactly, for the reason LifecycleRow gives: an unmeasured third entry point is the gap that made two thirds of this programme's earlier evidence unusable, and a row with a hole in its surface would reintroduce it as a data structure.

replaced_by and override_argument are mutually exclusive and one is required, so this table cannot hold a row that was neither replaced nor argued about -- "the triage recommended REPLACE and the code is silent" is then not a state anybody has to infer from an absence. A replaced_by without an adopter_break is refused for the reason this lane's acceptance gives in as many words: collapsing REPLACE into MIGRATE ships a breaking change as a migration note.