Skip to content

feat(kt-db): find_out_of_date_graphs helper (Phase 7 #58)#260

Merged
charlie83Gs merged 2 commits intomainfrom
feat/find-out-of-date-graphs
Apr 20, 2026
Merged

feat(kt-db): find_out_of_date_graphs helper (Phase 7 #58)#260
charlie83Gs merged 2 commits intomainfrom
feat/find-out-of-date-graphs

Conversation

@charlie83Gs
Copy link
Copy Markdown
Contributor

Summary

Pure-ish helper for the Phase 7 startup auto-dispatch hook. Given a DB session + plugin registry, returns every active graph whose `graph_type_id` resolves to a registered `GraphTypePlugin` with a higher `current_version` than the graph's `graph_type_version`.

Split from the eventual dispatcher so discovery is unit-testable without Hatchet. The auto-dispatch call site will compose this with:

Once all three land, the startup hook is ~20 lines of orchestration.

Filtering decisions

Condition Behaviour
`status='deleted'` skip (don't resurrect banner)
`status='provisioning'` or `'error'` skip (don't migrate on top of half-built schemas)
`version >= current` (at-current or rollback) skip
`graph_type_id` unregistered log + skip, not raise

Unregistered-plugin posture is deliberate: the runtime resolver falls back to `'default'` for the live pipeline, so writes keep flowing. Auto-dispatch shouldn't fail the whole sweep over one stale pointer. Per CLAUDE.md fail-fast rule this is NOT silent data drop — the WARNING line surfaces the stale id so ops can fix it.

Ordering

Results ordered by `created_at` for deterministic output. Operator tailing logs during a migration rollout sees the same order every boot.

Test plan

  • 10 new integration tests: empty, detects behind, skips at-current, skips ahead-of-plugin (rollback), skips deleted/provisioning/error statuses, unregistered plugin logs + skips without raising, unregistered does not abort other graphs, deterministic ordering
  • `libs/kt-db` unit: 57 pass; integration: 162 pass (10 new)
  • `ruff` + pre-push lefthook green

🤖 Generated with Claude Code

charlie83Gs and others added 2 commits April 20, 2026 13:02
Pure-ish helper for the Phase 7 startup auto-dispatch hook. Takes a
DB session + plugin registry, returns every active graph whose
declared graph_type_id resolves to a registered GraphTypePlugin
with a higher current_version than the graph's graph_type_version.

Split from the dispatcher so the discovery half is unit-testable
without Hatchet. The auto-dispatch call site (#58 workflow wiring)
composes this with:
- plan_migrations (#254, landed) for the hop list
- GraphMigrationRunRepository.most_recent_for_graph (PR #258) for
  "is anything already in flight?"
- graph_migration_wf dispatch (#57)

Filters:
- status='deleted' skipped (don't resurrect banner)
- status='provisioning'/'error' skipped (don't run data migrations
  on half-built schemas)
- graph_type_version >= plugin.current_version skipped (at-current
  or rollback)
- unregistered graph_type_id: LOG + skip (not raise). The runtime
  resolver falls back to 'default'; auto-dispatch should not fail
  the whole sweep over a single stale pointer. Per CLAUDE.md fail-
  fast rule: not silently dropping bad data — the log line surfaces
  the stale id so ops can fix it.

Ordered by created_at for deterministic output — operator tailing
logs during a migration rollout sees the same order every boot.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…elta tiebreak

- Status filter inverted from ``status == 'active'`` to
  ``status.not_in(('deleted', 'provisioning', 'error'))`` — matches
  the docstring's exclusion list and makes future statuses
  (``archived`` / ``suspended`` / etc.) opt-out rather than opt-in.
  Reviewer flagged the drift risk correctly; the inversion moves
  the question to "do we skip this?" at review time.
- Test uses ``timedelta(microseconds=1)`` instead of mutating via
  ``replace()`` — same 1/1M flake guard as PR #258.
- Comment on ``_V2Plugin.migrations()`` + ``_M`` stub explaining the
  stub is load-bearing for ``register_graph_type``'s hop-coverage
  validator (PR #253), not dead code. Discovery tests don't call
  the planner, but they DO call ``register_graph_type``, which is
  what needs the migration row.
- New test ``test_unknown_status_is_auto_included`` pins the
  inverted-filter contract so a future refactor that silently
  narrows back to ``== 'active'`` trips here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@charlie83Gs
Copy link
Copy Markdown
Contributor Author

Review fixes applied

1. Status filter drift — inverted from `status == 'active'` to `status.not_in(('deleted', 'provisioning', 'error'))`. Docstring and code now match; a future status (`archived`, `suspended`, …) is auto-included in auto-dispatch unless we explicitly skip it, which forces the decision at review time. New test `test_unknown_status_is_auto_included` pins the contract so nobody silently narrows back to `== 'active'` later.

2. `created_at.replace(microsecond=…)` flake — swapped for `first.created_at + timedelta(microseconds=1)`. Same fix pattern as PR #258.

3. `_M` stub "dead code" claim — actually load-bearing. Discovery tests don't call the planner, but they DO call `PluginRegistry.register_graph_type`, which validates (via PR #253) that every hop from v1 to `current_version` has at least one migration declared. Without `_V2Plugin.migrations()` returning the stub, registration would raise and every test for v2 plugin scenarios would fail setup. Added a comment on both the `migrations()` override and the `_M` class explaining this.

4. Load-all-active-graphs — noted as future concern for world-graph vision. No fix this PR (per your note).

5. Plugin ref on `OutOfDateGraph` — bootup-scoped use; kept as-is per your note.

11/11 tests pass (1 new).

@charlie83Gs charlie83Gs merged commit 6665254 into main Apr 20, 2026
24 checks passed
@charlie83Gs charlie83Gs deleted the feat/find-out-of-date-graphs branch April 20, 2026 19:49
@github-actions
Copy link
Copy Markdown


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

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