Skip to content

Adr 024 deployment profiles

ADR-024: Deployment profiles and regional capability validation

Section titled “ADR-024: Deployment profiles and regional capability validation”

Status: proposed Date: 2026-10-25 Issue: #836

Per the ADR lifecycle, this decision remains proposed until its implementing PR merges.

The application stack deploys Slack, Linear, Jira, deploy-preview screenshots and the Agent Registry whether or not an operator uses them. Each adds Lambdas, tables, secrets, IAM grants, API routes and alarms, so unused integrations cost money, slow deploys, widen the security-review surface and consume the root stack’s 490-resource budget (ADR-023). Some depend on Region-limited services: AWS Agent Registry is available in nine Regions, and AgentCore Memory, which every deployment uses, in sixteen. An unsupported Region fails late, inside CloudFormation, with an opaque service error.

Issue #836 also suggests moving substantial optional modules into nested stacks. Slack, Linear, Jira and screenshots each attach methods to the shared Task API RestApi. ADR-023 §1 and the #852 proof of concept record that extracting such integrations loses deployment dependencies, CORS, attribution and tags even when synthesis passes, and moving their tables and secrets would replace them.

  1. One validated selection. DeploymentFeatures (cdk/src/deployment/features.ts) is resolved once in main.ts and passed to AgentStack. Constructs read the selection, never feature context directly. Optional features: slack, linear, jira, deployPreviewScreenshots, agentRegistry, toolGateway. Authentication, Cedar HITL, secure egress, task state, admission control, GitHub workflow, alarms and diagnostics are core and not selectable.

  2. Persistent profile. abca.deployment.json at the repository root (schemaVersion: 1, every feature an explicit boolean, unknown keys rejected) is the shared input for local and CI synthesis. Precedence: per-feature context switch (enableSlack, enableLinear, enableJira, enableDeployPreviewScreenshots, enableAgentRegistry, enableToolGateway) → profile → legacy topology. -c deploymentProfile=<path> selects another file; -c deploymentProfile=none ignores it (used by the synthesis census so a developer’s profile cannot change its results).

  3. Absent profile = legacy topology. Without a profile, everything that deploys today still deploys (Tool Gateway stays opt-in). Shipping profiles deletes nothing from existing installations.

  4. Omit, do not relocate. A disabled feature synthesizes no routes, WAF body-size exemptions, Lambdas, grants, tables, secrets, wiring, alarms or outputs. Enabled features stay in the application stack, with the existing Registry nested stacks unchanged. Headroom comes from omission, which is safe; relocation is not safe without the populated migration rehearsal that ADR-023 defers. The stranded-orchestration sweep and iteration heartbeat deploy only with Linear or Jira. Screenshot-to-Linear/Jira wiring exists only when screenshots and that tracker are both enabled. Two parts of the issue’s “shared orchestration” scope stay core. The orchestration table and live reconciler remain because the reconciler also owns the per-task budget rollup. The task table’s LinearIssueIndex/JiraIssueIndex remain because removing a GSI is a schema change outside this decision. A DeploymentFeatures output records the selection so tools can read it back.

  5. Regional capability catalog. contracts/deployment-capabilities.json records, per capability, the required services, supported Regions, remediation, maturity, footprint and statefulness, with the source URL and last-verified date. CDK reads the JSON directly; the CLI keeps a package-safe literal mirror enforced by a parity test (the contracts/constants.json pattern). AgentStack rejects an unsupported concrete Region during synthesis, so the CLI is not the only enforcement point. Environment-agnostic synthesis is not checked.

  6. bgagent setup. The wizard runs these steps in order:

    • resolves the Region first and checks core availability;
    • lists every feature but marks unavailable ones as unselectable;
    • preselects features already deployed, read from DeploymentFeatures or inferred for older stacks;
    • recommends the minimal profile for new stacks;
    • diffs a temporary candidate profile, so cancelling leaves the repository untouched;
    • asks for explicit confirmation before deleting or replacing state-bearing resources, then writes the profile and deploys through the existing mise task.

    Non-interactive runs fail on an unsupported feature, and require --accept-data-loss in addition to --yes to remove state-bearing resources. Operator subcommands for an omitted feature fail early with a pointer to bgagent setup when they can identify the stack: from --stack-name, or the stack the CLI was configured from.

  7. First-install compute and account preparation. Issue #836 kept compute out of the first wizard; this record extends it for new installations only, where no backend can be removed.

    • The wizard asks where agents run (AgentCore, ECS, or both) and records the ordered list as the profile’s optional compute. Context compute_types / compute_type still wins.
    • On existing stacks the deployed backends are read from ComputeTypes. Stacks from before #912 publish only ComputeSubstrate; if that names a backend other than AgentCore, the wizard requires --compute rather than guessing. Adding a backend is allowed; removing one is refused in favour of the COMPUTE.md transition procedure.
    • Before the deploy, after confirmation, it installs or updates ABCA’s least-privilege CDK bootstrap and sets the bootstrap ComputeTypes parameter (AgentCore always included). It never modifies a standard CDK bootstrap that other apps may share.
  8. Several deployments per checkout and account. The wizard confirms the caller’s account (STS), lists existing ABCA stacks in the Region and offers to update one or create a named new one. Each non-default stack reads only abca.deployment.<stackName>.json, never the shared file, which belongs to the default stack. A stack without its own file therefore keeps the legacy topology (rule 3), and one checkout can drive several deployments without one profile deleting another’s modules. When the account uses ABCA’s least-privilege bootstrap, whose deploy role is scoped to backgroundagent-dev and backgroundagent-dev-*, the wizard only creates stacks with those names; a create outside that scope fails and cannot roll back its IAM roles. A bootstrap parameter for other prefixes is a follow-up.

  • (+) The minimal profile synthesizes 317 root resources against 463 for the legacy topology (−146), with the corresponding IAM, secrets and routes, and fails fast in unsupported Regions.
  • (+) The shared census grows to 142 profiles. The new cells cover minimal, each single feature, the paired trackers/screenshots and all features in both topologies. They assert that disabled features leave nothing behind and that catalogued footprints track synthesis.
  • (+) One input file feeds local deploys, CI and the wizard; existing context switches keep working.
  • (-) Disabling a stateful feature deletes its tables and secrets (RemovalPolicy.DESTROY today). Re-enabling creates empty resources; operators must re-run the feature’s setup command. No import path is provided.
  • (-) Regional data is a maintained copy of AWS documentation and can go stale. A stale entry blocks a newly supported Region until the catalog is updated (fail closed); the update process is in the deployment guide.
  • (!) Strict boolean parsing now rejects malformed enableToolGateway, enableAgentRegistry (unchanged) and the new switches; values that were previously treated as false, such as enableToolGateway=yes, now fail synthesis. enableLinearIdentityVault keeps its lenient reading.
  • (!) Legacy-default stacks in core Regions without AWS Agent Registry (7 of 16) now fail synthesis rather than at the registry custom resource; such stacks could never deploy the registry. The error names the profile key and context switch that disable it.
  • (!) Enabling every feature leaves the root stack where it is today; nested-stack extraction of route-owning integrations remains out of scope until ADR-023’s migration criteria are met.