100%

Goal

R6 + R13 (staleness half of the unified heal) — every /query-intel call auto-checks its own staleness and self-heals inline (no separate refresh call), with a freshness threshold to avoid thrash, lock-aware, graceful-degrade. Reconcile to ONE staleness owner (retire the package-generator stamp duplication).

Implementation Sketch

  • scripts/intel-query.sh / intel_repo/neo4j/query_graph.py read path: on each query, compute staleness via intel_repo/neo4j/staleness.py and compare Section.intel_package.generated_at_sha against the current graph head; on a stale-flag whose age exceeds the freshness-threshold TTL, trigger the existing lock-aware refresh inline, then serve fresh data; refresh failure => serve stale + flag (never block the query).
  • Freshness-threshold TTL: intel_repo/neo4j/staleness.py is binary-stale today (stale | fresh, no age horizon). Add a freshness_ttl parameter (a Duration-typed staleness horizon) so inline auto-refresh fires ONLY when the stale window exceeds the TTL — without it every stale query thrashes a refresh. SCOPE inline auto-refresh to the LOW-LATENCY staleness classes (code, plan) only; HIGH-LATENCY classes (embedding, cpg) serve stale + flag and never trigger an inline refresh (their rebuild cost would block the query past any acceptable horizon — they refresh out-of-band).
  • Expose ONE heal entry — the unified heal wrapper scripts/intel_package_runtime/heal.py:heal_section_package(plan_dir, section_id, *, freshness_ttl) — that the presence half (consumer-required-reading / s-9a707429) ALSO calls. Both halves invoke this single entry, comparing Section.intel_package.generated_at_sha against the graph head: the presence half routes the missing -> generate arm, the staleness half (this section) routes the stale-past-TTL -> refresh arm. No second heal path (SSOT); the wrapper is the SOLE generate-or-refresh entry both halves share. Placement is ori_lang-side (scripts/intel_package_runtime/, the package runtime that owns should_regenerate + generate + persist), NOT intel_repo/neo4j/ — the wrapper operates on plan Section + intel-package sidecars (ori_lang concepts) and intel_repo must not depend on ori_lang per its one-way data-flow boundary (intel_repo/CLAUDE.md).
  • Retire the package-generator --prior stamp comparison once the query layer self-heals (one owner per R6).

Spec References

  • scripts/intel-query.sh (refresh subcommand — already lock-aware, exits 0)
  • intel_repo/neo4j/staleness.py (binary-stale staleness classifier — gains a freshness_ttl horizon parameter + low-latency-vs-high-latency class scoping)
  • scripts/intel_package_runtime/heal.py:heal_section_package (the SINGLE unified heal wrapper shared with the presence half; ori_lang-side per the intel_repo one-way boundary; compares Section.intel_package.generated_at_sha vs graph head via the existing should_regenerate decision)
  • intel_repo/neo4j/staleness.py graph-side freshness_ttl is the GRAPH self-heal horizon (distinct concern from the plan-section-package heal above; graph-older-than-HEAD inline refresh scoped to low-latency classes)
  • scripts/intel_package_runtime/generate.py (the --prior stamp logic being retired)
  • plans/intel-package-dossier-rebuild/content/consumer-required-reading--s-9a707429.md (presence half — calls the SAME heal_section_package entry)
  • intel-package-rebuild-notes.md R6, R13

Work Items

  • Auto-refresh-on-stale read path: add a freshness_ttl (Duration-typed staleness horizon) parameter to intel_repo/neo4j/staleness.py (today binary-stale, no age horizon); inline auto-refresh fires ONLY when the stale window exceeds the TTL, SCOPED to the low-latency staleness classes (code, plan); high-latency classes (embedding, cpg) serve stale + flag, never inline-refresh. Lock-aware (reuse intel-query.sh refresh) + graceful degrade (refresh failure => serve stale + flag, never block).
  • Single heal entry — scripts/intel_package_runtime/heal.py:heal_section_package — shared with the presence half (consumer-required-reading / s-9a707429); this section routes the stale-past-TTL -> refresh arm of the shared wrapper. No parallel heal path.
  • Retire the package-generator staleness stamp (one staleness owner).

Verification

FLAGGED FOR EXECUTION PHASE (plan.json edits — out of editor scope per the route-access ban at plan-read-discipline.md §2.1; same sanctioned pattern as schema-integration (s-e2117453) used for sc-0007):

  • sc-0008 per-edge-case pins: success_criterion sc-0008 currently probes a generic suite bucket. During execution, tighten sc-0008 to NAMED per-edge-case pytest pins via the sanctioned write API (scripts/plan_corpus/write.py:amend-success-criterion / criterion edit), so the criterion’s probe asserts each edge case rather than a single bucket: (a) stale-past-TTL on a low-latency class (code / plan) triggers exactly ONE inline lock-aware refresh then serves fresh; (b) stale-within-TTL serves stale WITHOUT a refresh (no thrash); (c) a high-latency class (embedding / cpg) serves stale + flag and NEVER inline-refreshes; (d) refresh failure serves stale + flag and never blocks the query (graceful degrade); (e) the presence half and this staleness half both resolve through the SAME heal_section_package entry (SSOT — no second heal path).
  • work_items ordering dependency: w-eee3706d (auto-refresh read path establishing the freshness_ttl parameter + heal-arm wiring) MUST precede w-36680518 (retire the package-generator stamp) — the stamp cannot be retired as the staleness owner until the query layer self-heals. Execution ORDER is ALREADY encoded by the lexorank key (w-eee3706d < w-36680518); during execution, add the ADVISORY ordering edge as hint_needs: on w-36680518 via the sanctioned work_item write API per .claude/rules/v7-plan-system.md INV-6/INV-15. depends_on is a retired v7 field (WorkItem is additionalProperties:false) — NEVER a raw plan.json edit, NEVER prose reordering.

HISTORY

  • 2026-06-13 — heal-wrapper placement corrected (boundary-correct): the heal wrapper was specified at intel_repo/neo4j/heal.py; execution-phase analysis found that placement inverts the intel_repo one-way data-flow boundary (intel_repo/CLAUDE.md: ori_lang has no dependency on / knowledge of the graph DB; data flows graph → ori_lang). heal_section_package operates on plan Section + intel-package sidecars (ori_lang concepts) and consumes should_regenerate + generate + persist (all scripts/intel_package_runtime/), so it lands ori_lang-side at scripts/intel_package_runtime/heal.py. Two distinct staleness concerns are kept separate: GRAPH staleness (intel_repo/neo4j/staleness.py freshness_ttl graph self-heal, low-latency classes inline) vs PLAN-SECTION-PACKAGE staleness (ori_lang heal_section_package via should_regenerate). Each retains ONE owner (R6); the cross-boundary “single graph-side entry” reading is the corrected design.