Add durable reconciliation decisions

This commit is contained in:
2026-08-06 12:42:20 +02:00
parent 3dae80b6b8
commit b14c693bde
14 changed files with 2037 additions and 19 deletions
+46 -2
View File
@@ -200,7 +200,12 @@ DOCUMENTATION = (
"Every graph node declares typed inputs, configuration, output schema, and validation rules. "
"Source nodes pin inline content or governed Datasource references; combine, filter, transform, "
"quality, reconciliation, reusable-subflow, and output nodes remain explicit in the canonical graph. "
"Reconciliation rows expose stable key hashes, explicit before/after values, and input hashes. A separate decision-table input can annotate exact matches, invalidate changed inputs, and report orphaned decisions without silently rewriting business data. "
"Reconciliation rows expose stable key hashes, explicit before/after values, and input hashes. The "
"review dialog records accept, reject, correct, or defer decisions in tenant-owned immutable decision "
"sets. Their current projection is a fingerprinted Dataflow source; every superseded revision retains "
"the actor, reason, exact input hash, time, and optional correction. A reconcile.decisions node can "
"annotate exact matches, invalidate changed inputs, and report orphaned decisions without silently "
"rewriting business data. "
"Expressions use the typed Dataflow expression language and never execute arbitrary host or database "
"code. Selecting a node may request a bounded intermediate preview; preview rows are transient, "
"privacy-filtered for the actor, and are not retained as run output. SQL editing compiles into the same "
@@ -218,6 +223,7 @@ DOCUMENTATION = (
"dataflow.field.expression",
"dataflow.field.schema",
"dataflow.action.preview-node",
"dataflow.action.review-decisions",
],
},
),
@@ -232,6 +238,8 @@ DOCUMENTATION = (
"the revision and authorization grant, then re-evaluate authority for every delivery. Runs create "
"durable command and recovery evidence. Publishing creates a governed Datasource materialization, and "
"environment promotion changes which immutable revision is eligible for staging or production runs. "
"Recording a reconciliation decision uses optimistic concurrency and appends an immutable revision; "
"it never mutates the reviewed business row. "
"Deletion prevents future use while retained run, deployment, lineage, audit, and recovery evidence "
"continues under its retention policy."
),
@@ -247,12 +255,14 @@ DOCUMENTATION = (
"dataflow.action.save",
"dataflow.action.derive",
"dataflow.action.trigger",
"dataflow.action.record-decision",
"dataflow.action.delete",
],
"consequence_classes": {
"save_revision": "Appends an immutable pipeline definition revision.",
"derive_copy": "Creates a separately governed copy pinned to the source revision and hash.",
"configure_trigger": "Creates or changes an automation command with revision and authorization evidence.",
"record_decision": "Appends an actor-attributed immutable decision revision against an exact input hash.",
"delete_pipeline": "Prevents future use while retained evidence remains governed.",
},
},
@@ -362,6 +372,22 @@ def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
)
.count()
),
"dataflow_reconciliation_decision_sets": (
session.query(dataflow_models.DataflowReconciliationDecisionSet)
.filter(
dataflow_models.DataflowReconciliationDecisionSet.tenant_id
== tenant_id
)
.count()
),
"dataflow_reconciliation_decisions": (
session.query(dataflow_models.DataflowReconciliationDecision)
.filter(
dataflow_models.DataflowReconciliationDecision.tenant_id
== tenant_id
)
.count()
),
}
@@ -532,6 +558,14 @@ manifest = ModuleManifest(
parent_id="dataflow.page",
order=50,
),
ViewSurface(
id="dataflow.decisions",
module_id=MODULE_ID,
kind="action",
label="Reconciliation decisions",
parent_id="dataflow.results",
order=55,
),
ViewSurface(
id="dataflow.triggers",
module_id=MODULE_ID,
@@ -580,6 +614,8 @@ manifest = ModuleManifest(
script_location=str(Path(__file__).with_name("migrations") / "versions"),
retirement_supported=True,
retirement_provider=drop_table_retirement_provider(
dataflow_models.DataflowReconciliationDecision,
dataflow_models.DataflowReconciliationDecisionSet,
dataflow_models.DataflowTriggerDelivery,
dataflow_models.DataflowTrigger,
dataflow_models.DataflowRun,
@@ -597,6 +633,8 @@ manifest = ModuleManifest(
persistent_table_uninstall_guard(
dataflow_models.DataflowPipeline,
dataflow_models.DataflowPipelineRevision,
dataflow_models.DataflowReconciliationDecisionSet,
dataflow_models.DataflowReconciliationDecision,
dataflow_models.DataflowPipelineDeployment,
dataflow_models.DataflowRun,
dataflow_models.DataflowTrigger,
@@ -614,7 +652,13 @@ manifest = ModuleManifest(
known_limits=(
"Execution adapters do not yet cover every declared node family.",
),
owned_concepts=("dataflow definition", "dataflow revision", "dataflow run", "transformation graph"),
owned_concepts=(
"dataflow definition",
"dataflow revision",
"dataflow run",
"reconciliation decision set",
"transformation graph",
),
non_owned_concepts=("datasource binding", "connector transport", "report presentation", "workflow task"),
recovery_docs=("README.md", "docs/DURABLE_RUN_RECOVERY.md"),
security_docs=("README.md",),