Skip to content

Reactive system doesn't propagate changes to derived attributes #103

@jameshgrn

Description

@jameshgrn

Summary

The reactive recalculation system (reactive.py) has gaps where derived attributes don't auto-update when their dependencies change.

Gaps Found

Derived Attribute Depends On Auto-Recalculates?
stream_order path_freq ❌ NO
path_segs topology ❌ NO
path_order dist_out ❌ NO
main_side path_freq ✅ YES

Impact

If topology is modified (e.g., fixing flow direction), these attributes become stale:

  • stream_order won't reflect new path_freq
  • path_segs won't reflect new segment boundaries
  • path_order won't reflect new distance ordering

Evidence

From reactive.py analysis:

  • _recalc_reach_main_side() exists and uses path_freq
  • No equivalent _recalc_reach_stream_order() or _recalc_reach_path_segs()

Recommendation

  1. Add stream_order to reactive dependency graph with trigger on path_freq change
  2. Add path_segs to reactive dependency graph with trigger on topology change
  3. Add path_order to reactive dependency graph with trigger on dist_out change
  4. Or document these as "static after initial computation" if intentional

Related


Design Notes (2026-02-17 brainstorming session)

Rescoped to v18. This is bigger than patching reactive.py — the real fix is a PostGIS trigger-based reactive system.

What we learned

  • The Python-side reactive.py (DuckDB) is obsolete for this purpose. Production data lives in PostgreSQL, edited via QGIS.
  • Geometry edits cascade: geometry → reach_id, reach_len → topology (rch_id_up/dn) → dist_out → path_freq → stream_order, path_order, path_segs. Too many dependencies to patch incrementally.
  • path_segs in reconstruction.py is also broken (0.1% match with v17b). Needs a rewrite regardless.

Target architecture (v18)

  • Pure PL/pgSQL trigger functions on sword_reaches_v17b (or v18 table)
  • CONSTRAINT TRIGGER ... INITIALLY DEFERRED so bulk QGIS edits recompute once at transaction commit
  • Scope recalculation to network column (subnetwork ID). If an edit merges two networks, recompute both.
  • path_freq: recursive CTE from outlets (n_rch_dn=0) upstream
  • stream_order: round(ln(path_freq)) + 1, side channels → -9999
  • path_order: ROW_NUMBER() OVER (PARTITION BY path_freq ORDER BY dist_out)
  • path_segs: needs correct junction-based segmentation algorithm (separate sub-task)

Schema reference (sword_reaches_v17b)

  • rch_id_up / rch_id_dn: space-delimited text
  • network: integer subnetwork ID (247 networks, largest is 125K reaches)
  • strm_order, path_freq, path_order, path_segs: bigint columns

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions