Skip to content

feat(#89): reactive consumer (stage 1) — dependents reader + staleness predicates - #126

Merged
paddymul merged 2 commits into
mainfrom
feat/89-reactive-consumer-staleness
Jun 18, 2026
Merged

paddymul merged 2 commits into
mainfrom
feat/89-reactive-consumer-staleness

Conversation

@paddymul

Copy link
Copy Markdown
Contributor

First landable increment of the reactive consumer (epic #89) — the read-only
half: walk the recorded manifest.parents DAG and flag which entries are stale
relative to their inputs. No recompute yet (that is the next increment), so this
is independent of #115 (cold-read content-faithfulness) and #121 (determinism
attribution) and can land in parallel.

Design: plans/reactive-staleness-recalc.md (prerequisite item #1 + Stage 1).

What lands

  • tallyman_xorq/dependents.py — parents_of, build_dag,
    dependents_index (the reverse index recalc needs, which did not exist
    anywhere), and descendant_cone (topologically ordered, parents-before-
    children, cycle-detecting). Pure graph logic over manifest.parents; rebuilds
    the DAG-walk caching: expensive-parent from_catalog children pin the parent's untracked compute_cache snapshot by path — child read fails (no recompute) when it's pruned or uncloned #76 deleted with lineage.py without reviving column lineage.
  • tallyman_xorq/staleness.py — entry_staleness over two read-only axes:
    a follow=True parent whose alias head advanced, and a recorded source digest
    that no longer matches the file on disk (re-digested with the same
    source_identity helper, forced past its stat-memo for a sound check). scan
    runs it over every live entry and tags direct (own inputs moved) vs
    transitive (a directly-stale ancestor in the cone). off-mode reports the
    source axis unknown, never fresh.

TDD

Red→green. This PR's first commit is the failing tests (modules absent); the fix
commit follows once CI shows them red. Watching CI.

Decision defaults exercised (confirm)

  • off-mode source axis → unknown (not fresh).
  • hash-pinned (follow=False) child → never stale on the alias axis.

Not in this PR

The recalc action (rebuild the cone) and the MCP/companion surfaces — they
depend on the cascade-failure and trigger-model decisions and on #115/#118.

🤖 Generated with Claude Code

@paddymul
paddymul force-pushed the feat/89-reactive-consumer-staleness branch 3 times, most recently from 4f14672 to 7185e2d Compare June 18, 2026 19:43
@paddymul
paddymul marked this pull request as ready for review June 18, 2026 19:54
@paddymul

Copy link
Copy Markdown
Contributor Author

✅ CI green (lint + fast + integration) on 67272c6.

TDD trail: 7185e2d (tests only) was red on CI, then 67272c6 (the two modules) turns it green. Note on the red: because a red-first commit imports tallyman_xorq.dependents/.staleness before those files exist on disk, ruff's isort can't see them as first-party and reports I001 — so the failing-tests commit fails on lint, not on the test assertions (the fast job is gated behind lint). The assertion-level red was verified locally (the tests caught two wrong scenario assumptions during development — the source-axis cone leak and the cheap-chain grandparent leak — before going green). Once the modules exist, ruff classifies them first-party and lint passes.

paddymul and others added 2 commits June 18, 2026 16:06
Failing-first (TDD): tests/test_dependents.py and tests/test_staleness.py
exercise the read-only half of the reactive consumer (epic #89), which does
not exist yet — both error on import of tallyman_xorq.dependents /
tallyman_xorq.staleness.

dependents: parents_of / build_dag / dependents_index / descendant_cone over
manifest.parents, covered with synthetic manifests (chain, diamond, mixed
follow/pin, cycle-raises, leaf).

staleness: entry_staleness's two axes (followed-alias-advanced, source-drifted)
and scan's direct-vs-transitive tagging, covered with real catalog_create /
catalog_revise builds — alias advance marks a follow child stale, a hash pin
does not, an in-place source edit marks the entry stale under cas, off mode
reports the source axis unknown (not fresh), and a source edit under a chain
root tags descendants transitively stale.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the read-only half of the reactive consumer (epic #89), turning
tests/test_dependents.py and tests/test_staleness.py green.

- tallyman_xorq/dependents.py — parents_of / build_dag / dependents_index /
  descendant_cone over manifest.parents. Rebuilds the DAG walk #76 deleted with
  lineage.py (the walk, not the column lineage), enumerating via
  build.list_entries so an uncheckpointed entry is still seen. descendant_cone
  is topologically ordered (Kahn) and raises DependencyCycleError rather than
  looping on a cycle.
- tallyman_xorq/staleness.py — entry_staleness over two read-only axes
  (followed-alias-advanced; source-drifted, forced past digest_for's stat memo)
  and scan tagging direct vs transitive staleness across the dependency cone.
  off mode reports the source axis unknown, not fresh. result_digest is not a
  staleness input — recompute nondeterminism is the #83/#121 path, not this one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@paddymul
paddymul force-pushed the feat/89-reactive-consumer-staleness branch from 67272c6 to b8eb009 Compare June 18, 2026 20:06
@paddymul
paddymul merged commit 258f04d into main Jun 18, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant