@lifecycle.storage.tasks. Add @lifecycle.storage.cron to the same factory for recurring work. There is no default database.
Choose and register a provider
harnest_postgres and harnest_redis ship with the Harnest runtime, not as separate PyPI distributions. Managed ADK and LangGraph environments include both drivers.
From your agent folder, synchronize the matching runtime:
[postgres,redis] to install both drivers. Imports do not open connections.
Register one shared factory in lifecycle/storage.py, replacing the corresponding existing storage factories:
Harnest calls the factory, starts the provider, and closes it once. Remove unused decorators; keep any file that still provides sessions or checkpoints.
Share connection configuration
Start your database service before the agent. Workers run inside Harnest; no separate queue engine is needed. For reusable configuration, move the factory intolib/. Use this instead of the inline task/cron factory above:
harnest_postgres.PostgresStore and a PostgreSQL URL. Declare custom adapter dependencies in your agent’s pyproject.toml.
From your agent folder, point the factory at your database and start the service:
Write a custom database adapter
Implement the public structural contracts; subclassing a Harnest store is not required:
Follow the protocol signatures and method docstrings. Records use UTC epoch seconds, JSON-safe payloads, and detached snapshots.
Register the adapter with
@lifecycle.storage.tasks and, when supported, @lifecycle.storage.cron.
Test a custom adapter
Put this in your adapter package’s tests and run it against an isolated real database, not only a mock:Execution and recovery guarantees
Delivery is at least once, not exactly-once external effects. A worker may crash after sending an email but before committing its result. Keep side effects idempotent. Cancellation prevents later state commits and requests cooperative execution cancellation; it cannot undo an effect or forcibly stop a synchronous function already running in a thread. The provider runtime polls for work, renews attempt leases, and periodically reconciles retained results with durable continuation storage. Use a persistent Harnest-owned checkpointer forhandle.result() to resume across restarts; persistent Task storage alone does not make a memory checkpointer durable. Queue ordering is best-effort, not strict FIFO across replicas and recovery.
Cron cursors survive restart. Missed occurrences catch up in bounded batches; there is no skip/coalesce misfire option in this version. Cancelling a schedule before its atomic occurrence commit prevents that enqueue; already committed jobs continue. Removed static declarations are paused at the next deployment startup, while a retargeted declaration gets a new identity. Do not run old and new conflicting static declarations concurrently during a rolling deployment.
Move an existing application safely
The Procrastinate backend and automatic PostgreSQL fallback have been removed. If you used them, drain work on your currently deployed version before upgrading. The new runtime cannot process the old queue tables.- Back up your database and inventory recurring schedules for each owner.
- Stop new submissions and pause recurring schedules on the old runtime.
- Drain or explicitly cancel queued/running Tasks and resolve their waiting continuations before stopping the old workers.
- Configure the new provider, preserving session/checkpoint storage when it contains history you need. Synchronize the runtime and recompile your agent. Start the new deployment and recreate dynamic schedules under their original user scopes. Static declarations reconcile automatically.
- Verify task execution, recovery, and owner isolation before reopening traffic.