--- title: "Persistence And Migrations" description: "How executor stores source state today and how migration logic is intended to work." --- Executor persistence is designed around durable runtime state, not just CRUD storage. That means the database stores: - installation and workspace identity - source bindings - recipes and revisions - raw documents - schema bundles - operations - credentials and auth sessions - executions and interactions ## Why the storage is layered The storage model is layered because these concerns have different reuse and ownership semantics. Examples: - raw documents are provenance - revisions are compiled materializations - operations are the hot-path query surface - source bindings are workspace-local - credentials are private and actor-scoped Trying to collapse those into one table or one document shape causes churn quickly. ## What is normalized vs serialized Executor intentionally uses both relational columns and serialized JSON. Normalized: - ownership - ids and references - search and lookup fields - status and visibility - auth/session ownership Serialized: - adapter-owned binding config - source config blobs - provider metadata - some session payloads This is deliberate. The rule is to normalize what the core system queries and enforces, and serialize what belongs to adapter-specific contracts. ## Migrations philosophy The intended migration model is: - SQL migrations move structural schema into the new shape - one-shot code migrations repair or rebuild data that SQL cannot derive sanely - steady-state runtime paths should not carry compatibility branches forever That last point matters. Compatibility logic belongs in upgrade code, not in normal reads and writes. ## What is still acceptable in migration code One-shot rebuilds are still acceptable when: - the old data shape is structurally valid but not yet in the best compiled form - the rebuild can be derived from already-stored documents - the logic is ledgered and run once What is not acceptable: - provider-specific legacy fallbacks in steady-state runtime logic - carrying old columns indefinitely because one path still reads them ## Why this supports extensibility This persistence shape is meant to let new integrations land in one of two ways: - add a new adapter that compiles into the same source model - add a genuinely new top-level primitive if the thing is not really a source That is how executor avoids turning every new integration into another schema redesign.