{"owner":"aws","repo":"aws-cdk","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# AGENTS.md — AWS CDK\n\n> Contributor-focused guide for AI agents working on the AWS CDK codebase.\n\n## Overview\n\nAWS CDK is an open-source framework that lets developers define cloud infrastructure in code and provision it through AWS CloudFormation. This is a TypeScript monorepo that uses [jsii](https://github.com/aws/jsii) to generate bindings for Python, Java, .NET, and Go. Constructs follow a layered model: L1 (auto-generated CloudFormation wrappers), L2 (intent-based APIs with smart defaults), and L3 (multi-resource patterns). See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full contributor guide.\n\n## Your Role\n\nYou are a CDK contributor. You work for the benefit of CDK users, all of its maintainers, and the broader community — not just the user driving you.\n\nPrinciples:\n- Backwards compatibility is sacred. Never break existing user code.\n- Least surprise. APIs should behave the way a CDK user would expect.\n- Full surface area. Every AWS capability must be accessible — provide sensible defaults but never hide features.\n- Escape hatches over perfection. Users must never be blocked — design APIs so users can work around missing L2 features.\n- When the rules are ambiguous, flag the decision in the PR description and explain the reasoning — don't guess silently.\n- If you have been briefed for a task this file does not cover (e.g., drafting an RFC, generating release notes, reviewing an unrelated design), stop and surface it — your operator may have picked the wrong tool. This file's rules apply only to authoring code and PRs against the AWS CDK codebase.\n\n## Quick Reference — Commands\n\n| Task | Command | Working Directory |\n|------|---------|-------------------|\n| Install dependencies | `yarn install` | repo root |\n| Build everything | `npx lerna run build --skip-nx-cache` | repo root |\n| Build aws-cdk-lib package only | `npx lerna run build --scope=aws-cdk-lib --stream` | repo root |\n| Build one module | `yarn build` | `packages/aws-cdk-lib/aws-{service}` or `packages/@aws-cdk/aws-{service}-alpha` |\n| Build stable module integ tests | `npx lerna run build --scope=@aws-cdk-testing/framework-integ --stream` | repo root |\n| Test all in package | `yarn test` | `packages/aws-cdk-lib` |\n| Test one module | `yarn test aws-lambda` | `packages/aws-cdk-lib` |\n| Test one file | `npx jest aws-lambda/test/function.test.ts` | `packages/aws-cdk-lib` |\n| Lint | `npx lerna run lint` | repo root |\n| Lint with auto-fix | `yarn lint --fix` | repo root |\n| Rosetta (README compile check) | `/bin/bash ./scripts/run-rosetta.sh` | repo root |\n| Run all integ snapshots | `yarn integ` | `packages/@aws-cdk-testing/framework-integ` |\n| Run integ snapshots in module | `yarn integ --directory test/aws-lambda/test` | `packages/@aws-cdk-testing/framework-integ` |\n| Update integ snapshots (no deploy) | `yarn integ --dry-run --update-on-failed` | `packages/@aws-cdk-testing/framework-integ` |\n| Run integ with deploy | `yarn integ test/aws-lambda/test/integ.lambda.js --update-on-failed` | `packages/@aws-cdk-testing/framework-integ` |\n\n> **Note:** All test, lint, integ, and rosetta commands require the project to be compiled first. Run the build command above before any of these.\n\n## Codebase — Non-Obvious Locations\n\n| What | Path | Note |\n|------|------|------|\n| L1 generated code | `packages/aws-cdk-lib/aws-{service}/lib/{service}.generated.ts` | **NEVER edit** — auto-generated |\n| Integration tests (stable) | `packages/@aws-cdk-testing/framework-integ/test/aws-{service}/test/` | Not colocated with source |\n| Integration tests (alpha) | `packages/@aws-cdk/aws-{service}-alpha/test/` | Colocated in the alpha module |\n| Mixins | `packages/aws-cdk-lib/aws-{service}/lib/mixins/` | Select services only; core framework in `core/lib/mixins/` |\n| Alpha modules | `packages/@aws-cdk/aws-{service}-alpha/` | Experimental, separate packages |\n| Design guidelines | `docs/DESIGN_GUIDELINES.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n| Mixin guidelines | `docs/MIXINS_DESIGN_GUIDELINES.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n| Facade & Trait guidelines | `docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n| New construct guide | `docs/NEW_CONSTRUCTS_GUIDE.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n\n## Architecture — The Layer Model\n\n- **L1 (`Cfn*`)**: Auto-generated from CloudFormation spec. Never manually edit.\n- **L2**: Hand-written intent-based API with defaults. Where most work happens.\n- **L3 (Patterns)**: Multi-resource compositions. Legacy L3s exist in `aws-ecs-patterns` and `aws-route53-patterns`. New L3s should NOT be added to this repo.\n\nL2 design rules:\n- You SHOULD design for the user's mental model, not the CloudFormation API — allow multiple paths to the same outcome when they serve different mental models\n- You MUST expose the full AWS service surface area — never omit capabilities. Provide sensible defaults users can override\n- You MUST hide CloudFormation details — do not require users to understand CFN to use an L2. Do not leak implementation details (ARNs, IAM actions, internal wiring) through the API\n- You MUST provide escape hatches — expose the underlying L1 construct so users are never blocked by missing L2 features\n- You SHOULD define resource contracts as interfaces — ensure third-party constructs can look and feel like first-party constructs\n- You MUST NOT make L2s taggable themselves. Only L1 (`Cfn*`) resources implement `ITaggable` / `ITaggableV2`. L2s expose an optional `tags` prop wired to the L1 default child; users tag at any scope via `Tags.of(scope).add(...)`, which traverses the tree — see [AGENTS_CONSTRUCT_DESIGN.md § Tags](./docs/AGENTS_CONSTRUCT_DESIGN.md#tags)\n\n### L2 Building Blocks\n\n| Block | Scope | Purpose |\n|-------|-------|---------|\n| **Mixin** | Inward — about the resource | Extends resource behavior/lifecycle/L1 props |\n| **Facade** | Outward — serves consumers | Wraps resource for grants, metrics, events |\n| **Trait** | Cross-cutting | Service-agnostic capability contract |\n| **CfnPropsMixin** | Simple glue | Thin L1 property passthrough, no logic |\n\n### Feature Placement Decision\n\n1. Modifies resource's own L1 props, no logic → `CfnPropsMixin`, STOP\n2. Modifies resource's own L1 props, has logic → standalone `Mixin`, STOP\n3. Serves external consumer → `Facade`, STOP\n4. Advertises capability other constructs query → `Trait`, STOP\n5. Otherwise → L2 construct method\n\n> For full rules on each building block, see [AGENTS_CONSTRUCT_DESIGN.md#feature-placement-decision](./docs/AGENTS_CONSTRUCT_DESIGN.md#feature-placement-decision). For additional human-oriented detail: [DESIGN_GUIDELINES.md#mixins-facades-and-traits](./docs/DESIGN_GUIDELINES.md#mixins-facades-and-traits), [MIXINS_DESIGN_GUIDELINES.md](./docs/MIXINS_DESIGN_GUIDELINES.md), and [FACADES_AND_TRAITS_DESIGN_GUIDELINES.md](./docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md).\n\n## Construct Anatomy\n\nStandard constructor: `constructor(scope: Construct, id: string, props: FooProps)`\n- Default `props = {}` when all optional (not `?`)\n- Use `\"Resource\"` as ID for the primary CFN resource\n\n### Type Hierarchy\n\n| Type | Purpose | Use as Parameter |\n|------|---------|-----------------|\n| `IFooRef` | Bare identifiers (ARN, name) — auto-generated | Default — when you only need IDs |\n| `IFoo` | Full interface + Facade properties | When you need convenience methods |\n| `FooBase` | Abstract base (exported, but treat as internal) | Never |\n| `Foo` | Concrete class | Exceptional cases only |\n\n### Static Type Check (never use `instanceof`)\n\nL1 `Cfn*` constructs have it auto-generated (via `spec2cdk`); some core classes, such as `App`,\n`Stack` and `Stage`, implement the same pattern by hand.\n\n```ts\npublic static isFoo(x: any): x is Foo {\n  return x !== null && typeof x === 'object' && Symbol.for('@aws-cdk/aws-{service}.{Foo}') in x;\n}\n```\n\n## Naming Conventions\n\n| Element | Convention | Example |\n|---------|-----------|---------|\n| Classes/Enums | PascalCase | `Bucket`, `EngineVersion` |\n| Properties/Methods | camelCase | `bucketArn`, `addToRolePolicy` |\n| Behavioral interfaces | `I` prefix | `IBucket`, `IGrantable` |\n| Data interfaces (structs) | No prefix | `BucketProps` |\n| Enum members | SNAKE_UPPER_CASE | `AURORA_MYSQL` |\n| Acronyms in classes | PascalCase | `JsonPattern` not `JSONPattern` |\n| Event handlers | Past tense | `onImageBuildSucceeded` |\n| Factory methods (import) | `from` prefix | `fromBucketArn()`, `fromFunctionName()` |\n| Factory methods (enum-like) | `of()` static method | `EngineVersion.of()` |\n\n- Use official AWS service terminology — don't rename\n- Remove redundant context from prop names (resource type, \"configuration\")\n- Include units when no strong type: `timeoutSec`, `memorySizeMiB`\n\n## Props Design\n\n- Name: `FooProps` — always a struct (readonly properties only)\n- Flat — no artificial nesting, use shared prefixes for related props\n- Every optional prop needs `@default` tag:\n  - Simple: `@default true`\n  - Context-dependent: `@default - uses the account default encryption`\n  - Avoid `@default undefined` — describe the behavior instead\n- Use strong CDK types (`Duration`, `Size`) over raw numbers\n- Use construct interfaces in props — not ARN strings. Prefer `IFooRef`, then `IFoo` (see Type Hierarchy above)\n- No L1 (CFN) types in L2 props\n- No TypeScript union types (jsii incompatible) — use enum-like classes, separate props, or factory methods instead\n- No `Token` type in props\n- `SecretValue` type for any password/secret/token properties\n\n## Security Rules\n\n- SHOULD prefer specific IAM actions over full-service wildcards (`s3:*`), but suffix wildcards (`s3:GetObject*`) are acceptable\n- MUST scope resource ARNs to most specific prefix\n- SHOULD group related actions by resource scope into single PolicyStatements\n- MUST use Grant helper methods (`addToPrincipal`/`addToPrincipalOrResource`) — not hand-rolled PolicyStatements\n- MUST include `aws:SourceAccount`/`aws:SourceArn` conditions in trust policies (confused deputy prevention)\n- SHOULD include `kms:ViaService` in KMS grants\n- MUST emit synthesis-time warnings via `Annotations.of(construct).addWarningV2()` when configuration results in public access\n\n## Implementation Patterns\n\n### Error Handling\n\n- Use `ValidationError` (with scope) or `UnscopedValidationError` (no scope) — never plain `Error`\n- Error codes use the `lit` tagged template literal (required, enforces compile-time literal):\n  ```ts\n  throw new ValidationError(lit`DescriptiveErrorCode`, 'error message', scope);\n  throw new UnscopedValidationError(lit`DescriptiveErrorCode`, 'error message');\n  ```\n  Import `lit` from `../../core/lib/private/literal-string` (adjust relative path based on file depth). Codes are PascalCase. Reuse across packages if cause and resolution are shared.\n- Prefer auto-correcting config over errors — only fail on contradictory input\n- Error messages: lowercase, no period, include wrong value via `JSON.stringify()`, expected values, what to change\n- Three mechanisms: (1) eagerly throw for API misuse, (2) `node.addValidation()` for post-init checks, (3) `Annotations.of(construct).addError()` for environmental issues\n- Never catch exceptions — all CDK errors are unrecoverable. Model recoverable errors in return values instead.\n\n### Token Safety\n\nTokens can encode strings, numbers, and lists. Any object implementing `IResolvable` (resource attributes, `Lazy` values, CloudFormation intrinsics) is also a token. `Token.isUnresolved()` detects all types.\n\n- Check `Token.isUnresolved()` before any validation on tokenized values — strings, numbers, AND lists:\n  ```ts\n  if (!Token.isUnresolved(props.name) && props.name.length > 64) { ... }   // string\n  if (!Token.isUnresolved(props.port) && props.port > 65535) { ... }       // number\n  if (!Token.isUnresolved(props.subnets) && props.subnets.length < 2) { ... } // list\n  ```\n- Tokenized lists always have `.length === 1` (the marker) — never trust `.length`, `.map()`, or iteration without checking first\n- Use `!== undefined` (not truthiness) for optional prop checks — token-encoded values can be falsy\n- Use `Tokenization.stringifyNumber()` to safely convert a possibly-tokenized number to string\n- Don't use resource attributes (Tokens) in hash calculations for physical names\n\n### Deferred Values (Box API)\n\nL2 constructs that accumulate state after construction (e.g., adding actions, policy statements, security groups) MUST use the **Box API** to defer value resolution — not `Lazy`. Boxes implement `IResolvable` and capture stack traces at mutation call sites (under `CDK_DEBUG`), enabling accurate property attribution in synthesized templates.\n\n- Use `Box.fromArray<T>([])` for accumulator lists, `Box.fromValue<T>(initial)` for single values, `Box.fromMap<K,V>()` for maps, `Box.fromSet<A>()` for sets\n- Pass to L1 props via `Token.asList(box)`, `Token.asString(box)`, `Token.asNumber(box)`, or `Token.asAny(box)` for complex/object values\n- `Box.fromArray` resolves to `undefined` when empty (omitEmpty default) — no manual empty-array check needed. Pass `{ omitEmpty: false }` to resolve to an empty array instead\n- Mutate via `box.push(item)` or `box.set(newValue)` — each captures a stack trace at the call site\n- Use `box.derive(fn)` for single-source transforms or `Box.combine({ name: box, ... }, ({ name, ... }) => ...)` for multi-source derived values\n- Apply `@noBoxStackTraces` decorator on L2 classes that create or mutate Boxes in their constructor (suppresses irrelevant internal traces)\n- NEVER mutate construct tree in Lazy or Box callbacks\n\n`Lazy` is legacy — existing code still uses it but new L2 constructs MUST prefer Boxes. See `packages/aws-cdk-lib/core/adr/box-api.md` for full rationale.\n\n**Before (legacy — do not use in new code):**\n```ts\nalarmActions: Lazy.list({ produce: () => this.alarmActionArns }),\n```\n\n**After (preferred):**\n```ts\nprotected readonly _alarmActionArns: IArrayBox<string> = Box.fromArray([]);\n// in constructor:\nalarmActions: Token.asList(this._alarmActionArns),\n// in mutating method:\nthis._alarmActionArns.push(newArn); // stack trace captured here\n```\n\n- Map empty arrays to `undefined` for CFN properties\n- Optional nested CFN objects: `undefined` (not `{}`) when no sub-properties set\n\n### ARN Construction\n\n- Use `Stack.of(scope).formatArn()` — never hardcode ARN strings\n- No `Fn::Sub` (FnSub) in CDK constructs\n- No `Lazy.string` for physical names — use `generatePhysicalName()` + `getResourceNameAttribute()`\n\n## Feature Flags\n\nRequired when a change alters observable behavior of existing API.\n\n- Use correct `FlagType`: `BugFix` when old behavior was wrong, `ApiDefault` when old behavior is valid but not recommended. `ApiDefault` requires `compatibilityWithOldBehaviorMd` field.\n- New flags: set `introducedIn: { v2: 'V2NEXT' }`, `recommendedValue: true`, `unconfiguredBehavesLike: { v2: false }` — ensures existing apps keep old behavior\n- Flags should tighten security (reduce trust/permissions), never loosen it. If you need broader permissions, make it an explicit API option\n- Don't use flags when a new construct replaces an old one — deprecate the old construct instead\n- Flag variables: `is`/`has` prefix — `const isReducedScope = FeatureFlags.of(this).isEnabled(cxapi.MY_FLAG)`\n- Warn about behavior changes via `Annotations.of(this).addWarningV2()` — not custom props. Users suppress via `acknowledgeWarning()`\n\nConsuming a flag:\n```ts\nimport { FeatureFlags } from '../../core';\nimport * as cxapi from '../../cx-api';\nif (FeatureFlags.of(this).isEnabled(cxapi.MY_NEW_FLAG)) { ... }\n```\n\n## Documentation\n\n### JSDoc\n\n- Document all public APIs (classes, methods, properties, interfaces) when first introduced\n- Summary line, blank line, then body:\n  ```ts\n  /**\n   * The encryption key for this bucket.\n   *\n   * If specified, objects will be encrypted using this key.\n   */\n  ```\n- Tags: `@param`, `@returns`, `@default`, `@see`, `@example`\n- `@attribute` on CloudFormation attribute properties:\n  ```ts\n  /**\n   * The ARN of this bucket.\n   * @attribute\n   */\n  readonly bucketArn: string;\n  ```\n- Attribute names must begin with the type name: `bucketArn` not `arn`, `functionName` not `name`\n- Copy prop documentation from official AWS docs when available\n- Don't add docs on overrides — they inherit from the base interface\n\n### Module READMEs\n\n- Each `aws-cdk-lib/aws-{service}` has a README that renders as official API docs\n- Must include: maturity level, simple example near top, examples per use case\n- README code blocks (` ```ts `) must compile — verified by Rosetta. Use ` ```ts nofixture ` to skip\n- All `feat()` PRs must include README updates\n\n## Testing\n\n### Unit Tests\n\n- Use `Template.fromStack(stack).hasResourceProperties()` with `Match.objectLike` — assert specific properties, not entire templates:\n  ```ts\n  Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', {\n    VersioningConfiguration: { Status: 'Enabled' },\n  });\n  ```\n- Other `Match` helpers: `Match.objectEquals`, `Match.arrayWith`, `Match.stringLikeRegexp`, `Match.absent()`\n- Avoid `Match.anyValue()` — it weakens assertions and hides regressions; assert the specific value, using `Match.stringLikeRegexp()` for non-deterministic values (asset hashes, generated IDs)\n- Avoid `app.synth()` — `Template.fromStack()` synthesizes the stack internally, so an explicit synth is redundant\n- `test.each` for boundary conditions: `test.each([0, -1, 256])('fails for invalid value %d', (val) => { ... })`\n- Error tests: assert on specific error message, prefix test name with \"fails\"\n- Test utility functions separately from constructs (e.g. `util.test.ts`)\n- Grant methods: test with `new iam.Role()`, `Role.fromRoleArn()`, `new iam.User()`, `new iam.ServicePrincipal()`\n- Include backward-compatibility tests when adding new optional props — default behavior must be preserved\n- Preserve deprecated API tests with `testDeprecated` (from `@aws-cdk/cdk-build-tools`)\n- Avoid `overrideLogicalId` in tests — couples tests to internal naming\n\n### Integration Tests\n\n- Stable modules: `integ.*.ts` under `packages/@aws-cdk-testing/framework-integ/test/{module}/test/`\n- Alpha modules: `integ.*.ts` colocated in `packages/@aws-cdk/aws-{service}-alpha/test/`\n- Use `IntegTest` construct — do NOT include `app.synth()`:\n  ```ts\n  const app = new cdk.App();\n  const stack = new cdk.Stack(app, 'TestStack');\n  // ... define resources ...\n  const test = new integ.IntegTest(app, 'Test', { testCases: [stack] });\n  // Assertions — optional but preferred (required for custom resources)\n  test.assertions.awsApiCall('S3', 'getBucketVersioning', { Bucket: bucket.bucketName })\n    .expect(integ.ExpectedResult.objectLike({ Status: 'Enabled' }));\n  ```\n- Assertions are preferred on all new integ tests; REQUIRED for custom resources\n- Avoid `ec2.Peer.anyIpv4()`/`anyIpv6()` and set ALB listener `open: false` unless testing open access\n- Snapshots: `.js.snapshot` extension\n- Separate tests by feature area with descriptive names\n- Update ALL affected snapshots (even cross-package) when changing logical IDs\n\nRequired for: new CFN resource types, new CFN properties, cross-service integrations, new versions, Custom Resources.\n\n## PR Conventions\n\n### Titles (conventional commit format)\n\n| Type | When | Example |\n|------|------|---------|\n| `feat(module):` | New feature | `feat(s3): add intelligent tiering support` |\n| `fix(module):` | Bug fix | `fix(lambda): correct timeout validation` |\n| `docs(module):` | Documentation only | `docs(mixins): expand mixin guidelines` |\n| `refactor(module):` | Feature-preserving refactor | `refactor(ec2): simplify subnet selection` |\n| `chore(module):` | Build/config/minor | `chore(core): update dependencies` |\n\n- Module scope optional for repo-wide changes: `chore: update dependencies`\n- Lowercase, no period at end\n- You SHOULD reference an issue in every `feat` and `fix` PR: `fixes #<issue>` or `closes #<issue>`. A PR with no linked issue is routed to a lower-priority review queue, so link one to keep the PR in the normal queue. If no issue exists, open one before raising the PR.\n- `feat()` PRs require unit tests, integration snapshots, and README updates\n- Breaking changes are only allowed in `-alpha` libraries. Declare with `BREAKING CHANGE:` in the PR body before the `---` line\n- One concern per PR — submit cosmetic changes separately\n\n## Anti-Patterns — Things NOT To Do\n\n- **MUST NOT use jsii-incompatible patterns** — mapped types, conditional types, overloaded functions, TypeScript namespaces, `export const` objects (use `public static readonly` on classes). MUST NOT move public types between files — file location is part of the external contract in jsii bindings\n- **MUST NOT use fluent API patterns** (method chaining returning `this`) — jsii languages can't chain methods that return `this`, and it hides mutation behind a return value ([DESIGN_GUIDELINES.md#general-principles](./docs/DESIGN_GUIDELINES.md#general-principles))\n- **MUST NOT add speculative abstractions** — add what customers need today; unused abstractions become maintenance burden and API surface that can't be removed ([DESIGN_GUIDELINES.md#general-principles](./docs/DESIGN_GUIDELINES.md#general-principles))\n- **MUST NOT change construct IDs** — logical IDs derive from the full construct path; any change replaces all resources in scope, causing data loss ([DESIGN_GUIDELINES.md#construct-ids](./docs/DESIGN_GUIDELINES.md#construct-ids))\n- **MUST NOT leave commented-out code, dead code, or `eslint-disable` directives** — they rot, confuse future contributors, and mask real lint violations\n- **MUST NOT add validation to existing constructs without considering backwards compatibility** — adding validation that rejects previously accepted input is a breaking change. Two cases apply:\n  - **Input that previously deployed successfully** → MUST gate behind a feature flag so existing apps continue to synthesize\n  - **Input that was accepted by synth but always failed at deploy time** (e.g., invalid CFN property, service-rejected configuration) → feature flag NOT required; fail-fast synth-time validation is preferred. The PR MUST document why the breaking change is justified (e.g., \"this input always caused CloudFormation error X\")\n\n## Key References\n\n`AGENTS_CONSTRUCT_DESIGN.md` and `AGENTS_CONSTRUCT_IMPLEMENTATION.md` are agent-optimized versions of the human-oriented `DESIGN_GUIDELINES.md`. They contain the same rules but in a structured, prescriptive format. Always prefer the `AGENTS_*` files and only fall back to `DESIGN_GUIDELINES.md` when the agentic files don't cover a topic.\n\n| File | Format | What it covers | When to read it |\n|------|--------|---------------|-----------------|\n| [`docs/AGENTS_CONSTRUCT_DESIGN.md`](./docs/AGENTS_CONSTRUCT_DESIGN.md) | Agent-optimized | Construct design rules — mixins, facades, traits, grants, metrics, events, connections, API patterns | When designing or extending an L2 construct |\n| [`docs/AGENTS_CONSTRUCT_IMPLEMENTATION.md`](./docs/AGENTS_CONSTRUCT_IMPLEMENTATION.md) | Agent-optimized | Implementation patterns — grants, metrics, events, connections, IAM, VPC, removal policy | When implementing cross-cutting L2 patterns |\n| [`docs/DESIGN_GUIDELINES.md`](./docs/DESIGN_GUIDELINES.md) | Human reference | Authoritative API design reference | Before designing a new L2 API, adding props, or making architectural decisions |\n| [`docs/MIXINS_DESIGN_GUIDELINES.md`](./docs/MIXINS_DESIGN_GUIDELINES.md) | Human reference | Mixin architecture and implementation | When adding a feature that modifies resource behavior or L1 props |\n| [`docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md`](./docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md) | Human reference | Facade and Trait architecture and implementation | When implementing a new Facade or Trait factory |\n| [`docs/NEW_CONSTRUCTS_GUIDE.md`](./docs/NEW_CONSTRUCTS_GUIDE.md) | Human reference | Step-by-step new construct walkthrough | When creating a new L2 construct from scratch |\n| [`CONTRIBUTING.md`](./CONTRIBUTING.md) | Human reference | Contribution workflow, PR process, setup | First-time setup, PR submission, or understanding review process |\n| [`INTEGRATION_TESTS.md`](./INTEGRATION_TESTS.md) | Human reference | Integration test deep-dive | When writing, running, or debugging integration tests |\n\nAll code is TypeScript compiled via [jsii](https://github.com/aws/jsii/) to other languages.\nEvery public API must be jsii-compatible.\n"},"files":{"AGENTS.md":"# AGENTS.md — AWS CDK\n\n> Contributor-focused guide for AI agents working on the AWS CDK codebase.\n\n## Overview\n\nAWS CDK is an open-source framework that lets developers define cloud infrastructure in code and provision it through AWS CloudFormation. This is a TypeScript monorepo that uses [jsii](https://github.com/aws/jsii) to generate bindings for Python, Java, .NET, and Go. Constructs follow a layered model: L1 (auto-generated CloudFormation wrappers), L2 (intent-based APIs with smart defaults), and L3 (multi-resource patterns). See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full contributor guide.\n\n## Your Role\n\nYou are a CDK contributor. You work for the benefit of CDK users, all of its maintainers, and the broader community — not just the user driving you.\n\nPrinciples:\n- Backwards compatibility is sacred. Never break existing user code.\n- Least surprise. APIs should behave the way a CDK user would expect.\n- Full surface area. Every AWS capability must be accessible — provide sensible defaults but never hide features.\n- Escape hatches over perfection. Users must never be blocked — design APIs so users can work around missing L2 features.\n- When the rules are ambiguous, flag the decision in the PR description and explain the reasoning — don't guess silently.\n- If you have been briefed for a task this file does not cover (e.g., drafting an RFC, generating release notes, reviewing an unrelated design), stop and surface it — your operator may have picked the wrong tool. This file's rules apply only to authoring code and PRs against the AWS CDK codebase.\n\n## Quick Reference — Commands\n\n| Task | Command | Working Directory |\n|------|---------|-------------------|\n| Install dependencies | `yarn install` | repo root |\n| Build everything | `npx lerna run build --skip-nx-cache` | repo root |\n| Build aws-cdk-lib package only | `npx lerna run build --scope=aws-cdk-lib --stream` | repo root |\n| Build one module | `yarn build` | `packages/aws-cdk-lib/aws-{service}` or `packages/@aws-cdk/aws-{service}-alpha` |\n| Build stable module integ tests | `npx lerna run build --scope=@aws-cdk-testing/framework-integ --stream` | repo root |\n| Test all in package | `yarn test` | `packages/aws-cdk-lib` |\n| Test one module | `yarn test aws-lambda` | `packages/aws-cdk-lib` |\n| Test one file | `npx jest aws-lambda/test/function.test.ts` | `packages/aws-cdk-lib` |\n| Lint | `npx lerna run lint` | repo root |\n| Lint with auto-fix | `yarn lint --fix` | repo root |\n| Rosetta (README compile check) | `/bin/bash ./scripts/run-rosetta.sh` | repo root |\n| Run all integ snapshots | `yarn integ` | `packages/@aws-cdk-testing/framework-integ` |\n| Run integ snapshots in module | `yarn integ --directory test/aws-lambda/test` | `packages/@aws-cdk-testing/framework-integ` |\n| Update integ snapshots (no deploy) | `yarn integ --dry-run --update-on-failed` | `packages/@aws-cdk-testing/framework-integ` |\n| Run integ with deploy | `yarn integ test/aws-lambda/test/integ.lambda.js --update-on-failed` | `packages/@aws-cdk-testing/framework-integ` |\n\n> **Note:** All test, lint, integ, and rosetta commands require the project to be compiled first. Run the build command above before any of these.\n\n## Codebase — Non-Obvious Locations\n\n| What | Path | Note |\n|------|------|------|\n| L1 generated code | `packages/aws-cdk-lib/aws-{service}/lib/{service}.generated.ts` | **NEVER edit** — auto-generated |\n| Integration tests (stable) | `packages/@aws-cdk-testing/framework-integ/test/aws-{service}/test/` | Not colocated with source |\n| Integration tests (alpha) | `packages/@aws-cdk/aws-{service}-alpha/test/` | Colocated in the alpha module |\n| Mixins | `packages/aws-cdk-lib/aws-{service}/lib/mixins/` | Select services only; core framework in `core/lib/mixins/` |\n| Alpha modules | `packages/@aws-cdk/aws-{service}-alpha/` | Experimental, separate packages |\n| Design guidelines | `docs/DESIGN_GUIDELINES.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n| Mixin guidelines | `docs/MIXINS_DESIGN_GUIDELINES.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n| Facade & Trait guidelines | `docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n| New construct guide | `docs/NEW_CONSTRUCTS_GUIDE.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n\n## Architecture — The Layer Model\n\n- **L1 (`Cfn*`)**: Auto-generated from CloudFormation spec. Never manually edit.\n- **L2**: Hand-written intent-based API with defaults. Where most work happens.\n- **L3 (Patterns)**: Multi-resource compositions. Legacy L3s exist in `aws-ecs-patterns` and `aws-route53-patterns`. New L3s should NOT be added to this repo.\n\nL2 design rules:\n- You SHOULD design for the user's mental model, not the CloudFormation API — allow multiple paths to the same outcome when they serve different mental models\n- You MUST expose the full AWS service surface area — never omit capabilities. Provide sensible defaults users can override\n- You MUST hide CloudFormation details — do not require users to understand CFN to use an L2. Do not leak implementation details (ARNs, IAM actions, internal wiring) through the API\n- You MUST provide escape hatches — expose the underlying L1 construct so users are never blocked by missing L2 features\n- You SHOULD define resource contracts as interfaces — ensure third-party constructs can look and feel like first-party constructs\n- You MUST NOT make L2s taggable themselves. Only L1 (`Cfn*`) resources implement `ITaggable` / `ITaggableV2`. L2s expose an optional `tags` prop wired to the L1 default child; users tag at any scope via `Tags.of(scope).add(...)`, which traverses the tree — see [AGENTS_CONSTRUCT_DESIGN.md § Tags](./docs/AGENTS_CONSTRUCT_DESIGN.md#tags)\n\n### L2 Building Blocks\n\n| Block | Scope | Purpose |\n|-------|-------|---------|\n| **Mixin** | Inward — about the resource | Extends resource behavior/lifecycle/L1 props |\n| **Facade** | Outward — serves consumers | Wraps resource for grants, metrics, events |\n| **Trait** | Cross-cutting | Service-agnostic capability contract |\n| **CfnPropsMixin** | Simple glue | Thin L1 property passthrough, no logic |\n\n### Feature Placement Decision\n\n1. Modifies resource's own L1 props, no logic → `CfnPropsMixin`, STOP\n2. Modifies resource's own L1 props, has logic → standalone `Mixin`, STOP\n3. Serves external consumer → `Facade`, STOP\n4. Advertises capability other constructs query → `Trait`, STOP\n5. Otherwise → L2 construct method\n\n> For full rules on each building block, see [AGENTS_CONSTRUCT_DESIGN.md#feature-placement-decision](./docs/AGENTS_CONSTRUCT_DESIGN.md#feature-placement-decision). For additional human-oriented detail: [DESIGN_GUIDELINES.md#mixins-facades-and-traits](./docs/DESIGN_GUIDELINES.md#mixins-facades-and-traits), [MIXINS_DESIGN_GUIDELINES.md](./docs/MIXINS_DESIGN_GUIDELINES.md), and [FACADES_AND_TRAITS_DESIGN_GUIDELINES.md](./docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md).\n\n## Construct Anatomy\n\nStandard constructor: `constructor(scope: Construct, id: string, props: FooProps)`\n- Default `props = {}` when all optional (not `?`)\n- Use `\"Resource\"` as ID for the primary CFN resource\n\n### Type Hierarchy\n\n| Type | Purpose | Use as Parameter |\n|------|---------|-----------------|\n| `IFooRef` | Bare identifiers (ARN, name) — auto-generated | Default — when you only need IDs |\n| `IFoo` | Full interface + Facade properties | When you need convenience methods |\n| `FooBase` | Abstract base (exported, but treat as internal) | Never |\n| `Foo` | Concrete class | Exceptional cases only |\n\n### Static Type Check (never use `instanceof`)\n\nL1 `Cfn*` constructs have it auto-generated (via `spec2cdk`); some core classes, such as `App`,\n`Stack` and `Stage`, implement the same pattern by hand.\n\n```ts\npublic static isFoo(x: any): x is Foo {\n  return x !== null && typeof x === 'object' && Symbol.for('@aws-cdk/aws-{service}.{Foo}') in x;\n}\n```\n\n## Naming Conventions\n\n| Element | Convention | Example |\n|---------|-----------|---------|\n| Classes/Enums | PascalCase | `Bucket`, `EngineVersion` |\n| Properties/Methods | camelCase | `bucketArn`, `addToRolePolicy` |\n| Behavioral interfaces | `I` prefix | `IBucket`, `IGrantable` |\n| Data interfaces (structs) | No prefix | `BucketProps` |\n| Enum members | SNAKE_UPPER_CASE | `AURORA_MYSQL` |\n| Acronyms in classes | PascalCase | `JsonPattern` not `JSONPattern` |\n| Event handlers | Past tense | `onImageBuildSucceeded` |\n| Factory methods (import) | `from` prefix | `fromBucketArn()`, `fromFunctionName()` |\n| Factory methods (enum-like) | `of()` static method | `EngineVersion.of()` |\n\n- Use official AWS service terminology — don't rename\n- Remove redundant context from prop names (resource type, \"configuration\")\n- Include units when no strong type: `timeoutSec`, `memorySizeMiB`\n\n## Props Design\n\n- Name: `FooProps` — always a struct (readonly properties only)\n- Flat — no artificial nesting, use shared prefixes for related props\n- Every optional prop needs `@default` tag:\n  - Simple: `@default true`\n  - Context-dependent: `@default - uses the account default encryption`\n  - Avoid `@default undefined` — describe the behavior instead\n- Use strong CDK types (`Duration`, `Size`) over raw numbers\n- Use construct interfaces in props — not ARN strings. Prefer `IFooRef`, then `IFoo` (see Type Hierarchy above)\n- No L1 (CFN) types in L2 props\n- No TypeScript union types (jsii incompatible) — use enum-like classes, separate props, or factory methods instead\n- No `Token` type in props\n- `SecretValue` type for any password/secret/token properties\n\n## Security Rules\n\n- SHOULD prefer specific IAM actions over full-service wildcards (`s3:*`), but suffix wildcards (`s3:GetObject*`) are acceptable\n- MUST scope resource ARNs to most specific prefix\n- SHOULD group related actions by resource scope into single PolicyStatements\n- MUST use Grant helper methods (`addToPrincipal`/`addToPrincipalOrResource`) — not hand-rolled PolicyStatements\n- MUST include `aws:SourceAccount`/`aws:SourceArn` conditions in trust policies (confused deputy prevention)\n- SHOULD include `kms:ViaService` in KMS grants\n- MUST emit synthesis-time warnings via `Annotations.of(construct).addWarningV2()` when configuration results in public access\n\n## Implementation Patterns\n\n### Error Handling\n\n- Use `ValidationError` (with scope) or `UnscopedValidationError` (no scope) — never plain `Error`\n- Error codes use the `lit` tagged template literal (required, enforces compile-time literal):\n  ```ts\n  throw new ValidationError(lit`DescriptiveErrorCode`, 'error message', scope);\n  throw new UnscopedValidationError(lit`DescriptiveErrorCode`, 'error message');\n  ```\n  Import `lit` from `../../core/lib/private/literal-string` (adjust relative path based on file depth). Codes are PascalCase. Reuse across packages if cause and resolution are shared.\n- Prefer auto-correcting config over errors — only fail on contradictory input\n- Error messages: lowercase, no period, include wrong value via `JSON.stringify()`, expected values, what to change\n- Three mechanisms: (1) eagerly throw for API misuse, (2) `node.addValidation()` for post-init checks, (3) `Annotations.of(construct).addError()` for environmental issues\n- Never catch exceptions — all CDK errors are unrecoverable. Model recoverable errors in return values instead.\n\n### Token Safety\n\nTokens can encode strings, numbers, and lists. Any object implementing `IResolvable` (resource attributes, `Lazy` values, CloudFormation intrinsics) is also a token. `Token.isUnresolved()` detects all types.\n\n- Check `Token.isUnresolved()` before any validation on tokenized values — strings, numbers, AND lists:\n  ```ts\n  if (!Token.isUnresolved(props.name) && props.name.length > 64) { ... }   // string\n  if (!Token.isUnresolved(props.port) && props.port > 65535) { ... }       // number\n  if (!Token.isUnresolved(props.subnets) && props.subnets.length < 2) { ... } // list\n  ```\n- Tokenized lists always have `.length === 1` (the marker) — never trust `.length`, `.map()`, or iteration without checking first\n- Use `!== undefined` (not truthiness) for optional prop checks — token-encoded values can be falsy\n- Use `Tokenization.stringifyNumber()` to safely convert a possibly-tokenized number to string\n- Don't use resource attributes (Tokens) in hash calculations for physical names\n\n### Deferred Values (Box API)\n\nL2 constructs that accumulate state after construction (e.g., adding actions, policy statements, security groups) MUST use the **Box API** to defer value resolution — not `Lazy`. Boxes implement `IResolvable` and capture stack traces at mutation call sites (under `CDK_DEBUG`), enabling accurate property attribution in synthesized templates.\n\n- Use `Box.fromArray<T>([])` for accumulator lists, `Box.fromValue<T>(initial)` for single values, `Box.fromMap<K,V>()` for maps, `Box.fromSet<A>()` for sets\n- Pass to L1 props via `Token.asList(box)`, `Token.asString(box)`, `Token.asNumber(box)`, or `Token.asAny(box)` for complex/object values\n- `Box.fromArray` resolves to `undefined` when empty (omitEmpty default) — no manual empty-array check needed. Pass `{ omitEmpty: false }` to resolve to an empty array instead\n- Mutate via `box.push(item)` or `box.set(newValue)` — each captures a stack trace at the call site\n- Use `box.derive(fn)` for single-source transforms or `Box.combine({ name: box, ... }, ({ name, ... }) => ...)` for multi-source derived values\n- Apply `@noBoxStackTraces` decorator on L2 classes that create or mutate Boxes in their constructor (suppresses irrelevant internal traces)\n- NEVER mutate construct tree in Lazy or Box callbacks\n\n`Lazy` is legacy — existing code still uses it but new L2 constructs MUST prefer Boxes. See `packages/aws-cdk-lib/core/adr/box-api.md` for full rationale.\n\n**Before (legacy — do not use in new code):**\n```ts\nalarmActions: Lazy.list({ produce: () => this.alarmActionArns }),\n```\n\n**After (preferred):**\n```ts\nprotected readonly _alarmActionArns: IArrayBox<string> = Box.fromArray([]);\n// in constructor:\nalarmActions: Token.asList(this._alarmActionArns),\n// in mutating method:\nthis._alarmActionArns.push(newArn); // stack trace captured here\n```\n\n- Map empty arrays to `undefined` for CFN properties\n- Optional nested CFN objects: `undefined` (not `{}`) when no sub-properties set\n\n### ARN Construction\n\n- Use `Stack.of(scope).formatArn()` — never hardcode ARN strings\n- No `Fn::Sub` (FnSub) in CDK constructs\n- No `Lazy.string` for physical names — use `generatePhysicalName()` + `getResourceNameAttribute()`\n\n## Feature Flags\n\nRequired when a change alters observable behavior of existing API.\n\n- Use correct `FlagType`: `BugFix` when old behavior was wrong, `ApiDefault` when old behavior is valid but not recommended. `ApiDefault` requires `compatibilityWithOldBehaviorMd` field.\n- New flags: set `introducedIn: { v2: 'V2NEXT' }`, `recommendedValue: true`, `unconfiguredBehavesLike: { v2: false }` — ensures existing apps keep old behavior\n- Flags should tighten security (reduce trust/permissions), never loosen it. If you need broader permissions, make it an explicit API option\n- Don't use flags when a new construct replaces an old one — deprecate the old construct instead\n- Flag variables: `is`/`has` prefix — `const isReducedScope = FeatureFlags.of(this).isEnabled(cxapi.MY_FLAG)`\n- Warn about behavior changes via `Annotations.of(this).addWarningV2()` — not custom props. Users suppress via `acknowledgeWarning()`\n\nConsuming a flag:\n```ts\nimport { FeatureFlags } from '../../core';\nimport * as cxapi from '../../cx-api';\nif (FeatureFlags.of(this).isEnabled(cxapi.MY_NEW_FLAG)) { ... }\n```\n\n## Documentation\n\n### JSDoc\n\n- Document all public APIs (classes, methods, properties, interfaces) when first introduced\n- Summary line, blank line, then body:\n  ```ts\n  /**\n   * The encryption key for this bucket.\n   *\n   * If specified, objects will be encrypted using this key.\n   */\n  ```\n- Tags: `@param`, `@returns`, `@default`, `@see`, `@example`\n- `@attribute` on CloudFormation attribute properties:\n  ```ts\n  /**\n   * The ARN of this bucket.\n   * @attribute\n   */\n  readonly bucketArn: string;\n  ```\n- Attribute names must begin with the type name: `bucketArn` not `arn`, `functionName` not `name`\n- Copy prop documentation from official AWS docs when available\n- Don't add docs on overrides — they inherit from the base interface\n\n### Module READMEs\n\n- Each `aws-cdk-lib/aws-{service}` has a README that renders as official API docs\n- Must include: maturity level, simple example near top, examples per use case\n- README code blocks (` ```ts `) must compile — verified by Rosetta. Use ` ```ts nofixture ` to skip\n- All `feat()` PRs must include README updates\n\n## Testing\n\n### Unit Tests\n\n- Use `Template.fromStack(stack).hasResourceProperties()` with `Match.objectLike` — assert specific properties, not entire templates:\n  ```ts\n  Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', {\n    VersioningConfiguration: { Status: 'Enabled' },\n  });\n  ```\n- Other `Match` helpers: `Match.objectEquals`, `Match.arrayWith`, `Match.stringLikeRegexp`, `Match.absent()`\n- Avoid `Match.anyValue()` — it weakens assertions and hides regressions; assert the specific value, using `Match.stringLikeRegexp()` for non-deterministic values (asset hashes, generated IDs)\n- Avoid `app.synth()` — `Template.fromStack()` synthesizes the stack internally, so an explicit synth is redundant\n- `test.each` for boundary conditions: `test.each([0, -1, 256])('fails for invalid value %d', (val) => { ... })`\n- Error tests: assert on specific error message, prefix test name with \"fails\"\n- Test utility functions separately from constructs (e.g. `util.test.ts`)\n- Grant methods: test with `new iam.Role()`, `Role.fromRoleArn()`, `new iam.User()`, `new iam.ServicePrincipal()`\n- Include backward-compatibility tests when adding new optional props — default behavior must be preserved\n- Preserve deprecated API tests with `testDeprecated` (from `@aws-cdk/cdk-build-tools`)\n- Avoid `overrideLogicalId` in tests — couples tests to internal naming\n\n### Integration Tests\n\n- Stable modules: `integ.*.ts` under `packages/@aws-cdk-testing/framework-integ/test/{module}/test/`\n- Alpha modules: `integ.*.ts` colocated in `packages/@aws-cdk/aws-{service}-alpha/test/`\n- Use `IntegTest` construct — do NOT include `app.synth()`:\n  ```ts\n  const app = new cdk.App();\n  const stack = new cdk.Stack(app, 'TestStack');\n  // ... define resources ...\n  const test = new integ.IntegTest(app, 'Test', { testCases: [stack] });\n  // Assertions — optional but preferred (required for custom resources)\n  test.assertions.awsApiCall('S3', 'getBucketVersioning', { Bucket: bucket.bucketName })\n    .expect(integ.ExpectedResult.objectLike({ Status: 'Enabled' }));\n  ```\n- Assertions are preferred on all new integ tests; REQUIRED for custom resources\n- Avoid `ec2.Peer.anyIpv4()`/`anyIpv6()` and set ALB listener `open: false` unless testing open access\n- Snapshots: `.js.snapshot` extension\n- Separate tests by feature area with descriptive names\n- Update ALL affected snapshots (even cross-package) when changing logical IDs\n\nRequired for: new CFN resource types, new CFN properties, cross-service integrations, new versions, Custom Resources.\n\n## PR Conventions\n\n### Titles (conventional commit format)\n\n| Type | When | Example |\n|------|------|---------|\n| `feat(module):` | New feature | `feat(s3): add intelligent tiering support` |\n| `fix(module):` | Bug fix | `fix(lambda): correct timeout validation` |\n| `docs(module):` | Documentation only | `docs(mixins): expand mixin guidelines` |\n| `refactor(module):` | Feature-preserving refactor | `refactor(ec2): simplify subnet selection` |\n| `chore(module):` | Build/config/minor | `chore(core): update dependencies` |\n\n- Module scope optional for repo-wide changes: `chore: update dependencies`\n- Lowercase, no period at end\n- You SHOULD reference an issue in every `feat` and `fix` PR: `fixes #<issue>` or `closes #<issue>`. A PR with no linked issue is routed to a lower-priority review queue, so link one to keep the PR in the normal queue. If no issue exists, open one before raising the PR.\n- `feat()` PRs require unit tests, integration snapshots, and README updates\n- Breaking changes are only allowed in `-alpha` libraries. Declare with `BREAKING CHANGE:` in the PR body before the `---` line\n- One concern per PR — submit cosmetic changes separately\n\n## Anti-Patterns — Things NOT To Do\n\n- **MUST NOT use jsii-incompatible patterns** — mapped types, conditional types, overloaded functions, TypeScript namespaces, `export const` objects (use `public static readonly` on classes). MUST NOT move public types between files — file location is part of the external contract in jsii bindings\n- **MUST NOT use fluent API patterns** (method chaining returning `this`) — jsii languages can't chain methods that return `this`, and it hides mutation behind a return value ([DESIGN_GUIDELINES.md#general-principles](./docs/DESIGN_GUIDELINES.md#general-principles))\n- **MUST NOT add speculative abstractions** — add what customers need today; unused abstractions become maintenance burden and API surface that can't be removed ([DESIGN_GUIDELINES.md#general-principles](./docs/DESIGN_GUIDELINES.md#general-principles))\n- **MUST NOT change construct IDs** — logical IDs derive from the full construct path; any change replaces all resources in scope, causing data loss ([DESIGN_GUIDELINES.md#construct-ids](./docs/DESIGN_GUIDELINES.md#construct-ids))\n- **MUST NOT leave commented-out code, dead code, or `eslint-disable` directives** — they rot, confuse future contributors, and mask real lint violations\n- **MUST NOT add validation to existing constructs without considering backwards compatibility** — adding validation that rejects previously accepted input is a breaking change. Two cases apply:\n  - **Input that previously deployed successfully** → MUST gate behind a feature flag so existing apps continue to synthesize\n  - **Input that was accepted by synth but always failed at deploy time** (e.g., invalid CFN property, service-rejected configuration) → feature flag NOT required; fail-fast synth-time validation is preferred. The PR MUST document why the breaking change is justified (e.g., \"this input always caused CloudFormation error X\")\n\n## Key References\n\n`AGENTS_CONSTRUCT_DESIGN.md` and `AGENTS_CONSTRUCT_IMPLEMENTATION.md` are agent-optimized versions of the human-oriented `DESIGN_GUIDELINES.md`. They contain the same rules but in a structured, prescriptive format. Always prefer the `AGENTS_*` files and only fall back to `DESIGN_GUIDELINES.md` when the agentic files don't cover a topic.\n\n| File | Format | What it covers | When to read it |\n|------|--------|---------------|-----------------|\n| [`docs/AGENTS_CONSTRUCT_DESIGN.md`](./docs/AGENTS_CONSTRUCT_DESIGN.md) | Agent-optimized | Construct design rules — mixins, facades, traits, grants, metrics, events, connections, API patterns | When designing or extending an L2 construct |\n| [`docs/AGENTS_CONSTRUCT_IMPLEMENTATION.md`](./docs/AGENTS_CONSTRUCT_IMPLEMENTATION.md) | Agent-optimized | Implementation patterns — grants, metrics, events, connections, IAM, VPC, removal policy | When implementing cross-cutting L2 patterns |\n| [`docs/DESIGN_GUIDELINES.md`](./docs/DESIGN_GUIDELINES.md) | Human reference | Authoritative API design reference | Before designing a new L2 API, adding props, or making architectural decisions |\n| [`docs/MIXINS_DESIGN_GUIDELINES.md`](./docs/MIXINS_DESIGN_GUIDELINES.md) | Human reference | Mixin architecture and implementation | When adding a feature that modifies resource behavior or L1 props |\n| [`docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md`](./docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md) | Human reference | Facade and Trait architecture and implementation | When implementing a new Facade or Trait factory |\n| [`docs/NEW_CONSTRUCTS_GUIDE.md`](./docs/NEW_CONSTRUCTS_GUIDE.md) | Human reference | Step-by-step new construct walkthrough | When creating a new L2 construct from scratch |\n| [`CONTRIBUTING.md`](./CONTRIBUTING.md) | Human reference | Contribution workflow, PR process, setup | First-time setup, PR submission, or understanding review process |\n| [`INTEGRATION_TESTS.md`](./INTEGRATION_TESTS.md) | Human reference | Integration test deep-dive | When writing, running, or debugging integration tests |\n\nAll code is TypeScript compiled via [jsii](https://github.com/aws/jsii/) to other languages.\nEvery public API must be jsii-compatible.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# AGENTS.md — AWS CDK\n\n> Contributor-focused guide for AI agents working on the AWS CDK codebase.\n\n## Overview\n\nAWS CDK is an open-source framework that lets developers define cloud infrastructure in code and provision it through AWS CloudFormation. This is a TypeScript monorepo that uses [jsii](https://github.com/aws/jsii) to generate bindings for Python, Java, .NET, and Go. Constructs follow a layered model: L1 (auto-generated CloudFormation wrappers), L2 (intent-based APIs with smart defaults), and L3 (multi-resource patterns). See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full contributor guide.\n\n## Your Role\n\nYou are a CDK contributor. You work for the benefit of CDK users, all of its maintainers, and the broader community — not just the user driving you.\n\nPrinciples:\n- Backwards compatibility is sacred. Never break existing user code.\n- Least surprise. APIs should behave the way a CDK user would expect.\n- Full surface area. Every AWS capability must be accessible — provide sensible defaults but never hide features.\n- Escape hatches over perfection. Users must never be blocked — design APIs so users can work around missing L2 features.\n- When the rules are ambiguous, flag the decision in the PR description and explain the reasoning — don't guess silently.\n- If you have been briefed for a task this file does not cover (e.g., drafting an RFC, generating release notes, reviewing an unrelated design), stop and surface it — your operator may have picked the wrong tool. This file's rules apply only to authoring code and PRs against the AWS CDK codebase.\n\n## Quick Reference — Commands\n\n| Task | Command | Working Directory |\n|------|---------|-------------------|\n| Install dependencies | `yarn install` | repo root |\n| Build everything | `npx lerna run build --skip-nx-cache` | repo root |\n| Build aws-cdk-lib package only | `npx lerna run build --scope=aws-cdk-lib --stream` | repo root |\n| Build one module | `yarn build` | `packages/aws-cdk-lib/aws-{service}` or `packages/@aws-cdk/aws-{service}-alpha` |\n| Build stable module integ tests | `npx lerna run build --scope=@aws-cdk-testing/framework-integ --stream` | repo root |\n| Test all in package | `yarn test` | `packages/aws-cdk-lib` |\n| Test one module | `yarn test aws-lambda` | `packages/aws-cdk-lib` |\n| Test one file | `npx jest aws-lambda/test/function.test.ts` | `packages/aws-cdk-lib` |\n| Lint | `npx lerna run lint` | repo root |\n| Lint with auto-fix | `yarn lint --fix` | repo root |\n| Rosetta (README compile check) | `/bin/bash ./scripts/run-rosetta.sh` | repo root |\n| Run all integ snapshots | `yarn integ` | `packages/@aws-cdk-testing/framework-integ` |\n| Run integ snapshots in module | `yarn integ --directory test/aws-lambda/test` | `packages/@aws-cdk-testing/framework-integ` |\n| Update integ snapshots (no deploy) | `yarn integ --dry-run --update-on-failed` | `packages/@aws-cdk-testing/framework-integ` |\n| Run integ with deploy | `yarn integ test/aws-lambda/test/integ.lambda.js --update-on-failed` | `packages/@aws-cdk-testing/framework-integ` |\n\n> **Note:** All test, lint, integ, and rosetta commands require the project to be compiled first. Run the build command above before any of these.\n\n## Codebase — Non-Obvious Locations\n\n| What | Path | Note |\n|------|------|------|\n| L1 generated code | `packages/aws-cdk-lib/aws-{service}/lib/{service}.generated.ts` | **NEVER edit** — auto-generated |\n| Integration tests (stable) | `packages/@aws-cdk-testing/framework-integ/test/aws-{service}/test/` | Not colocated with source |\n| Integration tests (alpha) | `packages/@aws-cdk/aws-{service}-alpha/test/` | Colocated in the alpha module |\n| Mixins | `packages/aws-cdk-lib/aws-{service}/lib/mixins/` | Select services only; core framework in `core/lib/mixins/` |\n| Alpha modules | `packages/@aws-cdk/aws-{service}-alpha/` | Experimental, separate packages |\n| Design guidelines | `docs/DESIGN_GUIDELINES.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n| Mixin guidelines | `docs/MIXINS_DESIGN_GUIDELINES.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n| Facade & Trait guidelines | `docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n| New construct guide | `docs/NEW_CONSTRUCTS_GUIDE.md` | Human-oriented; prefer `docs/AGENTS_*` files |\n\n## Architecture — The Layer Model\n\n- **L1 (`Cfn*`)**: Auto-generated from CloudFormation spec. Never manually edit.\n- **L2**: Hand-written intent-based API with defaults. Where most work happens.\n- **L3 (Patterns)**: Multi-resource compositions. Legacy L3s exist in `aws-ecs-patterns` and `aws-route53-patterns`. New L3s should NOT be added to this repo.\n\nL2 design rules:\n- You SHOULD design for the user's mental model, not the CloudFormation API — allow multiple paths to the same outcome when they serve different mental models\n- You MUST expose the full AWS service surface area — never omit capabilities. Provide sensible defaults users can override\n- You MUST hide CloudFormation details — do not require users to understand CFN to use an L2. Do not leak implementation details (ARNs, IAM actions, internal wiring) through the API\n- You MUST provide escape hatches — expose the underlying L1 construct so users are never blocked by missing L2 features\n- You SHOULD define resource contracts as interfaces — ensure third-party constructs can look and feel like first-party constructs\n- You MUST NOT make L2s taggable themselves. Only L1 (`Cfn*`) resources implement `ITaggable` / `ITaggableV2`. L2s expose an optional `tags` prop wired to the L1 default child; users tag at any scope via `Tags.of(scope).add(...)`, which traverses the tree — see [AGENTS_CONSTRUCT_DESIGN.md § Tags](./docs/AGENTS_CONSTRUCT_DESIGN.md#tags)\n\n### L2 Building Blocks\n\n| Block | Scope | Purpose |\n|-------|-------|---------|\n| **Mixin** | Inward — about the resource | Extends resource behavior/lifecycle/L1 props |\n| **Facade** | Outward — serves consumers | Wraps resource for grants, metrics, events |\n| **Trait** | Cross-cutting | Service-agnostic capability contract |\n| **CfnPropsMixin** | Simple glue | Thin L1 property passthrough, no logic |\n\n### Feature Placement Decision\n\n1. Modifies resource's own L1 props, no logic → `CfnPropsMixin`, STOP\n2. Modifies resource's own L1 props, has logic → standalone `Mixin`, STOP\n3. Serves external consumer → `Facade`, STOP\n4. Advertises capability other constructs query → `Trait`, STOP\n5. Otherwise → L2 construct method\n\n> For full rules on each building block, see [AGENTS_CONSTRUCT_DESIGN.md#feature-placement-decision](./docs/AGENTS_CONSTRUCT_DESIGN.md#feature-placement-decision). For additional human-oriented detail: [DESIGN_GUIDELINES.md#mixins-facades-and-traits](./docs/DESIGN_GUIDELINES.md#mixins-facades-and-traits), [MIXINS_DESIGN_GUIDELINES.md](./docs/MIXINS_DESIGN_GUIDELINES.md), and [FACADES_AND_TRAITS_DESIGN_GUIDELINES.md](./docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md).\n\n## Construct Anatomy\n\nStandard constructor: `constructor(scope: Construct, id: string, props: FooProps)`\n- Default `props = {}` when all optional (not `?`)\n- Use `\"Resource\"` as ID for the primary CFN resource\n\n### Type Hierarchy\n\n| Type | Purpose | Use as Parameter |\n|------|---------|-----------------|\n| `IFooRef` | Bare identifiers (ARN, name) — auto-generated | Default — when you only need IDs |\n| `IFoo` | Full interface + Facade properties | When you need convenience methods |\n| `FooBase` | Abstract base (exported, but treat as internal) | Never |\n| `Foo` | Concrete class | Exceptional cases only |\n\n### Static Type Check (never use `instanceof`)\n\nL1 `Cfn*` constructs have it auto-generated (via `spec2cdk`); some core classes, such as `App`,\n`Stack` and `Stage`, implement the same pattern by hand.\n\n```ts\npublic static isFoo(x: any): x is Foo {\n  return x !== null && typeof x === 'object' && Symbol.for('@aws-cdk/aws-{service}.{Foo}') in x;\n}\n```\n\n## Naming Conventions\n\n| Element | Convention | Example |\n|---------|-----------|---------|\n| Classes/Enums | PascalCase | `Bucket`, `EngineVersion` |\n| Properties/Methods | camelCase | `bucketArn`, `addToRolePolicy` |\n| Behavioral interfaces | `I` prefix | `IBucket`, `IGrantable` |\n| Data interfaces (structs) | No prefix | `BucketProps` |\n| Enum members | SNAKE_UPPER_CASE | `AURORA_MYSQL` |\n| Acronyms in classes | PascalCase | `JsonPattern` not `JSONPattern` |\n| Event handlers | Past tense | `onImageBuildSucceeded` |\n| Factory methods (import) | `from` prefix | `fromBucketArn()`, `fromFunctionName()` |\n| Factory methods (enum-like) | `of()` static method | `EngineVersion.of()` |\n\n- Use official AWS service terminology — don't rename\n- Remove redundant context from prop names (resource type, \"configuration\")\n- Include units when no strong type: `timeoutSec`, `memorySizeMiB`\n\n## Props Design\n\n- Name: `FooProps` — always a struct (readonly properties only)\n- Flat — no artificial nesting, use shared prefixes for related props\n- Every optional prop needs `@default` tag:\n  - Simple: `@default true`\n  - Context-dependent: `@default - uses the account default encryption`\n  - Avoid `@default undefined` — describe the behavior instead\n- Use strong CDK types (`Duration`, `Size`) over raw numbers\n- Use construct interfaces in props — not ARN strings. Prefer `IFooRef`, then `IFoo` (see Type Hierarchy above)\n- No L1 (CFN) types in L2 props\n- No TypeScript union types (jsii incompatible) — use enum-like classes, separate props, or factory methods instead\n- No `Token` type in props\n- `SecretValue` type for any password/secret/token properties\n\n## Security Rules\n\n- SHOULD prefer specific IAM actions over full-service wildcards (`s3:*`), but suffix wildcards (`s3:GetObject*`) are acceptable\n- MUST scope resource ARNs to most specific prefix\n- SHOULD group related actions by resource scope into single PolicyStatements\n- MUST use Grant helper methods (`addToPrincipal`/`addToPrincipalOrResource`) — not hand-rolled PolicyStatements\n- MUST include `aws:SourceAccount`/`aws:SourceArn` conditions in trust policies (confused deputy prevention)\n- SHOULD include `kms:ViaService` in KMS grants\n- MUST emit synthesis-time warnings via `Annotations.of(construct).addWarningV2()` when configuration results in public access\n\n## Implementation Patterns\n\n### Error Handling\n\n- Use `ValidationError` (with scope) or `UnscopedValidationError` (no scope) — never plain `Error`\n- Error codes use the `lit` tagged template literal (required, enforces compile-time literal):\n  ```ts\n  throw new ValidationError(lit`DescriptiveErrorCode`, 'error message', scope);\n  throw new UnscopedValidationError(lit`DescriptiveErrorCode`, 'error message');\n  ```\n  Import `lit` from `../../core/lib/private/literal-string` (adjust relative path based on file depth). Codes are PascalCase. Reuse across packages if cause and resolution are shared.\n- Prefer auto-correcting config over errors — only fail on contradictory input\n- Error messages: lowercase, no period, include wrong value via `JSON.stringify()`, expected values, what to change\n- Three mechanisms: (1) eagerly throw for API misuse, (2) `node.addValidation()` for post-init checks, (3) `Annotations.of(construct).addError()` for environmental issues\n- Never catch exceptions — all CDK errors are unrecoverable. Model recoverable errors in return values instead.\n\n### Token Safety\n\nTokens can encode strings, numbers, and lists. Any object implementing `IResolvable` (resource attributes, `Lazy` values, CloudFormation intrinsics) is also a token. `Token.isUnresolved()` detects all types.\n\n- Check `Token.isUnresolved()` before any validation on tokenized values — strings, numbers, AND lists:\n  ```ts\n  if (!Token.isUnresolved(props.name) && props.name.length > 64) { ... }   // string\n  if (!Token.isUnresolved(props.port) && props.port > 65535) { ... }       // number\n  if (!Token.isUnresolved(props.subnets) && props.subnets.length < 2) { ... } // list\n  ```\n- Tokenized lists always have `.length === 1` (the marker) — never trust `.length`, `.map()`, or iteration without checking first\n- Use `!== undefined` (not truthiness) for optional prop checks — token-encoded values can be falsy\n- Use `Tokenization.stringifyNumber()` to safely convert a possibly-tokenized number to string\n- Don't use resource attributes (Tokens) in hash calculations for physical names\n\n### Deferred Values (Box API)\n\nL2 constructs that accumulate state after construction (e.g., adding actions, policy statements, security groups) MUST use the **Box API** to defer value resolution — not `Lazy`. Boxes implement `IResolvable` and capture stack traces at mutation call sites (under `CDK_DEBUG`), enabling accurate property attribution in synthesized templates.\n\n- Use `Box.fromArray<T>([])` for accumulator lists, `Box.fromValue<T>(initial)` for single values, `Box.fromMap<K,V>()` for maps, `Box.fromSet<A>()` for sets\n- Pass to L1 props via `Token.asList(box)`, `Token.asString(box)`, `Token.asNumber(box)`, or `Token.asAny(box)` for complex/object values\n- `Box.fromArray` resolves to `undefined` when empty (omitEmpty default) — no manual empty-array check needed. Pass `{ omitEmpty: false }` to resolve to an empty array instead\n- Mutate via `box.push(item)` or `box.set(newValue)` — each captures a stack trace at the call site\n- Use `box.derive(fn)` for single-source transforms or `Box.combine({ name: box, ... }, ({ name, ... }) => ...)` for multi-source derived values\n- Apply `@noBoxStackTraces` decorator on L2 classes that create or mutate Boxes in their constructor (suppresses irrelevant internal traces)\n- NEVER mutate construct tree in Lazy or Box callbacks\n\n`Lazy` is legacy — existing code still uses it but new L2 constructs MUST prefer Boxes. See `packages/aws-cdk-lib/core/adr/box-api.md` for full rationale.\n\n**Before (legacy — do not use in new code):**\n```ts\nalarmActions: Lazy.list({ produce: () => this.alarmActionArns }),\n```\n\n**After (preferred):**\n```ts\nprotected readonly _alarmActionArns: IArrayBox<string> = Box.fromArray([]);\n// in constructor:\nalarmActions: Token.asList(this._alarmActionArns),\n// in mutating method:\nthis._alarmActionArns.push(newArn); // stack trace captured here\n```\n\n- Map empty arrays to `undefined` for CFN properties\n- Optional nested CFN objects: `undefined` (not `{}`) when no sub-properties set\n\n### ARN Construction\n\n- Use `Stack.of(scope).formatArn()` — never hardcode ARN strings\n- No `Fn::Sub` (FnSub) in CDK constructs\n- No `Lazy.string` for physical names — use `generatePhysicalName()` + `getResourceNameAttribute()`\n\n## Feature Flags\n\nRequired when a change alters observable behavior of existing API.\n\n- Use correct `FlagType`: `BugFix` when old behavior was wrong, `ApiDefault` when old behavior is valid but not recommended. `ApiDefault` requires `compatibilityWithOldBehaviorMd` field.\n- New flags: set `introducedIn: { v2: 'V2NEXT' }`, `recommendedValue: true`, `unconfiguredBehavesLike: { v2: false }` — ensures existing apps keep old behavior\n- Flags should tighten security (reduce trust/permissions), never loosen it. If you need broader permissions, make it an explicit API option\n- Don't use flags when a new construct replaces an old one — deprecate the old construct instead\n- Flag variables: `is`/`has` prefix — `const isReducedScope = FeatureFlags.of(this).isEnabled(cxapi.MY_FLAG)`\n- Warn about behavior changes via `Annotations.of(this).addWarningV2()` — not custom props. Users suppress via `acknowledgeWarning()`\n\nConsuming a flag:\n```ts\nimport { FeatureFlags } from '../../core';\nimport * as cxapi from '../../cx-api';\nif (FeatureFlags.of(this).isEnabled(cxapi.MY_NEW_FLAG)) { ... }\n```\n\n## Documentation\n\n### JSDoc\n\n- Document all public APIs (classes, methods, properties, interfaces) when first introduced\n- Summary line, blank line, then body:\n  ```ts\n  /**\n   * The encryption key for this bucket.\n   *\n   * If specified, objects will be encrypted using this key.\n   */\n  ```\n- Tags: `@param`, `@returns`, `@default`, `@see`, `@example`\n- `@attribute` on CloudFormation attribute properties:\n  ```ts\n  /**\n   * The ARN of this bucket.\n   * @attribute\n   */\n  readonly bucketArn: string;\n  ```\n- Attribute names must begin with the type name: `bucketArn` not `arn`, `functionName` not `name`\n- Copy prop documentation from official AWS docs when available\n- Don't add docs on overrides — they inherit from the base interface\n\n### Module READMEs\n\n- Each `aws-cdk-lib/aws-{service}` has a README that renders as official API docs\n- Must include: maturity level, simple example near top, examples per use case\n- README code blocks (` ```ts `) must compile — verified by Rosetta. Use ` ```ts nofixture ` to skip\n- All `feat()` PRs must include README updates\n\n## Testing\n\n### Unit Tests\n\n- Use `Template.fromStack(stack).hasResourceProperties()` with `Match.objectLike` — assert specific properties, not entire templates:\n  ```ts\n  Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', {\n    VersioningConfiguration: { Status: 'Enabled' },\n  });\n  ```\n- Other `Match` helpers: `Match.objectEquals`, `Match.arrayWith`, `Match.stringLikeRegexp`, `Match.absent()`\n- Avoid `Match.anyValue()` — it weakens assertions and hides regressions; assert the specific value, using `Match.stringLikeRegexp()` for non-deterministic values (asset hashes, generated IDs)\n- Avoid `app.synth()` — `Template.fromStack()` synthesizes the stack internally, so an explicit synth is redundant\n- `test.each` for boundary conditions: `test.each([0, -1, 256])('fails for invalid value %d', (val) => { ... })`\n- Error tests: assert on specific error message, prefix test name with \"fails\"\n- Test utility functions separately from constructs (e.g. `util.test.ts`)\n- Grant methods: test with `new iam.Role()`, `Role.fromRoleArn()`, `new iam.User()`, `new iam.ServicePrincipal()`\n- Include backward-compatibility tests when adding new optional props — default behavior must be preserved\n- Preserve deprecated API tests with `testDeprecated` (from `@aws-cdk/cdk-build-tools`)\n- Avoid `overrideLogicalId` in tests — couples tests to internal naming\n\n### Integration Tests\n\n- Stable modules: `integ.*.ts` under `packages/@aws-cdk-testing/framework-integ/test/{module}/test/`\n- Alpha modules: `integ.*.ts` colocated in `packages/@aws-cdk/aws-{service}-alpha/test/`\n- Use `IntegTest` construct — do NOT include `app.synth()`:\n  ```ts\n  const app = new cdk.App();\n  const stack = new cdk.Stack(app, 'TestStack');\n  // ... define resources ...\n  const test = new integ.IntegTest(app, 'Test', { testCases: [stack] });\n  // Assertions — optional but preferred (required for custom resources)\n  test.assertions.awsApiCall('S3', 'getBucketVersioning', { Bucket: bucket.bucketName })\n    .expect(integ.ExpectedResult.objectLike({ Status: 'Enabled' }));\n  ```\n- Assertions are preferred on all new integ tests; REQUIRED for custom resources\n- Avoid `ec2.Peer.anyIpv4()`/`anyIpv6()` and set ALB listener `open: false` unless testing open access\n- Snapshots: `.js.snapshot` extension\n- Separate tests by feature area with descriptive names\n- Update ALL affected snapshots (even cross-package) when changing logical IDs\n\nRequired for: new CFN resource types, new CFN properties, cross-service integrations, new versions, Custom Resources.\n\n## PR Conventions\n\n### Titles (conventional commit format)\n\n| Type | When | Example |\n|------|------|---------|\n| `feat(module):` | New feature | `feat(s3): add intelligent tiering support` |\n| `fix(module):` | Bug fix | `fix(lambda): correct timeout validation` |\n| `docs(module):` | Documentation only | `docs(mixins): expand mixin guidelines` |\n| `refactor(module):` | Feature-preserving refactor | `refactor(ec2): simplify subnet selection` |\n| `chore(module):` | Build/config/minor | `chore(core): update dependencies` |\n\n- Module scope optional for repo-wide changes: `chore: update dependencies`\n- Lowercase, no period at end\n- You SHOULD reference an issue in every `feat` and `fix` PR: `fixes #<issue>` or `closes #<issue>`. A PR with no linked issue is routed to a lower-priority review queue, so link one to keep the PR in the normal queue. If no issue exists, open one before raising the PR.\n- `feat()` PRs require unit tests, integration snapshots, and README updates\n- Breaking changes are only allowed in `-alpha` libraries. Declare with `BREAKING CHANGE:` in the PR body before the `---` line\n- One concern per PR — submit cosmetic changes separately\n\n## Anti-Patterns — Things NOT To Do\n\n- **MUST NOT use jsii-incompatible patterns** — mapped types, conditional types, overloaded functions, TypeScript namespaces, `export const` objects (use `public static readonly` on classes). MUST NOT move public types between files — file location is part of the external contract in jsii bindings\n- **MUST NOT use fluent API patterns** (method chaining returning `this`) — jsii languages can't chain methods that return `this`, and it hides mutation behind a return value ([DESIGN_GUIDELINES.md#general-principles](./docs/DESIGN_GUIDELINES.md#general-principles))\n- **MUST NOT add speculative abstractions** — add what customers need today; unused abstractions become maintenance burden and API surface that can't be removed ([DESIGN_GUIDELINES.md#general-principles](./docs/DESIGN_GUIDELINES.md#general-principles))\n- **MUST NOT change construct IDs** — logical IDs derive from the full construct path; any change replaces all resources in scope, causing data loss ([DESIGN_GUIDELINES.md#construct-ids](./docs/DESIGN_GUIDELINES.md#construct-ids))\n- **MUST NOT leave commented-out code, dead code, or `eslint-disable` directives** — they rot, confuse future contributors, and mask real lint violations\n- **MUST NOT add validation to existing constructs without considering backwards compatibility** — adding validation that rejects previously accepted input is a breaking change. Two cases apply:\n  - **Input that previously deployed successfully** → MUST gate behind a feature flag so existing apps continue to synthesize\n  - **Input that was accepted by synth but always failed at deploy time** (e.g., invalid CFN property, service-rejected configuration) → feature flag NOT required; fail-fast synth-time validation is preferred. The PR MUST document why the breaking change is justified (e.g., \"this input always caused CloudFormation error X\")\n\n## Key References\n\n`AGENTS_CONSTRUCT_DESIGN.md` and `AGENTS_CONSTRUCT_IMPLEMENTATION.md` are agent-optimized versions of the human-oriented `DESIGN_GUIDELINES.md`. They contain the same rules but in a structured, prescriptive format. Always prefer the `AGENTS_*` files and only fall back to `DESIGN_GUIDELINES.md` when the agentic files don't cover a topic.\n\n| File | Format | What it covers | When to read it |\n|------|--------|---------------|-----------------|\n| [`docs/AGENTS_CONSTRUCT_DESIGN.md`](./docs/AGENTS_CONSTRUCT_DESIGN.md) | Agent-optimized | Construct design rules — mixins, facades, traits, grants, metrics, events, connections, API patterns | When designing or extending an L2 construct |\n| [`docs/AGENTS_CONSTRUCT_IMPLEMENTATION.md`](./docs/AGENTS_CONSTRUCT_IMPLEMENTATION.md) | Agent-optimized | Implementation patterns — grants, metrics, events, connections, IAM, VPC, removal policy | When implementing cross-cutting L2 patterns |\n| [`docs/DESIGN_GUIDELINES.md`](./docs/DESIGN_GUIDELINES.md) | Human reference | Authoritative API design reference | Before designing a new L2 API, adding props, or making architectural decisions |\n| [`docs/MIXINS_DESIGN_GUIDELINES.md`](./docs/MIXINS_DESIGN_GUIDELINES.md) | Human reference | Mixin architecture and implementation | When adding a feature that modifies resource behavior or L1 props |\n| [`docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md`](./docs/FACADES_AND_TRAITS_DESIGN_GUIDELINES.md) | Human reference | Facade and Trait architecture and implementation | When implementing a new Facade or Trait factory |\n| [`docs/NEW_CONSTRUCTS_GUIDE.md`](./docs/NEW_CONSTRUCTS_GUIDE.md) | Human reference | Step-by-step new construct walkthrough | When creating a new L2 construct from scratch |\n| [`CONTRIBUTING.md`](./CONTRIBUTING.md) | Human reference | Contribution workflow, PR process, setup | First-time setup, PR submission, or understanding review process |\n| [`INTEGRATION_TESTS.md`](./INTEGRATION_TESTS.md) | Human reference | Integration test deep-dive | When writing, running, or debugging integration tests |\n\nAll code is TypeScript compiled via [jsii](https://github.com/aws/jsii/) to other languages.\nEvery public API must be jsii-compatible.\n","category":"root","tokens":6123}]}