Skip to main content

Appendix M: The Canonical Technology Stack

This is the reference profile for Python/PostgreSQL web applications, not a universal requirement to replace an enterprise's Java, .NET, identity, cloud or delivery platform. Select an applicable profile during inception and pin its revision. The durable method is the set of controls and evidence contracts; libraries are replaceable implementation choices. See methodology §4.5 and the governed factory.

Existing-system work starts with an inventory of current versions, support status, operating constraints, interfaces and ownership. A different stack is not itself a defect. Do not add a database, cloud service or agent framework solely because it appears below.

Tier definitions

  • Mandatory within the adopted scope: a required capability or control with an accountable owner, implementation and evidence. A project may explicitly adopt a particular library as mandatory; that binding is not inherited by every customer.
  • Default: a starting choice to evaluate against requirements, existing skills, support, licensing, security, cost and deployment constraints.
  • Forbidden within a defined scope: a prohibited practice or dependency under the adopted policy. An agent cannot waive it. A proposed policy change follows the responsible owner's approval path; methodology prose does not override organizational or legal constraints.

Prior successful projects provide useful experience, not an automatic promotion rule. Two deployments do not establish universal suitability, and one failure does not establish that a library is inherently unsafe. Record the reproducer, affected version/configuration and mitigation before generalizing.

Profile selection and change

In prose: inventory first, agree the required capabilities, review the proposed profile, then verify its implementation. An important change reopens the affected decision. Approval of a technology profile is not permission to deploy or process new categories of data.

Keep the normative profile in one declared home. Project instructions link its immutable revision and accepted local decisions. A draft ADR may be edited during review; an accepted ADR changes through linked supersession or an approved, clearly identified factual erratum, not a silent “deviation rationale” appended as if previously approved. check-single-source.sh detects certain copied text, not semantic equivalence or every contradiction.

Backend Runtime + Framework

Required outcome: supported runtimes, explicit input/output contracts, bounded I/O, reproducible dependencies and verification of changed behaviour.

Reference defaultAdoption check
Supported Python with FastAPI for an async HTTP servicePin the tested version and supported deployment image. Do not force async onto CPU-bound work or block an event loop with synchronous I/O.
Pydantic boundary models and a configured type checkerDecide extra-field handling per contract. Closed command inputs can reject unknown fields; third-party responses may need forward-compatible parsing. Unknown-field policy does not replace authorization.
Uvicorn for ASGI; a documented settings providerSeparate local development configuration from production secret injection. Do not commit real credentials.

Pydantic supports different extra-field policies; extra="forbid" is a deliberate contract choice, not a safe universal setting for every DTO and environment file. Pydantic configuration.

For another runtime, retain its established framework and prove the same applicable capabilities. A synchronous HTTP client can be appropriate outside the event loop; the relevant failure is blocking or unbounded I/O, not the mere presence of a library name.

Data Layer

Required outcome: reviewed data ownership, migrations, tenant/authorization boundaries where applicable, recovery and tests against the intended database semantics.

Reference defaultAdoption check
Supported PostgreSQL; versioned migrations, with Alembic for Python projectsBuild integration-test schemas through the migration chain. Verify actual constraints and runtime roles, not only ORM metadata.
SQLAlchemy async and/or asyncpg where neededUse the selected driver's binding syntax and test the emitted query. Raw asyncpg positional parameters are not SQLAlchemy named parameters.
Redis for bounded cache/ephemeral use where justifiedDefine expiry, eviction, outage and durability semantics. Do not assume a cache safely owns irreversible decisions or deduplication forever.
An approved object-store test environmentAn emulator's S3-like API does not prove the production provider's permissions, consistency or failure behaviour. Record the remaining contract checks.

PostgreSQL search_path is name resolution, not tenant authorization; trusting a schema also trusts users able to create objects there. Verify permissions, quoting, connection-state reset and cross-tenant denial with the actual runtime role. PostgreSQL schema security.

