/** * Environment + account-axis constants shared across CLI, webapp, and * deploy-core. Two independent axes live here: the workload STAGE * ([[ACCOUNT_STAGES]]) and the structural TIER ([[ACCOUNT_TIERS]]). "root" is * NOT a stage and NOT a tier — it is the wire environment marker that decodes * to tier "organisation" via [[environmentToTier]], and is never * user-selectable. */ import { z } from "zod"; export declare const ACCOUNT_STAGES: readonly ["production", "staging", "development", "platform", "compliance"]; export type AccountStage = (typeof ACCOUNT_STAGES)[number]; /** Human-readable labels for each workload stage. */ export declare const ACCOUNT_STAGE_LABELS: Record; /** Type guard: checks whether a string is a user-selectable workload stage. */ export declare function isAccountStage(value: string): value is AccountStage; /** * Structural environments used for cascade account partitioning. * These are not user-selectable — "root" is implicit (management account), * "platform" hosts shared infrastructure (IPAM, Transit Gateway, etc.). */ export declare const STRUCTURAL_ENVIRONMENTS: { readonly ROOT: "root"; readonly PLATFORM: "platform"; }; /** * Wire-tolerance vocabulary: the workload stages PLUS the structural "root" * marker. Ingress schemas that face out-of-version CLIs or the org-config * GET→PUT round-trip accept this superset, then decode "root" → null at * PERSISTENCE via [[stageFromWireEnvironment]] (the DB never stores structural * root). NOT a user-facing picker vocabulary — pickers use ACCOUNT_STAGES. * PERMANENT ingest vocabulary, not a drain-gated shim: the separate-root * connect deliberately carries environment:"root" as its intent marker * (solo-to-org lifecycle ADR, decision 7), so wire-rejection of "root" is * unreachable; only derived-EMIT of "root" retires, per-org, behind the CLI * telemetry gate. See decisions/2026-06-07-account-tier-vs-stage-separation.md * § "Wire back-compat (permanent, not transitional)". */ export declare const ACCOUNT_STAGES_WITH_ROOT: readonly ["production", "staging", "development", "platform", "compliance", "root"]; export type AccountStageWithRoot = (typeof ACCOUNT_STAGES_WITH_ROOT)[number]; /** Returns the human-readable label for an environment, with capitalised fallback. */ export declare function getEnvironmentLabel(env: string): string; /** * Structural TIER axis: an account's role in the organisation. Independent of * the workload STAGE axis ([[ACCOUNT_STAGES]]) — a tier is never derived from a * stage and vice versa. Same literals as [[ACCOUNT_ROLES]]: its `satisfies` * clause rejects non-tier values, and [[ACCOUNT_ROLE_TIER_PRESENCE]] rejects a * missing or duplicated tier, so the two cannot drift. */ export declare const ACCOUNT_TIERS: readonly ["organisation", "platform", "account"]; export type AccountTier = (typeof ACCOUNT_TIERS)[number]; export declare const AccountTierSchema: z.ZodEnum<{ platform: "platform"; organisation: "organisation"; account: "account"; }>; /** * AWS account roles in the wire format used across the CLI, webapp API * responses, and the Prisma `AccountRole` enum. This is the SINGLE SOURCE * for the literal values — the webapp's * `app/.server/types/database-enums.ts` rebinds `AccountRole` to these * values, and `database-enums-parity.test.ts` then enforces that the * resulting set matches the Prisma migration SQL CHECK constraint. Adding * a new role here without updating Prisma fails the parity test; renaming * a value here propagates structurally to the webapp. * * Independence: `role` (TIER) and `environment` (STAGE) are independent axes. * Inbound wire values that still carry a structural environment are decoded via * [[environmentToTier]] / [[stageFromWireEnvironment]] — nothing derives one * axis from the other on a write. See [[STRUCTURAL_ENVIRONMENTS]]. */ export declare const ACCOUNT_ROLES: { readonly ORGANISATION: "organisation"; readonly PLATFORM: "platform"; readonly ACCOUNT: "account"; }; /** Type guard: checks whether a string is a valid account tier. */ export declare function isAccountTier(value: string): value is AccountTier; /** * Tiers whose deploys are governance operations (design * 2026-07-23-ci-plugin-complete-surface.md §4.1): machine (CI deploy-token) * principals must carry the `deploy:governance` grant to record a deployment * for — or mint OIDC credentials naming — an account of these tiers. * * Derived by EXCLUDING the workload tier rather than enumerating governance * tiers — a deliberate inversion of the enumerate-don't-subtract rule: this * feeds deny gates, so a future tier added to [[ACCOUNT_TIERS]] defaults to * "governance grant required" (fail closed) instead of silently un-gated. */ export declare const GOVERNANCE_ACCOUNT_TIERS: readonly AccountTier[]; /** Whether an account tier's deploys are governance operations. */ export declare function isGovernanceTier(tier: string): boolean; /** * Decode an inbound wire `environment` to its structural TIER. The wire stays * superset-tolerant: out-of-version CLIs and the post-`fjall create org` * connect still submit "root"/"platform" as an environment. This is the only * sanctioned environment→tier derivation — write paths must NOT re-derive a * role from a stage. See decisions/2026-06-07-account-tier-vs-stage-separation.md. */ export declare function environmentToTier(environment: string | null | undefined): AccountTier; /** * What choosing a workload STAGE does to an account's structural TIER, and * what that costs the caller. The two axes are independent — nothing derives * one from the other on a write path — but they share the literal "platform", * and the normal connect path sends no explicit tier, so * [[environmentToTier]] decides the tier from the stage the caller picked. A * caller shown the five stages as five equal choices cannot see that one of * them lands the account on a tier no application can target. * * `tier` and `targetable` are DERIVED from [[environmentToTier]], the single * sanctioned decoder, so they cannot drift from what a connect actually does. * Only the copy is authored, and `Record` below means a new * stage does not compile until someone states its consequence. */ export interface AccountStageEffect { readonly stage: AccountStage; readonly label: string; /** The tier a connect lands on when it sends this stage and no explicit tier. */ readonly tier: AccountTier; /** * Whether an application can deploy to an account on this stage. False for * structural tiers: they are filtered out of account pickers and their * deploys are governance operations. */ readonly targetable: boolean; /** One-line hint for a picker row (Ink `SelectOption.description`). */ readonly summary: string; /** Full prose consequence for documentation and agent-skill surfaces. */ readonly consequence: string; } /** Resolve the tier consequence of a workload stage. */ export declare function accountStageEffect(stage: AccountStage): AccountStageEffect; /** * Every workload stage with its tier consequence, in [[ACCOUNT_STAGES]] order. * Surfaces that PRESENT the stage list to a human or an agent render from this * rather than restating the tuple, so the consequence travels with the option. * See decisions/2026-06-07-account-tier-vs-stage-separation.md. */ export declare const ACCOUNT_STAGE_EFFECTS: readonly AccountStageEffect[]; /** * The stage list for a ONE-LINE surface — `--environment` flag help, the * non-interactive "valid environments" error — with any structural stage * marked inline. Surfaces with room for a full sentence render * [[ACCOUNT_STAGE_EFFECTS]] and its `summary`/`consequence` instead; this is * the degenerate rendering for places that get one line, and it exists so * those places are not the last flat `ACCOUNT_STAGES.join(", ")` left. */ export declare function formatAccountStageList(): string; /** * Decode an inbound wire `environment` to its workload STAGE, or null. "root" * (the management account — no workloads), empty, null, or any unknown string * yields null. "platform" is a real stage and passes through unchanged. */ export declare function stageFromWireEnvironment(environment: string | null | undefined): AccountStage | null; /** * Canonical TIER accessor for deploy-core/CLI/MCP readers: prefer the explicit * `tier` wire field, falling back to decoding a legacy structural environment. * Replaces scattered `environment === STRUCTURAL_ENVIRONMENTS.*` comparisons. */ export declare function accountTier(acc: { tier?: AccountTier | null; environment?: string | null; }): AccountTier; /** * Resolve a provider account's synth-time environment — the value that reaches * a CDK synth as `-c environment=` and that `envAwareRemovalPolicyDefault()` * turns into RETAIN or DESTROY. * * The tier/stage separation (decisions/2026-06-07-account-tier-vs-stage-separation.md) * nulls the wire `environment` for organisation-tier accounts, but scaffolded * org entry points gate on `config.environment === "root"` — decode the tier * back to the historical "root" marker via the sanctioned `accountTier()` * decoder. Workload stages pass through verbatim; a null stage on any other * tier stays unresolved. * * Lives here, not in the constructs package, because deploy-core resolves the * environment BEFORE synth (to pass it explicitly) while * `components/infrastructure` resolves it DURING synth (when no explicit * signal arrived). Two derivations that disagree would put a stack's removal * policies on the wrong side of production — so there is only one. */ export declare function resolveSynthEnvironment(acc: { tier?: AccountTier | null; environment?: string | null; }): string | undefined;