graflo.architecture.profile.inverses¶
Audit of declared inverses across the whole manifest, and its inverses profile.
:mod:graflo.architecture.schema.inverse_realization says how each declared
pair is realized in the schema. That is half the picture: a materialized
inverse is an edge somebody has to feed, and whether every resource that writes
the forward relation also feeds the inverse is a question about pipelines.
:func:audit_inverses answers both halves in one report.
The split that matters in the findings is between what one side merely under-reports and what two sides contradict:
repairable
The manifest states a fact in one place and omits it in another -- the
inverse edge exists but a resource writing the forward relation feeds
nothing into it; one mirror declares a property the other lacks; a relation
is undirected everywhere but not declared symmetric. Propagating the fact
cannot change meaning, so a planner may do it and say what it did.
conflict
The two places disagree -- a pair read from the same side, mirrors keyed
differently, a native inverse whose reverse name is taken. Only the author
knows which is right; these are listed and never touched.
Nothing here raises, and nothing needs the manifest to have been through
finish_init: a manifest produced by a merge that no longer loads is exactly
the one worth auditing. A schema runs its cross-checks while it is validated,
so such a manifest cannot be built the usual way; :func:manifest_for_audit
builds it block by block instead.
Attributes¶
Feeding = Literal['emit_inverse', 'step', 'inference', 'none']
module-attribute
¶
How one resource feeds a materialized inverse: by mirroring its forward steps, by a step of its own, by edge inference, or not at all.
INVERSES_PROFILE = Profile(name='inverses', version=PROFILE_VERSION, assertions=(Assertion(_CONSISTENT, 'Declared inverses are realized without contradiction', True, check_inverses_consistent), Assertion(_COMPLETE, 'Every realization of an inverse is complete', True, check_inverses_complete)))
module-attribute
¶
PROFILE_VERSION = '1'
module-attribute
¶
__all__ = ['INVERSES_PROFILE', 'Feeding', 'InverseReport', 'PairStatus', 'audit_inverses', 'check_inverses_complete', 'check_inverses_consistent', 'manifest_for_audit']
module-attribute
¶
Classes¶
InverseReport
¶
Bases: ConfigBaseModel
Everything known about how a manifest realizes its declared inverses.
Source code in graflo/architecture/profile/inverses.py
Attributes¶
findings = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
pairs = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
symmetric = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
Methods:¶
conflicts()
¶
introduced_since(before)
¶
Findings of this report that before did not have.
audit_inverses(after).introduced_since(audit_inverses(before)) is what
a change did to the inverses, whatever produced the change.
Source code in graflo/architecture/profile/inverses.py
notes()
¶
pair(relation)
¶
The status of the pair that relation belongs to, either side.
repairable()
¶
to_lines()
¶
The report as text. The one renderer.
Source code in graflo/architecture/profile/inverses.py
PairStatus
¶
Bases: PairRealization
A declared pair across schema and ingestion.
Source code in graflo/architecture/profile/inverses.py
Attributes¶
feeding = PydanticField(default_factory=dict, description='Per resource that writes either relation of the pair: how it feeds the materialized inverse. Empty when nothing is materialized.')
class-attribute
instance-attribute
¶
native_candidate = PydanticField(default=None, description='The relation that would be listed in `native_inverses`: the one that has edges while its inverse has none.')
class-attribute
instance-attribute
¶
native_eligibility = PydanticField(default=None, description='Rules that would be broken if the database maintained this pair; empty means eligible. None when the pair is already native.')
class-attribute
instance-attribute
¶
Functions:¶
audit_inverses(manifest)
¶
Report how manifest realizes every declared inverse, and what is wrong with it.
Reads the schema, the physical profile and the ingestion model; changes nothing, raises nothing, and does not need the manifest to have loaded.
Source code in graflo/architecture/profile/inverses.py
check_inverses_complete(context)
¶
Nothing about an inverse is stated in one place and omitted in another.
Source code in graflo/architecture/profile/inverses.py
check_inverses_consistent(context)
¶
No two places of the manifest contradict each other about an inverse.
Source code in graflo/architecture/profile/inverses.py
manifest_for_audit(config)
¶
The manifest config describes, built without the cross-block checks.
Each block is still validated on its own -- vertices, edges and the inverse table, the physical profile, the resources -- so what comes back is well-formed. What is skipped is everything that relates one block to another, which is exactly what an audit is there to report on. A manifest that loads normally is returned as loaded.
Raises:
| Type | Description |
|---|---|
ValueError
|
when a block is malformed in itself. |