Client-side and server-side column defaults are different mechanisms. Specify which writers need the default, keep the migration and intended server default aligned, and test inserts from those writers. A Python default is not inherently forbidden merely because a server default exists. SQLAlchemy defaults.

For long-lived PostgreSQL notifications, define connection ownership and cancellation explicitly. A dedicated connection is one option; a leased pool connection is another if its lifetime is managed correctly. Releasing a pooled connection removes notification listeners. Do not prescribe “always close, never release” as a universal deadlock fix. Test notification delivery, disconnect/reconnect and shutdown under the selected design. asyncpg connection-pool lifecycle.

Testing

The authority for test policy is Appendix A and the adopted testing skill/profile, not a second competing table here.

  • Use real components for behaviour under test. Unit tests can inject controlled dependencies; an integration claim must identify the real boundary observed and any substitute.
  • Testcontainers is a useful default for disposable services where available. An isolated managed test instance may serve the same purpose. A container or emulator alone is not production equivalence.
  • SQLite does not establish PostgreSQL migration, SQL, locking or RLS correctness. A simulated boundary must not be presented as verified real integration.
  • Prefer controlled clocks and condition-based synchronization with deadlines. Use bounded real-time tests only when elapsed time or process cancellation is the behaviour being checked; sleeps are not readiness evidence.
  • Record actual test selection, execution and results. A slow marker or RUN_SLOW_TESTS variable changes nothing unless runner configuration implements it. Required tests remain required even when slower than the developer feedback loop.
  • Parallelize only after verifying isolation, service capacity and resource ownership; a ten-minute suite does not automatically justify more workers.

Python projects may use pytest and the selected async plugin mode; web components may use the established runner and Testing Library. Pin compatible versions and test the actual configuration. These are reference choices, not reasons to replace an enterprise's native tooling.

Frontend

Required outcome: a maintainable interface, accessible interaction, truthful authoritative states and tested critical journeys. See methodology §9 and s4u-ui-review.

React with TypeScript and an approved framework/design system is the reference starting point. Next.js, Vite, enterprise component libraries, Tailwind and shadcn/ui are choices, not universal requirements. Preserve a working routing and styling setup unless a reviewed need justifies migration. The Docusaurus overlay is a documentation renderer, not the interactive workspace's transaction store.

Choose notifications and dialogs according to the task and accessibility evidence. Toasts may supplement a durable error or decision state; they must not be the only way to discover a failed approval. An HTTP success response or optimistic animation is not an accepted business decision. Do not replace a valid accessible dialog simply to satisfy a library preference.

When using Auth.js with Next.js, server-side auth() is awaited in its documented examples; it is not a synchronous session getter. Do not automatically copy a provider access token into every browser-visible session. Decide which component needs the token, minimize its exposure and verify expiry, audience and authorization. Server-side API forwarding is a useful default, but the actual threat model and session configuration determine the design. Auth.js server authentication, backend integration guidance.

Observability + Logging

Required outcome: useful, correlated, access-controlled evidence with minimization, redaction and defined retention. Structured application logs, operational traces and accountable decision records are related but not interchangeable.

structlog is a Python reference choice; standard-library logging or an enterprise logging pipeline can satisfy the contract when correctly configured. Test the emitted records through the actual processors and sink used for the claim.

capture_logs() normally disables configured processors, so using it alone does not prove a production redaction processor ran. Explicitly include the relevant processors where supported or capture after the real pipeline. caplog can be appropriate when logs are routed through standard-library logging. Verify which path was exercised. structlog testing, pytest logging capture.

Avoid logging credentials, personal data or confidential source merely to provide an audit trail. A correlation ID plus protected, access-controlled evidence is preferable to copying full meeting transcripts or model context into operational logs. Neither a structured format nor an AI-generated summary proves who authorized a decision.

Authentication + Identity

Required outcome: approved identity integration, least privilege, explicit authorization at action boundaries, revocation handling and auditable service identities.

