Cache Behavior Workflow¶
This guide demonstrates the full cache behavior that bijux-dag currently
proves on one real repository workflow:
- the first run executes every retained stage
- the second run reuses cache across the full workflow
- changing one graph input invalidates only the dependent stages
- an unrelated branch stays cached
- a corrupted cache entry is refused instead of reused
- cache-miss explanation identifies both changed inputs and corruption refusal
- a corrupted retained run output can be restored from an exact verified cache entry through the internal repair lane
The workflow uses the repository regional sales example:
- graph:
evidence/dag/authoring/examples/regional-sales-pipeline.dag.json - inputs:
evidence/dag/authoring/examples/regional-sales-source/
cache verify is part of the stable operator surface. why-cache-missed is
repository-tested and callable by explicit path, but it remains outside the
stable bijux-dag --help contract in v0.4.0.
Prepare Inputs¶
Run these commands from repository root:
GRAPH_PATH="evidence/dag/authoring/examples/regional-sales-pipeline.dag.json"
ORDERS_CSV="$(pwd)/evidence/dag/authoring/examples/regional-sales-source/orders.csv"
TARGETS_JSON="$(pwd)/evidence/dag/authoring/examples/regional-sales-source/targets.json"
RUN_ROOT="./artifacts/regional-sales-runs"
CACHE_ROOT="./artifacts/regional-sales-cache"
UPDATED_ORDERS_CSV="./artifacts/regional-sales-inputs/orders-updated.csv"
mkdir -p ./artifacts/regional-sales-inputs
Validate The Graph¶
Run The Cold And Warm Workflow¶
Populate cache and retain the first run:
bijux-dag run --json "${GRAPH_PATH}" \
--out "${RUN_ROOT}" \
--run-id regional-sales-cold \
--cache readwrite \
--cache-dir "${CACHE_ROOT}" \
--input "orders_csv=${ORDERS_CSV}" \
--input "targets_json=${TARGETS_JSON}" \
--input "report_title=Regional Revenue Attainment"
Run the same workflow again with the same cache directory and inputs:
bijux-dag run --json "${GRAPH_PATH}" \
--out "${RUN_ROOT}" \
--run-id regional-sales-warm \
--cache readwrite \
--cache-dir "${CACHE_ROOT}" \
--input "orders_csv=${ORDERS_CSV}" \
--input "targets_json=${TARGETS_JSON}" \
--input "report_title=Regional Revenue Attainment"
The cold run should execute all seven retained stages. The warm run should reuse all seven from cache:
ingest_ordersclean_ordersderive_region_totalsderive_segment_totalsload_targetsvalidate_outputspublish_final_table
The published table remains under retained evidence:
artifacts/regional-sales-runs/run-regional-sales-warm/nodes/publish_final_table/outputs/final/revenue_attainment.csv
Change One Graph Input¶
Write a modified orders file at a new path so the retained run records the new input binding explicitly:
python3 - <<'PY'
from pathlib import Path
source = Path("evidence/dag/authoring/examples/regional-sales-source/orders.csv")
updated = Path("artifacts/regional-sales-inputs/orders-updated.csv")
updated.write_text(
source.read_text().replace(
"A-102,north,mid-market,3,15.00",
"A-102,north,mid-market,5,15.00",
),
encoding="utf-8",
)
PY
Run the workflow with the updated orders path while keeping the targets input unchanged:
bijux-dag run --json "${GRAPH_PATH}" \
--out "${RUN_ROOT}" \
--run-id regional-sales-updated \
--cache readwrite \
--cache-dir "${CACHE_ROOT}" \
--input "orders_csv=${UPDATED_ORDERS_CSV}" \
--input "targets_json=${TARGETS_JSON}" \
--input "report_title=Regional Revenue Attainment"
In this updated run:
load_targetsshould stay cached- the order-dependent stages should rerun
Use retained-run comparison to confirm the changed surface:
The comparison should show:
input_values.changed_inputsincludesorders_csvnode_statuses.changed_nodesincludes the order-dependent stagesnode_statuses.changed_nodesdoes not includeload_targets
Explain The Changed-Input Cache Miss¶
Use the explicit-path explanation route against the updated run:
bijux-dag --json why-cache-missed \
--run-dir "${RUN_ROOT}/run-regional-sales-updated" \
--node clean_orders \
--cache-dir "${CACHE_ROOT}"
The response should report:
data.mode = "node"data.outcome = "miss"data.taxonomyincludeschanged_input_hashes
This is the direct explanation for why the changed orders path invalidated the
clean_orders cache candidate.
Corrupt One Real Cache Entry¶
Read the exact cache key used by load_targets on the warm run:
export LOAD_TARGETS_CACHE_KEY="$(
python3 - <<'PY'
import json
from pathlib import Path
trace = Path("artifacts/regional-sales-runs/run-regional-sales-warm/nodes/load_targets/trace.json")
payload = json.loads(trace.read_text())
print(payload["cache_identity"]["cache_key"])
PY
)"
Tamper with one retained payload under that cache entry:
python3 - <<'PY'
from pathlib import Path
import os
entry = Path("artifacts/regional-sales-cache") / os.environ["LOAD_TARGETS_CACHE_KEY"] / "outputs"
for candidate in entry.rglob("*"):
if candidate.is_file() and candidate.name != "index.json":
candidate.write_text("corrupted-cache-payload\n", encoding="utf-8")
print(candidate)
break
else:
raise SystemExit(f"no payload found under {entry}")
PY
Verify Cache Integrity¶
Run cache verification against the same cache directory:
The command should exit with status 3 and report at least one corrupt entry
under data.corrupt_total.
That is the integrity boundary working as intended: the entry still exists, but the runtime no longer trusts it.
Explain The Corruption Refusal¶
Ask for the cache explanation on the original warm-run node that previously hit cache:
bijux-dag --json why-cache-missed \
--run-dir "${RUN_ROOT}/run-regional-sales-warm" \
--node load_targets \
--cache-dir "${CACHE_ROOT}"
The response should now report:
data.outcome = "unsafe_reuse_refused"data.exact_entry_report.eligible = falsedata.taxonomyis populated with corruption-related reasons
This is the operator-facing proof that cache reuse is rejected when integrity verification fails.
Restore A Corrupted Retained Output From Cache¶
Corrupt one retained output in the warm run while leaving the cache entry itself untouched:
python3 - <<'PY'
from pathlib import Path
target = Path(
"artifacts/regional-sales-runs/run-regional-sales-warm/"
"nodes/publish_final_table/outputs/final/revenue_attainment.csv"
)
target.write_text("corrupted-retained-output\n", encoding="utf-8")
print(target)
PY
The stable integrity surface should now fail on that retained run:
Restore the retained output from the exact verified cache entry:
BIJUX_DAG_ENABLE_INTERNAL=1 bijux-dag --json runtime repair --apply \
--cache-dir "${CACHE_ROOT}" \
--out "${RUN_ROOT}" \
"${RUN_ROOT}/run-regional-sales-warm"
The repair report should show:
data.cache_recoveries_applied[0].node_id = "publish_final_table"data.repair_run = nulldata.issues = []
That is the honest boundary of this repair lane: it restores retained outputs only when the exact persisted cache entry still verifies. If the cache entry is also missing or corrupt, repair must fall back to a child rerun or remain failed.
Rerun After Corruption¶
Run the original workflow again with the original inputs:
bijux-dag run --json "${GRAPH_PATH}" \
--out "${RUN_ROOT}" \
--run-id regional-sales-corrupt \
--cache readwrite \
--cache-dir "${CACHE_ROOT}" \
--input "orders_csv=${ORDERS_CSV}" \
--input "targets_json=${TARGETS_JSON}" \
--input "report_title=Regional Revenue Attainment"
In this rerun:
load_targetsshould execute again because its exact cache entry was refused- the order branch should remain cached because its cache entries stayed valid
- the final revenue table should match the warm run output
Inspect the retained traces directly if you need the per-node status proof:
artifacts/regional-sales-runs/run-regional-sales-corrupt/nodes/load_targets/trace.json
artifacts/regional-sales-runs/run-regional-sales-corrupt/nodes/clean_orders/trace.json
artifacts/regional-sales-runs/run-regional-sales-corrupt/nodes/derive_region_totals/trace.json
artifacts/regional-sales-runs/run-regional-sales-corrupt/nodes/derive_segment_totals/trace.json
Reading Rule¶
Use this guide when the question is whether bijux-dag cache reuse is both
selective and integrity-checked on a real retained workflow, not only on
synthetic fixtures.