{"schema":"stack-menu-rewrite/1","reviewed_at":"2026-10-11","evidence":"documentation-reviewed","qualification":"These are proposed engineering plans supported by documentation. No source/target pair, application integration, migration or performance claim has been qualified by execution.","shared":{"schema":"stack-menu-migration-contracts/1","reviewed_at":"2026-10-11","evidence":"documentation-reviewed","qualification":"These are proposed engineering plans supported by documentation. No source/target pair, application integration, migration or performance claim has been qualified by execution.","modes":[{"id":"compatible","title":"Compatible replacement","description":"Preserve the declared external behavior and operated dependencies. Any changed guarantee is a separate decision."},{"id":"incremental","title":"Staged transition","description":"Move a bounded responsibility while old and new systems coexist. Each data writer, job type and schedule has one declared execution owner."},{"id":"target","title":"Target architecture","description":"Evaluate the target stack's idioms and different dependencies. Qualify changed guarantees and recovery before retiring the old system."}],"contracts":[{"id":"data","title":"Data and schema","inventory":"Actual database, schema versions, migration owner, IDs, null/default semantics, time zones, transactions and backup/restore.","gate":"Keep one schema migration owner and explicitly partition authoritative writers. Use additive schema changes while both versions run; validate old reads against new writes and the reverse. Reconcile rows before destructive contraction.","rollback":"Keep backward-readable data during the rollback window. Reversing traffic cannot undo a destructive migration or external effect."},{"id":"jobs","title":"Jobs and schedules","inventory":"Actual adapter/broker, payload version, serialization, producer/consumer, retry policy, scheduled/failed/in-flight backlog and scheduler owner.","gate":"Route each job type to one execution owner. Prove crash recovery and idempotent business effects, including a crash after the effect but before acknowledgement. Same broker access does not prove payload compatibility.","rollback":"Stop new producers before returning ownership; reconcile every backlog state and preserve old consumers until the rollback window ends."},{"id":"cache","title":"Caching and shared state","inventory":"Every use of Redis or another store: disposable cache, sessions, locks, counters, broker or rate limits. Record keys, namespace, codec, TTL and invalidation.","gate":"Qualify shared formats or deliberately isolate namespaces. A local cache must tolerate loss and staleness; it cannot silently replace shared sessions, locks or global counters.","rollback":"Retain the previous shared service until all uses are replaced and reconciled. Invalidate incompatible entries and maintain an explicit routing strategy."},{"id":"identity","title":"Identity and authorization","inventory":"Session/cookie format, signing/encryption, expiry, revocation, issuer/audience, CSRF, role and tenant/resource permissions.","gate":"Test old login/new request, new login/old request, logout, expiry, denied access and tenant isolation. Connecting to the same identity provider does not recreate authorization.","rollback":"Keep old token/session verification while required. New-only credential or policy formats need a deliberate reauthentication or conversion strategy."},{"id":"realtime","title":"Realtime transport","inventory":"Wire protocol, channel names, subscription authorization, ordering, reconnect/replay, cross-node fanout and deployment proxy behavior.","gate":"Keep stable cohorts including sockets and reconnects. Verify delivery old-to-new and new-to-old. PubSub inside one ecosystem is not an adapter for another client's protocol.","rollback":"Keep the bridge and old endpoint until connections are drained and clients can reconnect; account for missed events and changed offsets."},{"id":"files","title":"Files and objects","inventory":"Object key and metadata schema, stored-file references, signed URLs, access policies, upload limits and lifecycle/retention owners.","gate":"Exercise old upload/new read and the reverse, expiry, rejected access and partial upload cleanup. Sharing a bucket does not translate framework-specific database records.","rollback":"Preserve old readable references and object retention until reconciliation. Do not delete objects while old clients or jobs can still reference them."},{"id":"email","title":"Email and external effects","inventory":"Provider, template IDs, delivery identity, deduplication keys, retries, webhooks and bounced/failed-message handling.","gate":"Use sandbox recipients for comparison and one live execution owner. A local sent flag cannot close the crash window after provider acceptance; reconcile using stable effect IDs where supported.","rollback":"Stop new sends before switching owner and reconcile accepted requests. Traffic rollback cannot unsend an email or reverse a payment."},{"id":"search","title":"Search indexes","inventory":"Source of truth, document IDs, analysis/mapping version, update/delete feeds, access filters and rebuild checkpoint.","gate":"Build a new versioned index from authoritative data; replay mutations and deletions, reconcile results and permissions, then switch readers. Keep only one indexing owner per feed.","rollback":"Keep the old index and update feed during the rollback window; reconcile lag before switching back."},{"id":"operations","title":"Delivery and recovery","inventory":"Process/service inventory, runtime/library versions, licensing, deployment ownership, health/readiness, logs, metrics, recovery objectives and rollback window.","gate":"Exercise graceful shutdown and forced termination separately. Compare matched behavior, durable storage, load and tuning budgets; report latency, errors, resource use and queue delay rather than a universal language score.","rollback":"Keep a runnable previous artifact, backward-readable contracts and tested recovery. Define application-specific promotion thresholds before rollout."},{"id":"cli","title":"CLI and pipe contracts","inventory":"Command/flag names, stdin/stdout/stderr, JSON/text formats, exit codes, paths, config versions and supported OS/architecture packages.","gate":"Run captured scripts against both binaries; compare output/exit behavior, cancellation and interrupted writes. A faster binary is not a compatible replacement if downstream parsers break.","rollback":"Retain the previous binary and a configuration backup. New persistent formats require backward readability or an explicit conversion."},{"id":"client","title":"Browser and installed clients","inventory":"Supported client/build versions, API schemas, local storage versions, offline writes, release cohorts, adoption and asset retention.","gate":"Test the oldest supported client against the new backend and fresh clients against retained APIs. Keep assets/endpoints while installed or open clients still use them; exercise offline conflicts and local data upgrades.","rollback":"A server rollback does not remove an installed application update. Preserve API compatibility and use a forward repair for irreversible local data migrations."}],"phases":[{"id":"inventory","title":"Inventory and choose the boundary","actions":["Inspect runtime pins, manifests, lockfiles and actual infrastructure uses.","Choose an endpoint, tenant, job type, command or client release cohort; name every writer, consumer and scheduler owner."],"gate":"Versioned contract fixtures, side-effect inventory, baseline workload and explicit unknowns are recorded.","rollback":"Existing implementation remains authoritative."},{"id":"contract","title":"Build behind retained contracts","actions":["Implement target-stack boundaries without copying source-framework internals.","Keep old workers or services where compatibility is required; qualify adapters before sharing payloads.","Use a shared transaction for data plus queue insertion when possible, or a durable outbox with replay-safe consumers across stores."],"gate":"Old/new reads, writes, identity and selected capability fixtures pass; changes to guarantees are documented.","rollback":"Retain old artifacts and backward-readable schemas."},{"id":"qualify","title":"Qualify behavior and recovery","actions":["Compare outcomes with isolated effects; never duplicate live emails or payments in shadow traffic.","Exercise crashes, retries, network interruption, load limits and restore.","Set business, latency, error, queue-age and resource thresholds before cohort promotion."],"gate":"Executed checks name exact versions, environment, expected/actual result and failures; documentation review alone does not pass.","rollback":"Run the recovery and owner-transfer procedure in a representative environment."},{"id":"cohort","title":"Route a bounded cohort","actions":["Use stable cohorts spanning sessions, writes, subscriptions and reconnects.","Transfer job and scheduler ownership explicitly rather than letting both execute.","Monitor business outcomes and reconcile data/backlog during each increase."],"gate":"Measured outcomes meet the application's declared thresholds and rollback remains viable.","rollback":"Reverse routing only while contracts remain backward-readable; reconcile in-flight effects."},{"id":"cutover","title":"Transfer ownership and reconcile","actions":["Move remaining producers, consumers and schedules in an explicit order.","Account for ready, scheduled, retry, failed and in-flight work.","Keep old workers, objects, assets and client contracts through the rollback window."],"gate":"Every responsibility has a single named owner and reconciliation finds no unaccounted work.","rollback":"Preserve the old execution path until the recorded window closes; irreversible effects need compensation or reconciliation."},{"id":"retire","title":"Evaluate the target and retire dependencies","actions":["Adopt local caching, another queue engine or different UI protocols as separately qualified changes.","Retire an operated service only after every consumer and recovery requirement is accounted for.","Measure the target architecture separately from coexistence and preserve the evidence."],"gate":"Claims of fewer services, lower cost or better performance have their prerequisites and actual measurement; a 100% traffic shift is insufficient.","rollback":"Use the preserved service/artifact while viable; after irreversible contraction, follow the documented restore or forward-repair plan."}],"pair_notes":[{"source":"rails-hotwire-postgresql","target":"elixir-phoenix-liveview","title":"Rails to Phoenix: three job paths","body":"Recommendation: first inspect the actual Active Job adapter; Rails does not imply Redis. Keep Ruby workers behind an explicit submission interface, qualify a Redis-compatible Elixir consumer against exact wrappers and retries, or route selected job types to Oban while retaining legacy workers. Exq is a compatibility candidate, not proof that arbitrary Active Job jobs can execute unchanged. Oban ships SQL persistence engines, not Redis persistence. Running SQL beside Redis is feasible but adds operations; atomic domain write and enqueue requires the same transaction, otherwise use an outbox. Rails cache codecs and Action Cable frames need explicit bridges. ETS suits reconstructible node-local cache only; Redis may remain the right permanent dependency.","sources":["https://guides.rubyonrails.org/active_job_basics.html","https://guides.rubyonrails.org/caching_with_rails.html","https://oban.hexdocs.pm/installation.html","https://oban.hexdocs.pm/Oban.html","https://exq.hexdocs.pm/readme.html","https://github.com/sidekiq/sidekiq/wiki/Job-Format","https://www.erlang.org/docs/27/apps/stdlib/ets.html"]},{"source":"typescript-next-node","target":"typescript-react-vite-browser","title":"Full-stack React to a browser client","body":"Recommendation: separate the frontend from server responsibilities before moving UI routes. A Vite bundle does not replace server rendering, server functions, database access or private secrets. Retain an API/backend and define identity, CORS/CSRF, cache invalidation and asset-version contracts. Qualify any changed rendering, routing or offline behavior with open old browser sessions.","sources":["https://nextjs.org/docs/app/getting-started/server-and-client-components","https://vite.dev/guide/backend-integration.html"]},{"source":"python-django","target":"python-fastapi","title":"Django to FastAPI: move an API boundary","body":"Recommendation: retain Django as schema and identity owner while moving an explicit API component. Sharing PostgreSQL or Python does not preserve Django sessions, ORM migrations, middleware, permissions or Celery payload contracts. Keep one migration owner and isolate workers; moving to async handlers does not turn blocking database or SDK calls into nonblocking work.","sources":["https://docs.djangoproject.com/en/6.0/topics/async/","https://fastapi.tiangolo.com/async/","https://fastapi.tiangolo.com/tutorial/background-tasks/"]}],"sources":[{"url":"https://learn.microsoft.com/en-us/azure/architecture/patterns/strangler-fig","title":"Incremental replacement with a facade","claim":"Routing boundaries and incremental coexistence; per-application design still requires qualification."},{"url":"https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html","title":"Transactional outbox","claim":"Cross-store delivery recommendation; committed outbox plus relay, duplicates and ordering require explicit handling."},{"url":"https://github.com/sidekiq/sidekiq/wiki/Job-Format","title":"Sidekiq job format","claim":"Payload serialization is a compatibility contract, not just a broker connection."},{"url":"https://github.com/sidekiq/sidekiq/wiki/Best-Practices","title":"Sidekiq retries and idempotency","claim":"Retryable jobs need replay-safe effects; no exactly-once execution assumption."}]},"profiles":[{"id":"elixir-phoenix-liveview","version_scope":"Fetched documentation: Phoenix 1.8.15, LiveView 1.2.12, PubSub 2.4.1, Plug 1.20.3, Ecto 3.14.2 / Ecto SQL 3.14.0, Oban 2.24.1, Exq 0.25.0 and OTP 29.1.1 ETS. Installed dependency compatibility is unqualified; pin the actual application before using version-specific APIs.","operating_model":"Phoenix web modules call application contexts; Ecto supplies persistence boundaries. LiveView maintains connected server-side interaction, while Channels expose a client-facing realtime protocol. Recommendation: model domain operations through contexts and supervised responsibilities rather than translating every old controller, global singleton or database serialization bottleneck literally.","architecture":["Recommendation: contexts own business operations, Ecto Repo owns database access and explicit transactions join related writes. A single GenServer around all queries is an application choice, not a Phoenix requirement.","Phoenix PubSub has a distributed-Elixir adapter and a Redis adapter. Redis can remain appropriate when deployment or interoperability requires it. Recommendation: identify the required delivery boundary before selecting the topology.","Oban requires an Ecto-backed supported SQL engine. Its installation documents PostgreSQL, SQLite and MySQL; selecting PostgreSQL is this catalogue's target composition, not a universal Oban prerequisite. Recommendation: preserve durable external effects through idempotency and recovery checks."],"developer_workflow":["For a disposable target, mix phx.new PATH generates a Phoenix application with selectable database and HTML/LiveView options. In an existing project, inspect mix.lock, generated modules and maintained conventions before regenerating anything.","When manually installing the selected Oban version, adapt mix ecto.gen.migration add_oban_jobs_table and mix ecto.migrate from its installation guide; configure the engine, Repo and supervised Oban child. Do not copy PostgreSQL settings into a SQLite or MySQL target.","Use mix test for application tests and the generated Phoenix connection/channel fixtures. Recommendation: add mixed-version payload, authorization and restart scenarios; a passing formatter or compiler does not establish those contracts.","For a release build, adapt MIX_ENV=prod mix assets.deploy and mix release from the Phoenix deployment workflow; runtime.exs owns runtime configuration. Recommendation: verify database migrations, job supervision and cluster/PubSub connectivity separately from HTTP startup."],"avoid":["Avoid replacing a durable queue with spawned tasks or an in-memory notification list while calling the change equivalent. Recommendation: explicitly accept the changed crash and retry behavior or retain a persistent queue.","Avoid treating Exq's Resque/Sidekiq-compatible transport format as proof that Ruby Active Job wrappers, classes and serializers execute in Elixir. The exact consumer and payload contract still needs qualification.","Avoid assuming Plug cookies decode Rails cookies: Plug exposes its own serializer, salts and cryptographic options. Avoid relying only on HTTP plugs for LiveView authorization; mount and events require checks.","Avoid using ETS as a shared session or global lock store. Tables are local to their node and normally disappear with their owner unless an heir takes ownership. Recommendation: reserve ETS for explicitly reconstructible local state."],"transitions":[{"capability":"data","existing_contract":"Inventory schema, constraints, record identifiers and callbacks previously triggered by writes. Ecto schemas and changesets must preserve the intended domain semantics, not simply the old table shapes.","coexistence":"Recommendation: keep the source application as the authoritative writer while Phoenix contexts read fixtures; move a tenant or domain operation with one named writer. Use additive schema changes and a single migration runner during shared-table operation.","target":"Contexts calling Ecto Repo with explicit validation and transaction boundaries. Ecto.Multi composes related Repo operations in one database transaction; cross-store side effects require a separate delivery design.","benefits":[{"benefit":"Recommendation: a context boundary can expose one reusable domain operation to LiveView, controllers and workers.","prerequisites":"Resource authorization, constraints and side effects moved with that operation.","tradeoffs":"Shared-schema coexistence limits schema changes; external effects do not become part of a database transaction."}],"checks":["Write a fixture through each allowed writer and read it from both runtimes; compare IDs, enum/null values, timestamps and constraint failures.","Fail the second operation in an Ecto.Multi and verify the first database operation is absent; prove old readers still accept successful new writes."],"rollback":"Recommendation: return the domain writer to the source while preserving additive schema and reconciling accepted new writes; delay destructive contractions until the rollback window closes.","sources":["https://hexdocs.pm/phoenix/contexts.html","https://hexdocs.pm/ecto/Ecto.Multi.html","https://hexdocs.pm/ecto_sql/Ecto.Migrator.html"],"id":"data-1"},{"capability":"jobs","existing_contract":"Rails workers currently own Ruby job execution, including Active Job/GlobalID serializers and backend retry/scheduling behavior. Record the actual Redis-backed backend and every backlog state rather than inferring it from the word Rails.","coexistence":"Recommendation, retain/wrap path: keep Ruby workers and their broker; expose a narrow producer endpoint or durable outbox relay for Phoenix. Ruby remains the only consumer and scheduler for retained categories, and the interface names payload version, business idempotency key and enqueue acknowledgement.","target":"Phoenix web/domain code with retained Ruby worker ownership is a valid permanent composition when queue interoperability or Ruby libraries remain necessary.","benefits":[{"benefit":"Recommendation: the web layer can transfer before job execution without transforming the old backlog.","prerequisites":"Defined producer interface, monitored delivery and retained worker deployment.","tradeoffs":"Two runtimes and the broker remain operated; domain write plus broker enqueue is not a shared SQL transaction."}],"checks":["Submit a captured serializer/GlobalID case through the interface and confirm the intended Ruby class executes with its normal failure policy.","Interrupt delivery after domain commit; recover it without duplicating a business effect. Verify only the Ruby scheduler owns the retained category."],"rollback":"Recommendation: restore the old web producer while retaining the same workers; reconcile accepted interface/outbox entries before disabling the relay.","sources":["https://guides.rubyonrails.org/active_job_basics.html","https://github.com/sidekiq/sidekiq/wiki/Job-Format","https://github.com/sidekiq/sidekiq/wiki/Best-Practices"],"id":"jobs-1","option_title":"Retain Ruby workers behind an interface"},{"capability":"jobs","existing_contract":"A retained Redis broker may carry Sidekiq/Resque-shaped jobs; framework wrappers, class names, serializer tags and version-sensitive timestamps are still part of the execution contract.","coexistence":"Recommendation, broker-retaining replacement path: qualify Exq for selected job types on isolated queues. Keep unsupported Ruby types on Ruby consumers; map only deliberately supported payload versions, and give each category one consumer family and schedule owner.","target":"Selected Elixir workers using Exq's documented Redis-backed Resque/Sidekiq-compatible format. This is a candidate for qualification, not a claim that arbitrary Active Job payloads are interoperable.","benefits":[{"benefit":"Recommendation: selected job execution can move to Elixir while preserving the broker boundary.","prerequisites":"Fixture-tested serialization, queue isolation, retry mapping and scheduler ownership.","tradeoffs":"Redis remains; compatibility and unsupported Ruby argument types require explicit maintenance."}],"checks":["Replay ordinary arguments, an Active Job wrapper, serializer edge cases and scheduled/retry payloads; reject unsupported cases without consuming them silently.","Crash after a business effect before acknowledgement and verify replay safety; confirm old and new consumers never race for an unqualified category."],"rollback":"Recommendation: stop new consumption and return only backward-readable queued work to Ruby; preserve both consumers for payloads accepted during the transition.","sources":["https://exq.hexdocs.pm/readme.html","https://github.com/sidekiq/sidekiq/wiki/Job-Format","https://guides.rubyonrails.org/active_job_basics.html"],"id":"jobs-2","option_title":"Keep Redis and qualify Exq consumers"},{"capability":"jobs","existing_contract":"Legacy Redis jobs and a proposed SQL queue have distinct payload/storage formats and retry/schedule state. A backend change does not copy the backlog or the recurring schedule.","coexistence":"Recommendation, SQL replacement path: run Oban alongside the legacy broker and route selected categories exclusively to one system. Keep old ready, retry, delayed, failed and running work accounted for. Transfer each schedule once; use a recoverable relay/outbox while domain data and queue insertion span stores.","target":"Oban workers on the chosen supported SQL engine. With the same Repo/transaction, Oban.insert in Ecto.Multi can join domain changes; a separate database cannot gain that atomicity merely from both databases being SQL.","benefits":[{"benefit":"Atomic domain-write-and-job-insert is available within a shared transaction.","prerequisites":"One supported database transaction/Repo for both writes and business idempotency for execution.","tradeoffs":"Queue load shares the database; SQL atomic insertion does not guarantee exactly-once external effects."},{"benefit":"Recommendation: broker retirement can reduce the operated service inventory.","prerequisites":"All Redis uses transferred, all backlog states reconciled and recovery/rollback checks passed.","tradeoffs":"Redis plus SQL coexistence temporarily adds operational work; no service reduction occurs while Redis remains needed."}],"checks":["Rollback the combined transaction and confirm neither domain change nor executable Oban job remains.","Pause a selected Oban queue, reconcile running work and inspect retained backlog; resume without duplicated effects.","Restart during a job and validate the selected engine's configured recovery policy, including a failure after an external side effect."],"rollback":"Recommendation: pause new producers/consumption, keep accepted Oban payloads executable, and reconcile effects before moving routing back. Do not blindly copy all rows into the old broker.","sources":["https://oban.hexdocs.pm/installation.html","https://oban.hexdocs.pm/Oban.html","https://hexdocs.pm/ecto/Ecto.Multi.html"],"id":"jobs-3","option_title":"Run Oban SQL workers alongside legacy queues"},{"capability":"identity","existing_contract":"Record old cookie encoding, serializer, keys/salts, expiration and session revocation; shared account IDs alone do not establish cookie compatibility. Plug COOKIE defaults its serializer to external_term_format and exposes rotating cryptographic options.","coexistence":"Recommendation: retain the existing identity authority through a qualified session exchange or compatibility decoder. Map authorization rules into Phoenix contexts and LiveView mount/events; keep account and revocation ownership explicit.","target":"Phoenix identity with the selected session store and LiveView authorization in mount/on_mount and actions. Recommendation: choose planned reauthentication when cookie compatibility is not worth maintaining.","benefits":[{"benefit":"Recommendation: users can preserve access while a connected feature moves.","prerequisites":"Qualified decoder/exchange, shared revocation and event-level authorization.","tradeoffs":"Long-lived sockets need explicit disconnect/recheck behavior when access changes; sharing secrets alone is insufficient."}],"checks":["Login through the old runtime, access the target feature, revoke the account/session and confirm HTTP plus existing/reconnected LiveView access follows the required policy.","Send a forged delete event for another tenant despite a hidden UI control; confirm the server rejects it."],"rollback":"Recommendation: retain the old decoder/exchange until routing can return; never reverse revocation merely to restore traffic.","sources":["https://hexdocs.pm/plug/Plug.Session.COOKIE.html","https://hexdocs.pm/phoenix_live_view/security-model.html","https://guides.rubyonrails.org/security.html"],"id":"identity-1"},{"capability":"realtime","existing_contract":"Action Cable, Phoenix Channels and LiveView are different protocols. Capture subscription identity, event envelope, authorization, ordering and missed-message recovery. Channels documents at-most-once client delivery without server persistence.","coexistence":"Recommendation: retain old client endpoints and bridge versioned domain events into each renderer. A room/tenant cohort owns its subscriptions and reconnects. Choose Redis PubSub or a qualified distributed-Elixir cluster deliberately; sharing Redis does not translate the wire protocol.","target":"LiveView for connected server-owned screens or Channels for explicit realtime clients, with durable domain state and an application catch-up mechanism when missed events matter.","benefits":[{"benefit":"Recommendation: connected UI and domain operations can share Phoenix contexts.","prerequisites":"Authorized subscriptions, working multi-node fanout and persisted state/cursors for recovery requirements.","tradeoffs":"Live protocol/client changes and cluster operations remain; PubSub does not supply a durable user-message history."}],"checks":["Place old/new subscribers on separate nodes; commit through either permitted path and verify every authorized recipient converges once.","Disconnect before an event, restart a node, reconnect with the chosen cursor/state mechanism and verify missed domain changes recover; test revocation while connected."],"rollback":"Recommendation: preserve the old endpoint/event envelope and cohort routing through reconnects; rebuild UI from persisted state instead of duplicating broadcasts.","sources":["https://hexdocs.pm/phoenix/channels.html","https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html","https://hexdocs.pm/phoenix_live_view/security-model.html"],"id":"realtime-1"},{"capability":"cache","existing_contract":"Classify every Redis use as cache, session, limiter, lock, job or PubSub. ETS tables are node-local and their ownership lifetime matters; they do not provide Redis's cross-node boundary.","coexistence":"Recommendation: move only reconstructible cache entries into supervised local ownership, with explicit TTL/invalidation and bounded memory. Keep shared identity, coordination, queues and broadcast infrastructure until their own transitions qualify.","target":"ETS for deliberately local reconstructed values, or a retained shared cache where consistency across nodes is required. Recommendation: state stale-read tolerance and rebuilding behavior in the application contract.","benefits":[{"benefit":"Recommendation: a local cache can remove a remote dependency from selected reads.","prerequisites":"Acceptable node-local semantics, bounded memory and verified invalidation/rebuild behavior.","tradeoffs":"Each node maintains separate contents; owner/node failure loses local state. Performance benefit requires measurement."}],"checks":["Warm two nodes, update the authoritative record and verify their documented staleness/invalidation policy.","Terminate the table owner or restart the node; verify the application rebuilds and no durable job/session/lock state was stored there."],"rollback":"Recommendation: switch those reads back to the qualified shared store or authoritative data; retain Redis until every remaining capability is reconciled.","sources":["https://www.erlang.org/doc/apps/stdlib/ets.html","https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html"],"id":"cache-1"},{"capability":"operations","existing_contract":"Record endpoint, worker, scheduler, Repo, assets and cluster/PubSub ownership. Phoenix runtime configuration, release build and schema migrations have distinct deployment steps.","coexistence":"Recommendation: maintain both releases with stable cohort routing and a single schedule/writer owner per responsibility. Monitor queue age, retry effects, node connectivity and authorization failures against application-specific promotion thresholds.","target":"A Phoenix release with runtime.exs configuration, built assets, named migration runner and supervised workers. Recommendation: make cluster formation, backups and restore ordering explicit instead of assuming VM startup establishes them.","benefits":[{"benefit":"Recommendation: explicit supervised roles and release inputs make restart behavior reviewable.","prerequisites":"Configured children, deployment inventory and exercised recovery procedures.","tradeoffs":"Durable recovery still depends on the chosen databases/queues; supervision alone cannot recreate lost external state."}],"checks":["Start the actual release with a disposable restored database and confirm assets, authenticated routes, a queued job and cross-node broadcast work.","Stop a worker/node, observe detection and recovery, and verify no second schedule owner activates during rollback."],"rollback":"Recommendation: retain the previous release and compatible database reads; return cohort routing and role ownership separately, reconciling new jobs and external effects.","sources":["https://hexdocs.pm/phoenix/deployment.html","https://oban.hexdocs.pm/installation.html","https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html"],"id":"operations-1"}],"sources":[{"url":"https://hexdocs.pm/phoenix/contexts.html","title":"Phoenix Intro to Contexts","claim":"Contexts separate web features from application operations."},{"url":"https://hexdocs.pm/phoenix/Mix.Tasks.Phx.New.html","title":"mix phx.new","claim":"Phoenix generator syntax and selectable target options."},{"url":"https://hexdocs.pm/ecto/Ecto.Multi.html","title":"Ecto.Multi","claim":"Composes related database operations within a transaction."},{"url":"https://hexdocs.pm/ecto_sql/Ecto.Migrator.html","title":"Ecto.Migrator","claim":"Migration execution, repository connection and release migration concerns."},{"url":"https://oban.hexdocs.pm/installation.html","title":"Oban Installation","claim":"PostgreSQL, SQLite and MySQL engine setup; migrations, configuration and supervision."},{"url":"https://oban.hexdocs.pm/Oban.html","title":"Oban API","claim":"Transactional Ecto.Multi insertion and queue pause/resume APIs; uniqueness is an enqueue feature."},{"url":"https://exq.hexdocs.pm/readme.html","title":"Exq README","claim":"Redis persistence, retrying and Resque/Sidekiq-format compatibility."},{"url":"https://github.com/sidekiq/sidekiq/wiki/Job-Format","title":"Sidekiq Job Format","claim":"Job envelopes and Active Job wrapper/version-sensitive payload details."},{"url":"https://github.com/sidekiq/sidekiq/wiki/Best-Practices","title":"Sidekiq Best Practices","claim":"Idempotent jobs and at-least-once execution."},{"url":"https://guides.rubyonrails.org/active_job_basics.html","title":"Rails Active Job Basics","claim":"Active Job serializers/GlobalID, adapter abstraction and scheduled/retry execution."},{"url":"https://guides.rubyonrails.org/security.html","title":"Rails Security","claim":"Rails cookie/session contract and rotation considerations."},{"url":"https://hexdocs.pm/plug/Plug.Session.COOKIE.html","title":"Plug cookie session store","claim":"Cookie serializer, signing/encryption configuration and rotating options."},{"url":"https://hexdocs.pm/phoenix_live_view/security-model.html","title":"LiveView Security","claim":"Authorization on mount/events and stateful connection revocation."},{"url":"https://hexdocs.pm/phoenix/channels.html","title":"Phoenix Channels","claim":"Channel protocol, joins and at-most-once non-persistent client delivery with application catch-up options."},{"url":"https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html","title":"Phoenix PubSub","claim":"Distributed-Elixir and Redis adapters, pool configuration and migration."},{"url":"https://www.erlang.org/doc/apps/stdlib/ets.html","title":"OTP ETS","claim":"Node-local tables, owner/heir lifetime and table options."},{"url":"https://hexdocs.pm/phoenix/testing.html","title":"Phoenix Testing","claim":"mix test and generated web test helpers."},{"url":"https://hexdocs.pm/phoenix/deployment.html","title":"Phoenix Deployment","claim":"Runtime configuration, assets.deploy, release and production build responsibilities."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Phoenix + LiveView","kind":["web","api"],"guide_url":"/stacks/elixir-phoenix-liveview/","markdown_url":"/stacks/elixir-phoenix-liveview/guide.md","json_url":"/stacks/elixir-phoenix-liveview/record.json"},{"id":"typescript-next-node","version_scope":"Documentation reviewed 11 October 2026: current Next.js App Router docs label 16.4.0. Pin the installed Next, React, Node and database packages; distinguish cacheComponents:true from the previous caching model. This profile does not qualify a lockfile or hosting adapter.","operating_model":"A Next application contains server rendering and browser interactivity with an explicit trust boundary. Recommendation: preserve an existing backend's domain ownership during UI adoption; for a new owned backend use server-only data access. Node/Docker and alternative adapters require separate runtime checks.","architecture":["Server Components load server-owned data; Client Components own interaction and browser APIs. Keep the client boundary near interactive leaves and pass minimal serializable DTOs.","Recommendation: one domain writer and migration owner per responsibility. Existing REST/GraphQL services remain authoritative during coexistence; a new server-only DAL can own validation, authorization and transactions after transfer.","Choose server, browser and CDN caches deliberately. Session-dependent output, invalidation and multiple instances require independent policies; adding a framework cache does not migrate a shared session or queue.","Recommendation: durable jobs and realtime run behind explicit service contracts when required. A render, Server Action or browser promise is not an execution backlog."],"developer_workflow":["Start in the external source checkout. Read README and inspect package.json, lockfile, next.config, current API ownership, schema and runtime. Record whether Cache Components are enabled before selecting examples.","For an existing npm project run npm ci; use its scripts. With Next installed, npx next typegen then npx tsc --noEmit checks generated route types and TypeScript. Run the configured ESLint/Biome command separately; the build is not a substitute for lint.","Run npm run dev for iteration and npm run build for production compilation; use npm run start only if its script runs the selected production server. Configure Playwright's isolated backend/data first, then npx playwright test for mixed-version journeys.","Recommendation: deploy the same built artifact to all instances, record build/deployment identifiers, cache coordination, migrations, secrets and adapter support. Reproduce cold/warm loads, sign-out, old-tab actions and rollback on a staging topology."],"avoid":["Marking the root tree use client brings imported dependencies into the browser bundle and broadens the secret/exposure surface.","Passing whole ORM records to Client Components exposes fields unrelated to presentation; enforce authorization and DTO filtering on the server.","UI-hidden buttons or Proxy redirects cannot replace authorization at the mutation/data boundary.","Copying pre-16 cache defaults into a Cache Components app silently changes freshness behavior; identify the installed model.","Treating an old browser build as gone after deployment ignores existing tabs, prefetched payloads and action identifiers."],"transitions":[{"capability":"data","existing_contract":"Existing REST/GraphQL behavior, DTOs, authorization, transactions and one schema owner.","coexistence":"Recommendation: Adopt server-rendered routes that call the old API; keep its writer and migration authority. Compare read-only fixtures without replaying real side effects.","target":"Recommendation: Retain the API permanently if appropriate, or transfer a bounded domain to a server-only DAL with minimal DTOs after mixed-version checks.","benefits":[{"benefit":"Server loading can reduce client waterfalls; an owned DAL centralizes authorization.","prerequisites":"Server-reachable API or reviewed database ownership; measurable user journeys.","tradeoffs":"A new domain writer requires schema/transaction review; framework adoption alone changes neither."}],"checks":["Old and new UI decode identical success/error/empty fixtures.","Forged resource IDs remain denied on both mutation paths.","An acknowledged write is readable through the old interface."],"rollback":"Route the cohort back while old-compatible schema remains; reconcile writes before any writer reversal.","sources":["https://nextjs.org/docs/app/guides/data-security","https://nextjs.org/docs/app/getting-started/server-and-client-components"],"id":"data-1"},{"capability":"identity","existing_contract":"Existing session issuer, cookie/token format, expiry, revocation, tenant membership and CSRF behavior.","coexistence":"Recommendation: Retain the issuer behind an adapter and authorize data/mutations in both implementations; inventory origin/domain/cookie differences before sharing.","target":"Recommendation: Use an explicitly selected issuer and server-side authorization boundary; transfer session contracts or require a controlled sign-in when compatibility cannot be preserved.","benefits":[{"benefit":"One identity authority can support gradual UI/backend replacement.","prerequisites":"Validated issuer integration and old/new expiry/revocation fixtures.","tradeoffs":"Session sharing is format/security work; sign-in continuity is not guaranteed by using cookies."}],"checks":["Revoked and expired sessions fail in old/new paths.","Cross-tenant direct action requests are denied.","Sign-out clears browser remote-state caches and back navigation reveals no protected data."],"rollback":"Restore old routes/issuer acceptance only while its keys and session semantics remain supported; do not resurrect revoked sessions.","sources":["https://nextjs.org/docs/app/guides/authentication","https://nextjs.org/docs/app/guides/data-security"],"id":"identity-1"},{"capability":"cache","existing_contract":"Actual enabled caching model, personalized response policy, keys, freshness, invalidation and shared-instance behavior.","coexistence":"Recommendation: Keep the prior policy while converting one route/data category; separate old/new cache namespaces and observe stale/account-crossing outcomes.","target":"Recommendation: Choose Cache Components or version-matched previous-model APIs explicitly; keep user-specific data outside shared public entries and coordinate shared invalidation when deployed across instances.","benefits":[{"benefit":"Repeated eligible work can be reused without refetching each time.","prerequisites":"A documented freshness tolerance, safe keys and working invalidation.","tradeoffs":"Coordination/storage adds operations; freshness and cost gains require measurement."}],"checks":["A write refreshes affected reads through every serving instance.","Account A cannot read cached output for account B.","Cold, warm and forced-refresh responses agree with the selected policy."],"rollback":"Disable the new cache path and purge its namespace; keep source data unchanged and retain old invalidation until transfer completes.","sources":["https://nextjs.org/docs/app/getting-started/caching","https://nextjs.org/docs/app/guides/caching-without-cache-components","https://nextjs.org/docs/app/guides/self-hosting"],"id":"cache-1"},{"capability":"jobs","existing_contract":"Existing job producer/consumer payloads, ready/scheduled/retry/failed/in-flight states and external-effect idempotency.","coexistence":"Recommendation: Keep existing workers; move a named job type behind a versioned event contract. Reconcile backlog and retain one consumer owner per item.","target":"Recommendation: Use selected durable execution such as Inngest for required workflows; a transactional outbox is a proposed design when domain commit and event submission are separate systems.","benefits":[{"benefit":"Checkpointed completed steps allow retry/resume without starting every step again.","prerequisites":"Selected service/SDK integration, stable step IDs, replay-safe external effects and delivery reconciliation.","tradeoffs":"Operating a workflow service adds dependency/cost; checkpoints do not supply business exactly-once effects."}],"checks":["Crash after an external effect but before step acknowledgement; retry causes one intended business effect.","Domain commit with event-send failure is eventually reconciled.","Old scheduled/retry work is drained or deliberately transformed."],"rollback":"Stop new producers for the transferred type, account for active runs and resume old workers with compatible payloads; reconcile irreversible effects first.","sources":["https://www.inngest.com/docs/durable-execution"],"id":"jobs-1"},{"capability":"operations","existing_contract":"Current hosting/runtime, static assets, open-tab state, prefetched payloads and Server Function compatibility.","coexistence":"Recommendation: Keep old assets and compatible API routes; promote one built artifact per deployment and rehearse an old tab against the new deployment.","target":"Recommendation: Use version-matched deploymentId support and infrastructure routing as needed; same-build instances share required action keys and cache coordination.","benefits":[{"benefit":"Explicit rollout handling reduces missing-chunk/navigation mismatches.","prerequisites":"A qualified host, retained assets and tested old-client behavior.","tradeoffs":"Hard navigation can lose component state; same encryption key does not make old action IDs compatible."}],"checks":["Open an old tab, deploy, navigate and submit an old form; observe success or a controlled update path.","Preserved drafts survive any required full reload.","All instances serve matching build/deployment identifiers."],"rollback":"Restore the prior artifact and routing while compatible assets/data remain; do not claim traffic rollback reverses schema or external effects.","sources":["https://nextjs.org/docs/app/guides/self-hosting"],"id":"operations-1"}],"sources":[{"url":"https://nextjs.org/docs/app/getting-started/server-and-client-components","title":"Next.js Server and Client Components","claim":"Pages/layouts default to server components; browser interaction and APIs require client boundaries."},{"url":"https://nextjs.org/docs/app/guides/data-security","title":"Next.js Data Security","claim":"Existing HTTP APIs or a new server-only DAL provide explicit data ownership; mutations require validation/authorization and minimal DTOs."},{"url":"https://nextjs.org/docs/app/guides/authentication","title":"Next.js Authentication","claim":"Authentication, session management and authorization are separate responsibilities; optimistic redirects do not replace secure checks."},{"url":"https://nextjs.org/docs/app/getting-started/caching","title":"Next.js Caching with Cache Components","claim":"Current caching guide applies when cacheComponents is enabled."},{"url":"https://nextjs.org/docs/app/guides/caching-without-cache-components","title":"Next.js Caching and Revalidating (Previous Model)","claim":"The alternative caching guide applies without Cache Components; defaults and APIs must match that model."},{"url":"https://nextjs.org/docs/app/guides/self-hosting","title":"Next.js Self-Hosting","claim":"Multi-instance cache coordination, action encryption keys, deployment identifiers and old-client version skew need deployment design."},{"url":"https://nextjs.org/docs/app/getting-started/installation","title":"Next.js Installation","claim":"Framework setup and deterministic lint/type/build checks have separate tooling."},{"url":"https://nextjs.org/docs/app/api-reference/cli/next","title":"Next.js CLI","claim":"next typegen, build and production server commands are qualified by installed framework and scripts."},{"url":"https://www.inngest.com/docs/durable-execution","title":"Inngest Durable Execution","claim":"Completed steps are checkpointed and workflows can retry/resume; durable endpoints are separately marked beta."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Next.js","kind":["web"],"guide_url":"/stacks/typescript-next-node/","markdown_url":"/stacks/typescript-next-node/guide.md","json_url":"/stacks/typescript-next-node/record.json"},{"id":"typescript-react-vite-browser","version_scope":"Documentation reviewed 11 October 2026: current React docs label 19.3; current Vite docs include migration from v7 and Rolldown build options. Preserve the project's resolved React/Vite/TypeScript/router versions and API schema. SPA guidance is not a claim that Vite supplies SSR or a backend.","operating_model":"React/Vite builds browser assets. Recommendation: select it when an existing API owns persistence, authorization, email, durable jobs and other privileged effects; choose routing and remote-state tools deliberately. A client-only build has no protected server runtime.","architecture":["Views consume route-loaded or query-cached DTOs; one API client module defines credentials, errors, cancellation and runtime validation. Backend ownership remains independent of UI language.","Recommendation: domain state stays on the server; transient form/navigation state stays in the browser. Use tenant-aware cache keys and an explicit account-switch/reset policy.","Bundled VITE_ values are public configuration. Keep database, provider and signing credentials in the backend; a development proxy is only local routing.","Recommendation: retain content-hashed old chunks during rollout, revalidate HTML and provide a guarded update path for stale tabs. Add service-worker storage/adoption policy only if offline operation is required."],"developer_workflow":["Start at the source README, API schema and lockfile. For a new local prototype npm create vite@latest my-app -- --template react-ts creates a starter; record resolved versions before promoting it. Existing applications keep their package manager.","Use npm ci, the configured TypeScript project check (for the standard scaffold npx tsc -b) and npm run build. npm run dev is local development; npm run preview previews a build and is not the production host.","Configure routing, typed API boundaries, TanStack Query only where useful and its QueryClientProvider. Define loading, stale, denied and offline states before using retries.","Recommendation: use browser network/debug tooling and React profiling for the actual slow interaction. Configure Playwright with old/new API fixtures; run npx playwright test only after installing its runner/browsers and setting an isolated webServer."],"avoid":["Bundling a secret as VITE_API_KEY publishes it to users; moving its string into a frontend module does not create a trust boundary.","Rebuilding server authorization as React route guards allows direct unauthorized API calls.","Independent fetching in nested effects creates waterfalls and duplicated state ownership; use route loaders or a selected remote-state cache where appropriate.","Treating TanStack Query defaults as an application freshness contract overlooks refetch/retry behavior and account boundaries.","Deleting old chunks on deployment breaks lazy imports in open tabs; repeated automatic reload can also discard unsaved work."],"transitions":[{"capability":"client","existing_contract":"Existing URLs, backend API DTOs, errors, permissions and navigation/deep-link semantics.","coexistence":"Recommendation: Replace one route/screen with React while keeping existing API ownership and deep links; contract fixtures cover old/new UI.","target":"Recommendation: Keep a browser/API split with route loaders or a selected query client; adopt a server framework only when required rendering/backend features justify it.","benefits":[{"benefit":"An independently delivered browser client can evolve behind a stable API.","prerequisites":"Owned routing/hosting fallback and backward-compatible API.","tradeoffs":"A SPA must own routing/data/loading infrastructure; SSR/RSC are additional architecture choices."}],"checks":["Direct deep links and refresh work from the production host.","Old/new clients decode success/error/unknown-field fixtures.","Unauthorized direct API requests remain denied."],"rollback":"Restore old HTML/assets/routes while API compatibility remains; preserve submitted data and reconcile pending mutations.","sources":["https://react.dev/learn/build-a-react-app-from-scratch","https://vite.dev/guide/static-deploy"],"id":"client-1"},{"capability":"identity","existing_contract":"Backend credential transport, allowed origins, cookies/CSRF, token expiry and account boundaries.","coexistence":"Recommendation: Keep the backend issuer; configure same-origin routing or narrowly allowed credentialed CORS and preserve old clients.","target":"Recommendation: Centralize credential/error behavior in the API client; enforce authorization and any cookie CSRF protection in the backend.","benefits":[{"benefit":"One backend authority reduces duplicated browser identity logic.","prerequisites":"Reviewed origin/credential policy and issuer behavior.","tradeoffs":"CORS controls browser response access, not authorization; frontend guards remain presentation only."}],"checks":["Credentialed requests succeed only for permitted origins.","Expired credentials transition to sign-in without endless retries.","Cross-tenant and CSRF probes are rejected by the backend."],"rollback":"Restore the old browser artifact/origin policy only if existing credential handling remains secure; retain revocation history.","sources":["https://fetch.spec.whatwg.org/","https://vite.dev/guide/env-and-mode"],"id":"identity-1"},{"capability":"cache","existing_contract":"Current fetch/cache behavior, stale tolerance, retry limits and account-switch isolation.","coexistence":"Recommendation: Introduce query keys for a bounded remote-state category; keep transient form state separate and compare outcomes with the old fetch path.","target":"Recommendation: Use explicit TanStack Query staleTime, retry, invalidation and account-clear policy where its remote-state cache serves a requirement.","benefits":[{"benefit":"Shared queries can reuse data and refresh deliberately.","prerequisites":"A QueryClientProvider, stable tenant-aware keys and mutation invalidation.","tradeoffs":"Default stale queries refetch on mount/focus/reconnect; retries can obscure application errors if left unspecified."}],"checks":["A mutation refreshes affected list/detail views.","Switching accounts cannot reuse the old account's records.","Network restoration produces one coherent refresh; permission errors do not retry indefinitely."],"rollback":"Return the category to the old loader and clear the new cache; do not discard durable pending user operations.","sources":["https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults"],"id":"cache-1"},{"capability":"realtime","existing_contract":"Existing event envelope, authorization, IDs, reconnect behavior and replay/retention rules.","coexistence":"Recommendation: Keep a backend bridge producing the current event contract; move clients by stable subscription cohort rather than arbitrary per-request routing.","target":"Recommendation: Use EventSource for one-way events when host/credentials fit, WebSocket for bidirectional needs; persist/replay events separately when delivery requires it.","benefits":[{"benefit":"A standard browser event stream can replace bespoke polling for suitable feeds.","prerequisites":"Authenticated endpoint, event IDs, retention/replay contract and streaming host.","tradeoffs":"EventSource reconnect does not create server event history; its constructor has no arbitrary header option."}],"checks":["Disconnect after event N and reconnect; no required event is omitted or applied twice.","Revocation terminates access and later reconnect is denied.","Old/new envelopes decode during mixed-version rollout."],"rollback":"Restore old polling/transport using the same cursor domain; reconcile delivered events and keep bridge/history through the rollback window.","sources":["https://html.spec.whatwg.org/multipage/server-sent-events.html"],"id":"realtime-1"},{"capability":"operations","existing_contract":"Existing HTML/CDN/service-worker policy, lazy chunks and open-tab unsaved state.","coexistence":"Recommendation: Keep prior hashed assets and compatible APIs while serving new HTML to a cohort; include any existing service worker's activation/cache rules.","target":"Recommendation: Revalidate HTML, retain old content-hashed chunks and handle vite:preloadError with a guarded refresh/update flow that preserves drafts.","benefits":[{"benefit":"Old tabs can continue loading their release's lazy chunks.","prerequisites":"Asset retention, correct base path and tested update UX.","tradeoffs":"Retention costs storage; refresh may lose state, and reload loops do not fix a wrong CDN policy."}],"checks":["After deployment an old tab lazy-loads its old route successfully.","If a chunk is intentionally unavailable, the update path retains the draft and recovers once.","Production HTML/cache headers and API compatibility match the rollout policy."],"rollback":"Restore the prior HTML and retained assets, roll service-worker changes through their actual lifecycle, and keep new-client API support.","sources":["https://vite.dev/guide/build","https://vite.dev/guide/static-deploy"],"id":"operations-1"}],"sources":[{"url":"https://react.dev/learn/build-a-react-app-from-scratch","title":"React Build an app from Scratch","claim":"A Vite client-only starter requires deliberate routing/data tools; SSR/RSC are additional architecture work."},{"url":"https://vite.dev/guide/","title":"Vite Getting Started","claim":"Scaffold command and runtime requirements must be checked against resolved Vite version."},{"url":"https://vite.dev/guide/env-and-mode","title":"Vite Environment Variables and Modes","claim":"VITE_ values are bundled into client code and must not contain secrets."},{"url":"https://vite.dev/guide/build","title":"Vite Building for Production","claim":"Deleted old chunks can fail dynamic imports; vite:preloadError and HTML cache policy support recovery design."},{"url":"https://vite.dev/guide/static-deploy","title":"Vite Deploying a Static Site","claim":"Build output needs a chosen production host; vite preview is for local preview."},{"url":"https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults","title":"TanStack Query Important Defaults","claim":"Staleness, mount/focus/reconnect refetch, garbage collection and retries require explicit application choices."},{"url":"https://fetch.spec.whatwg.org/","title":"WHATWG Fetch Standard","claim":"Cross-origin response sharing and credential handling are browser protocol rules, separate from application authorization."},{"url":"https://html.spec.whatwg.org/multipage/server-sent-events.html","title":"WHATWG Server-sent Events","claim":"EventSource provides reconnect and event IDs; constructor options include withCredentials, not arbitrary headers."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"React + Vite","kind":["web"],"guide_url":"/stacks/typescript-react-vite-browser/","markdown_url":"/stacks/typescript-react-vite-browser/guide.md","json_url":"/stacks/typescript-react-vite-browser/record.json"},{"id":"rails-hotwire-postgresql","version_scope":"Rails guides showing Rails 8.1 examples; current Turbo handbook. These describe target capabilities, not the source application's installed versions. Detect Gemfile.lock, Ruby version and configured adapters before choosing a move.","operating_model":"Rails supplies a conventional MVC application and Active Record persistence. Turbo Drive, Frames and Streams support a server-rendered HTML interface with incremental browser updates. Recommendation: keep domain writes on the server and introduce browser-owned state only for interactions that need it; a framework rewrite is not a prerequisite for fixing an inefficient query or incorrect queue contract.","architecture":["Recommendation: establish one authoritative domain writer and one schema migration runner per shared database during coexistence. Route a stable tenant or feature boundary, and keep the old application able to read new writes until rollback expires.","Active Job is the application interface; Solid Queue is the Rails 8 default backend. Inspect the application's actual adapter before inferring a Redis dependency. Separate workers, recurring task configuration and domain writes are distinct responsibilities.","Recommendation: treat Turbo stream DOM targets, Action Cable channel identifiers, job serialization and session cookies as separate contracts. Sharing a datastore does not prove any of their compatibility."],"developer_workflow":["In an existing checkout, read Gemfile.lock and environment configuration before adapting generators. In a disposable feature prototype, bin/rails generate model Product name:string and bin/rails db:migrate demonstrate the conventional model/migration path; review generated schema changes before applying them.","bin/rails generate job guests_cleanup creates an Active Job class; configure its selected backend and worker process separately. Recipe only: use bin/jobs start for a configured Solid Queue application, then exercise enqueue, failure and recovery against its real queue database.","bin/rails console is useful for inspecting application objects. Recommendation: capture authorized and unauthorized request fixtures, mixed-version writes and browser stream behavior as acceptance checks rather than treating successful boot as migration qualification.","For a new local-account prototype, bin/rails generate authentication creates User and Session models plus login surfaces. Review account lifecycle and resource authorization separately; do not run it over an established authentication system without an intentional migration design."],"avoid":["Avoid calling a queue-adapter configuration change a backlog migration: the old backend retains work and Active Job can serialize GlobalID and custom types that need the original Ruby classes and database semantics.","Avoid claiming atomic domain-write-and-enqueue from a separate Solid Queue database. After-commit enqueue prevents premature execution but is not a transaction across two stores. Recommendation: use a durable outbox when a crash between commit and delivery must be recoverable.","Avoid the Action Cable async adapter for distributed production delivery; the guide restricts it to development/testing and a single process. Avoid replacing session or lock data with a disposable cache just because both use Redis."],"transitions":[{"capability":"data","existing_contract":"Inventory table names, primary keys, timestamps, enum values, constraints, callbacks and the application that currently owns migrations; Active Record migrations define schema evolution but do not describe all application side effects.","coexistence":"Recommendation: keep the existing writer authoritative while the target reads representative data. Add backward-readable fields first; transfer one domain boundary explicitly. Keep a single migration runner rather than allowing two framework migration histories to modify the same tables independently.","target":"Rails Active Record over the selected PostgreSQL schema, with reviewed database constraints and migrations. Recommendation: remove legacy fields only after old code and rollback readers have retired.","benefits":[{"benefit":"Recommendation: conventional migrations and model access can concentrate schema work in one maintained application.","prerequisites":"A named schema owner, compatible value semantics and staged writer transfer.","tradeoffs":"Shared tables constrain both deployments; deleting or rewriting data may require reconciliation rather than migration reversal."}],"checks":["Create in the old application and read in Rails, then reverse the direction after writer transfer; compare IDs, timestamps and nullable values.","Run the proposed migration against a disposable copy; verify both deployed versions can still read and update a fixture, including unique and foreign-key failures."],"rollback":"Recommendation: route writes back only while the old reader understands new records. Preserve additive schema changes; rehearse reversible migrations separately and reconcile irreversible data changes.","sources":["https://guides.rubyonrails.org/active_record_migrations.html","https://guides.rubyonrails.org/getting_started.html"],"id":"data-1"},{"capability":"jobs","existing_contract":"Record adapter, job class, argument serializers/GlobalIDs, queue names, retries, scheduled work, failed work and running leases. Rails exposes these through Active Job while each backend supplies execution behavior.","coexistence":"Recommendation: retain old consumers for old payloads; route a new job category to a new queue/backend. Transfer its producers and recurring-task owner together. Drain or deliberately transform ready, scheduled, retry, failed and in-flight work; do not run both schedulers for the same business operation.","target":"Active Job with configured Solid Queue or the retained qualified adapter. Same-database enqueue may join a domain transaction; a separate queue database needs an explicit delivery design. Recommendation: require business idempotency even when job uniqueness exists.","benefits":[{"benefit":"SQL-backed job persistence may allow retirement of a dedicated broker.","prerequisites":"All broker uses inventoried and retired; workers, queue schema, failure monitoring and backlog reconciliation in place.","tradeoffs":"Database work and queue operations remain operational responsibilities; retaining Redis for another capability yields no service-count reduction."}],"checks":["Replay captured old serializer fixtures using the retained consumer; a deleted GlobalID target must follow the intended failure policy.","Rollback a domain transaction and confirm no executable job escapes it; kill after an external effect before acknowledgement and verify a retry cannot repeat the business effect.","Account for every backlog state and observe only one scheduled enqueue for the transferred category."],"rollback":"Recommendation: stop new producers before restoring old routing; retain both consumers through the rollback window and reconcile completed external effects before re-enqueueing.","sources":["https://guides.rubyonrails.org/active_job_basics.html","https://github.com/sidekiq/sidekiq/wiki/Best-Practices"],"id":"jobs-1"},{"capability":"identity","existing_contract":"Distinguish the application's generated Session model from Rails session storage. Capture cookie name, serializer, signing/encryption configuration, expiration, account IDs, logout/revocation and per-resource authorization.","coexistence":"Recommendation: keep a shared identity authority or a documented session exchange endpoint. If cookies are shared, qualify the complete cookie format and rotation rules; route stable users and retain the old decoder for the agreed window.","target":"An intentionally selected Rails authentication implementation with application authorization. CookieStore stores session data on the client; account/session lifecycle rules must be designed separately.","benefits":[{"benefit":"Recommendation: coherent account and session ownership can preserve login continuity during feature migration.","prerequisites":"Verified identity mapping, cookie/exchange fixtures and shared revocation behavior.","tradeoffs":"Compatibility code and key rotation increase maintenance; planned reauthentication is an alternative when continuity is unnecessary."}],"checks":["Authenticate through the old path and visit the new route; then logout, expire or revoke the session and confirm both paths deny access.","Replay a modified cookie, a stale cookie and a valid session from another tenant against a protected resource; each unauthorized operation must be rejected."],"rollback":"Recommendation: preserve old account IDs and accepted session formats until routing stabilizes; coordinate revocation and key rotation so restoring traffic cannot revive invalidated sessions.","sources":["https://guides.rubyonrails.org/security.html","https://guides.rubyonrails.org/getting_started.html"],"id":"identity-1"},{"capability":"realtime","existing_contract":"Capture channel names, connection authentication, subscription commands, DOM target IDs, Turbo stream actions and reconnect behavior. Turbo Streams can arrive over WebSockets, SSE or request responses; Action Cable owns its own client/server protocol.","coexistence":"Recommendation: keep the current realtime endpoint for old clients; bridge versioned domain events to the selected target renderer. Assign one event publisher per committed change and route a room/user cohort including reconnects, rather than randomly splitting individual socket requests.","target":"Turbo streams over the selected transport, with Action Cable using a configured shared adapter when multiple server processes must communicate. Solid Cable is database-backed; Redis remains a documented alternative.","benefits":[{"benefit":"HTML updates can reuse server-rendered partials.","prerequisites":"Stable DOM targets, authorized subscriptions and compatible event-to-view rendering.","tradeoffs":"A new socket protocol still needs client changes or a bridge; reconnection recovery is an application acceptance requirement."}],"checks":["Place old and new clients on different server processes; commit one change and verify both authorized clients display it once.","Disconnect during a write, reconnect and verify the UI converges to the persisted state; unauthorized origins and subscriptions must fail."],"rollback":"Recommendation: preserve the old event renderer/endpoint and backward-readable stream payloads until the cohort can reconnect there; reconcile already-delivered state rather than blindly rebroadcasting.","sources":["https://guides.rubyonrails.org/action_cable_overview.html","https://turbo.hotwired.dev/handbook/streams"],"id":"realtime-1"},{"capability":"cache","existing_contract":"List reconstructible values separately from sessions, counters and locks. Capture key namespaces, serialized values, TTLs and invalidation triggers; Rails.cache and request-local SQL query caching have different lifetimes.","coexistence":"Recommendation: use a new cache namespace when representations differ, read through to the authoritative data and maintain invalidation for both deployed readers. Keep shared coordination uses on their qualified store while moving only disposable values.","target":"A deliberately selected Rails cache store; Solid Cache is database-backed and new Rails 8 applications include it. MemoryStore and RedisCacheStore remain choices with different process/service boundaries.","benefits":[{"benefit":"Database-backed caching can remove a dedicated cache service when it is the last use.","prerequisites":"Reconstructible contents, tested expiration/invalidation, adequate database capacity and retirement of all other service uses.","tradeoffs":"Cache traffic moves to the database; cache durability does not make it the authoritative domain store."}],"checks":["Warm both versions, update the source record and verify neither version serves the prior value after its specified invalidation boundary.","Restart the application or empty only the dedicated cache namespace; requests must rebuild correctly without losing identity or coordination state."],"rollback":"Recommendation: switch to the previous namespace/store and repopulate from domain data; retain the old backend while any coordination capability still depends on it.","sources":["https://guides.rubyonrails.org/caching_with_rails.html"],"id":"cache-1"},{"capability":"operations","existing_contract":"Record deployed web and Cable processes, configured workers/dispatchers, recurring.yml tasks and database connections. A web deployment alone does not establish that workers or broadcasts are running.","coexistence":"Recommendation: give each process a named operational owner; keep existing backups and queue monitoring while deploying the new cohort. Transfer recurring schedule ownership once, after checking overlapping old and new schedules.","target":"An explicit Rails process inventory with worker lifecycle, queue failure inspection and the chosen realtime adapter. Recommendation: document separate domain, queue and cache database restore ordering where applicable.","benefits":[{"benefit":"Recommendation: visible process and schedule ownership makes failures and recovery boundaries reviewable.","prerequisites":"A deployment recipe for the selected adapters and an exercised restore/restart procedure.","tradeoffs":"Coexistence temporarily operates both systems; one healthy HTTP endpoint cannot certify all processes."}],"checks":["Stop a worker, enqueue a representative task and verify monitoring identifies its growing age; restart and observe the intended completion/failure policy.","Restore a disposable backup and prove domain records, delayed tasks and session behavior are accounted for before promoting traffic."],"rollback":"Recommendation: freeze producers and scheduler ownership transfers, restore the previous process configuration, then reconcile work accepted by the new deployment.","sources":["https://guides.rubyonrails.org/active_job_basics.html","https://guides.rubyonrails.org/action_cable_overview.html"],"id":"operations-1"}],"sources":[{"url":"https://guides.rubyonrails.org/getting_started.html","title":"Getting Started with Rails","claim":"Rails 8.1 examples, MVC/Active Record, model and authentication generators, migration and console commands."},{"url":"https://guides.rubyonrails.org/active_record_migrations.html","title":"Active Record Migrations","claim":"Schema migration files, database constraints, reversal and rollback semantics."},{"url":"https://guides.rubyonrails.org/active_job_basics.html","title":"Active Job Basics","claim":"Active Job serialization, Solid Queue default and process roles, bin/jobs, separate database default, enqueue timing, recurring tasks and retry handling."},{"url":"https://guides.rubyonrails.org/security.html","title":"Securing Rails Applications","claim":"CookieStore, signing/encryption rotation, replay/fixation and session expiration considerations."},{"url":"https://guides.rubyonrails.org/action_cable_overview.html","title":"Action Cable Overview","claim":"Cable connections/channels, adapter configuration, async locality, Solid Cable, origins and server deployment."},{"url":"https://guides.rubyonrails.org/caching_with_rails.html","title":"Caching with Rails","claim":"Rails cache interfaces, SQL cache lifetime, Solid Cache and alternative stores."},{"url":"https://turbo.hotwired.dev/handbook/introduction","title":"Turbo introduction","claim":"Drive, Frames and Streams extend server-rendered HTML navigation and updates."},{"url":"https://turbo.hotwired.dev/handbook/streams","title":"Turbo Streams","claim":"DOM target operations and stream transport options."},{"url":"https://github.com/sidekiq/sidekiq/wiki/Best-Practices","title":"Sidekiq Best Practices","claim":"Jobs must be idempotent; execution is at least once rather than exactly once."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Rails + Hotwire","kind":["web"],"guide_url":"/stacks/rails-hotwire-postgresql/","markdown_url":"/stacks/rails-hotwire-postgresql/guide.md","json_url":"/stacks/rails-hotwire-postgresql/record.json"},{"id":"laravel-livewire-postgresql","version_scope":"Laravel 13.x and Livewire 4.x documentation fetched 11 October 2026. These are documentation scopes, not proof of installed compatibility; inspect composer.lock, PHP version, queue/session/cache drivers and current Livewire component format before implementation.","operating_model":"Laravel combines conventional routes, middleware, authentication guards/providers and database migrations. Livewire components combine PHP behavior with Blade rendering. Recommendation: use these conventions for connected forms and business screens; keep server authorization and domain writes explicit instead of treating public component properties as trusted state.","architecture":["Recommendation: keep a single domain writer and migration owner during shared-schema coexistence; let controllers, Livewire actions and queued workers call the same application operation with its authorization and transaction rules.","Queue connections select backends while queue names select work categories. The documented choices include relational storage, Redis and SQS. Recommendation: choose a backend from recovery and operation requirements rather than assuming every Laravel application needs Redis.","Recommendation: list HTTP PHP execution, queue workers, the scheduler and a selected broadcast server separately. Livewire component interactivity does not by itself establish multi-client event delivery; broadcasting has its own backend/client and queue configuration."],"developer_workflow":["In a configured local Laravel project, php artisan make:livewire post.create creates a Livewire 4 component; --class retains the established class-based style and --mfc creates a multi-file component. Inspect existing conventions before choosing a format.","php artisan make:job ProcessPodcast creates a queued job. Configure the selected connection and run php artisan queue:work --queue=high,default only after those queues and priorities match the application; supervise the production worker separately.","php artisan migrate applies reviewed migrations. For missing database session/queue tables, the selected-version docs provide php artisan make:session-table and php artisan make:queue-table; check existing migrations first to avoid duplicate tables.","Recommendation: verify behavior with real driver instances in a disposable environment, including timeout/retry interaction, denied Livewire actions, concurrent session writes and private broadcast authorization. Faking queue submission alone cannot exercise worker serialization or crash recovery."],"avoid":["Avoid switching queue consumers solely because both use Redis: Laravel payloads and Eloquent identifier rehydration are framework execution contracts. Recommendation: preserve old consumers or translate deliberately versioned payloads.","Avoid worker timeout values longer than retry_after; Laravel warns this can allow the job to be processed twice. Avoid assuming unique-job locks make external effects exactly once; recommendation: use business idempotency.","Avoid calling onOneServer a cross-stack scheduler lock. It uses a shared supported Laravel cache store; recommendation: explicitly retire the old scheduler for the transferred business category.","Avoid trusting client-editable Livewire properties or hidden buttons for authorization. Avoid Cache::flush on a backend shared with other applications; the documented flush ignores the configured prefix."],"transitions":[{"capability":"data","existing_contract":"Inventory table identifiers, casts/nullability, timestamps, constraints and side effects currently attached to writes. Migrations evolve the schema; Eloquent models do not automatically reproduce another runtime’s callbacks.","coexistence":"Recommendation: keep the existing writer authoritative, add compatible fields and move one domain operation at a time. Let one named migration runner own shared tables and keep old readers functional during the rollback window.","target":"Laravel database migrations and the selected PostgreSQL schema, with application operations reused by controllers/Livewire/workers. Recommendation: delay destructive schema contraction until all old readers retire.","benefits":[{"benefit":"Recommendation: conventional schema and application ownership can make changes reviewable in one maintained application.","prerequisites":"Explicit writer transfer, tested value mappings and a single schema owner.","tradeoffs":"Both runtimes remain constrained by the shared schema; reversal does not recreate deleted business data."}],"checks":["Create and update a representative fixture through both allowed paths; compare IDs, nulls, timezone handling and unique/foreign-key failures.","Apply the reviewed migration to a disposable restored copy and confirm both deployed versions still understand new writes."],"rollback":"Recommendation: restore the old writer before contracting fields; preserve additive schema and reconcile writes accepted by Laravel.","sources":["https://laravel.com/docs/13.x/migrations"],"id":"data-1"},{"capability":"jobs","existing_contract":"Record connection, queue, PHP job class, serialized model identifiers/relationships, timeout, retry_after, failures and delayed/running work. Model arguments may be re-fetched when the worker executes.","coexistence":"Recommendation: keep old consumers for old payloads; route new categories to isolated Laravel queues. Transfer producers and each schedule owner together. Account for ready, delayed, failed, retry and in-flight work before retiring the old backend.","target":"ShouldQueue jobs on an intentionally selected database/Redis/SQS connection, with supervised workers and explicit attempts/timeouts. after_commit or afterCommit delays dispatch until commit; recommendation: use a durable outbox for a required recoverable commit-to-broker handoff.","benefits":[{"benefit":"Recommendation: a job category can move without rebuilding the entire backlog.","prerequisites":"Versioned payload fixtures, isolated queues and retained old consumers.","tradeoffs":"Two queue systems may remain temporarily; serialized framework objects are not a portable cross-runtime API."}],"checks":["Enqueue a model, alter/delete it before execution and verify the intended rehydration/failure behavior.","Roll back the domain transaction and verify after-commit dispatch produces no job; kill after an external effect and prove retry cannot duplicate the business effect.","Set worker timeout below the applicable retry/visibility boundary and verify a long job cannot have two concurrent effects."],"rollback":"Recommendation: stop the new producer, keep consumers for accepted payload versions and reconcile external effects before restoring old queue routing.","sources":["https://laravel.com/docs/13.x/queues"],"id":"jobs-1"},{"capability":"identity","existing_contract":"Capture guard/provider, account mapping, session driver, cookie/encryption configuration, remember-me behavior, expiry and logout. The current docs default to database sessions; the application may use another driver.","coexistence":"Recommendation: retain a named identity authority or qualified session exchange; share cookies only after full-format fixtures pass. Map old per-resource permissions into every Livewire/server action and keep concurrent session semantics explicit.","target":"Laravel authentication guard/provider plus a selected session store and resource authorization. Session blocking needs a supported atomic-lock cache driver and is unavailable with cookie sessions.","benefits":[{"benefit":"Recommendation: explicit session/account ownership can preserve user continuity during feature migration.","prerequisites":"Qualified identity/session mapping and common revocation rules.","tradeoffs":"Lock-capable session coordination adds a store dependency; a shared database alone does not prove cookie compatibility."}],"checks":["Login through the old application, access the Laravel feature and revoke/logout; both paths must deny future operations.","Run concurrent requests that modify one session and verify required data is not lost.","Modify a Livewire public identifier to another tenant’s record and directly invoke the action; the server must reject it."],"rollback":"Recommendation: retain old session/exchange support until the cohort can return, while preserving every revocation and account mapping.","sources":["https://laravel.com/docs/13.x/authentication","https://laravel.com/docs/13.x/session","https://livewire.laravel.com/docs/4.x/security"],"id":"identity-1"},{"capability":"realtime","existing_contract":"Capture client protocol, event name/payload, private/presence channel identity, authorization endpoint and queue connection. ShouldBroadcast dispatches broadcast work; event production and broadcast execution are separate concerns.","coexistence":"Recommendation: retain the old endpoint for old clients and bridge versioned domain events to the selected Laravel broadcaster. Route a stable room/user cohort including reconnects; designate one publisher per committed domain change.","target":"Laravel broadcasting with an explicitly chosen supported driver and client integration, plus working queue workers when required. Defer transactional events until commit with the documented mechanism; recommendation: persist domain state for reconnect recovery.","benefits":[{"benefit":"Recommendation: backend events can update multiple authorized clients.","prerequisites":"Configured broadcaster, workers and matching event/client contracts.","tradeoffs":"Livewire alone does not remove those operational pieces; changing the broadcaster protocol may require a bridge or client rollout."}],"checks":["Put clients on separate server instances and verify one committed event reaches all authorized recipients.","Rollback the domain write and confirm no premature broadcast occurs.","Revoke private-channel access, disconnect/reconnect and verify unauthorized delivery fails and the UI catches up from source state."],"rollback":"Recommendation: preserve old event names/endpoints and stop new publication before restoring the old publisher; reconcile already-visible state.","sources":["https://laravel.com/docs/13.x/broadcasting","https://laravel.com/docs/13.x/queues"],"id":"realtime-1"},{"capability":"cache","existing_contract":"Inventory cached values, serialization, TTLs and invalidation separately from locks, unique jobs, scheduler coordination and sessions. Laravel supports several cache stores with different capabilities.","coexistence":"Recommendation: version namespaces for changed representations and keep coordination on the same qualified central store. Move reconstructible values separately; never globally flush a shared backend during cutover.","target":"Selected Laravel cache driver for disposable cached data; a supported central store for atomic locks across servers. Recommendation: use explicit dedicated stores/namespaces for distinct ownership boundaries.","benefits":[{"benefit":"Recommendation: a cache backend can change independently of domain storage when contents are reconstructible.","prerequisites":"Verified invalidation, serialization and coordination inventory.","tradeoffs":"Lock owners and dependent scheduler/session features may still prevent retiring the old backend."}],"checks":["Warm both application versions, update authoritative data and verify both observe the required invalidation/TTL rule.","Contend for a lock from two servers; only the intended owner may act.","Clear only the migration’s disposable cache and verify other applications and coordination keys survive."],"rollback":"Recommendation: restore previous cache routing and rebuild from domain data; preserve lock/session store ownership until a separate qualified transition.","sources":["https://laravel.com/docs/13.x/cache"],"id":"cache-1"},{"capability":"operations","existing_contract":"List scheduler definitions, timezones, overlapping task prevention, worker lifecycle and cache-backed coordination. Laravel schedules and old cron/Celery/Rails schedules may describe the same business action.","coexistence":"Recommendation: give each scheduled action one logical owner. Transfer it at an explicit boundary; Laravel onOneServer coordinates Laravel instances using a shared supported store and does not retire an external scheduler.","target":"A documented Laravel scheduler invocation and supervised queue workers. Use withoutOverlapping/onOneServer only with their selected store and intended lock scope; recommendation: document missed-run and business idempotency policies separately.","benefits":[{"benefit":"Recommendation: explicit schedule and worker ownership can prevent accidental duplicate production during rollout.","prerequisites":"Schedule inventory, shared supported locking when needed and a coordinated old-scheduler retirement.","tradeoffs":"A lock prevents concurrent starts within its scope, not every replayed business effect; coexistence still needs two operational inventories."}],"checks":["Run the scheduled fixture across two Laravel servers and verify one enqueue, then confirm the old scheduler stays disabled for that category.","Exercise timezone and long-running/overlap cases; restart the worker and scheduler and verify intended missed-run behavior."],"rollback":"Recommendation: stop the Laravel schedule before restoring the old owner; reconcile accepted jobs and completed effects before replay.","sources":["https://laravel.com/docs/13.x/scheduling","https://laravel.com/docs/13.x/queues"],"id":"operations-1"}],"sources":[{"url":"https://laravel.com/docs/13.x/migrations","title":"Laravel 13 Database Migrations","claim":"Schema migration and rollback commands."},{"url":"https://laravel.com/docs/13.x/queues","title":"Laravel 13 Queues","claim":"Connections versus queues, model serialization, after-commit dispatch, workers and timeout/retry_after interaction."},{"url":"https://laravel.com/docs/13.x/session","title":"Laravel 13 HTTP Session","claim":"Database session default, driver/table prerequisites and session-blocking requirements."},{"url":"https://laravel.com/docs/13.x/authentication","title":"Laravel 13 Authentication","claim":"Guard/provider architecture, sessions, logout and authentication configuration."},{"url":"https://laravel.com/docs/13.x/broadcasting","title":"Laravel 13 Broadcasting","claim":"Broadcast drivers, ShouldBroadcast/queue execution, private authorization and after-commit dispatch."},{"url":"https://laravel.com/docs/13.x/cache","title":"Laravel 13 Cache","claim":"Backend capabilities, atomic lock prerequisites and prefix-independent cache flush."},{"url":"https://laravel.com/docs/13.x/scheduling","title":"Laravel 13 Scheduling","claim":"Scheduler operation, timezones, overlap prevention and shared-store onOneServer locking."},{"url":"https://livewire.laravel.com/docs/4.x/components","title":"Livewire 4 Components","claim":"PHP/Blade components and make:livewire format options."},{"url":"https://livewire.laravel.com/docs/4.x/security","title":"Livewire 4 Security","claim":"Client-controlled properties/actions require validation and authorization."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Laravel + Livewire","kind":["web"],"guide_url":"/stacks/laravel-livewire-postgresql/","markdown_url":"/stacks/laravel-livewire-postgresql/guide.md","json_url":"/stacks/laravel-livewire-postgresql/record.json"},{"id":"python-django","version_scope":"Django 6.0 versioned docs; Celery 5.6.3 and Channels 4.3.2 current docs fetched 11 October 2026. The Django 6 Tasks API is version-specific and does not imply an existing Django 5.2 project should upgrade or abandon Celery. Pin actual Python/package versions before qualifying composition.","operating_model":"Django organizes projects into applications, with conventional URL/view/model boundaries and integrated authentication. Recommendation: keep domain operations and permissions reusable across HTTP views, admin actions and workers. Optional Celery and Channels are separate execution/messaging systems rather than core Django features.","architecture":["Django defaults to database autocommit; transaction.atomic defines an explicit transaction boundary. Recommendation: keep transactions around the domain operation and do external delivery after commit through a recoverable design when required.","Django 6 Tasks defines and enqueues tasks but supplies no worker mechanism. A production backend/worker must be evaluated; Celery remains a separate established candidate. Recommendation: do not equate an API-level task declaration with durable execution.","Channels channel layers exchange high-level application events. channels_redis is the official production-supported layer; the in-memory layer cannot communicate across processes. Recommendation: persist business state separately from transient fanout."],"developer_workflow":["In a disposable target, django-admin startproject mysite creates the project and python manage.py startapp polls creates an application. Preserve the existing project/application structure when migrating a feature.","Use python manage.py makemigrations to prepare reviewed migration files, python manage.py migrate to apply them and python manage.py showmigrations to inspect recorded state. Recommendation: nominate one deployment migration runner for shared tables.","For a configured Celery application, adapt celery -A tasks worker --loglevel=INFO from the official first-steps recipe, replacing tasks with the actual application module. Broker and optional result backend are separate configuration choices.","Before a production target, adapt python manage.py check --deploy to its production settings as the checklist instructs. Recommendation: exercise real database, session, task worker and Channels topology; the development runserver is not a production deployment."],"avoid":["Avoid deploying Django 6 ImmediateBackend/DummyBackend as a substitute for a durable production queue; the Tasks framework requires an external executing backend and documents built-in development/test backends.","Avoid treating transaction.on_commit as durable cross-store delivery: callbacks run after commit and their failures cannot roll back the transaction. Recommendation: use an outbox when lost dispatch after commit is unacceptable.","Avoid using LocMemCache or Channels in-memory layers as shared multi-process state; both are process-local. Avoid using cache-only session storage when eviction/restart logout violates the product contract.","Avoid running two Celery beat schedulers for the same schedule; Celery documents duplicate task production. Recommendation: transfer ownership across frameworks as well as within Celery."],"transitions":[{"capability":"data","existing_contract":"Capture schema, account IDs, database constraints and model/write side effects. Migration history is framework-owned metadata and must be reconciled deliberately when an existing schema changes owner.","coexistence":"Recommendation: retain one domain writer and schema runner; add compatible fields and move one application/domain boundary while old code still reads new records. Use historical models for Django data migrations as documented.","target":"Django models/migrations with explicit transaction.atomic boundaries for related database writes. Recommendation: delay field deletion until the rollback reader retires.","benefits":[{"benefit":"Recommendation: coherent model and migration ownership makes a domain boundary reviewable.","prerequisites":"Tested schema mapping and explicit writer transfer.","tradeoffs":"Database reversal may be irreversible for data operations; application objects/cache state also need reconciliation."}],"checks":["Create in each permitted writer and read in both runtimes; compare identifiers, null values, timestamps and constraint failures.","Fail inside transaction.atomic and verify database writes roll back; inspect whether in-memory/cache state requires explicit restoration."],"rollback":"Recommendation: restore the old writer while retaining additive fields; rehearse reverse migrations on a disposable copy and reconcile non-reversible data operations.","sources":["https://docs.djangoproject.com/en/6.0/topics/migrations/","https://docs.djangoproject.com/en/6.0/topics/db/transactions/"],"id":"data-1"},{"capability":"jobs","existing_contract":"Inventory task names, argument serializer, broker/result backend, retry/acknowledgement policy, periodic schedule and ready/delayed/failed/running work. Django 6 Tasks and Celery payloads are distinct contracts.","coexistence":"Recommendation: keep old consumers for old tasks; isolate new categories and route producers deliberately. Retain the existing scheduler until an explicit one-owner transfer. Qualify any Django Tasks backend separately from the task API.","target":"Celery with a selected broker/optional result backend, or a qualified Django 6 executing backend. Schedule dispatch after database commit; recommendation: add recoverable outbox delivery when a crash can occur before broker acceptance.","benefits":[{"benefit":"Recommendation: execution can migrate by task category while the old backlog remains executable.","prerequisites":"Qualified payload fixtures, retained consumers and one schedule owner.","tradeoffs":"Two task systems may be operated; a framework API switch does not translate backlog or broker guarantees."}],"checks":["Capture a task envelope and run it through its intended consumer, including argument conversion and missing-record behavior.","Rollback the domain transaction and verify no dispatch; kill after an external effect and validate replay-safe completion.","Account for delayed/retry/failed/in-flight tasks and confirm one periodic enqueue for each transferred schedule."],"rollback":"Recommendation: pause new producers, retain consumers for accepted payloads and reconcile completed effects before switching categories back.","sources":["https://docs.djangoproject.com/en/6.0/topics/tasks/","https://docs.djangoproject.com/en/6.0/topics/db/transactions/","https://docs.celeryq.dev/en/stable/userguide/tasks.html","https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html"],"id":"jobs-1"},{"capability":"identity","existing_contract":"Record authentication backend, user mapping, session engine/cookie name, serializer, expiry and revocation. Django database, cached_db, cache-only and signed-cookie sessions have different persistence/failure behavior.","coexistence":"Recommendation: retain a shared identity authority or qualified exchange; do not treat Redis key sharing as session-format compatibility. Keep every resource permission enforced in the target view/operation.","target":"Django authentication with a selected session backend. cached_db writes through to database/cache and reads the database on cache misses; recommendation: prefer it over cache-only sessions when eviction logout is unacceptable.","benefits":[{"benefit":"Recommendation: a durable session authority can preserve access through cache eviction.","prerequisites":"Configured database sessions, compatible account mapping and tested revocation.","tradeoffs":"Write-through caching adds operations; cookie continuity across runtimes still requires a decoder/exchange or planned reauthentication."}],"checks":["Login in the old system, access the Django feature, then logout/expire/revoke and verify both deny unauthorized operations.","Evict the selected session cache and confirm behavior matches the chosen engine; test another tenant’s identifier with a valid login."],"rollback":"Recommendation: preserve previous identity/session contracts through the rollback window and retain revocation state during traffic reversal.","sources":["https://docs.djangoproject.com/en/6.0/topics/http/sessions/","https://docs.djangoproject.com/en/6.0/topics/auth/default/"],"id":"identity-1"},{"capability":"realtime","existing_contract":"Capture WebSocket protocol, group/event names, authentication and required missed-event behavior. Channels channel layers carry application-level events, not a universal client protocol.","coexistence":"Recommendation: keep old client endpoints, bridge versioned domain events and route room/user cohorts including reconnects. Designate one committed-change publisher; use the configured shared layer for multiple processes.","target":"Channels with channels_redis when using the documented official production channel layer. Recommendation: reconstruct/replay from persistent domain state when missed broadcasts matter.","benefits":[{"benefit":"Recommendation: a shared channel layer enables cross-process fanout.","prerequisites":"Configured Redis layer, matching consumers/events and authorized subscriptions.","tradeoffs":"Redis remains operated; transient fanout alone does not provide durable business-message recovery."}],"checks":["Connect subscribers to separate processes and verify one committed change reaches both authorized groups.","Restart/disconnect during an event and verify reconnect obtains missing domain state; revoke access and confirm rejected subscriptions."],"rollback":"Recommendation: retain the previous endpoint/event envelope and route cohorts back including their reconnects; avoid replaying already-applied effects.","sources":["https://channels.readthedocs.io/en/stable/topics/channel_layers.html"],"id":"realtime-1"},{"capability":"cache","existing_contract":"Inventory key versions/prefixes, serializers, TTLs and invalidation, separating disposable values from session/coordination ownership. Django local-memory cache is per-process even though it is thread-safe.","coexistence":"Recommendation: use separate namespaces for changed representations and invalidate both old/new readers. Retain shared storage for cross-process requirements; move only reconstructible local data to LocMemCache.","target":"A deliberately selected Django cache backend with configured timeout/key version. Recommendation: use local caches only where node/process staleness is accepted and rebuilding is safe.","benefits":[{"benefit":"Recommendation: reconstructible caching can change independently of domain writes.","prerequisites":"Defined staleness, invalidation and data ownership.","tradeoffs":"Per-process caches duplicate state; sensitive filesystem cache locations/serialization require care as the docs explain."}],"checks":["Warm two processes, commit a source update and verify both readers meet the stated staleness policy.","Restart or empty only the dedicated cache and verify reconstruction; session/permission correctness must remain intact."],"rollback":"Recommendation: restore old namespace/backend routing and repopulate from authoritative data without treating cache contents as business records.","sources":["https://docs.djangoproject.com/en/6.0/topics/cache/","https://docs.djangoproject.com/en/6.0/topics/db/transactions/"],"id":"cache-1"},{"capability":"operations","existing_contract":"List production application server, settings/secrets, migrations, static/media handling, Celery worker/beat and Channels processes. Celery requires only one scheduler for a schedule at a time.","coexistence":"Recommendation: keep both process inventories and one named migration/schedule owner during coexistence. Define promotion thresholds for outcomes, queue age and denied access rather than promoting from a clean startup alone.","target":"An explicitly configured deployment following Django’s checklist, plus independently supervised selected workers and scheduler. Recommendation: document restored database/session/queue consistency.","benefits":[{"benefit":"Recommendation: production checks and role ownership make recovery responsibilities visible.","prerequisites":"Actual production settings and a rehearsed restore/restart.","tradeoffs":"check --deploy detects configuration issues, not end-to-end job, permission or restore correctness."}],"checks":["Run the deployment checks against intended settings and exercise an authorized/denied request after a disposable restore.","Stop a worker and verify queued work is visible; restart it, then verify one scheduler and intended recovery behavior."],"rollback":"Recommendation: restore old routing/settings and schedule ownership explicitly; preserve accepted tasks and reconcile effects before replay.","sources":["https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/","https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html"],"id":"operations-1"}],"sources":[{"url":"https://docs.djangoproject.com/en/6.0/intro/tutorial01/","title":"Django first application","claim":"Project/application generator commands and development server boundary."},{"url":"https://docs.djangoproject.com/en/6.0/topics/migrations/","title":"Django Migrations","claim":"Migration commands, history, data migrations and reversibility."},{"url":"https://docs.djangoproject.com/en/6.0/topics/db/transactions/","title":"Django Transactions","claim":"Autocommit, atomic, on_commit timing/failure and application-state rollback limitations."},{"url":"https://docs.djangoproject.com/en/6.0/topics/tasks/","title":"Django 6 Tasks","claim":"Task API, no built-in worker, backend requirements, argument conversion and commit timing."},{"url":"https://docs.djangoproject.com/en/6.0/topics/http/sessions/","title":"Django Sessions","claim":"Database/cache/cached_db/cookie choices, persistence, eviction and serialization."},{"url":"https://docs.djangoproject.com/en/6.0/topics/auth/default/","title":"Django Authentication","claim":"Authentication, login/logout and permissions."},{"url":"https://docs.djangoproject.com/en/6.0/topics/cache/","title":"Django Cache","claim":"Backend setup, key versions, per-process local memory and filesystem security concerns."},{"url":"https://channels.readthedocs.io/en/stable/topics/channel_layers.html","title":"Channels Channel Layers","claim":"Official production channels_redis and lack of cross-process delivery in memory."},{"url":"https://docs.celeryq.dev/en/stable/userguide/tasks.html","title":"Celery Tasks","claim":"Task naming/serialization, retries, acknowledgement and idempotency concerns."},{"url":"https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html","title":"Celery Periodic Tasks","claim":"Single scheduler ownership, schedule timezone and overlap concerns."},{"url":"https://docs.celeryq.dev/en/stable/getting-started/first-steps-with-celery.html","title":"Celery First Steps","claim":"Worker command and distinction between broker and result backend."},{"url":"https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/","title":"Django Deployment Checklist","claim":"Production settings, check --deploy and deployment responsibilities."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Django","kind":["web"],"guide_url":"/stacks/python-django/","markdown_url":"/stacks/python-django/guide.md","json_url":"/stacks/python-django/record.json"},{"id":"go-service","version_scope":"Go 1.27 release documentation and current net/http/context APIs; sqlc latest transaction guide with pgx/v5; River current documentation, PostgreSQL composition. Resolve and record Go toolchain, sqlc, pgx and River versions in go.mod/go.sum and tool configuration before implementation. Official release notes identify Go 1.27 as the August 2026 release; this research does not qualify a lockfile or patch-version/security support.","operating_model":"Recommendation: a Go HTTP process owns request admission and cancellation, domain packages own invariants, and a bounded SQL connection pool provides parallel database access. Durable jobs have their own persisted queue and worker lifecycle. Goroutines and buffered channels are concurrency mechanisms; graceful drain does not turn them into a recoverable queue.","architecture":["Recommendation: compose net/http handlers around small domain packages and explicit interfaces at external boundaries. Pass request contexts into SQL and outbound calls. Use bounded errgroup fan-out or a worker pool, with an overload policy separate from concurrency limits.","Recommendation: maintain reviewed SQL migrations and sqlc-generated queries; share a pool, acquire transactions only around domain writes, and call generated WithTx for the transaction. A single global mutex around all queries recreates avoidable serialization.","Recommendation: PostgreSQL-backed River workers can run with the web binary initially or in an independent process when their scaling or shutdown needs differ. Insert business state and a job in the same database transaction when atomic enqueue is required; cross-database work needs a separately designed outbox.","Recommendation: retain an established identity provider, object store, email sender and broker during a service rewrite. Request/response, cookies, WebSocket application protocol, job payload and retry semantics are separate compatibility contracts."],"developer_workflow":["Recommendation: start from the existing repository; inspect go.mod, go.sum, schema migrations and queue configuration. For a new local source project, go mod init example.com/service creates a module; choose the actual module path and supported toolchain deliberately.","Recommendation: pin sqlc using the maintained tool installer, review sqlc.yaml and SQL source, then run sqlc generate after query/schema changes. Regenerate in CI and compare output; generated typing does not validate production schema drift.","Recommendation: run gofmt on changed files, go vet ./..., go test ./... and go test -race ./... on a supported target. Add HTTP contract fixtures and cancellation/queue crash tests against the selected database; these are proposed checks, not executed here.","Recommendation: build the service with go build ./cmd/server when that entry point exists. Set server/header/body deadlines, worker limits, pool capacity and shutdown budget from the deployment shape. Profile a representative workload with Go pprof; performance and cost remain unmeasured."],"avoid":["Avoid one fire-and-forget goroutine per email/payment: an acknowledged request can survive while its side effect disappears on process crash.","Avoid spawning unlimited goroutines to hide slow SQL. The downstream pool becomes the queue; cancellation, memory and overload must be bounded explicitly.","Avoid replacing Redis solely because Go has channels. Shared fan-out, sessions, queue persistence and locks have different semantics.","Avoid treating Server.Shutdown as complete WebSocket shutdown; hijacked connections need separate notification and tracking."],"transitions":[{"capability":"data","existing_contract":"Inventory the old schema, isolation, identifiers, null handling, timestamps and transaction invariants before changing language or driver.","coexistence":"Recommendation: let the old application own migrations while Go accesses backward-compatible columns through sqlc; route writes for one domain/cohort to one owner. Expand schema before either runtime needs it.","target":"Recommendation: sqlc plus pgx/v5 or database/sql, a shared bounded pool and explicit transaction-scoped queries. Keep the existing database if its guarantees fit; PostgreSQL replacement is a separate migration.","benefits":[{"benefit":"Typed query access and concurrent pool usage can improve maintainability and remove an unnecessary application-wide lock.","prerequisites":"Queries generated from the reviewed schema; representative null/time/transaction fixtures; a pool sized across all replicas.","tradeoffs":"Generated types do not prove isolation or business correctness; pool exhaustion and migrations still need operations."}],"checks":["Verify old-write/new-read and new-write/old-read round trips, including nulls and time zones.","Force a mid-transaction failure; neither half of the domain change may commit.","Saturate the pool and cancel a request; verify bounded waiting and connection release."],"rollback":"Recommendation: return domain traffic to the old writer while preserving expanded columns. Contract migrations wait until the rollback window closes; reconcile any new-only writes first.","sources":["https://docs.sqlc.dev/en/latest/howto/transactions.html","https://go.dev/doc/database/manage-connections"],"id":"data-1"},{"capability":"jobs","existing_contract":"Capture existing queue kind, JSON arguments, retry/schedule/dead-letter states and external-effect idempotency. Redis/Resque or Sidekiq payloads are not River payloads.","coexistence":"Recommendation: keep old workers consuming old queues. Route selected job kinds into River through versioned producers; transform outstanding states explicitly and retain one scheduler. River Ruby integration is a possible bridge, not automatic Active Job execution.","target":"Recommendation: River PostgreSQL jobs with domain writes plus InsertTx in the same transaction, explicit queue concurrency and bounded retry policy. Keep Redis for any remaining broker/session/cache responsibility.","benefits":[{"benefit":"Atomic enqueue closes the gap between committing domain state and creating work.","prerequisites":"Domain rows and River insertion use the same transaction/database; validated job-kind/argument mapping; replay-safe external effects.","tradeoffs":"Adds queue schema/maintenance to the database; retries can repeat effects; backlog migration and adapter compatibility are separate work."}],"checks":["Abort the domain transaction; no job may become available.","Crash after sending an email but before job completion; retry must reconcile or reuse a stable external idempotency key.","Account for ready, scheduled, retry, failed and in-flight jobs before retiring the old consumer."],"rollback":"Recommendation: stop new River producers and reroute only unstarted work after reconciliation. Keep a compatible River consumer for already accepted jobs; a broker switch cannot undo sent messages or payments.","sources":["https://riverqueue.com/docs/transactional-enqueueing","https://riverqueue.com/docs/job-retries","https://riverqueue.com/docs/ruby"],"id":"jobs-1"},{"capability":"operations","existing_contract":"Preserve response/error behavior, overload rules, deadlines and restart guarantees; establish separate budgets for HTTP and background jobs.","coexistence":"Recommendation: serve a fixed tenant/endpoint cohort from Go behind existing routing, with errgroup/context cancellation inside each operation and deployment-level readiness. Keep the old instance ready for contract-compatible rollback.","target":"Recommendation: signal.NotifyContext, bounded admission/fan-out, Server.Shutdown with a fresh timeout context, tracked worker termination and pool closure after accepted work ends.","benefits":[{"benefit":"Cooperative cancellation limits work that callers no longer need and makes planned shutdown observable.","prerequisites":"Every called operation honors context; in-flight tasks are tracked; the platform shutdown grace period exceeds the configured drain budget.","tradeoffs":"Cancellation does not reverse a committed write or external effect; forced termination still loses memory-only work."}],"checks":["Cancel a client while SQL/outbound I/O is blocked; verify resources and goroutines settle.","Send SIGTERM during a write; the server stops admitting traffic and records whether accepted work committed.","Force SIGKILL; verify durable jobs recover independently of graceful drain."],"rollback":"Recommendation: return routing to the old process and inspect committed outcomes and queued work before any replay. Preserve identical database/schema contracts for both versions.","sources":["https://pkg.go.dev/net/http#Server.Shutdown","https://pkg.go.dev/golang.org/x/sync/errgroup","https://pkg.go.dev/os/signal#NotifyContext"],"id":"operations-1"},{"capability":"realtime","existing_contract":"Preserve authentication, subscription identifiers, frame formats, ordering, reconnect cursors and cross-runtime fan-out. A WebSocket transport does not implement Action Cable automatically.","coexistence":"Recommendation: keep the existing broker and explicit application-protocol adapter while moving complete room/tenant cohorts, including reconnects, to Go. Publish/subscribe in both directions using captured protocol fixtures.","target":"Recommendation: coder/websocket for transport plus bounded per-connection queues, tracked close handshakes and an application resynchronization path. Retain Redis Pub/Sub if ephemeral cross-process fan-out fits; add durable history separately where missed events matter.","benefits":[{"benefit":"Explicit connection ownership and queue limits bound slow-client resource use.","prerequisites":"Capacity and disconnect policy are defined; clients can reconnect and reconstruct authoritative state; broker/protocol compatibility is tested.","tradeoffs":"Redis Pub/Sub is at-most-once and does not replay disconnected messages; replacing it with process-local channels removes cross-instance delivery."}],"checks":["Connect an old and new client to the same room; verify broadcasts in both directions and revocation.","Disconnect a subscriber and publish; reconnect must fetch missed authoritative state.","Throttle one client; verify the configured queue overflow policy and independent delivery to others."],"rollback":"Recommendation: route the cohort and reconnects back together. Keep the broker and old protocol alive; rebuild client state from durable records rather than pretending ephemeral events can be replayed.","sources":["https://github.com/coder/websocket","https://redis.io/docs/latest/develop/pubsub/","https://pkg.go.dev/net/http#Server.Shutdown"],"id":"realtime-1"}],"sources":[{"url":"https://go.dev/doc/go1.27","title":"Go 1.27 release notes","claim":"Identifies the August 2026 release; runtime/tool behavior and JSON v2 are documented with compatibility caveats."},{"url":"https://go.dev/doc/modules/managing-dependencies","title":"Managing Go dependencies","claim":"go.mod and go.sum own module dependency versions."},{"url":"https://docs.sqlc.dev/en/latest/howto/transactions.html","title":"sqlc transactions","claim":"Generated WithTx binds queries to a transaction; pgx/v5 example is documented."},{"url":"https://go.dev/doc/database/manage-connections","title":"Go SQL connection management","claim":"sql.DB is a concurrent pool; limits can cause waits and deadlocks."},{"url":"https://riverqueue.com/docs/transactional-enqueueing","title":"River transactional enqueue","claim":"Enqueue with domain changes in one transaction avoids a two-store commit gap."},{"url":"https://riverqueue.com/docs/job-retries","title":"River retries","claim":"Retry attempts and delays are configurable; errors lead to repeated execution."},{"url":"https://riverqueue.com/docs/ruby","title":"River Ruby integration","claim":"Current documentation describes a shared River schema and explicit job kinds/JSON arguments."},{"url":"https://pkg.go.dev/net/http#Server.Shutdown","title":"net/http server shutdown","claim":"HTTP drain does not close or await hijacked WebSocket connections."},{"url":"https://pkg.go.dev/golang.org/x/sync/errgroup","title":"errgroup","claim":"WithContext cancellation and SetLimit support bounded concurrent work."},{"url":"https://pkg.go.dev/os/signal#NotifyContext","title":"os/signal","claim":"NotifyContext connects OS signals to context cancellation."},{"url":"https://github.com/coder/websocket","title":"coder/websocket README","claim":"Go WebSocket transport supports contexts and close handshakes; application protocol is separate."},{"url":"https://redis.io/docs/latest/develop/pubsub/","title":"Redis Pub/Sub","claim":"Pub/Sub delivery is at-most-once; disconnected subscribers miss events."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Go service","kind":["api"],"guide_url":"/stacks/go-service/","markdown_url":"/stacks/go-service/guide.md","json_url":"/stacks/go-service/record.json"},{"id":"rust-service","version_scope":"Reviewed docs expose Axum 0.8.9, Tokio 1.53.2 and SQLx 0.9.0 on 11 October 2026; Rust std docs show 1.99.0. These are documentation scopes, not a qualified composition or an automatic upgrade recommendation. Record rust-toolchain.toml, Cargo.lock, enabled features, SQLx CLI compatibility and database version before implementation.","operating_model":"Recommendation: Axum/Tower owns request extraction and middleware; domain functions return explicit errors; Tokio drives async I/O; SQLx pools and transactions own database access. Track child tasks and cancellation separately from persisted jobs. A Tokio channel coordinates tasks inside one live process and cannot recover accepted work after a crash.","architecture":["Recommendation: route through Axum extractors and typed State with cloneable pool/client handles. Keep domain logic independent of HTTP responses, and map business/infrastructure errors deliberately through IntoResponse.","Recommendation: compose Tower limits and timeouts with explicit overload/error mapping. Use bounded channels/semaphores; a concurrency limit without an admission/queue policy does not bound every waiting request.","Recommendation: use SQLx query macros where schema checking fits and dynamic queries where needed. Store reviewed migrations and offline metadata; runtime authorization and isolation remain separate from compile-time type checks.","Recommendation: move sustained CPU work to a bounded blocking executor or dedicated worker process. Track async tasks with cancellation/termination coordination; a started spawn_blocking task cannot be forcibly aborted through Tokio."],"developer_workflow":["Recommendation: inspect Cargo.toml/Cargo.lock, rust-toolchain.toml and enabled dependency features. For a new project, cargo new service and cargo add axum can establish the package; select Tokio runtime and SQLx database/TLS features deliberately rather than copying a broad feature list.","Recommendation: run cargo fmt --check, cargo clippy --all-targets -- -D warnings and cargo test --locked with the project-supported feature matrix. Compile and run protocol/cancellation fixtures; no integration was executed in this research.","Recommendation: install a SQLx CLI version compatible with the selected crate; against a disposable database run cargo sqlx migrate run, then cargo sqlx prepare --workspace and commit .sqlx metadata. CI cargo sqlx prepare --check --workspace detects stale metadata; an offline build does not prove the live schema matches.","Recommendation: cargo build --release --locked prepares a release for the selected target. Use tracing/request IDs, pool-wait metrics and representative load profiles before asserting latency, cost or capacity advantages."],"avoid":["Avoid Arc<Mutex<Connection>> for all queries; a shared pool permits independent requests without a global database serialization boundary.","Avoid blocking SDK calls or heavy loops on async runtime workers. spawn_blocking needs its own capacity limits and cannot abort a running closure.","Avoid using Tokio mpsc as a drop-in replacement for durable Redis jobs; accepted jobs disappear with memory unless independently persisted.","Avoid confusing Rust memory safety with correct authorization, queue delivery or compatibility guarantees."],"transitions":[{"capability":"data","existing_contract":"Capture the old SQL schema, transactions, tenant filters, nullable values and database engine behavior.","coexistence":"Recommendation: keep existing schema ownership and backward-compatible columns; run SQLx reads for a selected boundary before transferring its writer. Generate metadata against exactly the reviewed migration state.","target":"Recommendation: SQLx pooled access with transaction-scoped domain changes; query! checks literals against a live build schema or checked-in .sqlx metadata. Keep database engine choice independent of the Rust rewrite.","benefits":[{"benefit":"Compile-time query/type checks can expose SQL/API mismatches before deployment.","prerequisites":"Query macros/features and metadata match the chosen engine and schema; nullable results and dynamic queries receive runtime checks.","tradeoffs":"Dynamic SQL is outside macro checking; stale offline metadata can compile; schema ownership and deployment order still need controls."}],"checks":["Run an offline locked build with the reviewed metadata and a live prepare --check against the migrated fixture DB.","Verify both runtimes read each other's writes, including null/time/ID mappings.","Cancel during a transaction and confirm the intended committed/rolled-back state plus pool recovery."],"rollback":"Recommendation: route writes back while retaining additive schema and a previous compatible binary. Delay destructive migrations; reconcile new-only persisted values before rollback.","sources":["https://docs.rs/sqlx/latest/sqlx/macro.query.html","https://docs.rs/sqlx/latest/sqlx/struct.Pool.html","https://github.com/transact-rs/sqlx/blob/main/sqlx-cli/README.md"],"id":"data-1"},{"capability":"jobs","existing_contract":"Preserve old broker serialization, acknowledgement, delayed/retry states, concurrency and effect IDs. Durable Redis workers cannot be replaced transparently by tokio::spawn.","coexistence":"Recommendation: Rust commits an outbox intent in its domain transaction; a replay-safe relay submits to the retained queue/worker interface. Existing workers retain queue ownership while new job kinds are qualified. This outbox design is inferred, not a shipped Axum feature.","target":"Recommendation: keep proven durable workers until a chosen Rust queue implementation is qualified for retries, scheduling, leases and crashes. River current Ruby docs describe a shared schema with Rust; that statement alone does not qualify a Rust worker API or migration.","benefits":[{"benefit":"A retained worker boundary allows the HTTP layer to change without dropping backlog or broker guarantees.","prerequisites":"Same-transaction outbox intent; stable relay/effect IDs; explicit payload translator; observed delivery and backlog ownership.","tradeoffs":"Two runtimes and a relay add operations; delivery may repeat; a SQLx transaction alone does not atomically submit to Redis."}],"checks":["Crash after the domain commit but before relay delivery; an intent remains recoverable.","Crash the relay after submission before marking sent; duplicate delivery produces one logical effect.","Reconcile ready, scheduled, retry, failed and in-flight old jobs before any worker replacement."],"rollback":"Recommendation: stop the new producer, let accepted outbox entries settle and retain the old worker. Roll back traffic only after ambiguous effects are reconciled; avoid consuming a queue with incompatible handlers.","sources":["https://tokio.rs/tokio/tutorial/channels","https://docs.rs/sqlx/latest/sqlx/macro.query.html","https://riverqueue.com/docs/ruby"],"id":"jobs-1"},{"capability":"operations","existing_contract":"Define HTTP admission/error behavior, task ownership, shutdown deadline and behavior for unavoidable CPU/blocking operations.","coexistence":"Recommendation: retain old routing while introducing Tower admission bounds and CancellationToken/TaskTracker-based ownership for a coherent endpoint cohort. Benchmark equivalent resource limits and durability before attributing gains to Rust.","target":"Recommendation: bounded async work, cooperative cancellation, explicit shutdown notification plus tracked task completion, and SQLx pool close. For untrusted/hard-deadline native work, isolate it in a process whose termination contract is tested.","benefits":[{"benefit":"Tracked tasks and explicit admission limits make resource use and planned termination easier to reason about.","prerequisites":"Every child task is tracked; limits include waiting work; cancellation-safe I/O and shutdown budget are verified.","tradeoffs":"spawn_blocking closures may continue after timeout; killing a process still requires durable recovery and external-effect reconciliation."}],"checks":["Cancel an overloaded request and verify queue permits/resources are released.","Send termination during async and blocking work; document tasks that drain and work that cannot be aborted.","Force a crash after acceptance; verify recovery of persisted intents and honest failure of memory-only work."],"rollback":"Recommendation: return the cohort to the previous service and reconcile persisted outcomes. Do not simply retry every cancelled request; its external effect may already have occurred.","sources":["https://docs.rs/tower/latest/tower/limit/index.html","https://tokio.rs/tokio/topics/shutdown","https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html","https://docs.rs/sqlx/latest/sqlx/struct.Pool.html"],"id":"operations-1"},{"capability":"realtime","existing_contract":"Preserve subscriptions, authorization revocation, event versions, reconnect behavior and cross-instance fan-out.","coexistence":"Recommendation: keep the existing shared broker while a Rust gateway serves entire room/tenant cohorts; adapters translate existing messages without dual-emitting user effects. Test cross-runtime publication in both directions.","target":"Recommendation: Axum WebSocket transport and bounded per-client queues. Tokio broadcast is appropriate only for local ephemeral fan-out with explicit Lagged handling; use durable state/event history to resync clients when loss is unacceptable.","benefits":[{"benefit":"Local bounded fan-out can prevent slow receivers from retaining unbounded memory.","prerequisites":"Overflow/disconnect/resync policy exists; replica routing and shared fan-out remain covered; every broadcast error is handled.","tradeoffs":"Tokio broadcast is process-local; old messages are evicted when capacity is exceeded; it is not a durable cross-node broker."}],"checks":["Force receiver lag and verify Lagged triggers resync or the documented disconnect policy.","Publish from each runtime and confirm the opposite runtime's subscribers receive the intended event.","Reconnect after deployment/crash and rebuild state without trusting missing ephemeral frames."],"rollback":"Recommendation: route the whole cohort back and retain the shared broker/protocol. Resynchronize from authoritative durable records; local channel state cannot be restored from a previous binary.","sources":["https://docs.rs/axum/latest/axum/","https://docs.rs/tokio/latest/tokio/sync/broadcast/index.html","https://redis.io/docs/latest/develop/pubsub/"],"id":"realtime-1"}],"sources":[{"url":"https://docs.rs/axum/latest/axum/","title":"Axum 0.8.9","claim":"Typed extractors/State and Tower middleware form the HTTP composition."},{"url":"https://docs.rs/tower/latest/tower/limit/index.html","title":"Tower limits","claim":"Concurrency and rate-limit middleware exist; admission/error policy needs application design."},{"url":"https://tokio.rs/tokio/topics/shutdown","title":"Tokio graceful shutdown","claim":"CancellationToken and TaskTracker coordinate shutdown and task completion."},{"url":"https://tokio.rs/tokio/tutorial/channels","title":"Tokio channels","claim":"Bounded mpsc coordinates tasks; capacity/backpressure is explicit, not disk persistence."},{"url":"https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html","title":"Tokio spawn_blocking","claim":"CPU work needs a parallelism limit; started blocking tasks cannot be aborted."},{"url":"https://docs.rs/sqlx/latest/sqlx/macro.query.html","title":"SQLx 0.9.0 query macro","claim":"Literal queries need a matching database schema or offline metadata; engine binding and nullability have limits."},{"url":"https://docs.rs/sqlx/latest/sqlx/struct.Pool.html","title":"SQLx pool","claim":"A shared async pool supports reuse; a mutex around one connection creates serialization; close should be awaited."},{"url":"https://github.com/transact-rs/sqlx/blob/main/sqlx-cli/README.md","title":"SQLx CLI README","claim":"Migrations and prepare/prepare --check maintain offline query metadata."},{"url":"https://docs.rs/tokio/latest/tokio/sync/broadcast/index.html","title":"Tokio broadcast","claim":"A bounded local channel drops older values and reports lagging receivers."},{"url":"https://riverqueue.com/docs/ruby","title":"River cross-language schema statement","claim":"Current Ruby guide explicitly names Go, Rust and TypeScript sharing a schema; Rust API itself is unqualified here."},{"url":"https://redis.io/docs/latest/develop/pubsub/","title":"Redis Pub/Sub","claim":"Ephemeral shared fan-out remains at-most-once and needs a separate recovery path."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Rust service","kind":["api"],"guide_url":"/stacks/rust-service/","markdown_url":"/stacks/rust-service/guide.md","json_url":"/stacks/rust-service/record.json"},{"id":"python-fastapi","version_scope":"Current FastAPI online guides fetched 11 October 2026; these pages do not pin an application package release. SQLAlchemy 2.0 documentation, Alembic 1.20.0 documentation and Celery 5.6.3 reviewed. Pin actual Python/FastAPI/Pydantic/Starlette/database-driver versions before implementation.","operating_model":"FastAPI exposes Python type-annotated HTTP operations and dependency boundaries. It supports synchronous and async handlers; the choice depends on whether called libraries support await. Recommendation: keep request models distinct from persistence operations and explicitly select identity, durable jobs, database migrations and distributed messaging instead of assuming the API framework provides an integrated monolith.","architecture":["Use async handlers with awaitable I/O, or normal def handlers for blocking libraries as the guide explains. Recommendation: keep CPU-heavy or durable work behind a deliberate worker boundary; adding async syntax does not change its durability or CPU execution model.","SQLAlchemy Sessions are mutable transaction state and cannot be shared across concurrent tasks/threads. Recommendation: scope a Session to its operation, keep the engine/pool separate and use reviewed Alembic revisions for schema evolution.","FastAPI lifespan manages startup/shutdown resources. Multiple worker processes are a deployment choice; recommendation: treat in-memory connections/caches as worker-local and keep one external scheduler and migration owner."],"developer_workflow":["For a configured application, adapt fastapi run --workers 4 main.py or uvicorn main:app --workers 4 from the deployment guide; replace module and worker count for the actual host/container topology. The example is a recipe, not a capacity recommendation.","For a configured SQLAlchemy/Alembic application, use alembic revision -m \"Add a column\", review upgrade/downgrade operations, then alembic upgrade head in a disposable database; alembic current and alembic history show recorded state.","Use FastAPI TestClient and pytest for HTTP contract tests. Recommendation: add real database/worker tests for transaction and retry boundaries, plus WebSocket tests across the intended multi-process topology.","Recommendation: initialize shared process resources through the lifespan context and create independent database sessions for concurrent operations. Validate selected sync/async client usage and cleanup on shutdown before rollout."],"avoid":["Avoid using BackgroundTasks as a durable queue replacement: it executes work associated with the application process, while the guide recommends larger worker tools such as Celery for work that belongs in multiple processes/servers. Recommendation: qualify crash/retry behavior explicitly.","Avoid copying the tutorial’s in-memory WebSocket connection list into a multi-worker product; the docs explicitly say it works only in one running process.","Avoid sharing one SQLAlchemy Session among requests/tasks or moving blocking library calls unchanged inside async handlers; the documented concurrency boundaries differ.","Avoid treating a JWT tutorial as a complete identity migration. Recommendation: retain existing identity authority until issuer, account mapping, validation, expiry and revocation are designed. Starlette SessionMiddleware signs readable cookie contents; signing is not confidentiality."],"transitions":[{"capability":"data","existing_contract":"Inventory schema, primary IDs, SQL value/constraint semantics and existing transaction ownership. The old application may use an ORM with callbacks absent from the target service.","coexistence":"Recommendation: keep one writer and migration runner; start with read-only target endpoints or a bounded domain operation. Preserve additive schema and qualify ORM/session behavior before transferring writes.","target":"SQLAlchemy 2.0 Sessions scoped to operations, with reviewed Alembic upgrade/downgrade revisions and the selected PostgreSQL driver. Recommendation: keep transaction and external delivery boundaries explicit.","benefits":[{"benefit":"Recommendation: explicit sessions and migrations expose the persistence boundary to review.","prerequisites":"Qualified schema mapping, operation-scoped sessions and one writer owner.","tradeoffs":"FastAPI does not remove ORM/migration choices; deleting data cannot be undone by traffic rollback."}],"checks":["Create through each allowed writer and read through both runtimes; compare IDs, timestamps/null values and constraint failures.","Run concurrent requests with separate sessions and fail one transaction; no other request’s changes or Session state may leak.","Apply a reviewed revision to a disposable restored database and verify both deployed versions still read new writes."],"rollback":"Recommendation: restore the old writer while preserving backward-readable schema; use downgrade only for a rehearsed reversible change and reconcile accepted new data.","sources":["https://fastapi.tiangolo.com/tutorial/sql-databases/","https://docs.sqlalchemy.org/en/20/orm/session_basics.html","https://alembic.sqlalchemy.org/en/latest/tutorial.html"],"id":"data-1"},{"capability":"jobs","existing_contract":"Record old task name/class, serialized arguments, broker, retries/acknowledgements, schedule ownership and every backlog state. BackgroundTasks and a broker-backed worker are different execution contracts.","coexistence":"Recommendation: retain old consumers and isolate new categories behind a versioned task interface. Use configured Celery or another separately qualified persistent system when restart recovery matters; keep one schedule owner and a recoverable domain-to-broker handoff.","target":"An explicitly selected durable worker/broker composition with business idempotency. Small best-effort same-process tasks may use BackgroundTasks only when their documented process boundary fits the requirement.","benefits":[{"benefit":"Recommendation: durable work can move independently of the HTTP service.","prerequisites":"Selected broker/worker configuration, tested payloads and explicit idempotency/recovery policy.","tradeoffs":"The broker and worker add operated components; returning HTTP success before enqueue acceptance must have a deliberate contract."}],"checks":["Enqueue while the worker is stopped, restart it and verify the intended completion/failure result.","Crash after an external effect before acknowledgement and verify retry is safe; test broker outage during a domain write.","Reconcile delayed/retry/failed/running tasks and verify the old and new schedulers never both produce the category."],"rollback":"Recommendation: stop new task production, retain consumers for accepted payload versions and reconcile external effects before restoring old routing.","sources":["https://fastapi.tiangolo.com/tutorial/background-tasks/","https://docs.celeryq.dev/en/stable/userguide/tasks.html","https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html","https://docs.celeryq.dev/en/stable/getting-started/first-steps-with-celery.html"],"id":"jobs-1"},{"capability":"identity","existing_contract":"Capture the source identity authority, token/cookie format, issuer/audience rules, account IDs, expiration and revocation. The FastAPI OAuth2/JWT guide is a concrete example, not proof of compatibility with existing sessions.","coexistence":"Recommendation: retain the existing identity authority through qualified token verification or session exchange. Move resource authorization with the domain operation, and keep cookie scope/CSRF behavior explicit when using browser cookies.","target":"Selected OAuth2/token or browser-session implementation with deliberate account mapping and authorization. If using Starlette SessionMiddleware, cookie data is signed and readable; use https_only and appropriate scope settings for the selected deployment.","benefits":[{"benefit":"Recommendation: a shared identity authority can preserve access across service boundaries.","prerequisites":"Qualified token/session validation and common revocation policy.","tradeoffs":"Bearer expiry and server-side revocation are distinct design choices; readable signed cookies must not contain secrets."}],"checks":["Accept a valid source credential and reject modified/expired credentials and a valid credential for another tenant’s protected record.","Logout/revoke and confirm both old/new paths follow the agreed policy, including an existing WebSocket connection.","Inspect the selected cookie contents and transport flags; no secret should be exposed by a signed-but-readable session."],"rollback":"Recommendation: preserve account mapping and old credential/exchange support without reversing revocations; return routing only when validation remains compatible.","sources":["https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/","https://starlette.dev/middleware/","https://fastapi.tiangolo.com/advanced/websockets/"],"id":"identity-1"},{"capability":"realtime","existing_contract":"Capture the old client protocol, message envelope, connection authentication, subscription scopes and recovery requirements. FastAPI provides WebSocket handlers; its in-memory chat example is explicitly single-process.","coexistence":"Recommendation: retain the old endpoint for old clients, bridge versioned domain events and route a stable user/room cohort including reconnects. Keep fanout in a separately qualified shared system when multiple worker processes must reach the same recipients.","target":"FastAPI WebSocket handlers with explicit shared fanout and persisted domain-state/cursor recovery when required. No specific broker/client combination is integration-qualified by this research.","benefits":[{"benefit":"Recommendation: an API service can take ownership of a realtime boundary incrementally.","prerequisites":"Matching message/client protocol, subscription authorization and a tested multi-worker fanout implementation.","tradeoffs":"Connection lists remain worker-local; selecting a shared broker does not itself provide catch-up or protocol compatibility."}],"checks":["Connect clients to different workers, commit one domain event and verify all authorized recipients converge.","Restart the worker containing a connection, reconnect and recover missed state; test a revoked identity attempting to rejoin."],"rollback":"Recommendation: preserve old endpoint/message compatibility through reconnects; return the cohort and reconcile current domain state instead of reissuing effects.","sources":["https://fastapi.tiangolo.com/advanced/websockets/","https://fastapi.tiangolo.com/deployment/server-workers/"],"id":"realtime-1"},{"capability":"cache","existing_contract":"List reconstructible values and representation/TTL/invalidation separately from identities, jobs, counters and locks. Multiple application workers have distinct in-memory resource ownership.","coexistence":"Recommendation: keep the existing shared store for coordination; use versioned keys for new representations. Move only disposable values to local caching when per-worker staleness and rebuilding are explicitly accepted.","target":"A selected shared cache accessed with a qualified client such as redis-py, or an explicitly bounded local cache. Recommendation: match sync/async client usage to the handler and manage process resource lifetime deliberately.","benefits":[{"benefit":"Recommendation: cache choices can evolve without transferring domain ownership.","prerequisites":"Reconstructible values, explicit invalidation and a complete list of shared-store uses.","tradeoffs":"Local values diverge across workers; removing Redis requires separately retiring sessions/queues/coordination, not merely replacing cached reads."}],"checks":["Warm two workers, update the source and verify required invalidation/staleness across both.","Restart one worker or evict its dedicated cache and verify reconstruction without losing identity or queued work.","Make the shared cache unavailable and verify the agreed failure/fallback behavior rather than silently accepting stale authorization."],"rollback":"Recommendation: restore old key/backend routing and repopulate from domain data; preserve shared coordination ownership throughout.","sources":["https://redis.io/docs/latest/develop/clients/redis-py/","https://fastapi.tiangolo.com/deployment/server-workers/","https://fastapi.tiangolo.com/advanced/events/"],"id":"cache-1"},{"capability":"operations","existing_contract":"Inventory API workers, startup resources, database migration runner, durable workers, external scheduler and shared fanout/cache. Worker replication and lifespan resources are not a singleton schedule mechanism.","coexistence":"Recommendation: keep one schema runner and logical schedule owner while both services operate. Initialize per-process resources deliberately and transfer traffic by a stable domain/user cohort.","target":"An explicit FastAPI/Uvicorn process/container deployment, lifespan cleanup, separate durable workers and monitored dependency boundaries. Recommendation: record actual worker count and deployment settings instead of inheriting tutorial values.","benefits":[{"benefit":"Recommendation: process/resource ownership makes multi-worker behavior reviewable.","prerequisites":"Named migration/schedule owners, configured shutdown and tested dependency failure behavior.","tradeoffs":"More workers multiply process-local resources; successful HTTP startup cannot qualify broker, identity or restore behavior."}],"checks":["Start the intended multi-worker deployment against disposable restored data; exercise authenticated HTTP, one task and cross-worker realtime delivery.","Terminate a worker during active work, verify shutdown/restart behavior and confirm no extra scheduler or migration runner starts."],"rollback":"Recommendation: preserve the old release and compatible schema, transfer role ownership before restoring traffic, and reconcile work accepted by the new service.","sources":["https://fastapi.tiangolo.com/advanced/events/","https://fastapi.tiangolo.com/deployment/server-workers/","https://alembic.sqlalchemy.org/en/latest/tutorial.html","https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html"],"id":"operations-1"}],"sources":[{"url":"https://fastapi.tiangolo.com/async/","title":"FastAPI Concurrency","claim":"Choice of async handlers versus normal def for awaitable/blocking libraries."},{"url":"https://fastapi.tiangolo.com/tutorial/sql-databases/","title":"FastAPI SQL Databases","claim":"Request-scoped database dependency examples and external SQL library choice."},{"url":"https://docs.sqlalchemy.org/en/20/orm/session_basics.html","title":"SQLAlchemy Session Basics","claim":"Mutable transaction/session lifecycle and prohibition on sharing across concurrent threads/tasks."},{"url":"https://alembic.sqlalchemy.org/en/latest/tutorial.html","title":"Alembic Tutorial","claim":"Revision, upgrade/downgrade, current/history commands and schema revision ownership."},{"url":"https://fastapi.tiangolo.com/tutorial/background-tasks/","title":"FastAPI Background Tasks","claim":"Same-process task examples and separate Celery/queue recommendation for larger execution boundaries."},{"url":"https://docs.celeryq.dev/en/stable/userguide/tasks.html","title":"Celery Tasks","claim":"Task serialization, retry/acknowledgement and idempotency concerns."},{"url":"https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html","title":"Celery Periodic Tasks","claim":"Only one scheduler per schedule to prevent duplicate production."},{"url":"https://docs.celeryq.dev/en/stable/getting-started/first-steps-with-celery.html","title":"Celery First Steps","claim":"Broker, worker and optional result backend are separate roles."},{"url":"https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/","title":"FastAPI OAuth2/JWT example","claim":"Token/password validation and expiry example, not cross-framework session interoperability."},{"url":"https://starlette.dev/middleware/","title":"Starlette Middleware","claim":"Signed readable sessions, session cookie configuration and secure transport option."},{"url":"https://fastapi.tiangolo.com/advanced/websockets/","title":"FastAPI WebSockets","claim":"Handler authentication/dependencies and explicit single-process limitation of in-memory connection list."},{"url":"https://fastapi.tiangolo.com/advanced/events/","title":"FastAPI Lifespan","claim":"Startup/shutdown resource ownership through lifespan."},{"url":"https://fastapi.tiangolo.com/deployment/server-workers/","title":"FastAPI Server Workers","claim":"FastAPI/Uvicorn worker commands and deployment process topology choices."},{"url":"https://fastapi.tiangolo.com/tutorial/testing/","title":"FastAPI Testing","claim":"TestClient and pytest request checks."},{"url":"https://redis.io/docs/latest/develop/clients/redis-py/","title":"Redis redis-py guide","claim":"Maintained Python client for Redis with sync/async documentation links."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"FastAPI","kind":["api"],"guide_url":"/stacks/python-fastapi/","markdown_url":"/stacks/python-fastapi/guide.md","json_url":"/stacks/python-fastapi/record.json"},{"id":"go-cli","version_scope":"Go 1.27 toolchain documentation, Cobra/pflag current guide, Bubble Tea upstream API and GoReleaser v2 documentation. Pin compatible module/tool versions; Bubble Tea major/module-path changes need explicit review. No release matrix was built.","operating_model":"Recommendation: keep the command tree as an adapter over testable application functions. A noninteractive command is a versioned automation interface; a Bubble Tea view is an optional interactive client of the same functions. The executable owns stream, exit-status, cancellation and configuration contracts.","architecture":["Recommendation: Cobra parses and validates arguments, then RunE calls domain operations that return typed results/errors. Formatters own text versus JSON output; diagnostics and progress go to stderr, leaving stdout usable by scripts.","Recommendation: expose a schema/version field in machine-readable output and configuration. Centralize environment/flag/file precedence and secret redaction; preserve old names through an explicit deprecation period.","Recommendation: use Bubble Tea Model/Update/View and commands for terminal interaction. Keep interactive state separate from persisted application configuration, and disable prompts/spinners for pipes or explicit noninteractive mode.","Recommendation: use the established module workflow and GoReleaser target matrix for binary distribution. Native libraries, signing, notarization and OS behavior require per-target checks even when cross-compilation succeeds."],"developer_workflow":["Recommendation: inspect existing flags, help output, scripts and configuration before changing the CLI. A new project can start with go mod init and Cobra CLI generation when desired; generation is optional, not an architecture.","Recommendation: run gofmt, go vet ./... and go test ./...; add subprocess-level contract fixtures that capture stdout, stderr and exit status. Domain unit tests alone miss terminal and shell behavior.","Recommendation: exercise --help, invalid flags, piped input, --output json (if implemented), interrupted work and no-TTY execution. Choose and document application exit codes; Cobra does not define every business error code.","Recommendation: after installing a pinned GoReleaser v2, review .goreleaser.yaml, run goreleaser check and goreleaser release --snapshot --clean to prepare local artifacts. These are review commands, not a published release; verify clean-machine install and upgrade on the supported targets. Current GoReleaser OSS installation docs require Go 1.27 when installing the tool from source; a prebuilt tool or a compatible pinned older tool is a separate choice from the application minimum Go version."],"avoid":["Avoid logging or progress bars on machine-readable stdout; one extra line can break the calling script.","Avoid os.Exit inside domain functions or cleanup scopes: os.Exit skips deferred calls. Map errors to a code after cleanup in the entry point.","Avoid requiring a TTY for every command; automation needs explicit inputs and a prompt-free path.","Avoid rewriting configuration in place without a version/backup; users may need to downgrade the executable.","Avoid silently switching encoding/json to Go 1.27 encoding/json/v2 during a rewrite; changed serialization defaults require compatibility fixtures and an explicit protocol decision."],"transitions":[{"capability":"cli","existing_contract":"Capture scripts consuming current flags, positional arguments, stdout/stderr, exit codes and completion output.","coexistence":"Recommendation: retain old subcommands/aliases as thin adapters while introducing an explicit JSON mode. Mark renamed flags deprecated through Cobra instead of silently changing their meaning.","target":"Recommendation: Cobra RunE plus centralized result/error mapping; versioned JSON on stdout and diagnostics on stderr. Freeze documented application codes for success, usage and operational failure.","benefits":[{"benefit":"A stable CLI protocol permits automation and humans to share one executable.","prerequisites":"Consumer fixtures cover streams/status; formatting and error categories are explicit; deprecation schedule preserves callers.","tradeoffs":"Versioned output requires ongoing compatibility ownership; usage errors and remote failures may need different handling."}],"checks":["Run old calling scripts against the new binary and compare all three output channels.","Verify invalid flags perform no effects and produce the documented exit code.","Pipe JSON to a parser; help/progress/logging must not contaminate its data."],"rollback":"Recommendation: install the preceding binary and continue serving the old protocol. Keep newly introduced machine schemas behind explicit versions so callers can choose a compatible response.","sources":["https://cobra.dev/docs/how-to-guides/working-with-flags/","https://pkg.go.dev/encoding/json#Decoder.DisallowUnknownFields","https://pkg.go.dev/os#Exit"],"id":"cli-1"},{"capability":"data","existing_contract":"Inventory config paths, default values, flag/environment precedence, secret fields and old readers.","coexistence":"Recommendation: parse both old and versioned schemas through adapters; display migration diagnostics on stderr and offer an explicit migration command with backup. Keep the old file untouched until conversion passes.","target":"Recommendation: a typed versioned config decoded deliberately, with unknown-field policy and semantic validation. Write a same-directory temporary file, validate it and replace only after success; qualify filesystem/OS behavior and cross-process ownership.","benefits":[{"benefit":"A versioned configuration contract makes upgrades and later rollback predictable.","prerequisites":"Old fixtures exist; migration preserves semantics and backups; one writer and tested replacement behavior are defined.","tradeoffs":"Strict unknown-field rejection can hinder mixed-version use; atomic replacement does not establish crash durability or coordinate concurrent writers."}],"checks":["Load representative old config, migrate and compare effective settings.","Reject unknown unsupported schema versions before modifying files.","Interrupt conversion and verify either the original or complete new file remains; test permissions on each target OS."],"rollback":"Recommendation: restore the backup and preceding executable together. Preserve old readable configuration throughout the downgrade window; do not roll back after newer-only values without an explicit mapping.","sources":["https://pkg.go.dev/encoding/json#Decoder.DisallowUnknownFields","https://cobra.dev/docs/how-to-guides/working-with-flags/"],"id":"data-1"},{"capability":"jobs","existing_contract":"Preserve per-item effects, retry decisions, ordering and interrupt behavior for batch commands; a process-local queue has no restart history.","coexistence":"Recommendation: keep the sequential executor available and compare the new bounded executor on fixture inputs. Route one operation class at a time; reuse stable operation IDs across retries.","target":"Recommendation: signal.NotifyContext plus errgroup limit or a fixed worker pool. Pass context through clients, close resources after workers finish, and persist a checkpoint/receipt when interrupted effects need later recovery.","benefits":[{"benefit":"Bounded fan-out can overlap independent I/O while maintaining a clear cancellation boundary.","prerequisites":"Concurrency quota and total deadline are set; downstream calls honor cancellation; completed and ambiguous effects can be reconciled.","tradeoffs":"No throughput gain is measured; cancellation cannot undo effects; a retry can duplicate a write without provider or business idempotency."}],"checks":["Interrupt midway through a batch and verify completed, pending and ambiguous items are reported separately.","Simulate one failed item; verify the selected stop/continue policy and bounded parallelism.","Resume using stable IDs and prove acknowledged effects are not repeated."],"rollback":"Recommendation: return to sequential execution using the same checkpoint contract. Reconcile ambiguous outcomes first; restarting from item zero is not a safe general rollback.","sources":["https://pkg.go.dev/golang.org/x/sync/errgroup","https://pkg.go.dev/os/signal#NotifyContext","https://pkg.go.dev/os#Exit"],"id":"jobs-1"},{"capability":"operations","existing_contract":"Preserve command name, install path, OS/architecture support, update channel and downgrade compatibility.","coexistence":"Recommendation: produce snapshot artifacts beside the old release and smoke-test them without changing the default download. Review generated package-manager changes and publish channels only after target verification.","target":"Recommendation: pinned GoReleaser v2 matrix with metadata/checksums, OS-specific install tests and an explicit signing path. CGO dependencies and macOS signing/notarization need separate toolchain/credential setup; the documented native package notarization flow is Pro; qualify the chosen binary or package flow separately.","benefits":[{"benefit":"A repeatable artifact matrix reduces manual release assembly.","prerequisites":"Build inputs/tool versions are pinned; each promised target is installed and exercised; signing credentials and package metadata are owned.","tradeoffs":"Cross-compilation is not execution evidence; CGO can require native toolchains; selected signing/notarization automation may require a paid tier."}],"checks":["Install on clean Linux, macOS and Windows hosts for every advertised architecture.","Verify version metadata, checksums, permissions and executable path.","Upgrade from the old package and downgrade with preserved config; inspect macOS Gatekeeper behavior for the actual downloaded artifact."],"rollback":"Recommendation: repoint the owned download/package channel to the previous verified release and keep its artifacts available. Restore compatible configuration with the binary; do not silently overwrite unrelated install locations.","sources":["https://goreleaser.com/getting-started/how-it-works/","https://goreleaser.com/customization/builds/go/","https://goreleaser.com/customization/sign/notarize/","https://goreleaser.com/customization/publish/snapshots/"],"id":"operations-1"}],"sources":[{"url":"https://cobra.dev/docs/how-to-guides/working-with-flags/","title":"Cobra flag guide","claim":"Flag scoping, validation, deprecation and optional configuration integration are explicit."},{"url":"https://github.com/charmbracelet/bubbletea","title":"Bubble Tea upstream","claim":"The TUI uses Model/Update/View with commands for effects."},{"url":"https://pkg.go.dev/encoding/json#Decoder.DisallowUnknownFields","title":"Go JSON decoder","claim":"Typed decoding and DisallowUnknownFields are available; schema policy is the application responsibility."},{"url":"https://pkg.go.dev/os#Exit","title":"os.Exit","claim":"Process exit skips deferred calls; exit-code meanings need an application contract."},{"url":"https://pkg.go.dev/golang.org/x/sync/errgroup","title":"errgroup","claim":"Group limit and cancellation support bounded batch execution."},{"url":"https://pkg.go.dev/os/signal#NotifyContext","title":"os/signal","claim":"NotifyContext exposes signal cancellation."},{"url":"https://goreleaser.com/getting-started/how-it-works/","title":"GoReleaser workflow","claim":"Release configuration and build/package/publish workflow are described; snapshot behavior has its own source."},{"url":"https://goreleaser.com/customization/builds/go/","title":"GoReleaser Go builds","claim":"GOOS/GOARCH matrix and environment/CGO settings are configurable."},{"url":"https://goreleaser.com/customization/sign/notarize/","title":"GoReleaser notarization","claim":"Signing/notarization requires Apple credentials; the native package flow is Pro and uses Apple tools. Cross-platform binary notarization is a separate documented flow."},{"url":"https://goreleaser.com/customization/publish/snapshots/","title":"GoReleaser snapshots","claim":"Snapshot mode builds review artifacts without publishing to a release channel."},{"url":"https://goreleaser.com/getting-started/quick-start/","title":"GoReleaser quick start","claim":"goreleaser check validates config; release --snapshot --clean builds local review artifacts."},{"url":"https://goreleaser.com/getting-started/install/oss/","title":"GoReleaser OSS installation","claim":"Current source installation requires Go 1.27; verified prebuilt binaries are another documented installation method."},{"url":"https://go.dev/doc/go1.27","title":"Go 1.27 release notes","claim":"Introduces encoding/json/v2 with changed defaults; serializer replacement requires compatibility review."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Go CLI + TUI","kind":["cli"],"guide_url":"/stacks/go-cli/","markdown_url":"/stacks/go-cli/guide.md","json_url":"/stacks/go-cli/record.json"},{"id":"rust-cli","version_scope":"Reviewed clap 4.6.7 and Rust std 1.99.0 documentation; Serde, Ratatui and current axodotdev cargo-dist book. Pin rust-toolchain.toml/Cargo.lock and dist version/config. The older opensource.axo.dev book path failed; no target install or release workflow was executed.","operating_model":"Recommendation: clap derives/parses a stable command interface, domain operations return Result, and the entry point converts errors to streams/status after cleanup. Serde owns a versioned machine format; Ratatui is an optional terminal client. Cargo/dist prepare artifacts for explicitly supported targets and update channels.","architecture":["Recommendation: use a library crate or modules for application operations and a thin binary adapter. Keep clap parsing errors separate from business errors; prefer explicit main ExitCode mapping when the contract requires more than generic failure.","Recommendation: Serde input/output types carry schema versions. Separate strict user configuration from forward-compatible result reading; deny_unknown_fields is an intentional policy, not universal compatibility.","Recommendation: encapsulate raw-mode/alternate-screen lifetime and restore the terminal on errors and panics. Share domain operations between Ratatui and a noninteractive JSON mode; interactive render state is not durable application state.","Recommendation: bounded Rayon/Tokio work is selected by CPU versus I/O needs, with explicit cancellation and effect receipts. A compiled binary reduces interpreter deployment needs but does not remove database, TLS or platform dependencies."],"developer_workflow":["Recommendation: start from existing Cargo manifest, consumers and rust-toolchain.toml. For a new local package, cargo new tool and cargo add clap --features derive are optional initial steps; add serde --features derive and serde_json only when a machine/config format is required.","Recommendation: cargo fmt --check, cargo clippy --all-targets -- -D warnings and cargo test --locked verify source checks in the selected feature matrix. Exercise subprocess stdout/stderr/status and panic/interrupt terminal restoration on supported OSs.","Recommendation: cargo build --release --locked builds one selected target. Inspect its native/TLS dependencies and execute on clean target hosts; --target compilation alone does not qualify distribution.","Recommendation: with a pinned dist tool, run dist init and review generated configuration/workflows before committing; init can overwrite manual workflow edits. dist plan --output-format=json previews artifact/announcement intent; actual target builds, installers and upgrade/downgrade checks remain required."],"avoid":["Avoid treating debug-formatted Result errors as a stable machine API; define error codes and schema deliberately.","Avoid process::exit in cleanup-sensitive paths; terminal restoration and destructors need normal control flow.","Avoid enabling raw terminal mode for piped/noninteractive invocations; they need predictable streams and no prompts.","Avoid hand-editing generated dist workflows without recording how customization survives regeneration."],"transitions":[{"capability":"cli","existing_contract":"Record current commands, option defaults, exit codes, stdout/stderr, JSON field names and shell completions consumed by callers.","coexistence":"Recommendation: keep an old command adapter and explicit schema version while migrating parsing to clap. Preserve old names/aliases and map new domain errors into the established protocol.","target":"Recommendation: clap plus typed result/error JSON and main ExitCode mapping. clap usage failures use status 2 while help/version uses success; application failures need their own documented policy.","benefits":[{"benefit":"Typed parsing and a stable output envelope make command automation explicit.","prerequisites":"Contract fixtures cover old callers; result schemas and error/status mapping are versioned; help/completion behavior is checked.","tradeoffs":"Parser defaults can change compatibility; numeric status meanings are not universal across platforms; Rust error types do not define the public protocol."}],"checks":["Verify help/version succeed and invalid arguments use the intended stream/code without effects.","Execute legacy scripts and compare stdout/stderr/status exactly where promised.","Validate empty, partial and failed JSON results against the advertised schema."],"rollback":"Recommendation: return consumers to the previous binary/schema while retaining old option adapters. Keep version selection explicit so a rollback does not silently alter JSON meaning.","sources":["https://docs.rs/clap/latest/clap/","https://docs.rs/clap/latest/clap/error/struct.Error.html","https://doc.rust-lang.org/std/process/struct.ExitCode.html"],"id":"cli-1"},{"capability":"data","existing_contract":"Inventory configuration file locations, schema versions, defaults, permissions and old binaries' ability to read newer files.","coexistence":"Recommendation: read old config into a versioned internal model and migrate only through an explicit command with backup. Keep concurrent writer ownership explicit; read-only startup must not silently rewrite files.","target":"Recommendation: Serde-tagged/versioned configuration and documented unknown-field handling. Validate a temporary representation before replacing the live file; platform atomicity, permissions and crash behavior require implementation checks.","benefits":[{"benefit":"Explicit config migration decouples executable upgrades from accidental data loss.","prerequisites":"Old/new fixtures and semantic transforms exist; backup/restore works; unsupported versions fail before write.","tradeoffs":"Strict Serde unknown-field rejection can prevent mixed-version use; deny_unknown_fields does not compose with flatten; replacing a file is not a complete durability protocol."}],"checks":["Load old config and compare effective settings after migration.","Verify unsupported versions and malformed values leave the source unchanged.","Downgrade the binary with its preserved config and confirm paths/permissions on each target OS."],"rollback":"Recommendation: restore the preserved old config and binary as a pair. Reconcile new-only fields before using an old reader; avoid destructive in-place downgrade guesses.","sources":["https://serde.rs/container-attrs.html"],"id":"data-1"},{"capability":"jobs","existing_contract":"Preserve ordering, per-item effects, timeout and interrupt semantics for batch/network subcommands.","coexistence":"Recommendation: retain sequential mode while qualifying bounded parallel work using dry-run/fixtures. Store item operation IDs and completion receipts before enabling resume against live systems.","target":"Recommendation: a bounded executor selected for CPU or async I/O with cooperative stop, tracked joins and cleanup. Started blocking work needs its own stop protocol or process boundary; do not label a task timeout as effect cancellation.","benefits":[{"benefit":"Bounded parallel execution can overlap independent work without unlimited task creation.","prerequisites":"Independent item semantics; capacity/budget limits; stable effect IDs; tested cancellation and resume contract.","tradeoffs":"Speed is unmeasured; running blocking tasks may continue; a cancelled remote write can have an ambiguous outcome."}],"checks":["Interrupt a batch and verify no new items start, joins are bounded and terminal state restores.","Simulate a remote success with a lost acknowledgement; resume reconciles rather than duplicating the effect.","Verify CPU tasks cannot starve I/O and the executor respects the declared capacity."],"rollback":"Recommendation: use sequential mode with the same receipt format and reconcile unknown outcomes. A previous executable must be able to read checkpoint versions or an explicit converter is required.","sources":["https://tokio.rs/tokio/topics/shutdown","https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html","https://ratatui.rs/recipes/apps/panic-hooks/"],"id":"jobs-1"},{"capability":"operations","existing_contract":"Capture existing target triples, libc/native dependencies, install path, package metadata and update/downgrade contracts.","coexistence":"Recommendation: prepare dist artifacts beside the previous release; review generated CI and installers, and use a prerelease or test channel for target qualification. Do not promise all platforms merely because dist generates a matrix.","target":"Recommendation: pinned dist configuration and Cargo.lock with release artifacts/installers for exercised targets. Keep signing/attestation, system dependency baselines and update channel ownership explicit.","benefits":[{"benefit":"Generated release plans coordinate binary artifacts and installers across targets.","prerequisites":"dist version is pinned; generated workflow is reviewed; native dependencies and every advertised target are exercised.","tradeoffs":"dist init may replace generated workflows; OS signing and Linux libc compatibility remain separate obligations; a plan is not build/publication evidence."}],"checks":["Inspect dist plan --output-format=json for expected target triples and artifact names.","Install downloaded artifacts on clean target hosts, including the oldest promised Linux baseline.","Exercise upgrade/downgrade, PATH behavior, checksum verification and preserved versioned config."],"rollback":"Recommendation: restore the previous verified package/update channel and retain older installers/checksums. Review data/config compatibility before allowing downgrade; never change a public channel based only on a plan.","sources":["https://axodotdev.github.io/cargo-dist/book/workspaces/simple-guide.html","https://axodotdev.github.io/cargo-dist/book/reference/config.html","https://doc.rust-lang.org/cargo/commands/cargo-build.html"],"id":"operations-1"}],"sources":[{"url":"https://docs.rs/clap/latest/clap/","title":"clap 4.6.7","claim":"Parser/derive command definition supports a typed command boundary."},{"url":"https://docs.rs/clap/latest/clap/error/struct.Error.html","title":"clap errors","claim":"Usage errors generally print to stderr with code 2; help-like output uses stdout and success."},{"url":"https://doc.rust-lang.org/std/process/struct.ExitCode.html","title":"Rust ExitCode","claim":"ExitCode can be returned by main; arbitrary numeric code meanings are platform-specific."},{"url":"https://serde.rs/container-attrs.html","title":"Serde container attributes","claim":"deny_unknown_fields rejects unknown inputs and is incompatible with flatten; version policy is application-owned."},{"url":"https://ratatui.rs/recipes/apps/panic-hooks/","title":"Ratatui panic hooks","claim":"Terminal restoration must be arranged around error/panic paths."},{"url":"https://tokio.rs/tokio/topics/shutdown","title":"Tokio shutdown","claim":"Track/notify/await tasks for planned completion."},{"url":"https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html","title":"Tokio blocking tasks","claim":"Started blocking tasks cannot be aborted; capacity is a separate concern."},{"url":"https://axodotdev.github.io/cargo-dist/book/workspaces/simple-guide.html","title":"dist simple application guide","claim":"init generates config/workflows, can overwrite manual edits, and plan previews artifacts."},{"url":"https://axodotdev.github.io/cargo-dist/book/reference/config.html","title":"dist configuration reference","claim":"Targets, installers and CI/download/update settings are configured explicitly."},{"url":"https://doc.rust-lang.org/cargo/commands/cargo-build.html","title":"Cargo build","claim":"Locked and target-specific builds have defined semantics; build success is not target execution."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Rust CLI + TUI","kind":["cli"],"guide_url":"/stacks/rust-cli/","markdown_url":"/stacks/rust-cli/guide.md","json_url":"/stacks/rust-cli/record.json"},{"id":"python-scripts-workers","version_scope":"Python APIs: TaskGroup/asyncio.timeout require 3.11+; inspected /3 docs currently show 3.15.0. Use a supported minor compatible with selected SDKs and lock it; this does not recommend blindly upgrading to 3.15. Reviewed uv current guides, HTTPX current guides, Celery 5.6.3 task docs and Temporal Python guides. Resolve actual SDK versions in uv.lock before qualification.","operating_model":"Recommendation: a maintained package owns validated inputs, a run/effect ledger, a CLI result contract and a bounded execution loop. A short task may run directly under a scheduler; independent queued jobs fit Celery when broker semantics are qualified; long waits and restart recovery may justify Temporal workflows with external effects in Activities. A prompt, validated model output or log is not proof of a completed effect.","architecture":["Recommendation: separate parsing/validation, pure transformation, provider adapters and effects. Use deterministic fixtures/dry-run for decision logic, scoped injected credentials and stable operation IDs; do not place secrets or raw provider tokens in run payloads.","Recommendation: reuse HTTPX clients with explicit connect/read/write/pool timeouts and connection limits; add a total task deadline and bounded TaskGroup/semaphore or worker-queue execution. Connection limits alone do not bound all queued coroutines.","Recommendation: persist source cursor, input/schema version, effect intent and acknowledged result where restart matters. One-machine SQLite can own local state; shared workers need a chosen shared durable store and lease/ownership model. A checkpoint is not atomically committed with a remote email or payment.","Recommendation: in Temporal, deterministic Workflow code orchestrates Activities, waits and retries; Activities perform provider calls and heartbeat progress. In Celery, configure acknowledgement/redelivery and retry policy to the task's replay semantics. Avoid layering unbounded SDK, decorator and worker retries."],"developer_workflow":["Recommendation: inspect existing scheduler, dependencies, input formats and receipts. For maintained local work, uv init --package worker-app creates a package; pin a supported Python minor, add SDK/validation dependencies and commit pyproject.toml plus uv.lock in the maintained source repository.","Recommendation: uv sync --locked, uv run ruff check ., uv run mypy src and uv run pytest require the corresponding dev tools configured/installed. Add fixtures for malformed provider/model output, permission failures, partial effects, ambiguous acknowledgements and resume.","Recommendation: define [project.scripts] for the package CLI and structured JSON results plus explicit success/usage/failure statuses. uv run --locked <entrypoint> uses the locked project; scheduler start, overlap policy, termination and resume command are deployment-owned choices.","Recommendation: for durable orchestration, uv add temporalio and implement Workflow/Activity/Worker components connected to a separately operated Temporal service. Replay representative histories with temporalio.worker.Replayer before workflow changes; test Activity heartbeat/cancellation and time-skipping integration flows.","Recommendation: use uv sync --locked --no-dev inside a reviewed container build, then record the actual entry point, writable state path, credential scopes and dependency/image pins. No scheduler, broker or Temporal integration was executed here."],"avoid":["Avoid resuming by rerunning every input: a crash after remote success before checkpoint creates ambiguous effects and needs reconciliation or a provider idempotency key.","Avoid synchronous SDK calls in async execution; use a documented async client or bounded offload, with explicit cancellation limits.","Avoid swallowing CancelledError in TaskGroup work; cooperative cancellation depends on it being propagated after cleanup.","Avoid describing Celery late acknowledgement or a Temporal Workflow as exactly-once external effects. Application/provider idempotency remains necessary.","Avoid importing time/random/network behavior into deterministic Temporal Workflow code; move external I/O into Activities and replay-test changes."],"transitions":[{"capability":"cli","existing_contract":"Capture old script arguments, environment/config precedence, file paths, exit codes and scheduler invocation behavior.","coexistence":"Recommendation: retain the old script as a thin entry point into an importable package while callers transition. Introduce structured output/version fields and an explicit noninteractive/dry-run mode without changing live side effects.","target":"Recommendation: uv package with pyproject.toml entry point, committed uv.lock and supported Python minor; JSON results on stdout, diagnostics on stderr, schema and status policy in the CLI documentation.","benefits":[{"benefit":"An importable locked package makes repeated automation easier to test and invoke consistently.","prerequisites":"Inputs/entry point/status are defined; compatible Python and SDKs are pinned; scheduler uses the same package build.","tradeoffs":"A lockfile does not fix provider behavior or scheduler overlap; deploy-time environment/credentials remain required."}],"checks":["Run old scheduler command against the package adapter and compare input/output/status.","Start from a clean locked environment and parse output without progress/log contamination.","Verify dry-run performs zero provider writes and reports the intended actions."],"rollback":"Recommendation: restore the previous package/environment and command while preserving versioned run/checkpoint files. Keep the old entry point until every scheduler/caller is migrated.","sources":["https://docs.astral.sh/uv/guides/projects/","https://docs.astral.sh/uv/guides/integration/docker/"],"id":"cli-1"},{"capability":"data","existing_contract":"Identify source pagination/cursors, input revisions, item identifiers, partial outputs and acknowledged versus uncertain effects.","coexistence":"Recommendation: introduce a run/effect ledger alongside the old logs and compare reconciliation in fixture mode. Keep one active execution owner; migrate old checkpoints with source-input/version provenance.","target":"Recommendation: transactional SQLite checkpoint/receipt state for one host or an operated shared database for multiple workers. For exported files, validate temporary output then os.replace on the same filesystem; qualify sync/permissions separately.","benefits":[{"benefit":"Durable progress records allow targeted restart and investigation instead of blind replay.","prerequisites":"Stable item/effect IDs; checkpoint transactions; single writer or tested shared ownership; provider lookup/idempotency for uncertain effects.","tradeoffs":"A file/database commit cannot atomically commit a remote side effect; os.replace atomicity does not establish power-loss durability or cross-filesystem replacement."}],"checks":["Crash before effect, after remote success and after checkpoint; verify each resume decision.","Resume the same input and prove acknowledged effects are skipped or reconciled.","Interrupt export and verify a complete prior/new artifact; restore ledger from backup and compare cursor ownership."],"rollback":"Recommendation: keep the ledger readable by the previous worker or convert it explicitly. Reconcile ambiguous remote outcomes before replay; reverting code cannot unsend a message or reverse a provider write.","sources":["https://docs.python.org/3/library/sqlite3.html","https://docs.python.org/3/library/os.html#os.replace"],"id":"data-1"},{"capability":"operations","existing_contract":"Preserve provider rate limits, retry budgets, total run deadline and cleanup/cancellation behavior; SDK inactivity timeout is not a whole-run deadline.","coexistence":"Recommendation: retain sequential mode, add shared HTTPX clients and explicit capacity, and compare fixture results before enabling concurrency. Use a bounded queue for large inputs rather than creating one task for every item.","target":"Recommendation: TaskGroup plus asyncio.timeout for operation lifetime, semaphore/worker limits for admission, HTTPX network timeouts and connection limits. Keep sync SDK work outside the event loop; cancellation of offloaded work needs a separate policy.","benefits":[{"benefit":"Bounded concurrency and total deadlines prevent unlimited waiting work and reduce abandoned I/O.","prerequisites":"Supported Python 3.11+; every provider adapter honors limits; aggregate retry budget and cancellation cleanup are configured.","tradeoffs":"Read timeout measures inactivity between chunks; cancelling a coroutine does not prove a server write was cancelled; throughput gains are unmeasured."}],"checks":["Slow streaming chunks beyond the total deadline while below read timeout; the whole operation still stops.","Overload input and verify active plus queued work remain bounded.","Cancel one task during provider I/O; TaskGroup cleanup closes clients and reports any ambiguous write."],"rollback":"Recommendation: return to sequential execution with the same receipt IDs and retry budget. Resolve uncertain effects from provider state before retrying; retain operation deadlines.","sources":["https://docs.python.org/3/library/asyncio-task.html","https://docs.python.org/3/library/asyncio-sync.html","https://www.python-httpx.org/advanced/timeouts/","https://www.python-httpx.org/advanced/resource-limits/"],"id":"operations-1"},{"capability":"jobs","existing_contract":"Capture independent task payload versions, broker durability, acknowledgement timing, retries, dead-letter handling and scheduler overlap.","coexistence":"Recommendation: keep the old scheduler for unconverted tasks while routing selected task kinds into a qualified Celery queue. One scheduler/consumer owns each kind, and old backlog states remain accounted for.","target":"Recommendation: Celery for independent jobs, with idempotent task bodies, bounded worker concurrency, explicit retry/backoff and intentionally selected acknowledgement/worker-loss settings. Keep existing brokers when their guarantees satisfy the contract.","benefits":[{"benefit":"A qualified queue permits independent worker restart and retry for bounded jobs.","prerequisites":"Broker persistence/redelivery and payload mapping are tested; task effects are replay-safe; acknowledgement and worker-loss policy are deliberately configured.","tradeoffs":"Late acknowledgement alone does not guarantee redelivery for every child-process termination; Celery worker settings and broker behavior matter; duplicate execution remains possible."}],"checks":["Terminate the worker during a task and verify actual redelivery for the selected settings.","Lose acknowledgement after an external effect; retries must not duplicate the logical effect.","Drain or transform ready/delayed/retry/failed work before retiring the old scheduler/consumer."],"rollback":"Recommendation: stop new producers, retain compatible workers for accepted tasks, and reroute only reconciled unstarted work. A scheduler rollback must not create a second schedule for the same task kind.","sources":["https://docs.celeryq.dev/en/stable/userguide/tasks.html"],"id":"jobs-1","option_title":"Independent jobs with Celery"},{"capability":"jobs","existing_contract":"Capture long-lived operation IDs, timer/human-wait behavior, provider effects and history/checkpoint schema before adopting durable orchestration.","coexistence":"Recommendation: keep old workflows running under their existing owner; start only newly routed operation IDs in Temporal. Keep provider calls in Activities and preserve effect IDs across retries. Do not duplicate a live run as a shadow test.","target":"Recommendation: Temporal Python Workflows/Activities with specified timeouts, heartbeats, replay tests and compatible worker deployments. Temporal service/visibility/history retention and operational ownership are prerequisites, not Python package installation side effects.","benefits":[{"benefit":"Persisted workflow history can resume orchestration across worker restarts and represent long waits.","prerequisites":"Temporal service and workers are operated; Workflow code is deterministic; Activity effects are idempotent/reconcilable; compatible histories pass replay.","tradeoffs":"Adds a service and history/versioning obligations; Activity retries may repeat effects; orchestration recovery is not automatic compensation of external changes."}],"checks":["Replay representative open and completed histories after code changes; fail promotion on nondeterminism.","Restart workers during timers and Activities; verify orchestration resumes and heartbeat/checkpoint behavior.","Crash after provider success before Activity completion; retry reconciles the stable effect ID."],"rollback":"Recommendation: stop routing new IDs to the new workflow version and keep compatible workers servicing existing histories. Replay-test the rollback code; if incompatible, complete/version old executions rather than abandoning their state.","sources":["https://docs.temporal.io/develop/python/workflows/basics","https://docs.temporal.io/develop/python/activities/basics","https://docs.temporal.io/develop/python/activities/timeouts","https://docs.temporal.io/develop/python/best-practices/testing-suite","https://docs.temporal.io/activity-definition"],"id":"jobs-2","option_title":"Recoverable multi-step workflows with Temporal"}],"sources":[{"url":"https://docs.astral.sh/uv/guides/projects/","title":"uv projects","claim":"Project/package entry points, lockfile-based execution and dependency management are documented."},{"url":"https://docs.astral.sh/uv/guides/integration/docker/","title":"uv Docker","claim":"Locked/no-dev sync and build/runtime environment setup are explicit."},{"url":"https://docs.python.org/3/library/sqlite3.html","title":"Python sqlite3","claim":"Transactions and persisted local state are available; transaction control and concurrency need deliberate configuration."},{"url":"https://docs.python.org/3/library/os.html#os.replace","title":"Python os.replace","claim":"Replacement can be atomic on success and can fail across filesystems; sync/durability is separate."},{"url":"https://docs.python.org/3/library/asyncio-task.html","title":"Python asyncio tasks","claim":"TaskGroup/timeout require 3.11+ and use cancellation; swallowing cancellation breaks structured behavior."},{"url":"https://docs.python.org/3/library/asyncio-sync.html","title":"Python asyncio synchronization","claim":"Semaphore supports bounded task access and is not thread-safe."},{"url":"https://www.python-httpx.org/advanced/timeouts/","title":"HTTPX timeouts","claim":"Connect/read/write/pool timeouts are separate; read timeout concerns waiting for chunks."},{"url":"https://www.python-httpx.org/advanced/resource-limits/","title":"HTTPX resource limits","claim":"Connection and keepalive pool bounds are configurable."},{"url":"https://docs.celeryq.dev/en/stable/userguide/tasks.html","title":"Celery 5.6.3 tasks","claim":"Idempotency, acknowledgements, retries and worker-loss behavior need explicit policy; late ack has exceptions."},{"url":"https://docs.temporal.io/develop/python/workflows/basics","title":"Temporal Python Workflow basics","claim":"Workflow code must respect deterministic/replay constraints."},{"url":"https://docs.temporal.io/develop/python/activities/basics","title":"Temporal Activity basics","claim":"Activity definitions and worker registration place external work outside deterministic Workflow code; idempotency has a separate concept source."},{"url":"https://docs.temporal.io/develop/python/activities/timeouts","title":"Temporal Activity timeouts","claim":"Activity timeouts, retry policies and heartbeats support bounded/recoverable execution."},{"url":"https://docs.temporal.io/develop/python/best-practices/testing-suite","title":"Temporal Python testing","claim":"Replay checks history compatibility; Activity and time-skipping environments support recovery testing."},{"url":"https://docs.temporal.io/activity-definition","title":"Temporal Activity idempotency","claim":"Activities may execute more than once after an unreported completion; completed Activities are not rerun merely by Workflow replay. Provider idempotency keys are enforced by the called service."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Python automation + AI","kind":["automation"],"guide_url":"/stacks/python-scripts-workers/","markdown_url":"/stacks/python-scripts-workers/guide.md","json_url":"/stacks/python-scripts-workers/record.json"},{"id":"swift-swiftui-ios","version_scope":"Documentation reviewed 11 October 2026. SwiftUI Observation and SwiftData are iOS/iPadOS 17+; support older OS targets through compatible alternatives. Pin Xcode/SDK/Swift/package resolution and retain the minimum OS/device matrix. No simulator, archive or store-release check was executed.","operating_model":"An installed iOS client persists across backend releases and is suspended or terminated under OS control. Recommendation: SwiftUI views render observable presentation state while repositories own URLSession DTOs and local persistence. Store rollout, API compatibility and local-store migration are separate adoption gates.","architecture":["Use SwiftUI state/Observation according to minimum OS availability; keep view rendering separate from asynchronous repositories and business decisions. Observation is UI dependency tracking, not disk persistence.","Recommendation: version API DTOs separately from SwiftData/Core Data entities; preserve old installed-client contracts and authorize all privileged operations on the backend.","Use a versioned local schema and reviewed migration plan for SwiftData. Recommendation: store unacknowledged user intents durably before claiming an offline write succeeded; reconcile conflicts with an explicit server policy.","Separate foreground structured concurrency, OS-scheduled refresh/processing, background URLSession transfers and server durable jobs. Each has different completion and cancellation semantics.","Recommendation: Keychain stores credential material; isolate account-local data and clear/lock the appropriate state on account switch. Keep backend provider secrets out of the app bundle."],"developer_workflow":["Start in the external Xcode source checkout. Read README, minimum OS, shared scheme, signing owner, capabilities, API fixtures and every persisted schema version.","Record xcodebuild -version and xcode-select -p; use xcodebuild -list -project App.xcodeproj and xcodebuild -showdestinations -project App.xcodeproj -scheme App with the actual names. Resolve packages with xcodebuild -resolvePackageDependencies -project App.xcodeproj -scheme App. Check current flags with the pinned xcodebuild -help; the linked command-line FAQ is archived.","After choosing an installed simulator UUID, run xcodebuild test -project App.xcodeproj -scheme App -destination 'platform=iOS Simulator,id=<UUID>' -resultBundlePath <new-path>.xcresult. Placeholders require substitution; simulator results do not establish device background behavior.","Recommendation: use Instruments/network diagnostics, physical-device background checks, upgrade fixtures, VoiceOver and large text. Produce a signed archive/TestFlight update under the established release workflow; record exact old/new client builds and compatible backend revisions."],"avoid":["Assuming users update when the backend deploys strands old installed clients and offline requests.","Assuming an older executable can read a migrated store confuses binary rollback with data rollback.","Keeping pending offline writes only in view state loses work after suspension or termination.","Using BGAppRefreshTask as an exact timer or unlimited durable worker ignores OS scheduling and expiration.","Using phased release as install reversal overlooks manual downloads and devices already running the new version."],"transitions":[{"capability":"client","existing_contract":"Installed versions, minimum OS, URLSession DTOs/errors, deep links, auth expiry and old/offline writes.","coexistence":"Recommendation: Ship additive backend responses and versioned fixtures before the new client; retain old contracts until measured adoption and an explicit support policy permit retirement.","target":"Recommendation: Separate API DTOs from local models and use capability negotiation for optional features; authorization remains backend-owned.","benefits":[{"benefit":"Independent backend/client deployment can preserve supported installations.","prerequisites":"Inventory of real installed versions, API fixtures and release/support owners.","tradeoffs":"Long-lived version support costs maintenance; a new store release cannot force immediate adoption."}],"checks":["Old and new builds run against the same new backend.","Unknown optional fields and absent new fields do not break supported clients.","A delayed old-client mutation receives the documented success/conflict/upgrade response."],"rollback":"Disable the new server feature and retain both API contracts; already-installed clients require a compatible forward fix when needed.","sources":["https://developer.apple.com/documentation/foundation/urlsession","https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases/"],"id":"client-1"},{"capability":"data","existing_contract":"Existing on-device store versions, entity meaning, relationships, pending local writes and upgrade paths.","coexistence":"Recommendation: Add compatible fields/stages before removing representations; migration fixtures include every supported historical store and unacknowledged operations.","target":"Recommendation: Use SwiftData VersionedSchema and SchemaMigrationPlan when OS support permits; preserve the established Core Data/store choice if migration value is unproven.","benefits":[{"benefit":"An explicit schema history makes upgrade behavior inspectable.","prerequisites":"Reviewed stages and fixtures proving values/relationships/pending writes survive.","tradeoffs":"Forward migration does not establish older-binary readability; cross-store conversion is application work."}],"checks":["Open each supported old store with the new build and compare user data and pending operations.","Interrupt migration and verify recovery behavior.","Attempt the intended rollback build against the migrated fixture; reject release rollback if unreadable."],"rollback":"Prefer a compatible forward repair if downgrade is unqualified. Restore a pre-upgrade copy only under a reconciled policy that does not erase later local writes.","sources":["https://developer.apple.com/documentation/swiftdata/schemamigrationplan","https://developer.apple.com/documentation/swiftdata/versionedschema"],"id":"data-1"},{"capability":"cache","existing_contract":"Local reads, pending offline changes, tombstones/cursors and server conflict semantics.","coexistence":"Recommendation: Keep the server as shared-domain reconciler; record each local mutation durably with a stable operation ID and schema version while old clients use the same conflict policy.","target":"Recommendation: Use repository-owned local reads and a persistent operation log when offline writes are required; choose revision checks, merge or user resolution per domain instead of accidental timestamp wins.","benefits":[{"benefit":"Users can retain local work through intermittent connectivity.","prerequisites":"Durable local storage, replay-safe backend, explicit conflict/tombstone protocol.","tradeoffs":"This is a proposed application design; SwiftData does not automatically provide arbitrary backend synchronization."}],"checks":["Two devices edit the same record offline; the declared policy preserves or exposes both intents.","Disconnect after server commit but before acknowledgement; replay does not duplicate the effect.","Restart/sign-out does not silently replay account A operations as account B."],"rollback":"Stop the new synchronizer, retain pending operations and translate/reconcile them before returning to old logic; never clear the log as a rollback shortcut.","sources":["https://developer.apple.com/documentation/swiftdata/schemamigrationplan","https://developer.apple.com/documentation/foundation/urlsession"],"id":"cache-1"},{"capability":"jobs","existing_contract":"Foreground task cancellation, pending intents and any existing background task identifiers/handlers.","coexistence":"Recommendation: Retain old handler identifiers and persisted payload readers while new code is installed; use one owner per sync operation.","target":"Recommendation: Choose BGAppRefreshTask/BGProcessingTask for eligible opportunistic device work, foreground tasks for interactive work and backend durable jobs for server completion obligations.","benefits":[{"benefit":"Eligible refresh/maintenance can execute while the app is backgrounded.","prerequisites":"Capabilities, registered handlers, durable checkpoints and expiration handling.","tradeoffs":"The OS selects launch time; device scheduling does not satisfy a fixed backend completion SLA."}],"checks":["Expire a task midway; it checkpoints/cancels and marks completion correctly.","Suspend/terminate/relaunch and resume without a duplicated business effect.","Offline, battery and permission constraints show honest pending state."],"rollback":"Disable new task submission while preserving payload readers/handlers long enough to reconcile pending work; continue foreground sync or a server job where appropriate.","sources":["https://developer.apple.com/documentation/backgroundtasks","https://developer.apple.com/documentation/backgroundtasks/choosing-background-strategies-for-your-app"],"id":"jobs-1"},{"capability":"files","existing_contract":"Existing upload/download IDs, authorized object keys, file hashes, task identifiers and completion acknowledgements.","coexistence":"Recommendation: Keep server upload endpoints/object ownership compatible; persist mapping from background task to logical operation and retain file-backed upload payloads.","target":"Recommendation: Use an identified background URLSession for eligible HTTP(S) transfer; recreate it after system termination and move completed temporary downloads before delegate return.","benefits":[{"benefit":"Transfers can continue during suspension without holding the app process.","prerequisites":"Delegate lifecycle, stable session identifier, protected files and authorized retry/acknowledgement contract.","tradeoffs":"Background uploads require files; scheduling is not exact and transfer completion differs from domain acceptance."}],"checks":["Suspend during upload/download and restore the same session after system termination.","Verify file integrity and durable location after completion.","Retry an ambiguous response without producing duplicate owned objects."],"rollback":"Stop creating new transfers, inspect/cancel or finish existing tasks and reconcile server objects; keep session callbacks until all retained tasks are accounted for.","sources":["https://developer.apple.com/documentation/foundation/downloading-files-in-the-background"],"id":"files-1"},{"capability":"operations","existing_contract":"Store release ownership, supported devices, adoption, crash reports and persisted-data compatibility.","coexistence":"Recommendation: Verify TestFlight install-over-existing fixtures; retain server compatibility through phased adoption and include manual downloads in risk assessment.","target":"Recommendation: Use phased release with explicit crash/data-loss/compatibility stop criteria; assess adoption before retiring old API behavior.","benefits":[{"benefit":"Gradual automatic-update distribution can limit initial exposure.","prerequisites":"Signed reviewed build, live telemetry and a compatible recovery release.","tradeoffs":"Apple's seven-day phases cover eligible automatic updates; anyone can manually download, and pausing does not reverse installs."}],"checks":["Verify real install/update preserves store, credentials and deep links.","Track old/new build support and migration failures separately.","Pause/recovery procedure leaves already-updated clients functional."],"rollback":"Pause further distribution and disable affected server capabilities if safe; ship a compatible repair rather than assuming an installed-version/store downgrade.","sources":["https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases/"],"id":"operations-1"}],"sources":[{"url":"https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app","title":"SwiftUI Managing Model Data","claim":"Observation connects model changes to UI and requires iOS/iPadOS 17+; it is not local persistence."},{"url":"https://developer.apple.com/documentation/swiftdata/schemamigrationplan","title":"SwiftData SchemaMigrationPlan","claim":"Versioned schemas and migration stages explicitly model local schema evolution; iOS/iPadOS 17+."},{"url":"https://developer.apple.com/documentation/swiftdata/versionedschema","title":"SwiftData VersionedSchema","claim":"A schema records a particular version and its model set."},{"url":"https://developer.apple.com/documentation/foundation/urlsession","title":"Foundation URLSession","claim":"Configured sessions own data/upload/download/WebSocket tasks, authentication callbacks and asynchronous transfer behavior."},{"url":"https://developer.apple.com/documentation/backgroundtasks","title":"Background Tasks","claim":"Register and schedule task handlers for eligible background processing; the framework has multiple task types."},{"url":"https://developer.apple.com/documentation/backgroundtasks/choosing-background-strategies-for-your-app","title":"Choosing Background Strategies","claim":"Select task strategy by workload, handle expiration and completion; the OS decides refresh/processing launch time."},{"url":"https://developer.apple.com/documentation/foundation/downloading-files-in-the-background","title":"Downloading Files in the Background","claim":"Background sessions require delegates/stable identifiers, support file-backed uploads and need completion-file lifecycle handling."},{"url":"https://developer.apple.com/documentation/security/keychain-services","title":"Keychain Services","claim":"Encrypted Keychain items can store small credential secrets."},{"url":"https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases/","title":"App Store Connect Phased Release","claim":"Automatic updates progress over seven days; manual downloads remain possible and phases can be paused."},{"url":"https://developer.apple.com/documentation/xcode/testing","title":"Xcode Testing","claim":"Swift Testing and XCTest/UI tests have complementary roles; testing is separate from distribution."},{"url":"https://developer.apple.com/library/archive/technotes/tn2339/_index.html","title":"Archived Xcode Command-line FAQ","claim":"Documents command shapes for scheme/project/destination tests and archives; recheck options with the pinned Xcode toolchain."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Swift + SwiftUI","kind":["ios"],"guide_url":"/stacks/swift-swiftui-ios/","markdown_url":"/stacks/swift-swiftui-ios/guide.md","json_url":"/stacks/swift-swiftui-ios/record.json"},{"id":"kotlin-compose-android","version_scope":"Documentation reviewed 11 October 2026. Use the project's minimum/target SDK, compatible Kotlin/Compose/AGP/Gradle/JDK/Room/WorkManager versions and version catalog. Current guidance is conditional on supported platform behavior; no APK, App Bundle, emulator or store release was tested.","operating_model":"An installed Android client renders immutable screen state with lifecycle-aware collection and persists important data separately. Recommendation: repositories mediate Room/API data and WorkManager handles selected persistent work; backend services own privileged domain effects. Installed versions coexist through staged distribution.","architecture":["Android guidance recommends separate UI and data layers, repositories, unidirectional data flow and lifecycle-aware Flow collection. Add a domain layer only when complexity/reuse warrants it.","Recommendation: keep API DTOs distinct from Room entities, and local observable data authoritative for offline screen reads while the backend owns shared-domain reconciliation.","Room owns schema versions and migrations; DataStore serves preferences rather than a general relational substitute. Record durable user operations and acknowledgement state when offline writes are required.","Coroutines serve lifecycle/in-process async work; WorkManager persists scheduled work across restart/reboot and remains subject to constraints and power policy.","Recommendation: use Keystore-backed keys to protect separately stored app-private credentials. A Keystore key is not arbitrary token storage, nor a guarantee that a revoked backend session remains usable."],"developer_workflow":["Start at the external source README, API and database fixtures, version catalog and Gradle wrapper. Record package identity, SDK/device matrix, signing owner and release track.","Use ./gradlew --version and ./gradlew tasks --all; with an actual app module run ./gradlew :app:lintDebug :app:testDebugUnitTest :app:assembleDebug. Keep wrapper/JDK/SDK inputs reproducible.","After adb devices identifies an authorized emulator/device, run ./gradlew :app:connectedDebugAndroidTest. Configure Room's migration testing/exported schemas; assertions must check preserved data as well as schema shape.","Recommendation: use Android Studio profilers/inspection, coroutine fakes, Compose navigation/accessibility tests and physical-device reboot/offline checks. Build ./gradlew :app:bundleRelease only with reviewed signing, then verify an internal-track install/update before staged promotion."],"avoid":["Collecting Flow without lifecycle ownership can keep work running when a screen is stopped; repeating effects inside recomposition can duplicate requests.","Treating a ViewModel or coroutine as disk persistence loses work on process death.","Using destructive migration for user-created offline data deletes the only copy.","Expecting WorkManager to execute at an exact wall-clock deadline ignores scheduling windows and power constraints.","Halt rollout is not uninstall or downgrade; already-updated users stay on the new build."],"transitions":[{"capability":"client","existing_contract":"Supported installed APK versions/OS levels, DTOs/errors, auth, deep links and delayed old-client operations.","coexistence":"Recommendation: Deploy backward-compatible server behavior first; run old/new clients against it and retain contracts through explicit adoption/support gates.","target":"Recommendation: Version API DTOs independently of Room entities and gate optional features by negotiated capability; keep backend authorization authoritative.","benefits":[{"benefit":"Client releases and backend changes can progress independently.","prerequisites":"Installed-version inventory, shared API fixtures and support ownership.","tradeoffs":"Maintaining old contracts costs work; Compose adoption does not upgrade installed clients."}],"checks":["Old/new builds decode missing/unknown fields and documented errors.","A delayed old offline operation succeeds or returns the declared conflict.","Deep links, session expiry and denied resources work across supported releases."],"rollback":"Disable the new capability and preserve old/new APIs; repair already-installed clients with a compatible higher-version release when needed.","sources":["https://developer.android.com/topic/architecture/recommendations","https://support.google.com/googleplay/android-developer/answer/6346149"],"id":"client-1"},{"capability":"data","existing_contract":"Room schema versions, exported schemas, all upgrade paths and user-owned unsynced records.","coexistence":"Recommendation: Add migrations without discarding old fields until adoption permits; include direct upgrades from every supported starting version.","target":"Recommendation: Keep exported Room schemas and explicit migration tests; use automatic or manual migrations according to the actual change.","benefits":[{"benefit":"Reviewed migrations preserve local user data across updates.","prerequisites":"Complete paths plus assertions on data/relationships/pending work.","tradeoffs":"Schema validation alone does not establish value preservation; destructive fallback permanently deletes table data."}],"checks":["MigrationTestHelper exercises each path and earliest-to-current upgrade.","Assert unsynced rows, IDs and relationships remain meaningful.","Verify intended downgrade behavior; prohibit destructive fallback for the only copy of user work."],"rollback":"Ship a forward repair when older code cannot read the new store; any restore requires reconciling writes made after upgrade.","sources":["https://developer.android.com/training/data-storage/room/migrating-db-versions"],"id":"data-1"},{"capability":"cache","existing_contract":"Local source-of-truth reads, pending offline operations, revision/cursor/tombstone and conflict rules.","coexistence":"Recommendation: Use one repository and one reconciler for each data category; persist queued mutations with operation IDs before reporting success.","target":"Recommendation: Use Room-backed local reads and a durable operation queue when offline writes are needed; select push/pull/hybrid sync and a domain-specific conflict policy.","benefits":[{"benefit":"Cached local data can keep required reads available and preserve offline intent.","prerequisites":"Server sync support, retry-safe writes and an explicit conflict policy.","tradeoffs":"Offline-first reads do not imply offline writes; last-write-wins can discard intent and depends on metadata semantics."}],"checks":["Two offline devices make conflicting changes and obtain the documented resolution.","Kill process after local commit and before network send; operation survives.","Repeat server-committed/unacknowledged mutation without duplication; honor deletes/tombstones."],"rollback":"Stop the new synchronizer and retain its queue; map remaining operations/cursors to old semantics before resume.","sources":["https://developer.android.com/topic/architecture/data-layer/offline-first"],"id":"cache-1"},{"capability":"jobs","existing_contract":"Existing queued Worker class names, input Data versions, unique-work identities and external effects.","coexistence":"Recommendation: Retain readers/worker entry points for already-enqueued payloads; transfer one job category and prevent duplicate schedulers.","target":"Recommendation: Use WorkManager for eligible persistent device work with constraints/backoff; keep server completion obligations in a backend durable job.","benefits":[{"benefit":"Scheduled work persists across restart and device reboot.","prerequisites":"Compatible worker payloads, stable unique names, replay-safe effects and documented constraints.","tradeoffs":"Scheduling windows/power policy affect timing; unique work does not provide exactly-once remote effects."}],"checks":["Reboot with queued work and verify it resumes when constraints permit.","Update app with old queued input; the new worker decodes it.","Crash after remote effect/before acknowledgement and confirm retry is safe."],"rollback":"Cancel new scheduling and account for pending/running work; keep compatibility workers until drained or safely transformed.","sources":["https://developer.android.com/develop/background-work/background-tasks/persistent","https://developer.android.com/topic/architecture/data-layer/offline-first"],"id":"jobs-1"},{"capability":"identity","existing_contract":"Existing stored credential encryption, key alias/access constraints, backup exclusions, issuer expiry/revocation.","coexistence":"Recommendation: Retain the backend issuer and read old encrypted credential formats during a bounded transition; isolate account-local data.","target":"Recommendation: Use Keystore-backed cryptographic keys with reviewed encrypted app-private credential storage and backend revocation.","benefits":[{"benefit":"OS key protection can reduce key extraction exposure.","prerequisites":"Qualified device/API support, key invalidation handling and a reviewed storage/backup policy.","tradeoffs":"Keystore stores keys rather than arbitrary tokens; device protection does not replace backend authorization."}],"checks":["Key invalidation produces a controlled sign-in path.","Backup/restore cannot silently expose unusable/plain credentials.","Sign-out/account switch stops old-account queued work and backend revocation is enforced."],"rollback":"Retain compatibility decryption only while secure; require sign-in if the old format/key cannot be restored safely.","sources":["https://developer.android.com/privacy-and-security/keystore"],"id":"identity-1"},{"capability":"operations","existing_contract":"Play tracks, signing/version codes, adoption, crash metrics and schema/API compatibility.","coexistence":"Recommendation: Verify an internal/closed-track update over supported old builds and preserve compatible backend behavior for the whole mixed-version population.","target":"Recommendation: Use a staged rollout with explicit crash/data-loss thresholds and a higher-version compatible repair path.","benefits":[{"benefit":"Percentage-based adoption can limit initial exposure.","prerequisites":"Signed build, account/track owner, telemetry and recovery artifact.","tradeoffs":"Halting only prevents further rollout; users who already updated remain on that version."}],"checks":["Install-over-old-build retains unsynced data and auth.","Track failures by app/OS/schema version.","Halt/recovery rehearsal keeps already-updated users functional."],"rollback":"Halt additional rollout and disable the affected capability if safe; publish a compatible fixed build without assuming a downgrade of installed clients.","sources":["https://support.google.com/googleplay/android-developer/answer/6346149"],"id":"operations-1"}],"sources":[{"url":"https://developer.android.com/topic/architecture/recommendations","title":"Android Architecture Recommendations","claim":"Separate UI/data layers, repositories, immutable state and lifecycle-aware collection are documented recommendations."},{"url":"https://developer.android.com/topic/architecture/data-layer/offline-first","title":"Android Offline-first Architecture","claim":"Local sources, queues, sync and conflict resolution need explicit policies; offline reads need not imply offline writes."},{"url":"https://developer.android.com/training/data-storage/room/migrating-db-versions","title":"Migrate Your Room Database","claim":"Migration testing should cover all paths; destructive fallback can permanently delete local table data."},{"url":"https://developer.android.com/develop/background-work/background-tasks/persistent","title":"Android WorkManager Task Scheduling","claim":"Work is persisted and rescheduled across reboots with constraints/retries; it is not an exact timer or all immediate work."},{"url":"https://developer.android.com/privacy-and-security/keystore","title":"Android Keystore System","claim":"Keystore protects cryptographic key material and usage constraints; arbitrary credentials need their own protected storage design."},{"url":"https://developer.android.com/build/building-cmdline","title":"Build Android Apps from the Command Line","claim":"Gradle wrapper build/test tasks and signed release artifacts require configured inputs."},{"url":"https://support.google.com/googleplay/android-developer/answer/6346149","title":"Play Console Staged Rollouts","claim":"Halting stops additional staged installs; already-updated users remain on that version."}],"reviewed_at":"2026-10-11","evidence":"documentation-reviewed","title":"Kotlin + Compose","kind":["android"],"guide_url":"/stacks/kotlin-compose-android/","markdown_url":"/stacks/kotlin-compose-android/guide.md","json_url":"/stacks/kotlin-compose-android/record.json"}]}
