graflo.architecture.evolution.inverse_plan¶
Planning what to do about declared inverses: realize, repair, switch, withdraw.
Pure planners in the manner of :mod:graflo.architecture.evolution.state_core.plan:
each reads a manifest, emits primitive :data:~graflo.architecture.evolution.ops.ManifestOp
values and applies nothing. The op list is the reviewable artifact, apply_evolution
applies it, invert_ops undoes it, and a commit records exactly those
primitives -- so a plan replays without this module.
What a planner adds over calling the ops by hand is the part that is awkward by hand: deciding which relations an op may name, in which order the ops must run, and saying what was left out and why. Each plan is checked against a copy of the manifest before it is returned, so an op that would be refused is reported as skipped rather than handed to the caller to trip over.
The line the planners hold is the one the audit draws
(:func:graflo.architecture.profile.inverses.audit_inverses): what one place
merely under-reports is propagated, and what two places contradict is listed
and left alone.
Attributes¶
Realization = Literal['native', 'materialized']
module-attribute
¶
The two ways a declared pair can be stored. A pair with neither is simply declared, which is the default and needs nothing.
RealizeStrategy = Literal['auto', 'native', 'materialized']
module-attribute
¶
__all__ = ['InversePlan', 'Realization', 'RealizeStrategy', 'Skipped', 'plan_declare_symmetric', 'plan_realize_inverses', 'plan_repair_inverses', 'plan_switch_realization', 'plan_withdraw_realization']
module-attribute
¶
Classes¶
InversePlan
¶
Bases: ConfigBaseModel
Ops that change how declared inverses are realized, with what was left out.
Source code in graflo/architecture/evolution/inverse_plan.py
Attributes¶
after = PydanticField(default_factory=InverseReport, description='The audit of the manifest with the ops applied.')
class-attribute
instance-attribute
¶
before = PydanticField(default_factory=InverseReport, description='The audit the plan was made from.')
class-attribute
instance-attribute
¶
ops = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
remaining
property
¶
Conflicts and unrepaired findings still present once the ops are applied.
selected = PydanticField(default_factory=list, description='Relations the ops act on.')
class-attribute
instance-attribute
¶
skipped = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
Methods:¶
to_lines()
¶
The plan as text: what it does, what it skipped, what is left.
Source code in graflo/architecture/evolution/inverse_plan.py
Skipped
¶
Bases: ConfigBaseModel
One relation a plan left alone, and why.
Source code in graflo/architecture/evolution/inverse_plan.py
Functions:¶
plan_declare_symmetric(manifest, relations)
¶
Ops that make relations symmetric: their edges undirected, then the declaration.
directed: false and symmetric state one fact at two granularities and
the schema refuses either without the other, so they are two ops in a fixed
order. Keeping them two ops -- rather than one op that does both -- is what
lets each be undone exactly.
Source code in graflo/architecture/evolution/inverse_plan.py
plan_realize_inverses(manifest, *, strategy='auto', relations=None)
¶
Ops that realize declared pairs, and every pair left as it is with the reason.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest
|
GraphManifest
|
The manifest to plan against; not changed. |
required |
strategy
|
RealizeStrategy
|
|
'auto'
|
relations
|
Sequence[str] | None
|
Restrict to the pairs these relations belong to (either side). Omitted: every declared pair. |
None
|
A pair is never realized two ways: one that is already realized the other
way is skipped, and :func:plan_switch_realization moves it.
Source code in graflo/architecture/evolution/inverse_plan.py
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | |
plan_repair_inverses(manifest)
¶
Ops that propagate what the manifest under-reports; contradictions are left alone.
Works through the repairable findings of the audit in a fixed order, and
keeps a repair only if, applied to a working copy, the finding it answers is
gone and no new finding has appeared. A repair that is refused, or that
trades one finding for another, is reported as skipped. Conflicts are never
touched: they are in remaining.
The manifest need not load -- one assembled by a merge often does not --
see :func:graflo.architecture.profile.inverses.manifest_for_audit.
Source code in graflo/architecture/evolution/inverse_plan.py
plan_switch_realization(manifest, relations, *, to)
¶
Ops that move pairs from the realization they have to to.
A pair is realized one way, so switching is withdraw-then-add, in that
order. Each relation named is the side that stays stored: switching
employed_by to native removes the declared employs edges and has
the database maintain them instead. Eligibility for native is checked
as the schema will be after the withdrawal, before anything is planned, so
a pair is never left withdrawn and unrealized. A pair that is only declared
has nothing to withdraw, so switching it is the same as realizing it.
Source code in graflo/architecture/evolution/inverse_plan.py
plan_withdraw_realization(manifest, relations)
¶
Ops that stop storing the inverse of pairs, keeping their declaration.
The reverse of realizing: a native inverse is handed back to nothing, and the
declared edges of a materialized inverse are removed -- which also clears the
emit_inverse flags that fed them. Each relation named is the side that
stays stored. The pair remains in edge_config.inverses, so the inverse
name keeps resolving on reads wherever the backend can follow an edge from
its target.