fix: align Crons API with LangGraph Platform spec (#44)#45
Open
webup wants to merge 15 commits into
Open
Conversation
…data, end_time, next_run_date)
- Add additive column migration on startup so end_time/on_run_completed exist on pre-existing tables (create_all does not alter existing tables); server_default backfills legacy rows (#1) - on_run_completed=delete no longer deletes interrupted (resumable) runs (#2) - Enforce end_time on the stale-'started' tick reclaim path (#4) - Share a validating stream-mode normalizer between runs and crons; empty list and unsupported modes are rejected, not silently dropped (#5, #11) - Make multitask_strategy patchable; simplify patch run-control fold to a dict-merge; explicit-null clears a field to its default (#8, #15) - Consistent null-field handling across cron search/get/patch responses (#9) - Extract shared delete_threads_cascade in thread_service, reused by the delete endpoint and the cron scheduler (batched); removes duplication (#10, #13, #14) - Sort tiebreaker follows the primary sort direction for stable pagination (#6) - Name the declared-field guard; consolidate run-control default constants (#12)
…olish - _apply_additive_migrations skips (with a loud error) any NOT NULL column lacking a server_default rather than emitting DDL that fails on a populated table — forward-looking guard for future columns - Drop dead apply_metadata_filters import from api/crons.py - Add tests: migration skips unsafe column; search select omits null fields
- Verify Run rows are actually deleted by delete_threads_cascade against the real DB, on both the stateless-cron path and the thread DELETE endpoint (previously only the Thread was checked; a dropped delete(Run) shipped green) - Cover on_run_completed=delete for error runs too (parametrized with interrupted): only success triggers deletion - Explicit-null clear for interrupt_before and stream_mode (stream_mode -> stream_modes key remap) - Pin the metadata non-string-value search limitation; assert select projection still honors owner auth filters - Add dedicated unit tests for the shared stream_modes normalizer - Add end_time == now boundary test; assert next_run_date in the API body Verified non-tautological via mutation testing (dropped delete(Run), weakened the delete gate, broke null-clear — each fails the corresponding test).
…on API The cron unit/integration tests run on in-memory SQLite. This adds e2e tests that drive the cron HTTP surface against the live API + real MySQL-family backend, so CI exercises the backend-divergent paths SQLite hides: the additive startup migration's ALTER TABLE ADD COLUMN DDL, JSON column round-trips for payload/metadata/kwargs, and JSON extraction for metadata filtering + sort_by. Runs automatically under the existing mysql-family-checkpoint-validation and pgsql-metadata-checkpoint-validation CI jobs (both invoke make test-checkpoints -> pytest tests/e2e -m e2e) across seekdb, oceanbase, and mysql backends — no workflow change needed. Validated locally against embedded SeekDB (2 passed).
- Add real-DB migration upgrade test: create a legacy table missing a column, run _apply_additive_migrations, assert the dialect's ALTER TABLE ADD COLUMN executes and backfills via server_default on the live backend (was SQLite-only) - Add real-DB scheduler cascade-delete test: on_run_completed='delete' reconcile removes the stateless thread AND its run via the cross-table cascade (the highest-risk data-deletion path; was SQLite-only) - Pin the numeric-metadata-filter safety invariant on the real backend (no cross-cron false positives, regardless of JSON coercion differences) - New e2e_db fixture wires the in-process db_manager to the real backend so scheduler/migration functions can be invoked directly (async fixture + async tests to share pytest-asyncio's loop and avoid cross-loop engine binding) - Record the deliberate FOR UPDATE-locking coverage boundary Validated against embedded SeekDB: 4 cron e2e tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #44.
Aligns the Crons API with the LangGraph Platform OpenAPI spec as an additive superset: every missing spec field is added and wired to real behavior, while the project's existing extensions (webhook delivery, timezone,
last_*tracking, theGET /runs/crons/{cron_id}endpoint) are kept.What changed
Schemas (
models/api.py)CronCreate:end_time,interrupt_before/after,on_run_completed,stream_mode,stream_subgraphs,stream_resumable,durability;extra="allow"per specadditionalProperties: true.ThreadCronCreate(separate schema withmultitask_strategy, noon_run_completed).CronPatch: same fields incl.multitask_strategy(now patchable).CronRead:user_id,payload,metadata,end_time,next_run_date(+next_run_atretained as a deprecated alias).CronSearchRequest:metadata,sort_by,sort_order,select,limitbounds (1–1000).CronCountRequest:metadata.Behavior
kwargs_json(mirrorsruns.py) and applied at dispatch.end_timeenforced by the scheduler (stops firing; disables oncenext_runcrosses it; honored on the stale-tick reclaim path too).on_run_completed=deletedeletes the ephemeral stateless thread after a successful run only (interrupted/error runs are preserved — resumable/inspectable).create_alldoes not alter existing tables); guards against unsafe NOT NULL columns without a server_default.delete_threads_cascade(inthread_service) reused by both the thread delete endpoint and the cron scheduler.Testing
tests/e2e/test_cron_api_live.py): cron CRUD + new fields + sort/metadata/select over real HTTP. Runs automatically under the existingmysql-family-checkpoint-validationandpgsql-metadata-checkpoint-validationCI jobs across seekdb / oceanbase / mysql (+ Postgres metadata). Validated locally against embedded SeekDB.Review
Went through two rounds of code review (correctness + test-solidity); all actionable findings addressed. Notable behavior changes flagged for reviewers:
on_run_completeddefaults todelete(spec default) — stateless crons now clean up their ephemeral threads.limitminimum tightened from 0 to 1.