symfonic.agent.cutover.pause_policy¶
pause_policy ¶
The 11.0 human-interaction pause-policy contract (TA8.42, C1-B + C1-T).
TA8.25's subgroups C1-B (ask_user_pause_ttl_seconds,
agent.ask_user_pause_ttl_seconds, ask_user_tool_descriptions) and
C1-T (ask_user_enabled) are one owner read from two ends: the pause a
person is asked for, and the lifetime and presentation of the token that pause
mints. Four rows, one capability, and this module is the contract 11.0
publishes for all four -- written before the guard below and the tests next
door depend on it. The rows themselves are one table in
:mod:~symfonic.agent.cutover.pause_surfaces and are re-exported here at the
address importers already hold.
The replacement, in one sentence. On the 11.0 line a pause is configured on
the capability the composition root wires
(:class:~symfonic.capabilities.human.capability.HumanInteractionCapability),
not on FrameworkConfig: its lifetime comes from
:class:~symfonic.capabilities.human.values.TTLPolicy, its presentation from
:class:~symfonic.capabilities.human.contribution.InteractionToolSpec, and its
door from an
:class:~symfonic.capabilities.human.registration.InteractionRegistration.
The three C1-B fields are the legacy spelling of the first two and they refuse
by name; C1-T's switch is the legacy spelling of the third and it does not
move here, for the reason
:data:~symfonic.agent.cutover.pause_surfaces.PAUSE_SURFACES records against
it.
Why C1-B is REPLACE and not RETIRE. The capability survives -- an adopter
who tuned a pause lifetime can still tune one, and one who reworded the
ask_user tool can still reword it. What does not survive is the field, and
with it the ambiguity of two spellings for one lifetime. That is a breaking
change and it is stated as one:
:attr:~symfonic.agent.cutover.pause_surfaces.PauseSurface.adopter_break
carries what an adopter loses per row, and the refusal message carries the
replacement, so a migration is never inferred from a dead end.
Why the TTL is refused on both spellings at once.
ask_user_pause_ttl_seconds and agent.ask_user_pause_ttl_seconds are the
same number in two places; retiring one and leaving the other would keep exactly
the ambiguity C1-B's REPLACE exists to end, and would leave the surviving
spelling honoured by only the body 11.0 retires. They therefore refuse together,
through two mechanisms only because they sit at two depths: the flat field is a
:data:~symfonic.agent.cutover.retired_settings.RETIRED_SETTINGS row, and the
nested one is refused here, because retired_setting_supplied reads flat
attributes by design and a nested row would make that table lie about its own
predicate. Both raise the same class, in the same group, on the same line.
Before dispatch, never from the verdict. The guard below runs at the top of
run/stream/stream_typed beside
:func:~symfonic.agent.cutover.config_retirement.refuse_retired_setting, and
for that function's three reasons: stream and stream_typed do not
consult the same verdict on every reported line, and a rolled-back switch would
re-honour a retired field. A retirement keyed on which body served the turn is
route-conditional, which is the shape this programme refuses.
PauseSurface
dataclass
¶
PauseSurface(path: str, subgroup: str, owner: 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 row of this lane, and what 11.0 does with it on each entry point.
served_by / refused_by / inert_on partition the public entry
points exactly. inert_on describes only the migrated route at those
entry points: it does not claim the legacy fallback lost the behaviour
recorded by consumer and observable. An unmeasured third entry
point is the gap that made earlier evidence unusable, and a row with a hole
would reintroduce it. replaced_by and override_argument are
mutually exclusive and one is required, so the table cannot hold a row
that was neither replaced nor argued about.
ReplacedPauseSettingError ¶
Bases: RetiredConfigurationError
A nested pause-policy field replaced on the 11.0 line was set.
Subclasses
:class:~symfonic.agent.cutover.config_retirement.RetiredConfigurationError
so except RetiredConfigurationError around the agent API keeps catching
this -- the widening-never-a-rename discipline that error states for its own
ancestry, and the one
:class:~symfonic.agent.cutover.container_semantics.UnownedContainerError
already follows. A subclass rather than a fourth sibling because it refuses
the same kind of thing at the same moment: a fact about how this agent was
built, named before dispatch.
It exists at all because the field is nested. The flat spelling is an
ordinary :data:~symfonic.agent.cutover.retired_settings.RETIRED_SETTINGS
row and raises the base class; a table whose predicate is "getattr
against a flat stock default" cannot hold agent.ask_user_pause_ttl_seconds
without lying about what it reads.
Source code in src/symfonic/agent/cutover/config_retirement.py
nested_pause_ttl_supplied ¶
The nested pause TTL this config actually asks for, or :data:MISSING.
Emptiness is not use, the rule TA8.26 states for the twenty-five retired
fields and TA8.41 restates for the container. AgentConfig's stock value
is None, which is what a caller who wants nothing passes, so only a
value that differs from it is a request.
Answers :data:~symfonic.agent.cutover.baseline.MISSING -- "nothing to
refuse" -- when the question cannot be put: a config object with no
agent attribute, or an agent with no such field. That is the one
place this rule does not fail closed, and it is the same bounded skip
retired_setting_supplied takes for the same reason: cutover
reflects over whatever object it is handed, and an object without the
attribute cannot be asking for the behaviour.
The attribute is spelled out as a literal chain rather than read through
:data:NESTED_TTL_PATH, so TA8.24's provenance detector can cite this
function as the row's consumer; one test holds the two spellings together.
Source code in src/symfonic/agent/cutover/pause_policy.py
pause_surfaces_for_subgroup ¶
Every row of one TA8.25 subgroup, in declaration order.
Parity is asserted per field and never aggregated, so the tests read the lane one subgroup at a time rather than reporting a single verdict.
Source code in src/symfonic/agent/cutover/pause_surfaces.py
refuse_replaced_pause_setting ¶
Refuse the nested pause TTL on entry_point, or return.
The nested half of C1-B's REPLACE, expressed once and called from all three
public entry points. The position, and the reasons for it, are
:func:~symfonic.agent.cutover.config_retirement.refuse_retired_setting's.
There is no detail parameter and no release-line branch, for that
function's reasons: the next step comes from :data:PAUSE_SURFACES, so no
call site can forget to carry one, and 9.12 and 10.4 keep the field in their
own artefacts because this module is not backported.