symfonic.agent.cutover.config_retirement¶
config_retirement ¶
The 11.0 configuration retirement — one rule for twenty-five fields (TA8.26).
:mod:~symfonic.agent.cutover.retirement retired three operator levers and
three per-call arguments. This module retires twenty-five configuration
fields — TA8.24's cluster C2 — and it is deliberately the same shape, because
"retired on 11.0" is one rule and a third spelling of it would be a third
chance to disagree about the wording, the ancestry and the line.
What changes for an adopter. Today a config carrying one of these fields is
merely refused by the envelope: admit_invocation is default-deny, no
allowlist entry names the field, and the turn quietly runs on the legacy body.
That is the absence of a decision. From this module on, the field refuses by
name, saying which line retired it, which contractual group it refuses with,
and either what replaced it or that nothing did.
The four alternatives retirement.py rejects for the levers are rejected
here verbatim, and one of them is what the field does today:
- Not deleted. The field stays on :class:
FrameworkConfig, so an adopter who sets it meets a named error rather than the silent drop_warn_unknown_fieldsperforms for an unknown key. - Not ignored. A field that changes nothing while the caller believes it did is the failure this task exists to end.
- Not silently redirected. Serving the turn on legacy because the field was not admitted, without saying so, is the status quo wearing a route.
- Not accepted-then-no-op.
Before dispatch, never from the verdict. The guard runs at the top of
run/stream/stream_typed, beside
:func:~symfonic.agent.engine._refuse_retired_arguments and for the same three
reasons. stream_typed consults no envelope at all, so a verdict-driven
refusal would honour a retired field there and refuse it on run — a
route-conditional retirement. stream skips the verdict entirely on a line
that predates the text-delta chunk contract, which is exactly the seam
retirement.py says a retirement must not be keyed on. And a rolled-back
switch would re-honour a retired field, which is the "silently redirected"
outcome. There is no release-line branch anywhere below: 9.12 and 10.4 keep
their behaviour in their own artefacts, and the compatibility window is kept by
not backporting this module.
Emptiness is not use. :meth:RetiredSetting.is_use answers one question —
"did the caller ask for this behaviour?" — and answers it in one place, so the
guard and any future reader cannot drift. A field at its stock default, or set
to a value :data:~symfonic.agent.cutover.settings_contract.INACTIVE_CLASSES
calls inactive, is what a caller who asks for nothing passes.
RetiredConfigurationError ¶
Bases: CutoverPathError
A configuration field retired on the 11.0 line was set.
Subclasses :class:~symfonic.services.shadow.errors.CutoverPathError, and
through it ConfigurationError and SymfonicError, for the reason
:class:~symfonic.agent.cutover.retirement.RetiredArgumentError gives for
its own ancestry: an adopter's existing except around the agent API
keeps catching this. A widening, never a rename.
Named separately from the lever and argument errors rather than folded into either. The reachability probe's validity predicate keys on the lever vocabulary and the envelope's guard loop keys on the argument one; a configuration field in either table would make one vocabulary carry two kinds of fact. Three exact classifiers beat one loose one.
Source code in src/symfonic/agent/cutover/config_retirement.py
declared_inactive ¶
True when the row's own inactivity class calls value inactive.
Narrower than :func:is_inactive, which also answers True for the
stock default. Kept separate so "the default does not refuse" and "an
inactive value does not refuse" stay two claims rather than one.
Source code in src/symfonic/agent/cutover/config_retirement.py
is_inactive ¶
True when value asks for nothing on setting.
The complement of :meth:RetiredSetting.is_use, exported so the contract
"emptiness is not use" can be asserted directly rather than inferred from a
refusal that did not happen -- which is also what a broken guard produces.
Source code in src/symfonic/agent/cutover/config_retirement.py
refuse_retired_setting ¶
Refuse setting on entry_point, citing the line that retired it.
There is no detail parameter, and its absence is the guarantee
:func:~symfonic.agent.cutover.retirement.refuse_retired_lever buys by
making one required: the next step comes from
:data:~symfonic.agent.cutover.retired_settings.RETIRED_SETTINGS, so every
refusal carries one and no call site can forget to pass it.
Source code in src/symfonic/agent/cutover/config_retirement.py
retired_setting_supplied ¶
The first retired field config actually uses, in table order, or None.
The single place that decides "was a retired configuration field used?", so every entry point answers it identically.
A field the object does not carry at all is skipped rather than refused.
cutover reflects over whatever config object it is handed — the
envelope's whole design — and an object without the attribute cannot be
asking for the behaviour. That is the one place this guard does not fail
closed, and it is bounded by the coverage test: every name in the table is
a live FrameworkConfig field, so a skip means a foreign object, never a
renamed field.
Source code in src/symfonic/agent/cutover/config_retirement.py
setting_call ¶
The construction the adopter wrote, quoted back at them.
Derived rather than stored beside each row.
:data:~symfonic.agent.cutover.retirement.RETIRED_ARGUMENTS stores its
call because three arguments have three different shapes; twenty-five
configuration fields have one, and writing it out twenty-five times would
be twenty-five chances to quote a field under a name it does not have.