Use the enterprise identity provider where available. Keycloak with OIDC is a self-managed reference option, not a requirement for a second identity estate or administrator access to a master realm. Separate application, administrative and agent identities. Authentication alone does not grant tenant access or approval authority.

No real secrets belong in source control, including short-lived PoC admin keys. Synthetic demonstrations can use clearly fake values; real-data pilots need approved secret delivery and access controls before use. A later milestone is not permission to expose a live credential today.

Infrastructure + Containers

Required outcome: reproducible deployment, isolated environments, bounded execution, secret delivery, recovery evidence and a named release authority.

Docker Compose is a useful local/reference option and may fit a reviewed small deployment. It does not establish high availability, zero downtime, backup recovery or suitability for regulated production. Use the customer's existing cloud/container platform when appropriate; neither Kubernetes nor a VPS is mandated by the method.

Record owned services, endpoints, port allocation and dependency boundaries in the appropriate project configuration/runbook. A local port table is not service discovery or a security boundary. Pin dependencies/images according to the chosen ecosystem and retain provenance; do not hand-install unrecorded production changes.

Voice + Conversational (project-specific tiers)

Only adopt this capability when in scope. Inventory languages, accessibility, latency, consent/recording constraints and provider/data-routing permissions before choosing STT, TTS, telephony or orchestration.

Deepgram, ElevenLabs, LiveKit and LangGraph are examples of providers/libraries to evaluate, not mandatory model versions or an automatic approved data path. Pin tested versions and current terms in the project profile. An upstream workaround needs an affected-version range, reproducer, owner and removal condition; do not inherit it indefinitely from another project.

Library Currency Policy

  1. Select a supported, compatible, security-reviewed release at lock-in. “Latest” alone is not the acceptance criterion; verify runtime, plugin, license and deployment compatibility.
  2. Use dependency-update proposals and risk-based response deadlines. Security urgency can require action sooner than a monthly or quarterly calendar.
  3. Automated merge is an explicitly approved policy with effective required checks, bounded scope and rollback/monitoring expectations. A patch version is not proof of zero risk. Renovate automerge configuration.
  4. Re-run relevant contract, migration, UI and operational checks for the changed dependency. Record exclusions and unsupported paths.
  5. Changes affecting architecture, security/data boundaries, business meaning or operating commitments return to the responsible owner. Version the adopted profile independently of the method and workspace protocol.

No package release table here substitutes for the adopter's lockfiles, support inventory and actual test evidence.

Deviation Templates

Template 1: Mandatory deviation (new ADR)

Propose a new decision with the actual affected control, not a pre-filled “Accepted” status:

  • Status: Proposed; decision ID allocated through the project process.
  • Baseline: profile ID/revision, current accepted decision and exact affected scope.
  • Requirement: capability/control to retain; applicable organizational constraints.
  • Options: reference choice, proposed alternative and credible alternatives.
  • Evidence: compatibility, risks, verification plan/results and gaps.
  • Ownership: architecture reviewer and affected business/security/operations approvers.
  • Outcome: owner decision on the exact revision; consequences and linked supersession if accepted.

An unavailable mandatory control blocks dependent work until an authorized decision resolves it. An ADR cannot waive law or another owner's authority.

Template 2: Default deviation (ADR-0001 entry)

The historical heading remains for existing links. While the initial stack ADR is draft, add the default alternative and rationale there. After acceptance, use a linked decision or approved change record at the appropriate scope. Do not alter accepted history silently, assume every project uses ADR-0001, or create a duplicate master list.

Summary

Preserve useful reference choices, make customer constraints explicit, and verify the control rather than the library name. The proposed factory interface exchanges a versioned method profile and scoped evidence across different stacks; its schemas do not implement or verify those adapters. Accountable owners make business decisions. The future workspace must record and enforce those decisions without assuming their authority, and the authorized release process remains separate.