{"owner":"floci-io","repo":"floci","hasSkills":true,"totalSkillsCount":3,"totalTokensCount":13836,"categories":["root-instruction","copilot-instructions","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md",".github/copilot-instructions.md","tools/docs/services.yaml"],"skills":{"AGENTS.md":"Guidance for AI coding agents working in the Floci repository.\n\nThis file defines repository-specific operating rules for autonomous or semi-autonomous coding agents. Follow these instructions unless a maintainer explicitly tells you otherwise.\n\n---\n\n## Project Overview\n\nFloci is a Java-based local AWS emulator built on Quarkus.\n\nIts goal is full AWS SDK and AWS CLI compatibility through real AWS wire protocols, not convenience APIs or simplified abstractions.\n\nFloci acts as an open-source alternative to LocalStack Community.\n\n- Port: 4566\n- Stack:\n  - Java 25\n  - Quarkus 3.40.1\n  - JUnit 5\n  - RestAssured\n  - Jackson\n  - Docker integrations for Lambda, RDS, and ElastiCache\n\n---\n\n## First Principles\n\nWhen making changes, follow these priorities:\n\n1. Preserve AWS protocol compatibility\n2. Match AWS SDK and CLI behavior\n3. Reuse existing Floci patterns\n4. Prefer correctness over convenience\n5. Keep changes narrow and testable\n\nCritical rules:\n\n- Do not introduce custom endpoint shapes\n- Do not change request or response formats for convenience\n- Do not perform broad refactors unless the task explicitly requires them\n- Keep behavior aligned with AWS expectations and existing Floci conventions\n\n---\n\n## Architecture\n\nFloci follows a layered design:\n\n- **Controller / Handler**\n  - Parses AWS protocol input\n  - Produces AWS-compatible responses\n\n- **Service**\n  - Contains business logic\n  - Throws `AwsException`\n\n- **Model**\n  - Domain objects\n\n### Core Infrastructure\n\n- `EmulatorConfig`\n- `ServiceRegistry`\n- `StorageBackend` + `StorageFactory`\n- `AwsJson11Controller`\n- `AwsQueryController`\n- `AwsException` + `AwsExceptionMapper`\n- `EmulatorLifecycle`\n\n---\n\n## Package Layout\n\n- `io.github.hectorvent.floci.config`\n- `io.github.hectorvent.floci.core.common`\n- `io.github.hectorvent.floci.core.storage`\n- `io.github.hectorvent.floci.lifecycle`\n- `io.github.hectorvent.floci.services.<service>`\n\nTypical service structure:\n\n- `services/<svc>/`\n  - `*Controller.java` (REST JSON / REST XML services, via JAX-RS)\n  - `*QueryHandler.java` (AWS Query/XML protocol services)\n  - `*JsonHandler.java` (AWS JSON 1.1 protocol services)\n  - `*Service.java`\n  - `model/`\n\nThe entry-point class name depends on the service's AWS protocol (see \"AWS Protocol\nRules\" below); `Controller` is not universal.\n\nRule:\nCopy an existing service pattern before introducing a new one.\n\n---\n\n## AWS Protocol Rules\n\nFloci must implement real AWS wire protocols.\n\n| Protocol | Services | Request Format | Response Format | Implementation |\n|----------|----------|----------------|-----------------|----------------|\n| Query | SQS, SNS, IAM, STS, RDS, ElastiCache, CloudFormation, CloudWatch Metrics | form-encoded POST + `Action` | XML | `AwsQueryController` |\n| JSON 1.1 | SSM, EventBridge, CloudWatch Logs, Kinesis, KMS, Cognito, Secrets Manager, ACM | POST + `X-Amz-Target` | JSON | `AwsJson11Controller` |\n| REST JSON | Lambda, API Gateway, SES V2 | REST paths | JSON | JAX-RS |\n| REST XML | S3 | REST paths | XML | JAX-RS |\n| TCP | ElastiCache, RDS | raw protocol | native | proxies |\n\n### Important exceptions\n\n- CloudWatch Metrics supports both Query and JSON 1.1; handlers must remain aligned\n- SQS and SNS may expose multiple compatibility paths; do not let them drift\n- Cognito well-known endpoints are OIDC REST JSON endpoints, not AWS management APIs\n- Data-plane protocols may use raw TCP sockets\n- Management APIs should be validated with AWS SDK clients, not only handcrafted HTTP requests\n\n---\n\n### Partition literals\n\nFloci serves every AWS partition, not only the commercial one, so a literal that bakes\n`aws` into a code path is a bug in a GovCloud, China, ISO or EUSC deployment even when it\nprints correctly in `us-east-1`. `make partition-check` (CI: Partition Literals) inventories\n`src/main/java` for `arn:aws:` prefixes, `amazonaws.com` hosts, Route 53 hosted-zone ids,\nhand-rolled `arn:aws[a-z-]*:` regexes, `Arn.of(...)` calls with a blank region and comparisons\nwith the literal `\"aws\"` against `tools/partition/baseline.tsv`, per file. The baseline is empty, so any new one fails.\nA literal in a new file fails, growth in an existing file fails, and a drop fails until you\nrun `make partition-baseline` and commit the smaller baseline.\n\n- Mint ARNs through `AwsArnUtils.Arn.of(...)` / `RegionResolver.buildArn(...)`, recognise\n  them with `AwsArnUtils.isArn` / `PARTITION_REGEX`, and derive hosts from the region's\n  DNS suffix (`AwsRegions.dnsSuffixFor`).\n- A regionless ARN (IAM, STS, CloudFront, Organizations, an S3 bucket) never goes through\n  `Arn.of(service, \"\", ...)`, which silently means `aws`: use `RegionResolver.buildGlobalArn`\n  for the request's partition, or `Arn.global(partition, ...)` with the resource's own\n  (`AwsRegions.partitionFor(region)` for a bucket or a stack's region).\n- Never compare a partition with a literal: `\"aws\".equals(arn.partition())` is commercial-only\n  logic that fails silently in every other partition. Compare with the partition the resource\n  belongs to, `AwsRegions.partitionFor(region)` for a regional resource or the stored ARN's own\n  partition for a global one, and accept any published partition with `AwsPartitions.find(...)`\n  when only the shape matters. `\"aws\"` as an account (managed policies) or a command name is not\n  a partition: end that line with `// partition-literal: <reason>`.\n- A service principal is `ServicePrincipals.of(\"<service>\")`, never a literal: it is\n  `<service>.amazonaws.com` in every partition, and `ServicePrincipals.canonical` folds the\n  legacy per-partition forms a policy may still use before matching.\n- A literal that is genuinely partition-invariant (an XML namespace URI, an S3 canned-ACL\n  grantee URI) goes in `tools/partition/allowlist.yaml` with a reason, or ends its line\n  with `// partition-literal: <reason>`. Both are printed by `make partition-audit`.\n- Tests are not scanned; only assert a partition-dependent value when the test is about\n  partitions.\n\n## XML / JSON Rules\n\n- Use `XmlBuilder` for XML responses\n- Use `XmlParser` for XML parsing; do not use regex\n- Use `AwsNamespaces` constants\n- JSON errors must follow AWS error structures\n- Types returned directly from controllers must remain compatible with native-image reflection requirements\n\n---\n\n## Storage Rules\n\nSupported storage modes:\n\n- `memory`\n- `persistent`\n- `hybrid`\n- `wal`\n\nRules:\n\n- Always use `StorageFactory`\n- Do not instantiate storage implementations directly inside services\n- Respect lifecycle hooks for load and flush behavior\n\nImportant nuance:\n\nConfiguration interfaces may declare fallback defaults, but `application.yml` defines effective runtime behavior. Treat repository YAML as the source of truth unless a task explicitly changes configuration semantics.\n\nWhen adding storage-related behavior:\n\n1. Update `EmulatorConfig`\n2. Update main `application.yml`\n3. Update test `application.yml`\n4. Wire through `StorageFactory`\n5. Verify lifecycle integration\n\n---\n\n## Configuration Rules\n\nConfiguration lives under `floci.*`.\n\nWhen adding config:\n\n1. Add it to `EmulatorConfig`\n2. Add it to main `application.yml`\n3. Add it to test `application.yml` if needed\n4. Update documentation if user-facing\n5. Follow `FLOCI_*` environment variable conventions\n\nCritical areas:\n\n- `base-url`\n- `hostname`\n- region and account defaults\n- port ranges\n- persistence paths\n- Docker networking\n\n---\n\n## Build & Run\n\n    ./mvnw quarkus:dev\n    ./mvnw test\n    ./mvnw clean package\n    ./mvnw clean package -DskipTests\n\n`make help` lists the Makefile shortcuts. The native binary and image: `make native`\nthen `make native-image` (CI's flags, staged in `native/<arch>/`), `make native-host` for a\nbinary built on this machine with the installed GraalVM, `make native-up` to run the image,\n`make compat SUITES=\"sdk-test-java compat-cdk\"` for compatibility suites against it.\n\n### Focused tests\n\n    ./mvnw test -Dtest=SsmIntegrationTest\n    ./mvnw test -Dtest=SsmIntegrationTest#putParameter\n\n---\n\n## Compatibility Project\n\nCompatibility test suite: `./compatibility-tests/`\n\nGuidelines:\n\n- Prefer AWS SDK clients over raw HTTP for management-plane validation\n- Use this suite when changes may affect real SDK behavior\n\n---\n\n## Testing Rules\n\n### Conventions\n\n- Unit tests: `*ServiceTest.java`\n- Integration tests: `*IntegrationTest.java`\n- Prefer package-private constructors for testability\n- Integration tests may use ordered execution when stateful behavior requires it\n\n### Expectations\n\n- Test any behavior affecting AWS compatibility\n- Do not rely only on manual HTTP testing\n- Prefer SDK-based validation where possible\n\n### When touching protocol behavior\n\nIf a change affects request parsing, response shape, error handling, persistence semantics, URL generation, or service enablement:\n\n1. Add or update automated tests\n2. Prefer SDK-based verification where possible\n3. Check compatibility across alternate protocol paths\n4. Document intentional deviations clearly\n\n---\n\n## Error Handling\n\n- Services should throw `AwsException`\n- Query and REST XML flows should use `AwsExceptionMapper`\n- JSON 1.1 flows should return structured AWS error responses where required\n- Controller return types must remain reflection-safe\n\n---\n\n## Service Implementation Pattern\n\nWhen adding functionality:\n\n1. Identify the AWS protocol\n2. Reuse an existing service pattern\n3. Keep controllers thin\n4. Use `AwsException` for domain errors\n5. Reuse shared utilities\n6. Update config, storage, docs, and tests together\n7. Validate behavior against AWS SDK expectations\n\n---\n\n## Adding a New AWS Service\n\n1. Create a package under `services/<svc>/` with a Controller or protocol-specific handler,\n   a Service, and `model/`\n2. Add a `<Svc>ServiceConfig` interface and its accessor on `ServicesConfig` in `EmulatorConfig`\n3. Add one `descriptor(...)` entry in `ResolvedServiceCatalog`. This is the registration point;\n   `ServiceRegistry` only reads the catalog and has no registration API\n4. Add `floci.services.<key>.enabled` to both `src/main/resources/application.yml` and\n   `src/test/resources/application.yml`\n5. JSON 1.1 only: inject the `<Svc>JsonHandler` in `AwsJson11Controller`\n6. Obtain storage through `StorageFactory` and implement `Resettable`\n7. List any static `Random` or `SecureRandom` field under `--initialize-at-run-time` in\n   `application.yml`\n8. Check every timestamp member you emit for a `TimestampFormatTrait` before using the\n   epoch-seconds idiom. It is the awsJson1.1 default, but a model can override it per\n   member, and the mismatch is invisible to the AWS CLI because botocore coerces the\n   value, while strict SDKs (Go, Java) reject the whole response. `javap -c` on the SDK\n   model class shows the traits on each `SdkField`\n9. Add `<Svc>ServiceTest` and `<Svc>IntegrationTest`\n10. If the service mints ARNs or AWS hosts (`buildArn`, `Arn.of`, `new AwsArnUtils.Arn`, `dnsSuffixFor`, `AwsEndpoints`),\n   add a case to `PartitionCrossServiceSmokeIntegrationTest` and its `COVERED_PACKAGES`, or a row\n   with a reason to `src/test/resources/partition/smoke-exemptions.tsv`;\n   `PartitionSmokeInventoryTest` fails until one of the two exists\n11. Document it: `docs/services/<svc>.md`, a `mkdocs.yml` nav entry, a Service Matrix row in\n   `docs/services/index.md`, and a row in the README category table\n12. Register the handler in `tools/docs/services.yaml`, then run `make docs-sync` and\n    `make docs-check`\n13. Add a `TestFixtures` client factory and a `<Svc>Test` in `compatibility-tests/sdk-test-java`\n\n---\n\n## Adding a CloudFormation Resource Type\n\n**Every type is served by a per-service provisioner under\n`services/cloudformation/provisioners/`.** `CfnResourceDispatcher` only routes a resource to\nthe registry and stubs what nothing serves; never add type-specific code to it.\n\n1. Add the type to the existing `<Service>CfnProvisioner`, or create one:\n   `@ApplicationScoped`, injecting only the service it wraps. CDI discovery via\n   `CloudFormationResourceRegistry` handles registration: no manual wiring, but a\n   missing `@ApplicationScoped` silently means the type is never provisioned.\n2. `resourceTypes()` lists the `AWS::*` types; `provision(resource, props, ctx)`\n   does the work, switching on `resource.getResourceType()` when it serves several.\n3. Set **both** reference mechanisms. They are separate:\n   - `resource.setPhysicalId(...)` backs `Ref`\n   - `resource.getAttributes().put(...)` backs `Fn::GetAtt`, one entry per attribute\n   Omitting an attribute does not fail; `Fn::GetAtt` resolves to the literal\n   `\"LogicalId.Attr\"`. Source the attribute names from the type's registry schema in\n   `local/aws/cfn-resource-schemas/us-east-1/` (`readOnlyProperties`), and validate\n   `required` from the same file.\n4. **`provision` serves create *and* update.** On `UpdateStack` it is re-invoked with\n   the prior physical id and attributes already populated on the resource. Branch with\n   `ctx.isUpdate()` / `ctx.priorPhysicalId()`, not by reading the id off the resource:\n   `provision` assigns the new id as it runs, so a resource-derived check flips\n   mid-method.\n5. Override `delete(...)` when the type has a backing delete; tolerate already-deleted\n   via `CfnDeletes.safeDelete`, passing the specific \"already gone\" error codes. Never\n   a catch-all: a real failure such as `BucketNotEmpty` must propagate so the stack\n   reports `DELETE_FAILED`. When the delete needs a create-time attribute rather than\n   just the physical id, override `delete(StackResource, String)`.\n6. **Register in `src/test/resources/cloudformation/supported-resource-types.tsv`**\n   (`type<TAB>Owner`). `CfnResourceInventoryTest` diffs that file against the\n   CDI-resolved registry, so it also catches a missing `@ApplicationScoped`.\n7. **Add the provisioner to `CfnProvisionerFixture.inferredProvisioners()`** when it\n   takes a single service, or a fixture test naming that service silently falls through\n   to the stub arm.\n8. Tests: focused unit test mocking one service (`SqsCfnProvisioner`'s test is the\n   pattern) plus an integration test asserting the **exact `Fn::GetAtt` keys**. An\n   unmapped type is stubbed as `CREATE_COMPLETE` with a fake ARN, so asserting status\n   alone cannot detect a type that was never wired. Note the engine's constructor is\n   package-private, so tests in `provisioners/` mock it.\n9. Run `make docs-sync` and commit the result. The resource-type table in\n   `docs/services/cloudformation.md` is **generated** from the step-6 inventory; hand\n   edits fail `docs-check`. Labels, ordering and notes live in\n   `tools/docs/cfn_resource_types.yaml`.\n10. A schema `readOnlyProperties` entry you cannot set goes in\n    `src/test/resources/cloudformation/getatt-attribute-gaps.tsv` with a reason;\n    `CfnSchemaCoverageTest` requires every unset attribute to be fixed or recorded.\n\nReferences: `SqsCfnProvisioner` (smallest), `Ec2LaunchTemplateCfnProvisioner`\n(update-in-place and replacement), `LogsCfnProvisioner` (reconcile-vs-replace update).\n\n---\n\n## Sidecars\n\nA dependency too heavy for the native image (a native runtime, a large engine, another language\necosystem) ships as a sidecar: a stateless HTTP service in its own container that Floci starts\nlazily over the Docker socket. Sidecars live in\n[floci-io/floci-sidecars](https://github.com/floci-io/floci-sidecars), one directory per sidecar\non the shared `sidecar-core`, and are published as `floci/floci-sidecar-<name>:<semver>` on\nDocker Hub. They implement that repository's `docs/contract.md` and carry no AWS vocabulary: the\nsidecar answers a generic question, Floci maps its service semantics onto the answer.\n\nFloci-side rules:\n\n- Never add a `sidecars/` directory to this repository, never publish a `floci/floci:<tag>-<name>`\n  suffix, and never default an image to `:latest` (`ImageCacheService` never re-pulls a cached tag).\n- The consuming service owns two config knobs, `<name>-image` (an exact version) and `<name>-url`\n  (skip container management), and a `<Name>SidecarManager` plus `<Name>SidecarClient` pair.\n  `CedarSidecarManager` is the reference: it reads the contract JSON from `/health` and fails fast\n  on a contract-major mismatch.\n- The sidecar releases before the Floci PR that needs it, as a final or an `X.Y.Z-rc.N` tag; the\n  Floci PR pins that tag. A PR that needs an unreleased sidecar cannot pass CI.\n- Tests: the client's wire contract is covered by a JDK `HttpServer` fake; one Docker-gated\n  `@QuarkusTest` runs the pinned image through the real manager under its own\n  `floci.docker.resource-namespace`. A Floci test imports only Floci classes; the sidecar's own\n  behaviour is tested in its repository.\n- A sidecar that is a stock upstream image plus a script from the classpath needs no repository\n  but still goes through a manager with the same two knobs.\n\n## Code Style\n\n### Checkstyle\n\n- Java code must pass `./mvnw checkstyle:check` (CI: Checkstyle). The rules live in\n  `tools/checkstyle/checkstyle.xml`. Code excluded in `tools/checkstyle/suppressions.xml`\n  is not checked yet, but the rules below still apply to it.\n\n### General\n\n- Use constructor injection\n- Prefer self-explanatory code over comments\n- Avoid unnecessary comments\n- Always use braces in conditionals\n- Never leave a `catch` block empty. If an exception is intentionally tolerated, log it with enough context to diagnose it later.\n  When swallowing really is correct and logging would be noise, name the variable\n  `ignored` or `expected` and say in a comment why it is safe. A bare\n  `catch (Exception e) {}` is never acceptable.\n- Follow existing project patterns\n- Use modern Java features only when they improve clarity\n\n### Types and names\n\n- **Do not use `var`. Write the explicit type.** Floci reproduces AWS wire\n  contracts, so the concrete type at a call site is usually the thing under\n  review: whether a value is a `LinkedHashMap` or a `Map`, an AWS model type or a\n  JDK one, is exactly what a reviewer needs to see. This covers local\n  declarations, enhanced-for (`for (Tag tag : tags)`), classic for-init, and\n  try-with-resources. The one exception is a record deconstruction pattern\n  (`case Node(var left, var right) ->`), where naming the component types is pure\n  noise.\n- **Import the classes you use. Do not write fully-qualified names inline.**\n  `new ArrayList<>()`, never `new java.util.ArrayList<>()`. Qualify inline only for\n  a genuine name clash inside one file: import the type used more often, qualify the\n  other, and leave a short comment naming the clash. Checkstyle (`NoFqcn`) enforces\n  this except in the files listed in `tools/checkstyle/suppressions.xml`.\n\n### Imports\n\n- No wildcard imports in `src/main`. Static wildcards stay fine in tests, where\n  `Assertions.*`, `Mockito.*` and `Matchers.*` are the established idiom.\n- Import order: non-`java`/`javax` imports, then `javax.*`, then `java.*`, then\n  static imports. Alphabetize each group. Separate the non-JDK, JDK, and static\n  sections with a blank line; no blank line is needed between `javax` and `java`.\n  This follows IntelliJ's default layout and is enforced by Checkstyle.\n\n### Conventions the codebase already follows\n\nWritten down so they stay true. New code should match them without thinking.\nA handful of files predate them; a violation you find in the tree is a straggler,\nnot a precedent.\n\n- 4-space indentation, K&R braces. Never indent with a tab.\n- JBoss Logging, in a field named `LOG`, using the parameterized `...v()` form.\n  No string concatenation in log calls.\n- No `printStackTrace`, anywhere. No `System.out` or `System.err` in `src/main`; the\n  one exception is the CLI entry point `io.github.hectorvent.floci.tools.ami.AmiImageTool`,\n  where stdout is the program's output. A few tests print a failure repro just before\n  failing, which is the only good reason to print from a test: an assertion message\n  usually says it better.\n- `java.time` for everything Floci owns. `Calendar` and `SimpleDateFormat` appear\n  nowhere and must not be introduced. A `Date` survives only at a third-party boundary\n  that forces one: the BouncyCastle certificate builder, the JAX-RS\n  `HttpHeaders.getDate()` override, and JDBC's `java.sql.Date` in the RDS Data mapper.\n  Convert at that boundary with `Date.from(instant)` and keep `java.time` on Floci's\n  side of it.\n- Constructor injection in `src/main`. Field injection is fine in tests, and\n  `Instance<T>` field injection is a legitimate CDI pattern.\n- `Optional` as a return type, and never as a field: there are none, keep it that way.\n  It reaches a parameter only where a Quarkus `@ConfigProperty Optional<T>` is threaded\n  through; do not introduce it as a parameter for anything else.\n- Switch expressions over switch statements. Pattern-matching `instanceof` over\n  cast-after-check.\n- `AwsException` for domain errors.\n- `final` on service fields, but not on locals or parameters.\n\n### Tests\n\nThese describe `src/test`. `compatibility-tests` is a separate module with the opposite\nidiom, AssertJ and `@DisplayName` in nearly every file. Follow the module you are in.\n\n- Name test methods either as a camelCase sentence (`putAndGetFromMemory`) or as\n  `method_scenario_expectation`. Both are established. `testX` names are common in\n  older tests and are not the pattern to copy.\n- JUnit 5 assertions with Hamcrest and RestAssured matchers. AssertJ is a declared test\n  dependency, used by the Lambda launcher tests; prefer the established matchers\n  everywhere else.\n- `@DisplayName` is not used here. The method name carries the intent.\n\n---\n\n## Documentation Style\n\n- No em-dashes anywhere, in any content. Use colons, commas, or periods.\n\n## Logging\n\n- Use JBoss Logging\n- Keep logs structured\n- Avoid noisy logs in hot paths\n\n---\n\n## Pull Request Guidelines\n\n- Keep changes focused\n- Avoid unrelated refactors\n- Preserve behavior unless the task explicitly requires change\n- Update docs when necessary\n- Explain missing tests when behavior changed but no automated coverage was added\n\nConventional commits:\n\n- `feat:`\n- `fix:`\n- `perf:`\n- `docs:`\n- `chore:`\n\nDo not add `Co-Authored-By` trailers for AI tools in commit messages. Keep attribution limited to human contributors.\n\n---\n\n## Release Awareness\n\n- Changes merged into `main` do not automatically imply a stable release\n- Releases are cut from `main` via the \"Release Cut\" workflow (`workflow_dispatch`\n  on `.github/workflows/release-cut.yml`), which runs semantic-release: it bumps\n  `pom.xml`, writes `CHANGELOG.md`, commits, tags, and creates the GitHub Release\n- `release/x.y.x` branches are retired for now\n- Tags still trigger the publishing workflows (`release.yml`)\n\nTreat release workflows as critical infrastructure.\n\n---\n\n## Agent Workflow\n\n### Before editing\n\n1. Identify service and protocol\n2. Locate an existing implementation to mirror\n3. Check config impact\n4. Check storage impact\n5. Check documentation impact\n6. Define the minimal useful test plan\n\n### Before finishing\n\n1. Run relevant tests\n2. Validate protocol behavior\n3. Ensure no custom endpoints were introduced\n4. Verify config and docs updates\n\n---\n\n## Common Mistakes\n\n- Creating non-AWS endpoints\n- Bypassing `StorageFactory`\n- Changing wire formats without tests\n- Forgetting YAML updates\n- Producing inconsistent URLs or ARNs\n- Testing only with raw HTTP\n- Introducing unnecessary new patterns\n- Adding type-specific code to `CfnResourceDispatcher` instead of a per-service\n  provisioner\n- Setting a CloudFormation resource's physical id but not its `Fn::GetAtt`\n  attributes (they are two separate mechanisms, and the miss is silent)\n- Hand-editing the resource-type table in `docs/services/cloudformation.md`, which is\n  generated, run `make docs-sync` instead\n- Adding a CloudFormation provisioner without a row in\n  `supported-resource-types.tsv` or an entry in `CfnProvisionerFixture`, either of\n  which leaves a type quietly served by the stub arm\n\n---\n\n## Human Handoff\n\nIf behavior is unclear:\n\n1. Prefer AWS behavior\n2. Then existing Floci behavior\n3. Then compatibility test expectations\n\nIf a task would require broad architectural changes, stop and surface the tradeoffs instead of refactoring across services blindly.\n",".github/copilot-instructions.md":"# Copilot Instructions for Pull Request Review\n\nReview pull requests in the Floci repository with AWS compatibility as the primary concern.\n\nFloci is a Java-based local AWS emulator built on Quarkus. Its goal is to match AWS SDK and AWS CLI behavior through real AWS wire protocols, not convenience APIs or custom abstractions.\n\n## Review Priorities\n\nEvaluate changes in this order:\n\n1. Preserve AWS protocol compatibility\n2. Match AWS SDK and AWS CLI behavior\n3. Reuse existing Floci patterns\n4. Prefer correctness over convenience\n5. Keep changes focused and testable\n\n## What to Flag\n\nRaise concerns when a PR introduces any of the following without strong justification:\n\n- Non-AWS endpoint shapes\n- Request or response format changes made for convenience\n- Broad refactors unrelated to the PR goal\n- New service patterns where an existing Floci pattern should be reused\n- Direct storage implementation usage instead of `StorageFactory`\n- `var` where an explicit type belongs. Floci reproduces AWS wire contracts, so the\n  concrete type at a call site is usually what a reviewer needs to see. The one\n  exception is a record deconstruction pattern.\n- Fully-qualified class names written inline instead of imported, such as\n  `new java.util.ArrayList<>()`. A genuine name clash inside one file is fine.\n  Checkstyle catches this except in the files it still suppresses.\n- Wildcard imports in `src/main`. Static wildcards in tests are fine.\n- An empty `catch` block. A tolerated exception is logged, or named `ignored` or\n  `expected` with a comment saying why swallowing is safe.\n\n## Architecture Expectations\n\nFloci follows a layered design:\n\n- Controllers / handlers parse AWS protocol input and produce AWS-compatible responses\n- Services contain business logic and should throw `AwsException`\n- Models hold domain data\n\nCore infrastructure commonly relevant in reviews:\n\n- `EmulatorConfig`\n- `ServiceRegistry`\n- `StorageFactory`\n- `AwsQueryController`\n- `AwsJson11Controller`\n- `AwsException`\n- `AwsExceptionMapper`\n- `EmulatorLifecycle`\n\nCheck that controllers stay thin, business logic remains in services, and new changes fit existing repository patterns.\n\n## Protocol Review Rules\n\nFloci implements real AWS wire protocols. Review protocol-affecting changes carefully.\n\n- Query services should keep form-encoded POST requests with `Action` and XML responses\n- JSON 1.1 services should keep `X-Amz-Target` requests and AWS-style JSON responses\n- REST JSON and REST XML services should stay aligned with AWS path and payload conventions\n- TCP-based services should not drift into HTTP-style abstractions\n\nPay extra attention to these cases:\n\n- CloudWatch Metrics supports both Query and JSON 1.1 and both paths must stay aligned\n- SQS and SNS may have multiple compatibility paths that must not drift\n- Cognito well-known endpoints are OIDC REST JSON endpoints, not AWS management APIs\n- Management APIs should ideally be validated with AWS SDK clients, not only handcrafted HTTP\n\n## XML and JSON Rules\n\nFlag PRs that:\n\n- Ignore `AwsNamespaces` constants\n- Return JSON errors that do not follow AWS error structures\n- Change controller return types in ways that may break reflection or native-image compatibility\n\n## Config and Storage Review\n\nWhen a PR changes configuration or persistence behavior, verify the change is wired consistently.\n\nCheck for updates to:\n\n- `EmulatorConfig`\n- main `application.yml`\n- test `application.yml`\n- `StorageFactory`\n- lifecycle hooks when relevant\n\nSupported storage modes include:\n\n- `memory`\n- `persistent`\n- `hybrid`\n- `wal`\n\nTreat repository YAML as the source of truth for runtime behavior unless the PR explicitly changes configuration semantics.\n\n## Testing Expectations\n\nExpect automated coverage for changes that affect:\n\n- request parsing\n- response shape\n- error handling\n- persistence semantics\n- URL generation\n- service enablement\n\nPrefer:\n\n- AWS SDK-based validation over raw HTTP-only testing\n- integration tests for compatibility-sensitive behavior\n- existing naming conventions such as `*ServiceTest.java` and `*IntegrationTest.java`\n\nIf behavior changes without automated coverage, call that out explicitly.\n\n## Review Checklist\n\nWhen analyzing a PR, check:\n\n- Is the change focused?\n- Does it preserve AWS-compatible wire behavior?\n- Does it reuse an existing Floci pattern?\n- Are controllers thin and services responsible for domain logic?\n- Are `AwsException` and existing error-mapping patterns used correctly?\n- Are config and YAML updates complete?\n- Are storage changes wired through `StorageFactory`?\n- Are tests added or updated where compatibility is affected?\n- Are docs updated when user-facing behavior changes?\n- Does new code follow the AGENTS.md Code Style rules: explicit types over `var`,\n  imported classes over inline fully-qualified names, no wildcard imports in\n  `src/main`, braces in conditionals, constructor injection?\n\n## How to Write Feedback\n\nWrite review comments that are:\n\n- specific\n- repository-aware\n- grounded in AWS compatibility risk\n\nUse severity when helpful:\n\n- `high`: likely breaks AWS SDK / CLI compatibility or protocol behavior\n- `medium`: inconsistent with Floci architecture, wiring, or testing expectations\n- `low`: maintainability, clarity, or minor convention issue\n\nPrefer comments that explain:\n\n- what is risky\n- why it matters in Floci\n- which existing pattern should be followed instead\n\n## If Behavior Is Unclear\n\nUse this fallback order:\n\n1. Prefer AWS behavior\n2. Then existing Floci behavior\n3. Then compatibility test expectations\n\nIf correctness would require a broader architectural change, call out the tradeoff instead of suggesting blind refactoring.\n","tools/docs/services.yaml":"# Registry of services whose Supported Actions tables are auto-generated.\n#\n# The action LIST comes from handler source (mode: switch|rest); the Description\n# column is hand-written and PRESERVED across regen, keyed by action name.\n# Per-service overrides: rename_actions (method->canonical), exclude_actions.\n#\n# Phase 1 covers single-table switch-mode services. REST controllers, multi-table\n# docs, mixed-mode (ses), S3 query-string dispatch, and non-tabular docs are tracked\n# as follow-ups; their switch handlers are acknowledged in deferred_handlers below so\n# the new-service sentinel only fires on genuinely unregistered handlers.\n\nservices:\n  - service: account\n    doc: docs/services/account.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/account/AccountController.java, mode: rest }\n  - service: accessanalyzer\n    doc: docs/services/access-analyzer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/accessanalyzer/AccessAnalyzerController.java, mode: rest }\n  - service: acm\n    doc: docs/services/acm.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/acm/AcmJsonHandler.java, mode: switch }\n  - service: applicationautoscaling\n    doc: docs/services/applicationautoscaling.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ApplicationAutoScalingJsonHandler.java, mode: switch }\n  - service: athena\n    doc: docs/services/athena.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/athena/AthenaJsonHandler.java, mode: switch }\n  - service: bedrock\n    doc: docs/services/bedrock.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrock/BedrockController.java, mode: rest }\n  - service: bedrock-agentcore-control\n    doc: docs/services/bedrock-agentcore.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreControlController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreIdentityController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreGatewayController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreMemoryController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreToolsController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreGatewayRuleController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreResourcePolicyController.java, mode: rest }\n    rename_actions:\n      CreateEndpoint: CreateAgentRuntimeEndpoint\n      GetEndpoint: GetAgentRuntimeEndpoint\n      UpdateEndpoint: UpdateAgentRuntimeEndpoint\n      DeleteEndpoint: DeleteAgentRuntimeEndpoint\n      ListEndpoints: ListAgentRuntimeEndpoints\n      Create: CreateWorkloadIdentity\n      Get: GetWorkloadIdentity\n      Update: UpdateWorkloadIdentity\n      Delete: DeleteWorkloadIdentity\n      List: ListWorkloadIdentities\n      CreateTarget: CreateGatewayTarget\n      GetTarget: GetGatewayTarget\n      UpdateTarget: UpdateGatewayTarget\n      DeleteTarget: DeleteGatewayTarget\n      ListTargets: ListGatewayTargets\n  - service: ce\n    doc: docs/services/ce.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ce/CostExplorerJsonHandler.java, mode: switch }\n  - service: cloudformation\n    doc: docs/services/cloudformation.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationQueryHandler.java, mode: switch }\n  - service: cloudhsmv2\n    doc: docs/services/cloudhsmv2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudhsmv2/CloudHsmV2JsonHandler.java, mode: switch }\n  - service: cloudtrail\n    doc: docs/services/cloudtrail.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudtrail/CloudTrailJsonHandler.java, mode: switch }\n  - service: codeartifact\n    doc: docs/services/codeartifact.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/codeartifact/CodeArtifactController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/V1TagsController.java, mode: rest }\n    rename_actions:\n      ListTagsForResourceByQuery: ListTagsForResource\n    exclude_actions:\n      - ListTags\n      - TagResourcePost\n      - TagResourcePut\n  - service: codegurureviewer\n    doc: docs/services/codegurureviewer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/codegurureviewer/CodeGuruReviewerController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: lambdaweb\n    doc: docs/services/lambda-web-functions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/lambdaweb/LambdaWebController.java, mode: rest }\n  - service: marketplace\n    doc: docs/services/marketplace.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceCatalogController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceAgreementService.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceEntitlementController.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceDeploymentController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceReportingController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceMeteringService.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceDiscoveryController.java, mode: rest }\n    exclude_actions:\n      - AgreementType\n      - Status\n      - AcceptorAccountId\n      - OfferId\n      - OfferSetId\n      - ResourceIdentifier\n      - ResourceType\n      - PartyType\n      - BeforeStartTime\n      - AfterStartTime\n      - BeforeEndTime\n      - AfterEndTime\n      - BeforeLastUpdateTime\n      - AfterLastUpdateTime\n      - EndTimeBehaviorType\n      - EndTimeBehaviorReasonCode\n      - InitialAgreementId\n      - LicenseArn\n      - StartTime\n      - LastUpdateTime\n      - EndTime\n      - ASCENDING\n      - DESCENDING\n  - service: comprehend\n    doc: docs/services/comprehend.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/comprehend/ComprehendJsonHandler.java, mode: switch }\n  - service: controlcatalog\n    doc: docs/services/controlcatalog.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/controlcatalog/ControlCatalogController.java, mode: rest }\n    rename_actions:\n      GetControl: GetControl\n      ListControls: ListControls\n  - service: appintegrations\n    doc: docs/services/appintegrations.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/appintegrations/AppIntegrationsController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: dlm\n    doc: docs/services/dlm.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dlm/DlmController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: connect\n    doc: docs/services/connect.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/connect/ConnectController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: rekognition\n    doc: docs/services/rekognition.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/rekognition/RekognitionJsonHandler.java, mode: switch }\n  - service: translate\n    doc: docs/services/translate.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/translate/TranslateJsonHandler.java, mode: switch }\n  - service: detective\n    doc: docs/services/detective.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/detective/DetectiveController.java, mode: rest }\n  - service: dms\n    doc: docs/services/dms.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dms/DmsJsonHandler.java, mode: switch }\n  - service: docdb\n    doc: docs/services/docdb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/docdb/DocDbQueryHandler.java, mode: switch }\n  - service: dsql\n    doc: docs/services/dsql.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dsql/DsqlController.java, mode: rest }\n  - service: efs\n    doc: docs/services/efs.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/efs/EfsController.java, mode: rest }\n  - service: organizations\n    doc: docs/services/organizations.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/organizations/OrganizationsJsonHandler.java, mode: switch }\n  - service: elasticache\n    doc: docs/services/elasticache.md\n    exclude_actions: [SubnetIds, SecurityGroupIds, SnapshotArnsToRestore]\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/elasticache/ElastiCacheQueryHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/elasticache/ElastiCacheServerlessQueryHandler.java, mode: switch }\n  - service: emr\n    doc: docs/services/emr.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/emr/EmrHandler.java, mode: switch }\n  - service: eventbridge\n    doc: docs/services/eventbridge.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/eventbridge/EventBridgeHandler.java, mode: switch }\n  - service: firehose\n    doc: docs/services/firehose.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/firehose/FirehoseJsonHandler.java, mode: switch }\n  - service: resource-explorer-2\n    doc: docs/services/resource-explorer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/resourceexplorer2/ResourceExplorer2Controller.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: fis\n    doc: docs/services/fis.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/fis/FisController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: kinesis\n    doc: docs/services/kinesis.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kinesis/KinesisJsonHandler.java, mode: switch }\n  - service: kinesisanalytics\n    doc: docs/services/kinesisanalytics.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kinesisanalytics/KinesisAnalyticsV2JsonHandler.java, mode: switch }\n  - service: kms\n    doc: docs/services/kms.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kms/KmsJsonHandler.java, mode: switch }\n  - service: memorydb\n    doc: docs/services/memorydb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/memorydb/MemoryDbHandler.java, mode: switch }\n  - service: macie2\n    doc: docs/services/macie2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/macie2/MacieController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/securityadmin/SecurityAdminController.java, mode: rest }\n    rename_actions:\n      ListAdmin: ListOrganizationAdminAccounts\n  - service: resource-groups\n    doc: docs/services/resource-groups.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/resourcegroups/ResourceGroupsController.java, mode: rest }\n  - service: inspector2\n    doc: docs/services/inspector2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/inspector2/Inspector2Controller.java, mode: rest }\n  - service: neptune\n    doc: docs/services/neptune.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/neptune/NeptuneQueryHandler.java, mode: switch }\n  - service: networkfirewall\n    doc: docs/services/network-firewall.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/networkfirewall/NetworkFirewallJsonHandler.java, mode: switch }\n  - service: ram\n    doc: docs/services/ram.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ram/RamController.java, mode: rest }\n  - service: rds\n    doc: docs/services/rds.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/rds/RdsQueryHandler.java, mode: switch }\n    exclude_actions: [SubnetIds, VpcSecurityGroupIds, OptionsToRemove, ValuesToAdd, ValuesToRemove]\n  - service: redshift\n    doc: docs/services/redshift.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshift/RedshiftQueryHandler.java, mode: switch }\n    # SubnetIds / VpcSecurityGroupIds / IamRoles / AddIamRoles / RemoveIamRoles / TagKeys / TagValues /\n    # DbGroups / ScheduleDefinitions are labels of an inner member-name switch (memberKeyRegex), not\n    # actions, same shape as the rds exclusions above.\n    exclude_actions: [SubnetIds, VpcSecurityGroupIds, IamRoles, AddIamRoles, RemoveIamRoles, TagKeys, TagValues, DbGroups, ScheduleDefinitions]\n  - service: redshiftdata\n    doc: docs/services/redshift-data.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftdata/RedshiftDataJsonHandler.java, mode: switch }\n    # ExecuteSql / BatchExecuteSql are the deprecated pre-2020 operations: the handler\n    # recognizes them only to return a ValidationException, they are not supported actions.\n    exclude_actions: [ExecuteSql, BatchExecuteSql]\n  - service: redshiftserverless\n    doc: docs/services/redshift-serverless.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftserverless/RedshiftServerlessJsonHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftserverless/RedshiftServerlessAdminJsonHandler.java, mode: switch }\n  - service: cognitoidentity\n    doc: docs/services/cognitoidentity.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cognitoidentity/CognitoIdentityJsonHandler.java, mode: switch }\n  - service: globalaccelerator\n    doc: docs/services/globalaccelerator.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/globalaccelerator/GlobalAcceleratorJsonHandler.java, mode: switch }\n  - service: datasync\n    doc: docs/services/datasync.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/datasync/DataSyncJsonHandler.java, mode: switch }\n  - service: route53\n    doc: docs/services/route53.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/route53/Route53Controller.java, mode: rest }\n    # JAX-RS method names use Java casing; map them onto the canonical Route 53 action names.\n    rename_actions:\n      AssociateVpcWithHostedZone: AssociateVPCWithHostedZone\n      DisassociateVpcFromHostedZone: DisassociateVPCFromHostedZone\n      CreateVpcAssociationAuthorization: CreateVPCAssociationAuthorization\n      DeleteVpcAssociationAuthorization: DeleteVPCAssociationAuthorization\n      ListVpcAssociationAuthorizations: ListVPCAssociationAuthorizations\n      ListHostedZonesByVpc: ListHostedZonesByVPC\n      GetDnssec: GetDNSSEC\n  - service: route53resolver\n    doc: docs/services/route53resolver.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/route53resolver/Route53ResolverJsonHandler.java, mode: switch }\n  - service: sagemaker\n    doc: docs/services/sagemaker.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/sagemaker/SageMakerJsonHandler.java, mode: switch }\n  - service: securityhub\n    doc: docs/services/securityhub.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/securityhub/SecurityHubController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: secretsmanager\n    doc: docs/services/secrets-manager.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/secretsmanager/SecretsManagerJsonHandler.java, mode: switch }\n  - service: servicecatalog\n    doc: docs/services/service-catalog.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/servicecatalog/ServiceCatalogJsonHandler.java, mode: switch }\n  - service: servicequotas\n    doc: docs/services/servicequotas.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/servicequotas/ServiceQuotasJsonHandler.java, mode: switch }\n  - service: ssoadmin\n    doc: docs/services/ssoadmin.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ssoadmin/SsoAdminJsonHandler.java, mode: switch }\n  - service: identitystore\n    doc: docs/services/identitystore.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/identitystore/IdentityStoreJsonHandler.java, mode: switch }\n  - service: budgets\n    doc: docs/services/budgets.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/budgets/BudgetsJsonHandler.java, mode: switch }\n  - service: verifiedpermissions\n    doc: docs/services/verifiedpermissions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/verifiedpermissions/VerifiedPermissionsJsonHandler.java, mode: switch }\n  - service: timestream-influxdb\n    doc: docs/services/timestream-influxdb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/timestreaminfluxdb/TimestreamInfluxDbJsonHandler.java, mode: switch }\n  - service: sqs\n    doc: docs/services/sqs.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/sqs/SqsJsonHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/sqs/SqsQueryHandler.java, mode: switch }\n  - service: stepfunctions\n    doc: docs/services/step-functions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/stepfunctions/StepFunctionsJsonHandler.java, mode: switch }\n  - service: swf\n    doc: docs/services/swf.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/swf/SwfJsonHandler.java, mode: switch }\n  - service: wafv2\n    doc: docs/services/wafv2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/wafv2/WafV2Handler.java, mode: switch }\n\n# Switch handlers that exist but are intentionally not yet covered (see reasons).\n# A NEW switch handler not listed here and not registered above fails `--strict`.\ndeferred_handlers:\n  - { path: src/main/java/io/github/hectorvent/floci/services/apigatewayv2/ApiGatewayV2JsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ScalingPolicyAlarmActionHandler.java }  # not a JSON/Query action dispatcher — its `case \"X\" ->` arms are the StepScaling AdjustmentType switch, not AWS actions\n  - { path: src/main/java/io/github/hectorvent/floci/services/autoscaling/AutoScalingQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/bcmpricingcalculator/BcmPricingCalculatorJsonHandler.java }  # focused workload-estimate surface\n  - { path: src/main/java/io/github/hectorvent/floci/services/bcmdataexports/BcmDataExportsJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudcontrol/CloudControlJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudmap/CloudMapHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/logs/CloudWatchLogsHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/logs/CloudWatchLogsMetricFilterHandler.java }  # multi-table doc (nested handler, reached through CloudWatchLogsHandler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/metrics/CloudWatchMetricsJsonHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/metrics/CloudWatchMetricsQueryHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/codebuild/CodeBuildJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/codedeploy/CodeDeployJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cognito/CognitoJsonHandler.java }  # non-tabular doc (grouped/inline); separate docs effort\n  - { path: src/main/java/io/github/hectorvent/floci/services/configservice/ConfigServiceJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cur/CurJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/dynamodb/NativeDynamoDbJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/dynamodb/NativeDynamoDbStreamsJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ec2/Ec2QueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/ecr/EcrJsonHandler.java }  # doc packs multiple actions per row (`A` / `B`) + REST endpoints; needs de-grouping\n  - { path: src/main/java/io/github/hectorvent/floci/services/ecs/EcsJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elasticbeanstalk/ElasticBeanstalkQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elb/ElbClassicQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2QueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/glue/GlueJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/iam/IamQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/iam/StsQueryHandler.java }  # sts (lives in iam package); single-table, registerable next\n  - { path: src/main/java/io/github/hectorvent/floci/services/lakeformation/LakeFormationJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/lightsail/LightsailJsonHandler.java }  # non-tabular doc (grouped/inline); standalone action table to be added\n  - { path: src/main/java/io/github/hectorvent/floci/services/pricing/PricingJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/resourcegroupstagging/ResourceGroupsTaggingJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/ses/SesQueryHandler.java }  # mixed-mode (Query + REST surface)\n  - { path: src/main/java/io/github/hectorvent/floci/services/sns/SnsJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/sns/SnsQueryHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ssm/Ec2MessagesJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ssm/SsmJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/textract/TextractJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/transcribe/TranscribeJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/transfer/TransferHandler.java }  # multi-table doc\n"},"files":{"AGENTS.md":"Guidance for AI coding agents working in the Floci repository.\n\nThis file defines repository-specific operating rules for autonomous or semi-autonomous coding agents. Follow these instructions unless a maintainer explicitly tells you otherwise.\n\n---\n\n## Project Overview\n\nFloci is a Java-based local AWS emulator built on Quarkus.\n\nIts goal is full AWS SDK and AWS CLI compatibility through real AWS wire protocols, not convenience APIs or simplified abstractions.\n\nFloci acts as an open-source alternative to LocalStack Community.\n\n- Port: 4566\n- Stack:\n  - Java 25\n  - Quarkus 3.40.1\n  - JUnit 5\n  - RestAssured\n  - Jackson\n  - Docker integrations for Lambda, RDS, and ElastiCache\n\n---\n\n## First Principles\n\nWhen making changes, follow these priorities:\n\n1. Preserve AWS protocol compatibility\n2. Match AWS SDK and CLI behavior\n3. Reuse existing Floci patterns\n4. Prefer correctness over convenience\n5. Keep changes narrow and testable\n\nCritical rules:\n\n- Do not introduce custom endpoint shapes\n- Do not change request or response formats for convenience\n- Do not perform broad refactors unless the task explicitly requires them\n- Keep behavior aligned with AWS expectations and existing Floci conventions\n\n---\n\n## Architecture\n\nFloci follows a layered design:\n\n- **Controller / Handler**\n  - Parses AWS protocol input\n  - Produces AWS-compatible responses\n\n- **Service**\n  - Contains business logic\n  - Throws `AwsException`\n\n- **Model**\n  - Domain objects\n\n### Core Infrastructure\n\n- `EmulatorConfig`\n- `ServiceRegistry`\n- `StorageBackend` + `StorageFactory`\n- `AwsJson11Controller`\n- `AwsQueryController`\n- `AwsException` + `AwsExceptionMapper`\n- `EmulatorLifecycle`\n\n---\n\n## Package Layout\n\n- `io.github.hectorvent.floci.config`\n- `io.github.hectorvent.floci.core.common`\n- `io.github.hectorvent.floci.core.storage`\n- `io.github.hectorvent.floci.lifecycle`\n- `io.github.hectorvent.floci.services.<service>`\n\nTypical service structure:\n\n- `services/<svc>/`\n  - `*Controller.java` (REST JSON / REST XML services, via JAX-RS)\n  - `*QueryHandler.java` (AWS Query/XML protocol services)\n  - `*JsonHandler.java` (AWS JSON 1.1 protocol services)\n  - `*Service.java`\n  - `model/`\n\nThe entry-point class name depends on the service's AWS protocol (see \"AWS Protocol\nRules\" below); `Controller` is not universal.\n\nRule:\nCopy an existing service pattern before introducing a new one.\n\n---\n\n## AWS Protocol Rules\n\nFloci must implement real AWS wire protocols.\n\n| Protocol | Services | Request Format | Response Format | Implementation |\n|----------|----------|----------------|-----------------|----------------|\n| Query | SQS, SNS, IAM, STS, RDS, ElastiCache, CloudFormation, CloudWatch Metrics | form-encoded POST + `Action` | XML | `AwsQueryController` |\n| JSON 1.1 | SSM, EventBridge, CloudWatch Logs, Kinesis, KMS, Cognito, Secrets Manager, ACM | POST + `X-Amz-Target` | JSON | `AwsJson11Controller` |\n| REST JSON | Lambda, API Gateway, SES V2 | REST paths | JSON | JAX-RS |\n| REST XML | S3 | REST paths | XML | JAX-RS |\n| TCP | ElastiCache, RDS | raw protocol | native | proxies |\n\n### Important exceptions\n\n- CloudWatch Metrics supports both Query and JSON 1.1; handlers must remain aligned\n- SQS and SNS may expose multiple compatibility paths; do not let them drift\n- Cognito well-known endpoints are OIDC REST JSON endpoints, not AWS management APIs\n- Data-plane protocols may use raw TCP sockets\n- Management APIs should be validated with AWS SDK clients, not only handcrafted HTTP requests\n\n---\n\n### Partition literals\n\nFloci serves every AWS partition, not only the commercial one, so a literal that bakes\n`aws` into a code path is a bug in a GovCloud, China, ISO or EUSC deployment even when it\nprints correctly in `us-east-1`. `make partition-check` (CI: Partition Literals) inventories\n`src/main/java` for `arn:aws:` prefixes, `amazonaws.com` hosts, Route 53 hosted-zone ids,\nhand-rolled `arn:aws[a-z-]*:` regexes, `Arn.of(...)` calls with a blank region and comparisons\nwith the literal `\"aws\"` against `tools/partition/baseline.tsv`, per file. The baseline is empty, so any new one fails.\nA literal in a new file fails, growth in an existing file fails, and a drop fails until you\nrun `make partition-baseline` and commit the smaller baseline.\n\n- Mint ARNs through `AwsArnUtils.Arn.of(...)` / `RegionResolver.buildArn(...)`, recognise\n  them with `AwsArnUtils.isArn` / `PARTITION_REGEX`, and derive hosts from the region's\n  DNS suffix (`AwsRegions.dnsSuffixFor`).\n- A regionless ARN (IAM, STS, CloudFront, Organizations, an S3 bucket) never goes through\n  `Arn.of(service, \"\", ...)`, which silently means `aws`: use `RegionResolver.buildGlobalArn`\n  for the request's partition, or `Arn.global(partition, ...)` with the resource's own\n  (`AwsRegions.partitionFor(region)` for a bucket or a stack's region).\n- Never compare a partition with a literal: `\"aws\".equals(arn.partition())` is commercial-only\n  logic that fails silently in every other partition. Compare with the partition the resource\n  belongs to, `AwsRegions.partitionFor(region)` for a regional resource or the stored ARN's own\n  partition for a global one, and accept any published partition with `AwsPartitions.find(...)`\n  when only the shape matters. `\"aws\"` as an account (managed policies) or a command name is not\n  a partition: end that line with `// partition-literal: <reason>`.\n- A service principal is `ServicePrincipals.of(\"<service>\")`, never a literal: it is\n  `<service>.amazonaws.com` in every partition, and `ServicePrincipals.canonical` folds the\n  legacy per-partition forms a policy may still use before matching.\n- A literal that is genuinely partition-invariant (an XML namespace URI, an S3 canned-ACL\n  grantee URI) goes in `tools/partition/allowlist.yaml` with a reason, or ends its line\n  with `// partition-literal: <reason>`. Both are printed by `make partition-audit`.\n- Tests are not scanned; only assert a partition-dependent value when the test is about\n  partitions.\n\n## XML / JSON Rules\n\n- Use `XmlBuilder` for XML responses\n- Use `XmlParser` for XML parsing; do not use regex\n- Use `AwsNamespaces` constants\n- JSON errors must follow AWS error structures\n- Types returned directly from controllers must remain compatible with native-image reflection requirements\n\n---\n\n## Storage Rules\n\nSupported storage modes:\n\n- `memory`\n- `persistent`\n- `hybrid`\n- `wal`\n\nRules:\n\n- Always use `StorageFactory`\n- Do not instantiate storage implementations directly inside services\n- Respect lifecycle hooks for load and flush behavior\n\nImportant nuance:\n\nConfiguration interfaces may declare fallback defaults, but `application.yml` defines effective runtime behavior. Treat repository YAML as the source of truth unless a task explicitly changes configuration semantics.\n\nWhen adding storage-related behavior:\n\n1. Update `EmulatorConfig`\n2. Update main `application.yml`\n3. Update test `application.yml`\n4. Wire through `StorageFactory`\n5. Verify lifecycle integration\n\n---\n\n## Configuration Rules\n\nConfiguration lives under `floci.*`.\n\nWhen adding config:\n\n1. Add it to `EmulatorConfig`\n2. Add it to main `application.yml`\n3. Add it to test `application.yml` if needed\n4. Update documentation if user-facing\n5. Follow `FLOCI_*` environment variable conventions\n\nCritical areas:\n\n- `base-url`\n- `hostname`\n- region and account defaults\n- port ranges\n- persistence paths\n- Docker networking\n\n---\n\n## Build & Run\n\n    ./mvnw quarkus:dev\n    ./mvnw test\n    ./mvnw clean package\n    ./mvnw clean package -DskipTests\n\n`make help` lists the Makefile shortcuts. The native binary and image: `make native`\nthen `make native-image` (CI's flags, staged in `native/<arch>/`), `make native-host` for a\nbinary built on this machine with the installed GraalVM, `make native-up` to run the image,\n`make compat SUITES=\"sdk-test-java compat-cdk\"` for compatibility suites against it.\n\n### Focused tests\n\n    ./mvnw test -Dtest=SsmIntegrationTest\n    ./mvnw test -Dtest=SsmIntegrationTest#putParameter\n\n---\n\n## Compatibility Project\n\nCompatibility test suite: `./compatibility-tests/`\n\nGuidelines:\n\n- Prefer AWS SDK clients over raw HTTP for management-plane validation\n- Use this suite when changes may affect real SDK behavior\n\n---\n\n## Testing Rules\n\n### Conventions\n\n- Unit tests: `*ServiceTest.java`\n- Integration tests: `*IntegrationTest.java`\n- Prefer package-private constructors for testability\n- Integration tests may use ordered execution when stateful behavior requires it\n\n### Expectations\n\n- Test any behavior affecting AWS compatibility\n- Do not rely only on manual HTTP testing\n- Prefer SDK-based validation where possible\n\n### When touching protocol behavior\n\nIf a change affects request parsing, response shape, error handling, persistence semantics, URL generation, or service enablement:\n\n1. Add or update automated tests\n2. Prefer SDK-based verification where possible\n3. Check compatibility across alternate protocol paths\n4. Document intentional deviations clearly\n\n---\n\n## Error Handling\n\n- Services should throw `AwsException`\n- Query and REST XML flows should use `AwsExceptionMapper`\n- JSON 1.1 flows should return structured AWS error responses where required\n- Controller return types must remain reflection-safe\n\n---\n\n## Service Implementation Pattern\n\nWhen adding functionality:\n\n1. Identify the AWS protocol\n2. Reuse an existing service pattern\n3. Keep controllers thin\n4. Use `AwsException` for domain errors\n5. Reuse shared utilities\n6. Update config, storage, docs, and tests together\n7. Validate behavior against AWS SDK expectations\n\n---\n\n## Adding a New AWS Service\n\n1. Create a package under `services/<svc>/` with a Controller or protocol-specific handler,\n   a Service, and `model/`\n2. Add a `<Svc>ServiceConfig` interface and its accessor on `ServicesConfig` in `EmulatorConfig`\n3. Add one `descriptor(...)` entry in `ResolvedServiceCatalog`. This is the registration point;\n   `ServiceRegistry` only reads the catalog and has no registration API\n4. Add `floci.services.<key>.enabled` to both `src/main/resources/application.yml` and\n   `src/test/resources/application.yml`\n5. JSON 1.1 only: inject the `<Svc>JsonHandler` in `AwsJson11Controller`\n6. Obtain storage through `StorageFactory` and implement `Resettable`\n7. List any static `Random` or `SecureRandom` field under `--initialize-at-run-time` in\n   `application.yml`\n8. Check every timestamp member you emit for a `TimestampFormatTrait` before using the\n   epoch-seconds idiom. It is the awsJson1.1 default, but a model can override it per\n   member, and the mismatch is invisible to the AWS CLI because botocore coerces the\n   value, while strict SDKs (Go, Java) reject the whole response. `javap -c` on the SDK\n   model class shows the traits on each `SdkField`\n9. Add `<Svc>ServiceTest` and `<Svc>IntegrationTest`\n10. If the service mints ARNs or AWS hosts (`buildArn`, `Arn.of`, `new AwsArnUtils.Arn`, `dnsSuffixFor`, `AwsEndpoints`),\n   add a case to `PartitionCrossServiceSmokeIntegrationTest` and its `COVERED_PACKAGES`, or a row\n   with a reason to `src/test/resources/partition/smoke-exemptions.tsv`;\n   `PartitionSmokeInventoryTest` fails until one of the two exists\n11. Document it: `docs/services/<svc>.md`, a `mkdocs.yml` nav entry, a Service Matrix row in\n   `docs/services/index.md`, and a row in the README category table\n12. Register the handler in `tools/docs/services.yaml`, then run `make docs-sync` and\n    `make docs-check`\n13. Add a `TestFixtures` client factory and a `<Svc>Test` in `compatibility-tests/sdk-test-java`\n\n---\n\n## Adding a CloudFormation Resource Type\n\n**Every type is served by a per-service provisioner under\n`services/cloudformation/provisioners/`.** `CfnResourceDispatcher` only routes a resource to\nthe registry and stubs what nothing serves; never add type-specific code to it.\n\n1. Add the type to the existing `<Service>CfnProvisioner`, or create one:\n   `@ApplicationScoped`, injecting only the service it wraps. CDI discovery via\n   `CloudFormationResourceRegistry` handles registration: no manual wiring, but a\n   missing `@ApplicationScoped` silently means the type is never provisioned.\n2. `resourceTypes()` lists the `AWS::*` types; `provision(resource, props, ctx)`\n   does the work, switching on `resource.getResourceType()` when it serves several.\n3. Set **both** reference mechanisms. They are separate:\n   - `resource.setPhysicalId(...)` backs `Ref`\n   - `resource.getAttributes().put(...)` backs `Fn::GetAtt`, one entry per attribute\n   Omitting an attribute does not fail; `Fn::GetAtt` resolves to the literal\n   `\"LogicalId.Attr\"`. Source the attribute names from the type's registry schema in\n   `local/aws/cfn-resource-schemas/us-east-1/` (`readOnlyProperties`), and validate\n   `required` from the same file.\n4. **`provision` serves create *and* update.** On `UpdateStack` it is re-invoked with\n   the prior physical id and attributes already populated on the resource. Branch with\n   `ctx.isUpdate()` / `ctx.priorPhysicalId()`, not by reading the id off the resource:\n   `provision` assigns the new id as it runs, so a resource-derived check flips\n   mid-method.\n5. Override `delete(...)` when the type has a backing delete; tolerate already-deleted\n   via `CfnDeletes.safeDelete`, passing the specific \"already gone\" error codes. Never\n   a catch-all: a real failure such as `BucketNotEmpty` must propagate so the stack\n   reports `DELETE_FAILED`. When the delete needs a create-time attribute rather than\n   just the physical id, override `delete(StackResource, String)`.\n6. **Register in `src/test/resources/cloudformation/supported-resource-types.tsv`**\n   (`type<TAB>Owner`). `CfnResourceInventoryTest` diffs that file against the\n   CDI-resolved registry, so it also catches a missing `@ApplicationScoped`.\n7. **Add the provisioner to `CfnProvisionerFixture.inferredProvisioners()`** when it\n   takes a single service, or a fixture test naming that service silently falls through\n   to the stub arm.\n8. Tests: focused unit test mocking one service (`SqsCfnProvisioner`'s test is the\n   pattern) plus an integration test asserting the **exact `Fn::GetAtt` keys**. An\n   unmapped type is stubbed as `CREATE_COMPLETE` with a fake ARN, so asserting status\n   alone cannot detect a type that was never wired. Note the engine's constructor is\n   package-private, so tests in `provisioners/` mock it.\n9. Run `make docs-sync` and commit the result. The resource-type table in\n   `docs/services/cloudformation.md` is **generated** from the step-6 inventory; hand\n   edits fail `docs-check`. Labels, ordering and notes live in\n   `tools/docs/cfn_resource_types.yaml`.\n10. A schema `readOnlyProperties` entry you cannot set goes in\n    `src/test/resources/cloudformation/getatt-attribute-gaps.tsv` with a reason;\n    `CfnSchemaCoverageTest` requires every unset attribute to be fixed or recorded.\n\nReferences: `SqsCfnProvisioner` (smallest), `Ec2LaunchTemplateCfnProvisioner`\n(update-in-place and replacement), `LogsCfnProvisioner` (reconcile-vs-replace update).\n\n---\n\n## Sidecars\n\nA dependency too heavy for the native image (a native runtime, a large engine, another language\necosystem) ships as a sidecar: a stateless HTTP service in its own container that Floci starts\nlazily over the Docker socket. Sidecars live in\n[floci-io/floci-sidecars](https://github.com/floci-io/floci-sidecars), one directory per sidecar\non the shared `sidecar-core`, and are published as `floci/floci-sidecar-<name>:<semver>` on\nDocker Hub. They implement that repository's `docs/contract.md` and carry no AWS vocabulary: the\nsidecar answers a generic question, Floci maps its service semantics onto the answer.\n\nFloci-side rules:\n\n- Never add a `sidecars/` directory to this repository, never publish a `floci/floci:<tag>-<name>`\n  suffix, and never default an image to `:latest` (`ImageCacheService` never re-pulls a cached tag).\n- The consuming service owns two config knobs, `<name>-image` (an exact version) and `<name>-url`\n  (skip container management), and a `<Name>SidecarManager` plus `<Name>SidecarClient` pair.\n  `CedarSidecarManager` is the reference: it reads the contract JSON from `/health` and fails fast\n  on a contract-major mismatch.\n- The sidecar releases before the Floci PR that needs it, as a final or an `X.Y.Z-rc.N` tag; the\n  Floci PR pins that tag. A PR that needs an unreleased sidecar cannot pass CI.\n- Tests: the client's wire contract is covered by a JDK `HttpServer` fake; one Docker-gated\n  `@QuarkusTest` runs the pinned image through the real manager under its own\n  `floci.docker.resource-namespace`. A Floci test imports only Floci classes; the sidecar's own\n  behaviour is tested in its repository.\n- A sidecar that is a stock upstream image plus a script from the classpath needs no repository\n  but still goes through a manager with the same two knobs.\n\n## Code Style\n\n### Checkstyle\n\n- Java code must pass `./mvnw checkstyle:check` (CI: Checkstyle). The rules live in\n  `tools/checkstyle/checkstyle.xml`. Code excluded in `tools/checkstyle/suppressions.xml`\n  is not checked yet, but the rules below still apply to it.\n\n### General\n\n- Use constructor injection\n- Prefer self-explanatory code over comments\n- Avoid unnecessary comments\n- Always use braces in conditionals\n- Never leave a `catch` block empty. If an exception is intentionally tolerated, log it with enough context to diagnose it later.\n  When swallowing really is correct and logging would be noise, name the variable\n  `ignored` or `expected` and say in a comment why it is safe. A bare\n  `catch (Exception e) {}` is never acceptable.\n- Follow existing project patterns\n- Use modern Java features only when they improve clarity\n\n### Types and names\n\n- **Do not use `var`. Write the explicit type.** Floci reproduces AWS wire\n  contracts, so the concrete type at a call site is usually the thing under\n  review: whether a value is a `LinkedHashMap` or a `Map`, an AWS model type or a\n  JDK one, is exactly what a reviewer needs to see. This covers local\n  declarations, enhanced-for (`for (Tag tag : tags)`), classic for-init, and\n  try-with-resources. The one exception is a record deconstruction pattern\n  (`case Node(var left, var right) ->`), where naming the component types is pure\n  noise.\n- **Import the classes you use. Do not write fully-qualified names inline.**\n  `new ArrayList<>()`, never `new java.util.ArrayList<>()`. Qualify inline only for\n  a genuine name clash inside one file: import the type used more often, qualify the\n  other, and leave a short comment naming the clash. Checkstyle (`NoFqcn`) enforces\n  this except in the files listed in `tools/checkstyle/suppressions.xml`.\n\n### Imports\n\n- No wildcard imports in `src/main`. Static wildcards stay fine in tests, where\n  `Assertions.*`, `Mockito.*` and `Matchers.*` are the established idiom.\n- Import order: non-`java`/`javax` imports, then `javax.*`, then `java.*`, then\n  static imports. Alphabetize each group. Separate the non-JDK, JDK, and static\n  sections with a blank line; no blank line is needed between `javax` and `java`.\n  This follows IntelliJ's default layout and is enforced by Checkstyle.\n\n### Conventions the codebase already follows\n\nWritten down so they stay true. New code should match them without thinking.\nA handful of files predate them; a violation you find in the tree is a straggler,\nnot a precedent.\n\n- 4-space indentation, K&R braces. Never indent with a tab.\n- JBoss Logging, in a field named `LOG`, using the parameterized `...v()` form.\n  No string concatenation in log calls.\n- No `printStackTrace`, anywhere. No `System.out` or `System.err` in `src/main`; the\n  one exception is the CLI entry point `io.github.hectorvent.floci.tools.ami.AmiImageTool`,\n  where stdout is the program's output. A few tests print a failure repro just before\n  failing, which is the only good reason to print from a test: an assertion message\n  usually says it better.\n- `java.time` for everything Floci owns. `Calendar` and `SimpleDateFormat` appear\n  nowhere and must not be introduced. A `Date` survives only at a third-party boundary\n  that forces one: the BouncyCastle certificate builder, the JAX-RS\n  `HttpHeaders.getDate()` override, and JDBC's `java.sql.Date` in the RDS Data mapper.\n  Convert at that boundary with `Date.from(instant)` and keep `java.time` on Floci's\n  side of it.\n- Constructor injection in `src/main`. Field injection is fine in tests, and\n  `Instance<T>` field injection is a legitimate CDI pattern.\n- `Optional` as a return type, and never as a field: there are none, keep it that way.\n  It reaches a parameter only where a Quarkus `@ConfigProperty Optional<T>` is threaded\n  through; do not introduce it as a parameter for anything else.\n- Switch expressions over switch statements. Pattern-matching `instanceof` over\n  cast-after-check.\n- `AwsException` for domain errors.\n- `final` on service fields, but not on locals or parameters.\n\n### Tests\n\nThese describe `src/test`. `compatibility-tests` is a separate module with the opposite\nidiom, AssertJ and `@DisplayName` in nearly every file. Follow the module you are in.\n\n- Name test methods either as a camelCase sentence (`putAndGetFromMemory`) or as\n  `method_scenario_expectation`. Both are established. `testX` names are common in\n  older tests and are not the pattern to copy.\n- JUnit 5 assertions with Hamcrest and RestAssured matchers. AssertJ is a declared test\n  dependency, used by the Lambda launcher tests; prefer the established matchers\n  everywhere else.\n- `@DisplayName` is not used here. The method name carries the intent.\n\n---\n\n## Documentation Style\n\n- No em-dashes anywhere, in any content. Use colons, commas, or periods.\n\n## Logging\n\n- Use JBoss Logging\n- Keep logs structured\n- Avoid noisy logs in hot paths\n\n---\n\n## Pull Request Guidelines\n\n- Keep changes focused\n- Avoid unrelated refactors\n- Preserve behavior unless the task explicitly requires change\n- Update docs when necessary\n- Explain missing tests when behavior changed but no automated coverage was added\n\nConventional commits:\n\n- `feat:`\n- `fix:`\n- `perf:`\n- `docs:`\n- `chore:`\n\nDo not add `Co-Authored-By` trailers for AI tools in commit messages. Keep attribution limited to human contributors.\n\n---\n\n## Release Awareness\n\n- Changes merged into `main` do not automatically imply a stable release\n- Releases are cut from `main` via the \"Release Cut\" workflow (`workflow_dispatch`\n  on `.github/workflows/release-cut.yml`), which runs semantic-release: it bumps\n  `pom.xml`, writes `CHANGELOG.md`, commits, tags, and creates the GitHub Release\n- `release/x.y.x` branches are retired for now\n- Tags still trigger the publishing workflows (`release.yml`)\n\nTreat release workflows as critical infrastructure.\n\n---\n\n## Agent Workflow\n\n### Before editing\n\n1. Identify service and protocol\n2. Locate an existing implementation to mirror\n3. Check config impact\n4. Check storage impact\n5. Check documentation impact\n6. Define the minimal useful test plan\n\n### Before finishing\n\n1. Run relevant tests\n2. Validate protocol behavior\n3. Ensure no custom endpoints were introduced\n4. Verify config and docs updates\n\n---\n\n## Common Mistakes\n\n- Creating non-AWS endpoints\n- Bypassing `StorageFactory`\n- Changing wire formats without tests\n- Forgetting YAML updates\n- Producing inconsistent URLs or ARNs\n- Testing only with raw HTTP\n- Introducing unnecessary new patterns\n- Adding type-specific code to `CfnResourceDispatcher` instead of a per-service\n  provisioner\n- Setting a CloudFormation resource's physical id but not its `Fn::GetAtt`\n  attributes (they are two separate mechanisms, and the miss is silent)\n- Hand-editing the resource-type table in `docs/services/cloudformation.md`, which is\n  generated, run `make docs-sync` instead\n- Adding a CloudFormation provisioner without a row in\n  `supported-resource-types.tsv` or an entry in `CfnProvisionerFixture`, either of\n  which leaves a type quietly served by the stub arm\n\n---\n\n## Human Handoff\n\nIf behavior is unclear:\n\n1. Prefer AWS behavior\n2. Then existing Floci behavior\n3. Then compatibility test expectations\n\nIf a task would require broad architectural changes, stop and surface the tradeoffs instead of refactoring across services blindly.\n",".github/copilot-instructions.md":"# Copilot Instructions for Pull Request Review\n\nReview pull requests in the Floci repository with AWS compatibility as the primary concern.\n\nFloci is a Java-based local AWS emulator built on Quarkus. Its goal is to match AWS SDK and AWS CLI behavior through real AWS wire protocols, not convenience APIs or custom abstractions.\n\n## Review Priorities\n\nEvaluate changes in this order:\n\n1. Preserve AWS protocol compatibility\n2. Match AWS SDK and AWS CLI behavior\n3. Reuse existing Floci patterns\n4. Prefer correctness over convenience\n5. Keep changes focused and testable\n\n## What to Flag\n\nRaise concerns when a PR introduces any of the following without strong justification:\n\n- Non-AWS endpoint shapes\n- Request or response format changes made for convenience\n- Broad refactors unrelated to the PR goal\n- New service patterns where an existing Floci pattern should be reused\n- Direct storage implementation usage instead of `StorageFactory`\n- `var` where an explicit type belongs. Floci reproduces AWS wire contracts, so the\n  concrete type at a call site is usually what a reviewer needs to see. The one\n  exception is a record deconstruction pattern.\n- Fully-qualified class names written inline instead of imported, such as\n  `new java.util.ArrayList<>()`. A genuine name clash inside one file is fine.\n  Checkstyle catches this except in the files it still suppresses.\n- Wildcard imports in `src/main`. Static wildcards in tests are fine.\n- An empty `catch` block. A tolerated exception is logged, or named `ignored` or\n  `expected` with a comment saying why swallowing is safe.\n\n## Architecture Expectations\n\nFloci follows a layered design:\n\n- Controllers / handlers parse AWS protocol input and produce AWS-compatible responses\n- Services contain business logic and should throw `AwsException`\n- Models hold domain data\n\nCore infrastructure commonly relevant in reviews:\n\n- `EmulatorConfig`\n- `ServiceRegistry`\n- `StorageFactory`\n- `AwsQueryController`\n- `AwsJson11Controller`\n- `AwsException`\n- `AwsExceptionMapper`\n- `EmulatorLifecycle`\n\nCheck that controllers stay thin, business logic remains in services, and new changes fit existing repository patterns.\n\n## Protocol Review Rules\n\nFloci implements real AWS wire protocols. Review protocol-affecting changes carefully.\n\n- Query services should keep form-encoded POST requests with `Action` and XML responses\n- JSON 1.1 services should keep `X-Amz-Target` requests and AWS-style JSON responses\n- REST JSON and REST XML services should stay aligned with AWS path and payload conventions\n- TCP-based services should not drift into HTTP-style abstractions\n\nPay extra attention to these cases:\n\n- CloudWatch Metrics supports both Query and JSON 1.1 and both paths must stay aligned\n- SQS and SNS may have multiple compatibility paths that must not drift\n- Cognito well-known endpoints are OIDC REST JSON endpoints, not AWS management APIs\n- Management APIs should ideally be validated with AWS SDK clients, not only handcrafted HTTP\n\n## XML and JSON Rules\n\nFlag PRs that:\n\n- Ignore `AwsNamespaces` constants\n- Return JSON errors that do not follow AWS error structures\n- Change controller return types in ways that may break reflection or native-image compatibility\n\n## Config and Storage Review\n\nWhen a PR changes configuration or persistence behavior, verify the change is wired consistently.\n\nCheck for updates to:\n\n- `EmulatorConfig`\n- main `application.yml`\n- test `application.yml`\n- `StorageFactory`\n- lifecycle hooks when relevant\n\nSupported storage modes include:\n\n- `memory`\n- `persistent`\n- `hybrid`\n- `wal`\n\nTreat repository YAML as the source of truth for runtime behavior unless the PR explicitly changes configuration semantics.\n\n## Testing Expectations\n\nExpect automated coverage for changes that affect:\n\n- request parsing\n- response shape\n- error handling\n- persistence semantics\n- URL generation\n- service enablement\n\nPrefer:\n\n- AWS SDK-based validation over raw HTTP-only testing\n- integration tests for compatibility-sensitive behavior\n- existing naming conventions such as `*ServiceTest.java` and `*IntegrationTest.java`\n\nIf behavior changes without automated coverage, call that out explicitly.\n\n## Review Checklist\n\nWhen analyzing a PR, check:\n\n- Is the change focused?\n- Does it preserve AWS-compatible wire behavior?\n- Does it reuse an existing Floci pattern?\n- Are controllers thin and services responsible for domain logic?\n- Are `AwsException` and existing error-mapping patterns used correctly?\n- Are config and YAML updates complete?\n- Are storage changes wired through `StorageFactory`?\n- Are tests added or updated where compatibility is affected?\n- Are docs updated when user-facing behavior changes?\n- Does new code follow the AGENTS.md Code Style rules: explicit types over `var`,\n  imported classes over inline fully-qualified names, no wildcard imports in\n  `src/main`, braces in conditionals, constructor injection?\n\n## How to Write Feedback\n\nWrite review comments that are:\n\n- specific\n- repository-aware\n- grounded in AWS compatibility risk\n\nUse severity when helpful:\n\n- `high`: likely breaks AWS SDK / CLI compatibility or protocol behavior\n- `medium`: inconsistent with Floci architecture, wiring, or testing expectations\n- `low`: maintainability, clarity, or minor convention issue\n\nPrefer comments that explain:\n\n- what is risky\n- why it matters in Floci\n- which existing pattern should be followed instead\n\n## If Behavior Is Unclear\n\nUse this fallback order:\n\n1. Prefer AWS behavior\n2. Then existing Floci behavior\n3. Then compatibility test expectations\n\nIf correctness would require a broader architectural change, call out the tradeoff instead of suggesting blind refactoring.\n","tools/docs/services.yaml":"# Registry of services whose Supported Actions tables are auto-generated.\n#\n# The action LIST comes from handler source (mode: switch|rest); the Description\n# column is hand-written and PRESERVED across regen, keyed by action name.\n# Per-service overrides: rename_actions (method->canonical), exclude_actions.\n#\n# Phase 1 covers single-table switch-mode services. REST controllers, multi-table\n# docs, mixed-mode (ses), S3 query-string dispatch, and non-tabular docs are tracked\n# as follow-ups; their switch handlers are acknowledged in deferred_handlers below so\n# the new-service sentinel only fires on genuinely unregistered handlers.\n\nservices:\n  - service: account\n    doc: docs/services/account.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/account/AccountController.java, mode: rest }\n  - service: accessanalyzer\n    doc: docs/services/access-analyzer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/accessanalyzer/AccessAnalyzerController.java, mode: rest }\n  - service: acm\n    doc: docs/services/acm.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/acm/AcmJsonHandler.java, mode: switch }\n  - service: applicationautoscaling\n    doc: docs/services/applicationautoscaling.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ApplicationAutoScalingJsonHandler.java, mode: switch }\n  - service: athena\n    doc: docs/services/athena.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/athena/AthenaJsonHandler.java, mode: switch }\n  - service: bedrock\n    doc: docs/services/bedrock.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrock/BedrockController.java, mode: rest }\n  - service: bedrock-agentcore-control\n    doc: docs/services/bedrock-agentcore.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreControlController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreIdentityController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreGatewayController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreMemoryController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreToolsController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreGatewayRuleController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreResourcePolicyController.java, mode: rest }\n    rename_actions:\n      CreateEndpoint: CreateAgentRuntimeEndpoint\n      GetEndpoint: GetAgentRuntimeEndpoint\n      UpdateEndpoint: UpdateAgentRuntimeEndpoint\n      DeleteEndpoint: DeleteAgentRuntimeEndpoint\n      ListEndpoints: ListAgentRuntimeEndpoints\n      Create: CreateWorkloadIdentity\n      Get: GetWorkloadIdentity\n      Update: UpdateWorkloadIdentity\n      Delete: DeleteWorkloadIdentity\n      List: ListWorkloadIdentities\n      CreateTarget: CreateGatewayTarget\n      GetTarget: GetGatewayTarget\n      UpdateTarget: UpdateGatewayTarget\n      DeleteTarget: DeleteGatewayTarget\n      ListTargets: ListGatewayTargets\n  - service: ce\n    doc: docs/services/ce.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ce/CostExplorerJsonHandler.java, mode: switch }\n  - service: cloudformation\n    doc: docs/services/cloudformation.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationQueryHandler.java, mode: switch }\n  - service: cloudhsmv2\n    doc: docs/services/cloudhsmv2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudhsmv2/CloudHsmV2JsonHandler.java, mode: switch }\n  - service: cloudtrail\n    doc: docs/services/cloudtrail.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudtrail/CloudTrailJsonHandler.java, mode: switch }\n  - service: codeartifact\n    doc: docs/services/codeartifact.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/codeartifact/CodeArtifactController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/V1TagsController.java, mode: rest }\n    rename_actions:\n      ListTagsForResourceByQuery: ListTagsForResource\n    exclude_actions:\n      - ListTags\n      - TagResourcePost\n      - TagResourcePut\n  - service: codegurureviewer\n    doc: docs/services/codegurureviewer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/codegurureviewer/CodeGuruReviewerController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: lambdaweb\n    doc: docs/services/lambda-web-functions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/lambdaweb/LambdaWebController.java, mode: rest }\n  - service: marketplace\n    doc: docs/services/marketplace.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceCatalogController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceAgreementService.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceEntitlementController.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceDeploymentController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceReportingController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceMeteringService.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceDiscoveryController.java, mode: rest }\n    exclude_actions:\n      - AgreementType\n      - Status\n      - AcceptorAccountId\n      - OfferId\n      - OfferSetId\n      - ResourceIdentifier\n      - ResourceType\n      - PartyType\n      - BeforeStartTime\n      - AfterStartTime\n      - BeforeEndTime\n      - AfterEndTime\n      - BeforeLastUpdateTime\n      - AfterLastUpdateTime\n      - EndTimeBehaviorType\n      - EndTimeBehaviorReasonCode\n      - InitialAgreementId\n      - LicenseArn\n      - StartTime\n      - LastUpdateTime\n      - EndTime\n      - ASCENDING\n      - DESCENDING\n  - service: comprehend\n    doc: docs/services/comprehend.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/comprehend/ComprehendJsonHandler.java, mode: switch }\n  - service: controlcatalog\n    doc: docs/services/controlcatalog.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/controlcatalog/ControlCatalogController.java, mode: rest }\n    rename_actions:\n      GetControl: GetControl\n      ListControls: ListControls\n  - service: appintegrations\n    doc: docs/services/appintegrations.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/appintegrations/AppIntegrationsController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: dlm\n    doc: docs/services/dlm.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dlm/DlmController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: connect\n    doc: docs/services/connect.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/connect/ConnectController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: rekognition\n    doc: docs/services/rekognition.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/rekognition/RekognitionJsonHandler.java, mode: switch }\n  - service: translate\n    doc: docs/services/translate.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/translate/TranslateJsonHandler.java, mode: switch }\n  - service: detective\n    doc: docs/services/detective.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/detective/DetectiveController.java, mode: rest }\n  - service: dms\n    doc: docs/services/dms.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dms/DmsJsonHandler.java, mode: switch }\n  - service: docdb\n    doc: docs/services/docdb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/docdb/DocDbQueryHandler.java, mode: switch }\n  - service: dsql\n    doc: docs/services/dsql.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dsql/DsqlController.java, mode: rest }\n  - service: efs\n    doc: docs/services/efs.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/efs/EfsController.java, mode: rest }\n  - service: organizations\n    doc: docs/services/organizations.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/organizations/OrganizationsJsonHandler.java, mode: switch }\n  - service: elasticache\n    doc: docs/services/elasticache.md\n    exclude_actions: [SubnetIds, SecurityGroupIds, SnapshotArnsToRestore]\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/elasticache/ElastiCacheQueryHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/elasticache/ElastiCacheServerlessQueryHandler.java, mode: switch }\n  - service: emr\n    doc: docs/services/emr.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/emr/EmrHandler.java, mode: switch }\n  - service: eventbridge\n    doc: docs/services/eventbridge.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/eventbridge/EventBridgeHandler.java, mode: switch }\n  - service: firehose\n    doc: docs/services/firehose.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/firehose/FirehoseJsonHandler.java, mode: switch }\n  - service: resource-explorer-2\n    doc: docs/services/resource-explorer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/resourceexplorer2/ResourceExplorer2Controller.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: fis\n    doc: docs/services/fis.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/fis/FisController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: kinesis\n    doc: docs/services/kinesis.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kinesis/KinesisJsonHandler.java, mode: switch }\n  - service: kinesisanalytics\n    doc: docs/services/kinesisanalytics.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kinesisanalytics/KinesisAnalyticsV2JsonHandler.java, mode: switch }\n  - service: kms\n    doc: docs/services/kms.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kms/KmsJsonHandler.java, mode: switch }\n  - service: memorydb\n    doc: docs/services/memorydb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/memorydb/MemoryDbHandler.java, mode: switch }\n  - service: macie2\n    doc: docs/services/macie2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/macie2/MacieController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/securityadmin/SecurityAdminController.java, mode: rest }\n    rename_actions:\n      ListAdmin: ListOrganizationAdminAccounts\n  - service: resource-groups\n    doc: docs/services/resource-groups.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/resourcegroups/ResourceGroupsController.java, mode: rest }\n  - service: inspector2\n    doc: docs/services/inspector2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/inspector2/Inspector2Controller.java, mode: rest }\n  - service: neptune\n    doc: docs/services/neptune.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/neptune/NeptuneQueryHandler.java, mode: switch }\n  - service: networkfirewall\n    doc: docs/services/network-firewall.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/networkfirewall/NetworkFirewallJsonHandler.java, mode: switch }\n  - service: ram\n    doc: docs/services/ram.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ram/RamController.java, mode: rest }\n  - service: rds\n    doc: docs/services/rds.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/rds/RdsQueryHandler.java, mode: switch }\n    exclude_actions: [SubnetIds, VpcSecurityGroupIds, OptionsToRemove, ValuesToAdd, ValuesToRemove]\n  - service: redshift\n    doc: docs/services/redshift.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshift/RedshiftQueryHandler.java, mode: switch }\n    # SubnetIds / VpcSecurityGroupIds / IamRoles / AddIamRoles / RemoveIamRoles / TagKeys / TagValues /\n    # DbGroups / ScheduleDefinitions are labels of an inner member-name switch (memberKeyRegex), not\n    # actions, same shape as the rds exclusions above.\n    exclude_actions: [SubnetIds, VpcSecurityGroupIds, IamRoles, AddIamRoles, RemoveIamRoles, TagKeys, TagValues, DbGroups, ScheduleDefinitions]\n  - service: redshiftdata\n    doc: docs/services/redshift-data.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftdata/RedshiftDataJsonHandler.java, mode: switch }\n    # ExecuteSql / BatchExecuteSql are the deprecated pre-2020 operations: the handler\n    # recognizes them only to return a ValidationException, they are not supported actions.\n    exclude_actions: [ExecuteSql, BatchExecuteSql]\n  - service: redshiftserverless\n    doc: docs/services/redshift-serverless.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftserverless/RedshiftServerlessJsonHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftserverless/RedshiftServerlessAdminJsonHandler.java, mode: switch }\n  - service: cognitoidentity\n    doc: docs/services/cognitoidentity.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cognitoidentity/CognitoIdentityJsonHandler.java, mode: switch }\n  - service: globalaccelerator\n    doc: docs/services/globalaccelerator.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/globalaccelerator/GlobalAcceleratorJsonHandler.java, mode: switch }\n  - service: datasync\n    doc: docs/services/datasync.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/datasync/DataSyncJsonHandler.java, mode: switch }\n  - service: route53\n    doc: docs/services/route53.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/route53/Route53Controller.java, mode: rest }\n    # JAX-RS method names use Java casing; map them onto the canonical Route 53 action names.\n    rename_actions:\n      AssociateVpcWithHostedZone: AssociateVPCWithHostedZone\n      DisassociateVpcFromHostedZone: DisassociateVPCFromHostedZone\n      CreateVpcAssociationAuthorization: CreateVPCAssociationAuthorization\n      DeleteVpcAssociationAuthorization: DeleteVPCAssociationAuthorization\n      ListVpcAssociationAuthorizations: ListVPCAssociationAuthorizations\n      ListHostedZonesByVpc: ListHostedZonesByVPC\n      GetDnssec: GetDNSSEC\n  - service: route53resolver\n    doc: docs/services/route53resolver.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/route53resolver/Route53ResolverJsonHandler.java, mode: switch }\n  - service: sagemaker\n    doc: docs/services/sagemaker.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/sagemaker/SageMakerJsonHandler.java, mode: switch }\n  - service: securityhub\n    doc: docs/services/securityhub.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/securityhub/SecurityHubController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: secretsmanager\n    doc: docs/services/secrets-manager.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/secretsmanager/SecretsManagerJsonHandler.java, mode: switch }\n  - service: servicecatalog\n    doc: docs/services/service-catalog.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/servicecatalog/ServiceCatalogJsonHandler.java, mode: switch }\n  - service: servicequotas\n    doc: docs/services/servicequotas.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/servicequotas/ServiceQuotasJsonHandler.java, mode: switch }\n  - service: ssoadmin\n    doc: docs/services/ssoadmin.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ssoadmin/SsoAdminJsonHandler.java, mode: switch }\n  - service: identitystore\n    doc: docs/services/identitystore.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/identitystore/IdentityStoreJsonHandler.java, mode: switch }\n  - service: budgets\n    doc: docs/services/budgets.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/budgets/BudgetsJsonHandler.java, mode: switch }\n  - service: verifiedpermissions\n    doc: docs/services/verifiedpermissions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/verifiedpermissions/VerifiedPermissionsJsonHandler.java, mode: switch }\n  - service: timestream-influxdb\n    doc: docs/services/timestream-influxdb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/timestreaminfluxdb/TimestreamInfluxDbJsonHandler.java, mode: switch }\n  - service: sqs\n    doc: docs/services/sqs.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/sqs/SqsJsonHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/sqs/SqsQueryHandler.java, mode: switch }\n  - service: stepfunctions\n    doc: docs/services/step-functions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/stepfunctions/StepFunctionsJsonHandler.java, mode: switch }\n  - service: swf\n    doc: docs/services/swf.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/swf/SwfJsonHandler.java, mode: switch }\n  - service: wafv2\n    doc: docs/services/wafv2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/wafv2/WafV2Handler.java, mode: switch }\n\n# Switch handlers that exist but are intentionally not yet covered (see reasons).\n# A NEW switch handler not listed here and not registered above fails `--strict`.\ndeferred_handlers:\n  - { path: src/main/java/io/github/hectorvent/floci/services/apigatewayv2/ApiGatewayV2JsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ScalingPolicyAlarmActionHandler.java }  # not a JSON/Query action dispatcher — its `case \"X\" ->` arms are the StepScaling AdjustmentType switch, not AWS actions\n  - { path: src/main/java/io/github/hectorvent/floci/services/autoscaling/AutoScalingQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/bcmpricingcalculator/BcmPricingCalculatorJsonHandler.java }  # focused workload-estimate surface\n  - { path: src/main/java/io/github/hectorvent/floci/services/bcmdataexports/BcmDataExportsJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudcontrol/CloudControlJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudmap/CloudMapHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/logs/CloudWatchLogsHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/logs/CloudWatchLogsMetricFilterHandler.java }  # multi-table doc (nested handler, reached through CloudWatchLogsHandler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/metrics/CloudWatchMetricsJsonHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/metrics/CloudWatchMetricsQueryHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/codebuild/CodeBuildJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/codedeploy/CodeDeployJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cognito/CognitoJsonHandler.java }  # non-tabular doc (grouped/inline); separate docs effort\n  - { path: src/main/java/io/github/hectorvent/floci/services/configservice/ConfigServiceJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cur/CurJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/dynamodb/NativeDynamoDbJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/dynamodb/NativeDynamoDbStreamsJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ec2/Ec2QueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/ecr/EcrJsonHandler.java }  # doc packs multiple actions per row (`A` / `B`) + REST endpoints; needs de-grouping\n  - { path: src/main/java/io/github/hectorvent/floci/services/ecs/EcsJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elasticbeanstalk/ElasticBeanstalkQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elb/ElbClassicQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2QueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/glue/GlueJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/iam/IamQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/iam/StsQueryHandler.java }  # sts (lives in iam package); single-table, registerable next\n  - { path: src/main/java/io/github/hectorvent/floci/services/lakeformation/LakeFormationJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/lightsail/LightsailJsonHandler.java }  # non-tabular doc (grouped/inline); standalone action table to be added\n  - { path: src/main/java/io/github/hectorvent/floci/services/pricing/PricingJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/resourcegroupstagging/ResourceGroupsTaggingJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/ses/SesQueryHandler.java }  # mixed-mode (Query + REST surface)\n  - { path: src/main/java/io/github/hectorvent/floci/services/sns/SnsJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/sns/SnsQueryHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ssm/Ec2MessagesJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ssm/SsmJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/textract/TextractJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/transcribe/TranscribeJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/transfer/TransferHandler.java }  # multi-table doc\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/floci-io/floci/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"Guidance for AI coding agents working in the Floci repository.\n\nThis file defines repository-specific operating rules for autonomous or semi-autonomous coding agents. Follow these instructions unless a maintainer explicitly tells you otherwise.\n\n---\n\n## Project Overview\n\nFloci is a Java-based local AWS emulator built on Quarkus.\n\nIts goal is full AWS SDK and AWS CLI compatibility through real AWS wire protocols, not convenience APIs or simplified abstractions.\n\nFloci acts as an open-source alternative to LocalStack Community.\n\n- Port: 4566\n- Stack:\n  - Java 25\n  - Quarkus 3.40.1\n  - JUnit 5\n  - RestAssured\n  - Jackson\n  - Docker integrations for Lambda, RDS, and ElastiCache\n\n---\n\n## First Principles\n\nWhen making changes, follow these priorities:\n\n1. Preserve AWS protocol compatibility\n2. Match AWS SDK and CLI behavior\n3. Reuse existing Floci patterns\n4. Prefer correctness over convenience\n5. Keep changes narrow and testable\n\nCritical rules:\n\n- Do not introduce custom endpoint shapes\n- Do not change request or response formats for convenience\n- Do not perform broad refactors unless the task explicitly requires them\n- Keep behavior aligned with AWS expectations and existing Floci conventions\n\n---\n\n## Architecture\n\nFloci follows a layered design:\n\n- **Controller / Handler**\n  - Parses AWS protocol input\n  - Produces AWS-compatible responses\n\n- **Service**\n  - Contains business logic\n  - Throws `AwsException`\n\n- **Model**\n  - Domain objects\n\n### Core Infrastructure\n\n- `EmulatorConfig`\n- `ServiceRegistry`\n- `StorageBackend` + `StorageFactory`\n- `AwsJson11Controller`\n- `AwsQueryController`\n- `AwsException` + `AwsExceptionMapper`\n- `EmulatorLifecycle`\n\n---\n\n## Package Layout\n\n- `io.github.hectorvent.floci.config`\n- `io.github.hectorvent.floci.core.common`\n- `io.github.hectorvent.floci.core.storage`\n- `io.github.hectorvent.floci.lifecycle`\n- `io.github.hectorvent.floci.services.<service>`\n\nTypical service structure:\n\n- `services/<svc>/`\n  - `*Controller.java` (REST JSON / REST XML services, via JAX-RS)\n  - `*QueryHandler.java` (AWS Query/XML protocol services)\n  - `*JsonHandler.java` (AWS JSON 1.1 protocol services)\n  - `*Service.java`\n  - `model/`\n\nThe entry-point class name depends on the service's AWS protocol (see \"AWS Protocol\nRules\" below); `Controller` is not universal.\n\nRule:\nCopy an existing service pattern before introducing a new one.\n\n---\n\n## AWS Protocol Rules\n\nFloci must implement real AWS wire protocols.\n\n| Protocol | Services | Request Format | Response Format | Implementation |\n|----------|----------|----------------|-----------------|----------------|\n| Query | SQS, SNS, IAM, STS, RDS, ElastiCache, CloudFormation, CloudWatch Metrics | form-encoded POST + `Action` | XML | `AwsQueryController` |\n| JSON 1.1 | SSM, EventBridge, CloudWatch Logs, Kinesis, KMS, Cognito, Secrets Manager, ACM | POST + `X-Amz-Target` | JSON | `AwsJson11Controller` |\n| REST JSON | Lambda, API Gateway, SES V2 | REST paths | JSON | JAX-RS |\n| REST XML | S3 | REST paths | XML | JAX-RS |\n| TCP | ElastiCache, RDS | raw protocol | native | proxies |\n\n### Important exceptions\n\n- CloudWatch Metrics supports both Query and JSON 1.1; handlers must remain aligned\n- SQS and SNS may expose multiple compatibility paths; do not let them drift\n- Cognito well-known endpoints are OIDC REST JSON endpoints, not AWS management APIs\n- Data-plane protocols may use raw TCP sockets\n- Management APIs should be validated with AWS SDK clients, not only handcrafted HTTP requests\n\n---\n\n### Partition literals\n\nFloci serves every AWS partition, not only the commercial one, so a literal that bakes\n`aws` into a code path is a bug in a GovCloud, China, ISO or EUSC deployment even when it\nprints correctly in `us-east-1`. `make partition-check` (CI: Partition Literals) inventories\n`src/main/java` for `arn:aws:` prefixes, `amazonaws.com` hosts, Route 53 hosted-zone ids,\nhand-rolled `arn:aws[a-z-]*:` regexes, `Arn.of(...)` calls with a blank region and comparisons\nwith the literal `\"aws\"` against `tools/partition/baseline.tsv`, per file. The baseline is empty, so any new one fails.\nA literal in a new file fails, growth in an existing file fails, and a drop fails until you\nrun `make partition-baseline` and commit the smaller baseline.\n\n- Mint ARNs through `AwsArnUtils.Arn.of(...)` / `RegionResolver.buildArn(...)`, recognise\n  them with `AwsArnUtils.isArn` / `PARTITION_REGEX`, and derive hosts from the region's\n  DNS suffix (`AwsRegions.dnsSuffixFor`).\n- A regionless ARN (IAM, STS, CloudFront, Organizations, an S3 bucket) never goes through\n  `Arn.of(service, \"\", ...)`, which silently means `aws`: use `RegionResolver.buildGlobalArn`\n  for the request's partition, or `Arn.global(partition, ...)` with the resource's own\n  (`AwsRegions.partitionFor(region)` for a bucket or a stack's region).\n- Never compare a partition with a literal: `\"aws\".equals(arn.partition())` is commercial-only\n  logic that fails silently in every other partition. Compare with the partition the resource\n  belongs to, `AwsRegions.partitionFor(region)` for a regional resource or the stored ARN's own\n  partition for a global one, and accept any published partition with `AwsPartitions.find(...)`\n  when only the shape matters. `\"aws\"` as an account (managed policies) or a command name is not\n  a partition: end that line with `// partition-literal: <reason>`.\n- A service principal is `ServicePrincipals.of(\"<service>\")`, never a literal: it is\n  `<service>.amazonaws.com` in every partition, and `ServicePrincipals.canonical` folds the\n  legacy per-partition forms a policy may still use before matching.\n- A literal that is genuinely partition-invariant (an XML namespace URI, an S3 canned-ACL\n  grantee URI) goes in `tools/partition/allowlist.yaml` with a reason, or ends its line\n  with `// partition-literal: <reason>`. Both are printed by `make partition-audit`.\n- Tests are not scanned; only assert a partition-dependent value when the test is about\n  partitions.\n\n## XML / JSON Rules\n\n- Use `XmlBuilder` for XML responses\n- Use `XmlParser` for XML parsing; do not use regex\n- Use `AwsNamespaces` constants\n- JSON errors must follow AWS error structures\n- Types returned directly from controllers must remain compatible with native-image reflection requirements\n\n---\n\n## Storage Rules\n\nSupported storage modes:\n\n- `memory`\n- `persistent`\n- `hybrid`\n- `wal`\n\nRules:\n\n- Always use `StorageFactory`\n- Do not instantiate storage implementations directly inside services\n- Respect lifecycle hooks for load and flush behavior\n\nImportant nuance:\n\nConfiguration interfaces may declare fallback defaults, but `application.yml` defines effective runtime behavior. Treat repository YAML as the source of truth unless a task explicitly changes configuration semantics.\n\nWhen adding storage-related behavior:\n\n1. Update `EmulatorConfig`\n2. Update main `application.yml`\n3. Update test `application.yml`\n4. Wire through `StorageFactory`\n5. Verify lifecycle integration\n\n---\n\n## Configuration Rules\n\nConfiguration lives under `floci.*`.\n\nWhen adding config:\n\n1. Add it to `EmulatorConfig`\n2. Add it to main `application.yml`\n3. Add it to test `application.yml` if needed\n4. Update documentation if user-facing\n5. Follow `FLOCI_*` environment variable conventions\n\nCritical areas:\n\n- `base-url`\n- `hostname`\n- region and account defaults\n- port ranges\n- persistence paths\n- Docker networking\n\n---\n\n## Build & Run\n\n    ./mvnw quarkus:dev\n    ./mvnw test\n    ./mvnw clean package\n    ./mvnw clean package -DskipTests\n\n`make help` lists the Makefile shortcuts. The native binary and image: `make native`\nthen `make native-image` (CI's flags, staged in `native/<arch>/`), `make native-host` for a\nbinary built on this machine with the installed GraalVM, `make native-up` to run the image,\n`make compat SUITES=\"sdk-test-java compat-cdk\"` for compatibility suites against it.\n\n### Focused tests\n\n    ./mvnw test -Dtest=SsmIntegrationTest\n    ./mvnw test -Dtest=SsmIntegrationTest#putParameter\n\n---\n\n## Compatibility Project\n\nCompatibility test suite: `./compatibility-tests/`\n\nGuidelines:\n\n- Prefer AWS SDK clients over raw HTTP for management-plane validation\n- Use this suite when changes may affect real SDK behavior\n\n---\n\n## Testing Rules\n\n### Conventions\n\n- Unit tests: `*ServiceTest.java`\n- Integration tests: `*IntegrationTest.java`\n- Prefer package-private constructors for testability\n- Integration tests may use ordered execution when stateful behavior requires it\n\n### Expectations\n\n- Test any behavior affecting AWS compatibility\n- Do not rely only on manual HTTP testing\n- Prefer SDK-based validation where possible\n\n### When touching protocol behavior\n\nIf a change affects request parsing, response shape, error handling, persistence semantics, URL generation, or service enablement:\n\n1. Add or update automated tests\n2. Prefer SDK-based verification where possible\n3. Check compatibility across alternate protocol paths\n4. Document intentional deviations clearly\n\n---\n\n## Error Handling\n\n- Services should throw `AwsException`\n- Query and REST XML flows should use `AwsExceptionMapper`\n- JSON 1.1 flows should return structured AWS error responses where required\n- Controller return types must remain reflection-safe\n\n---\n\n## Service Implementation Pattern\n\nWhen adding functionality:\n\n1. Identify the AWS protocol\n2. Reuse an existing service pattern\n3. Keep controllers thin\n4. Use `AwsException` for domain errors\n5. Reuse shared utilities\n6. Update config, storage, docs, and tests together\n7. Validate behavior against AWS SDK expectations\n\n---\n\n## Adding a New AWS Service\n\n1. Create a package under `services/<svc>/` with a Controller or protocol-specific handler,\n   a Service, and `model/`\n2. Add a `<Svc>ServiceConfig` interface and its accessor on `ServicesConfig` in `EmulatorConfig`\n3. Add one `descriptor(...)` entry in `ResolvedServiceCatalog`. This is the registration point;\n   `ServiceRegistry` only reads the catalog and has no registration API\n4. Add `floci.services.<key>.enabled` to both `src/main/resources/application.yml` and\n   `src/test/resources/application.yml`\n5. JSON 1.1 only: inject the `<Svc>JsonHandler` in `AwsJson11Controller`\n6. Obtain storage through `StorageFactory` and implement `Resettable`\n7. List any static `Random` or `SecureRandom` field under `--initialize-at-run-time` in\n   `application.yml`\n8. Check every timestamp member you emit for a `TimestampFormatTrait` before using the\n   epoch-seconds idiom. It is the awsJson1.1 default, but a model can override it per\n   member, and the mismatch is invisible to the AWS CLI because botocore coerces the\n   value, while strict SDKs (Go, Java) reject the whole response. `javap -c` on the SDK\n   model class shows the traits on each `SdkField`\n9. Add `<Svc>ServiceTest` and `<Svc>IntegrationTest`\n10. If the service mints ARNs or AWS hosts (`buildArn`, `Arn.of`, `new AwsArnUtils.Arn`, `dnsSuffixFor`, `AwsEndpoints`),\n   add a case to `PartitionCrossServiceSmokeIntegrationTest` and its `COVERED_PACKAGES`, or a row\n   with a reason to `src/test/resources/partition/smoke-exemptions.tsv`;\n   `PartitionSmokeInventoryTest` fails until one of the two exists\n11. Document it: `docs/services/<svc>.md`, a `mkdocs.yml` nav entry, a Service Matrix row in\n   `docs/services/index.md`, and a row in the README category table\n12. Register the handler in `tools/docs/services.yaml`, then run `make docs-sync` and\n    `make docs-check`\n13. Add a `TestFixtures` client factory and a `<Svc>Test` in `compatibility-tests/sdk-test-java`\n\n---\n\n## Adding a CloudFormation Resource Type\n\n**Every type is served by a per-service provisioner under\n`services/cloudformation/provisioners/`.** `CfnResourceDispatcher` only routes a resource to\nthe registry and stubs what nothing serves; never add type-specific code to it.\n\n1. Add the type to the existing `<Service>CfnProvisioner`, or create one:\n   `@ApplicationScoped`, injecting only the service it wraps. CDI discovery via\n   `CloudFormationResourceRegistry` handles registration: no manual wiring, but a\n   missing `@ApplicationScoped` silently means the type is never provisioned.\n2. `resourceTypes()` lists the `AWS::*` types; `provision(resource, props, ctx)`\n   does the work, switching on `resource.getResourceType()` when it serves several.\n3. Set **both** reference mechanisms. They are separate:\n   - `resource.setPhysicalId(...)` backs `Ref`\n   - `resource.getAttributes().put(...)` backs `Fn::GetAtt`, one entry per attribute\n   Omitting an attribute does not fail; `Fn::GetAtt` resolves to the literal\n   `\"LogicalId.Attr\"`. Source the attribute names from the type's registry schema in\n   `local/aws/cfn-resource-schemas/us-east-1/` (`readOnlyProperties`), and validate\n   `required` from the same file.\n4. **`provision` serves create *and* update.** On `UpdateStack` it is re-invoked with\n   the prior physical id and attributes already populated on the resource. Branch with\n   `ctx.isUpdate()` / `ctx.priorPhysicalId()`, not by reading the id off the resource:\n   `provision` assigns the new id as it runs, so a resource-derived check flips\n   mid-method.\n5. Override `delete(...)` when the type has a backing delete; tolerate already-deleted\n   via `CfnDeletes.safeDelete`, passing the specific \"already gone\" error codes. Never\n   a catch-all: a real failure such as `BucketNotEmpty` must propagate so the stack\n   reports `DELETE_FAILED`. When the delete needs a create-time attribute rather than\n   just the physical id, override `delete(StackResource, String)`.\n6. **Register in `src/test/resources/cloudformation/supported-resource-types.tsv`**\n   (`type<TAB>Owner`). `CfnResourceInventoryTest` diffs that file against the\n   CDI-resolved registry, so it also catches a missing `@ApplicationScoped`.\n7. **Add the provisioner to `CfnProvisionerFixture.inferredProvisioners()`** when it\n   takes a single service, or a fixture test naming that service silently falls through\n   to the stub arm.\n8. Tests: focused unit test mocking one service (`SqsCfnProvisioner`'s test is the\n   pattern) plus an integration test asserting the **exact `Fn::GetAtt` keys**. An\n   unmapped type is stubbed as `CREATE_COMPLETE` with a fake ARN, so asserting status\n   alone cannot detect a type that was never wired. Note the engine's constructor is\n   package-private, so tests in `provisioners/` mock it.\n9. Run `make docs-sync` and commit the result. The resource-type table in\n   `docs/services/cloudformation.md` is **generated** from the step-6 inventory; hand\n   edits fail `docs-check`. Labels, ordering and notes live in\n   `tools/docs/cfn_resource_types.yaml`.\n10. A schema `readOnlyProperties` entry you cannot set goes in\n    `src/test/resources/cloudformation/getatt-attribute-gaps.tsv` with a reason;\n    `CfnSchemaCoverageTest` requires every unset attribute to be fixed or recorded.\n\nReferences: `SqsCfnProvisioner` (smallest), `Ec2LaunchTemplateCfnProvisioner`\n(update-in-place and replacement), `LogsCfnProvisioner` (reconcile-vs-replace update).\n\n---\n\n## Sidecars\n\nA dependency too heavy for the native image (a native runtime, a large engine, another language\necosystem) ships as a sidecar: a stateless HTTP service in its own container that Floci starts\nlazily over the Docker socket. Sidecars live in\n[floci-io/floci-sidecars](https://github.com/floci-io/floci-sidecars), one directory per sidecar\non the shared `sidecar-core`, and are published as `floci/floci-sidecar-<name>:<semver>` on\nDocker Hub. They implement that repository's `docs/contract.md` and carry no AWS vocabulary: the\nsidecar answers a generic question, Floci maps its service semantics onto the answer.\n\nFloci-side rules:\n\n- Never add a `sidecars/` directory to this repository, never publish a `floci/floci:<tag>-<name>`\n  suffix, and never default an image to `:latest` (`ImageCacheService` never re-pulls a cached tag).\n- The consuming service owns two config knobs, `<name>-image` (an exact version) and `<name>-url`\n  (skip container management), and a `<Name>SidecarManager` plus `<Name>SidecarClient` pair.\n  `CedarSidecarManager` is the reference: it reads the contract JSON from `/health` and fails fast\n  on a contract-major mismatch.\n- The sidecar releases before the Floci PR that needs it, as a final or an `X.Y.Z-rc.N` tag; the\n  Floci PR pins that tag. A PR that needs an unreleased sidecar cannot pass CI.\n- Tests: the client's wire contract is covered by a JDK `HttpServer` fake; one Docker-gated\n  `@QuarkusTest` runs the pinned image through the real manager under its own\n  `floci.docker.resource-namespace`. A Floci test imports only Floci classes; the sidecar's own\n  behaviour is tested in its repository.\n- A sidecar that is a stock upstream image plus a script from the classpath needs no repository\n  but still goes through a manager with the same two knobs.\n\n## Code Style\n\n### Checkstyle\n\n- Java code must pass `./mvnw checkstyle:check` (CI: Checkstyle). The rules live in\n  `tools/checkstyle/checkstyle.xml`. Code excluded in `tools/checkstyle/suppressions.xml`\n  is not checked yet, but the rules below still apply to it.\n\n### General\n\n- Use constructor injection\n- Prefer self-explanatory code over comments\n- Avoid unnecessary comments\n- Always use braces in conditionals\n- Never leave a `catch` block empty. If an exception is intentionally tolerated, log it with enough context to diagnose it later.\n  When swallowing really is correct and logging would be noise, name the variable\n  `ignored` or `expected` and say in a comment why it is safe. A bare\n  `catch (Exception e) {}` is never acceptable.\n- Follow existing project patterns\n- Use modern Java features only when they improve clarity\n\n### Types and names\n\n- **Do not use `var`. Write the explicit type.** Floci reproduces AWS wire\n  contracts, so the concrete type at a call site is usually the thing under\n  review: whether a value is a `LinkedHashMap` or a `Map`, an AWS model type or a\n  JDK one, is exactly what a reviewer needs to see. This covers local\n  declarations, enhanced-for (`for (Tag tag : tags)`), classic for-init, and\n  try-with-resources. The one exception is a record deconstruction pattern\n  (`case Node(var left, var right) ->`), where naming the component types is pure\n  noise.\n- **Import the classes you use. Do not write fully-qualified names inline.**\n  `new ArrayList<>()`, never `new java.util.ArrayList<>()`. Qualify inline only for\n  a genuine name clash inside one file: import the type used more often, qualify the\n  other, and leave a short comment naming the clash. Checkstyle (`NoFqcn`) enforces\n  this except in the files listed in `tools/checkstyle/suppressions.xml`.\n\n### Imports\n\n- No wildcard imports in `src/main`. Static wildcards stay fine in tests, where\n  `Assertions.*`, `Mockito.*` and `Matchers.*` are the established idiom.\n- Import order: non-`java`/`javax` imports, then `javax.*`, then `java.*`, then\n  static imports. Alphabetize each group. Separate the non-JDK, JDK, and static\n  sections with a blank line; no blank line is needed between `javax` and `java`.\n  This follows IntelliJ's default layout and is enforced by Checkstyle.\n\n### Conventions the codebase already follows\n\nWritten down so they stay true. New code should match them without thinking.\nA handful of files predate them; a violation you find in the tree is a straggler,\nnot a precedent.\n\n- 4-space indentation, K&R braces. Never indent with a tab.\n- JBoss Logging, in a field named `LOG`, using the parameterized `...v()` form.\n  No string concatenation in log calls.\n- No `printStackTrace`, anywhere. No `System.out` or `System.err` in `src/main`; the\n  one exception is the CLI entry point `io.github.hectorvent.floci.tools.ami.AmiImageTool`,\n  where stdout is the program's output. A few tests print a failure repro just before\n  failing, which is the only good reason to print from a test: an assertion message\n  usually says it better.\n- `java.time` for everything Floci owns. `Calendar` and `SimpleDateFormat` appear\n  nowhere and must not be introduced. A `Date` survives only at a third-party boundary\n  that forces one: the BouncyCastle certificate builder, the JAX-RS\n  `HttpHeaders.getDate()` override, and JDBC's `java.sql.Date` in the RDS Data mapper.\n  Convert at that boundary with `Date.from(instant)` and keep `java.time` on Floci's\n  side of it.\n- Constructor injection in `src/main`. Field injection is fine in tests, and\n  `Instance<T>` field injection is a legitimate CDI pattern.\n- `Optional` as a return type, and never as a field: there are none, keep it that way.\n  It reaches a parameter only where a Quarkus `@ConfigProperty Optional<T>` is threaded\n  through; do not introduce it as a parameter for anything else.\n- Switch expressions over switch statements. Pattern-matching `instanceof` over\n  cast-after-check.\n- `AwsException` for domain errors.\n- `final` on service fields, but not on locals or parameters.\n\n### Tests\n\nThese describe `src/test`. `compatibility-tests` is a separate module with the opposite\nidiom, AssertJ and `@DisplayName` in nearly every file. Follow the module you are in.\n\n- Name test methods either as a camelCase sentence (`putAndGetFromMemory`) or as\n  `method_scenario_expectation`. Both are established. `testX` names are common in\n  older tests and are not the pattern to copy.\n- JUnit 5 assertions with Hamcrest and RestAssured matchers. AssertJ is a declared test\n  dependency, used by the Lambda launcher tests; prefer the established matchers\n  everywhere else.\n- `@DisplayName` is not used here. The method name carries the intent.\n\n---\n\n## Documentation Style\n\n- No em-dashes anywhere, in any content. Use colons, commas, or periods.\n\n## Logging\n\n- Use JBoss Logging\n- Keep logs structured\n- Avoid noisy logs in hot paths\n\n---\n\n## Pull Request Guidelines\n\n- Keep changes focused\n- Avoid unrelated refactors\n- Preserve behavior unless the task explicitly requires change\n- Update docs when necessary\n- Explain missing tests when behavior changed but no automated coverage was added\n\nConventional commits:\n\n- `feat:`\n- `fix:`\n- `perf:`\n- `docs:`\n- `chore:`\n\nDo not add `Co-Authored-By` trailers for AI tools in commit messages. Keep attribution limited to human contributors.\n\n---\n\n## Release Awareness\n\n- Changes merged into `main` do not automatically imply a stable release\n- Releases are cut from `main` via the \"Release Cut\" workflow (`workflow_dispatch`\n  on `.github/workflows/release-cut.yml`), which runs semantic-release: it bumps\n  `pom.xml`, writes `CHANGELOG.md`, commits, tags, and creates the GitHub Release\n- `release/x.y.x` branches are retired for now\n- Tags still trigger the publishing workflows (`release.yml`)\n\nTreat release workflows as critical infrastructure.\n\n---\n\n## Agent Workflow\n\n### Before editing\n\n1. Identify service and protocol\n2. Locate an existing implementation to mirror\n3. Check config impact\n4. Check storage impact\n5. Check documentation impact\n6. Define the minimal useful test plan\n\n### Before finishing\n\n1. Run relevant tests\n2. Validate protocol behavior\n3. Ensure no custom endpoints were introduced\n4. Verify config and docs updates\n\n---\n\n## Common Mistakes\n\n- Creating non-AWS endpoints\n- Bypassing `StorageFactory`\n- Changing wire formats without tests\n- Forgetting YAML updates\n- Producing inconsistent URLs or ARNs\n- Testing only with raw HTTP\n- Introducing unnecessary new patterns\n- Adding type-specific code to `CfnResourceDispatcher` instead of a per-service\n  provisioner\n- Setting a CloudFormation resource's physical id but not its `Fn::GetAtt`\n  attributes (they are two separate mechanisms, and the miss is silent)\n- Hand-editing the resource-type table in `docs/services/cloudformation.md`, which is\n  generated, run `make docs-sync` instead\n- Adding a CloudFormation provisioner without a row in\n  `supported-resource-types.tsv` or an entry in `CfnProvisionerFixture`, either of\n  which leaves a type quietly served by the stub arm\n\n---\n\n## Human Handoff\n\nIf behavior is unclear:\n\n1. Prefer AWS behavior\n2. Then existing Floci behavior\n3. Then compatibility test expectations\n\nIf a task would require broad architectural changes, stop and surface the tradeoffs instead of refactoring across services blindly.\n","isInternal":false,"tokens":5828,"sizeBytes":24184},{"name":"services.yaml","path":"tools/docs/services.yaml","rawUrl":"https://raw.githubusercontent.com/floci-io/floci/HEAD/tools/docs/services.yaml","title":"Docs Skill","category":"anthropic-skill","format":"yaml","content":"# Registry of services whose Supported Actions tables are auto-generated.\n#\n# The action LIST comes from handler source (mode: switch|rest); the Description\n# column is hand-written and PRESERVED across regen, keyed by action name.\n# Per-service overrides: rename_actions (method->canonical), exclude_actions.\n#\n# Phase 1 covers single-table switch-mode services. REST controllers, multi-table\n# docs, mixed-mode (ses), S3 query-string dispatch, and non-tabular docs are tracked\n# as follow-ups; their switch handlers are acknowledged in deferred_handlers below so\n# the new-service sentinel only fires on genuinely unregistered handlers.\n\nservices:\n  - service: account\n    doc: docs/services/account.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/account/AccountController.java, mode: rest }\n  - service: accessanalyzer\n    doc: docs/services/access-analyzer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/accessanalyzer/AccessAnalyzerController.java, mode: rest }\n  - service: acm\n    doc: docs/services/acm.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/acm/AcmJsonHandler.java, mode: switch }\n  - service: applicationautoscaling\n    doc: docs/services/applicationautoscaling.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ApplicationAutoScalingJsonHandler.java, mode: switch }\n  - service: athena\n    doc: docs/services/athena.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/athena/AthenaJsonHandler.java, mode: switch }\n  - service: bedrock\n    doc: docs/services/bedrock.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrock/BedrockController.java, mode: rest }\n  - service: bedrock-agentcore-control\n    doc: docs/services/bedrock-agentcore.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreControlController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreIdentityController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreGatewayController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreMemoryController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreToolsController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreGatewayRuleController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreResourcePolicyController.java, mode: rest }\n    rename_actions:\n      CreateEndpoint: CreateAgentRuntimeEndpoint\n      GetEndpoint: GetAgentRuntimeEndpoint\n      UpdateEndpoint: UpdateAgentRuntimeEndpoint\n      DeleteEndpoint: DeleteAgentRuntimeEndpoint\n      ListEndpoints: ListAgentRuntimeEndpoints\n      Create: CreateWorkloadIdentity\n      Get: GetWorkloadIdentity\n      Update: UpdateWorkloadIdentity\n      Delete: DeleteWorkloadIdentity\n      List: ListWorkloadIdentities\n      CreateTarget: CreateGatewayTarget\n      GetTarget: GetGatewayTarget\n      UpdateTarget: UpdateGatewayTarget\n      DeleteTarget: DeleteGatewayTarget\n      ListTargets: ListGatewayTargets\n  - service: ce\n    doc: docs/services/ce.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ce/CostExplorerJsonHandler.java, mode: switch }\n  - service: cloudformation\n    doc: docs/services/cloudformation.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationQueryHandler.java, mode: switch }\n  - service: cloudhsmv2\n    doc: docs/services/cloudhsmv2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudhsmv2/CloudHsmV2JsonHandler.java, mode: switch }\n  - service: cloudtrail\n    doc: docs/services/cloudtrail.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudtrail/CloudTrailJsonHandler.java, mode: switch }\n  - service: codeartifact\n    doc: docs/services/codeartifact.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/codeartifact/CodeArtifactController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/V1TagsController.java, mode: rest }\n    rename_actions:\n      ListTagsForResourceByQuery: ListTagsForResource\n    exclude_actions:\n      - ListTags\n      - TagResourcePost\n      - TagResourcePut\n  - service: codegurureviewer\n    doc: docs/services/codegurureviewer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/codegurureviewer/CodeGuruReviewerController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: lambdaweb\n    doc: docs/services/lambda-web-functions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/lambdaweb/LambdaWebController.java, mode: rest }\n  - service: marketplace\n    doc: docs/services/marketplace.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceCatalogController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceAgreementService.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceEntitlementController.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceDeploymentController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceReportingController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceMeteringService.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceDiscoveryController.java, mode: rest }\n    exclude_actions:\n      - AgreementType\n      - Status\n      - AcceptorAccountId\n      - OfferId\n      - OfferSetId\n      - ResourceIdentifier\n      - ResourceType\n      - PartyType\n      - BeforeStartTime\n      - AfterStartTime\n      - BeforeEndTime\n      - AfterEndTime\n      - BeforeLastUpdateTime\n      - AfterLastUpdateTime\n      - EndTimeBehaviorType\n      - EndTimeBehaviorReasonCode\n      - InitialAgreementId\n      - LicenseArn\n      - StartTime\n      - LastUpdateTime\n      - EndTime\n      - ASCENDING\n      - DESCENDING\n  - service: comprehend\n    doc: docs/services/comprehend.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/comprehend/ComprehendJsonHandler.java, mode: switch }\n  - service: controlcatalog\n    doc: docs/services/controlcatalog.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/controlcatalog/ControlCatalogController.java, mode: rest }\n    rename_actions:\n      GetControl: GetControl\n      ListControls: ListControls\n  - service: appintegrations\n    doc: docs/services/appintegrations.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/appintegrations/AppIntegrationsController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: dlm\n    doc: docs/services/dlm.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dlm/DlmController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: connect\n    doc: docs/services/connect.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/connect/ConnectController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: rekognition\n    doc: docs/services/rekognition.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/rekognition/RekognitionJsonHandler.java, mode: switch }\n  - service: translate\n    doc: docs/services/translate.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/translate/TranslateJsonHandler.java, mode: switch }\n  - service: detective\n    doc: docs/services/detective.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/detective/DetectiveController.java, mode: rest }\n  - service: dms\n    doc: docs/services/dms.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dms/DmsJsonHandler.java, mode: switch }\n  - service: docdb\n    doc: docs/services/docdb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/docdb/DocDbQueryHandler.java, mode: switch }\n  - service: dsql\n    doc: docs/services/dsql.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dsql/DsqlController.java, mode: rest }\n  - service: efs\n    doc: docs/services/efs.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/efs/EfsController.java, mode: rest }\n  - service: organizations\n    doc: docs/services/organizations.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/organizations/OrganizationsJsonHandler.java, mode: switch }\n  - service: elasticache\n    doc: docs/services/elasticache.md\n    exclude_actions: [SubnetIds, SecurityGroupIds, SnapshotArnsToRestore]\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/elasticache/ElastiCacheQueryHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/elasticache/ElastiCacheServerlessQueryHandler.java, mode: switch }\n  - service: emr\n    doc: docs/services/emr.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/emr/EmrHandler.java, mode: switch }\n  - service: eventbridge\n    doc: docs/services/eventbridge.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/eventbridge/EventBridgeHandler.java, mode: switch }\n  - service: firehose\n    doc: docs/services/firehose.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/firehose/FirehoseJsonHandler.java, mode: switch }\n  - service: resource-explorer-2\n    doc: docs/services/resource-explorer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/resourceexplorer2/ResourceExplorer2Controller.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: fis\n    doc: docs/services/fis.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/fis/FisController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: kinesis\n    doc: docs/services/kinesis.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kinesis/KinesisJsonHandler.java, mode: switch }\n  - service: kinesisanalytics\n    doc: docs/services/kinesisanalytics.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kinesisanalytics/KinesisAnalyticsV2JsonHandler.java, mode: switch }\n  - service: kms\n    doc: docs/services/kms.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kms/KmsJsonHandler.java, mode: switch }\n  - service: memorydb\n    doc: docs/services/memorydb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/memorydb/MemoryDbHandler.java, mode: switch }\n  - service: macie2\n    doc: docs/services/macie2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/macie2/MacieController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/securityadmin/SecurityAdminController.java, mode: rest }\n    rename_actions:\n      ListAdmin: ListOrganizationAdminAccounts\n  - service: resource-groups\n    doc: docs/services/resource-groups.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/resourcegroups/ResourceGroupsController.java, mode: rest }\n  - service: inspector2\n    doc: docs/services/inspector2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/inspector2/Inspector2Controller.java, mode: rest }\n  - service: neptune\n    doc: docs/services/neptune.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/neptune/NeptuneQueryHandler.java, mode: switch }\n  - service: networkfirewall\n    doc: docs/services/network-firewall.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/networkfirewall/NetworkFirewallJsonHandler.java, mode: switch }\n  - service: ram\n    doc: docs/services/ram.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ram/RamController.java, mode: rest }\n  - service: rds\n    doc: docs/services/rds.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/rds/RdsQueryHandler.java, mode: switch }\n    exclude_actions: [SubnetIds, VpcSecurityGroupIds, OptionsToRemove, ValuesToAdd, ValuesToRemove]\n  - service: redshift\n    doc: docs/services/redshift.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshift/RedshiftQueryHandler.java, mode: switch }\n    # SubnetIds / VpcSecurityGroupIds / IamRoles / AddIamRoles / RemoveIamRoles / TagKeys / TagValues /\n    # DbGroups / ScheduleDefinitions are labels of an inner member-name switch (memberKeyRegex), not\n    # actions, same shape as the rds exclusions above.\n    exclude_actions: [SubnetIds, VpcSecurityGroupIds, IamRoles, AddIamRoles, RemoveIamRoles, TagKeys, TagValues, DbGroups, ScheduleDefinitions]\n  - service: redshiftdata\n    doc: docs/services/redshift-data.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftdata/RedshiftDataJsonHandler.java, mode: switch }\n    # ExecuteSql / BatchExecuteSql are the deprecated pre-2020 operations: the handler\n    # recognizes them only to return a ValidationException, they are not supported actions.\n    exclude_actions: [ExecuteSql, BatchExecuteSql]\n  - service: redshiftserverless\n    doc: docs/services/redshift-serverless.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftserverless/RedshiftServerlessJsonHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftserverless/RedshiftServerlessAdminJsonHandler.java, mode: switch }\n  - service: cognitoidentity\n    doc: docs/services/cognitoidentity.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cognitoidentity/CognitoIdentityJsonHandler.java, mode: switch }\n  - service: globalaccelerator\n    doc: docs/services/globalaccelerator.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/globalaccelerator/GlobalAcceleratorJsonHandler.java, mode: switch }\n  - service: datasync\n    doc: docs/services/datasync.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/datasync/DataSyncJsonHandler.java, mode: switch }\n  - service: route53\n    doc: docs/services/route53.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/route53/Route53Controller.java, mode: rest }\n    # JAX-RS method names use Java casing; map them onto the canonical Route 53 action names.\n    rename_actions:\n      AssociateVpcWithHostedZone: AssociateVPCWithHostedZone\n      DisassociateVpcFromHostedZone: DisassociateVPCFromHostedZone\n      CreateVpcAssociationAuthorization: CreateVPCAssociationAuthorization\n      DeleteVpcAssociationAuthorization: DeleteVPCAssociationAuthorization\n      ListVpcAssociationAuthorizations: ListVPCAssociationAuthorizations\n      ListHostedZonesByVpc: ListHostedZonesByVPC\n      GetDnssec: GetDNSSEC\n  - service: route53resolver\n    doc: docs/services/route53resolver.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/route53resolver/Route53ResolverJsonHandler.java, mode: switch }\n  - service: sagemaker\n    doc: docs/services/sagemaker.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/sagemaker/SageMakerJsonHandler.java, mode: switch }\n  - service: securityhub\n    doc: docs/services/securityhub.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/securityhub/SecurityHubController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: secretsmanager\n    doc: docs/services/secrets-manager.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/secretsmanager/SecretsManagerJsonHandler.java, mode: switch }\n  - service: servicecatalog\n    doc: docs/services/service-catalog.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/servicecatalog/ServiceCatalogJsonHandler.java, mode: switch }\n  - service: servicequotas\n    doc: docs/services/servicequotas.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/servicequotas/ServiceQuotasJsonHandler.java, mode: switch }\n  - service: ssoadmin\n    doc: docs/services/ssoadmin.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ssoadmin/SsoAdminJsonHandler.java, mode: switch }\n  - service: identitystore\n    doc: docs/services/identitystore.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/identitystore/IdentityStoreJsonHandler.java, mode: switch }\n  - service: budgets\n    doc: docs/services/budgets.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/budgets/BudgetsJsonHandler.java, mode: switch }\n  - service: verifiedpermissions\n    doc: docs/services/verifiedpermissions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/verifiedpermissions/VerifiedPermissionsJsonHandler.java, mode: switch }\n  - service: timestream-influxdb\n    doc: docs/services/timestream-influxdb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/timestreaminfluxdb/TimestreamInfluxDbJsonHandler.java, mode: switch }\n  - service: sqs\n    doc: docs/services/sqs.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/sqs/SqsJsonHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/sqs/SqsQueryHandler.java, mode: switch }\n  - service: stepfunctions\n    doc: docs/services/step-functions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/stepfunctions/StepFunctionsJsonHandler.java, mode: switch }\n  - service: swf\n    doc: docs/services/swf.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/swf/SwfJsonHandler.java, mode: switch }\n  - service: wafv2\n    doc: docs/services/wafv2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/wafv2/WafV2Handler.java, mode: switch }\n\n# Switch handlers that exist but are intentionally not yet covered (see reasons).\n# A NEW switch handler not listed here and not registered above fails `--strict`.\ndeferred_handlers:\n  - { path: src/main/java/io/github/hectorvent/floci/services/apigatewayv2/ApiGatewayV2JsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ScalingPolicyAlarmActionHandler.java }  # not a JSON/Query action dispatcher — its `case \"X\" ->` arms are the StepScaling AdjustmentType switch, not AWS actions\n  - { path: src/main/java/io/github/hectorvent/floci/services/autoscaling/AutoScalingQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/bcmpricingcalculator/BcmPricingCalculatorJsonHandler.java }  # focused workload-estimate surface\n  - { path: src/main/java/io/github/hectorvent/floci/services/bcmdataexports/BcmDataExportsJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudcontrol/CloudControlJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudmap/CloudMapHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/logs/CloudWatchLogsHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/logs/CloudWatchLogsMetricFilterHandler.java }  # multi-table doc (nested handler, reached through CloudWatchLogsHandler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/metrics/CloudWatchMetricsJsonHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/metrics/CloudWatchMetricsQueryHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/codebuild/CodeBuildJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/codedeploy/CodeDeployJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cognito/CognitoJsonHandler.java }  # non-tabular doc (grouped/inline); separate docs effort\n  - { path: src/main/java/io/github/hectorvent/floci/services/configservice/ConfigServiceJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cur/CurJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/dynamodb/NativeDynamoDbJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/dynamodb/NativeDynamoDbStreamsJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ec2/Ec2QueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/ecr/EcrJsonHandler.java }  # doc packs multiple actions per row (`A` / `B`) + REST endpoints; needs de-grouping\n  - { path: src/main/java/io/github/hectorvent/floci/services/ecs/EcsJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elasticbeanstalk/ElasticBeanstalkQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elb/ElbClassicQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2QueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/glue/GlueJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/iam/IamQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/iam/StsQueryHandler.java }  # sts (lives in iam package); single-table, registerable next\n  - { path: src/main/java/io/github/hectorvent/floci/services/lakeformation/LakeFormationJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/lightsail/LightsailJsonHandler.java }  # non-tabular doc (grouped/inline); standalone action table to be added\n  - { path: src/main/java/io/github/hectorvent/floci/services/pricing/PricingJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/resourcegroupstagging/ResourceGroupsTaggingJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/ses/SesQueryHandler.java }  # mixed-mode (Query + REST surface)\n  - { path: src/main/java/io/github/hectorvent/floci/services/sns/SnsJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/sns/SnsQueryHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ssm/Ec2MessagesJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ssm/SsmJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/textract/TextractJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/transcribe/TranscribeJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/transfer/TransferHandler.java }  # multi-table doc\n","isInternal":false,"tokens":6829,"sizeBytes":26585},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/floci-io/floci/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"# Copilot Instructions for Pull Request Review\n\nReview pull requests in the Floci repository with AWS compatibility as the primary concern.\n\nFloci is a Java-based local AWS emulator built on Quarkus. Its goal is to match AWS SDK and AWS CLI behavior through real AWS wire protocols, not convenience APIs or custom abstractions.\n\n## Review Priorities\n\nEvaluate changes in this order:\n\n1. Preserve AWS protocol compatibility\n2. Match AWS SDK and AWS CLI behavior\n3. Reuse existing Floci patterns\n4. Prefer correctness over convenience\n5. Keep changes focused and testable\n\n## What to Flag\n\nRaise concerns when a PR introduces any of the following without strong justification:\n\n- Non-AWS endpoint shapes\n- Request or response format changes made for convenience\n- Broad refactors unrelated to the PR goal\n- New service patterns where an existing Floci pattern should be reused\n- Direct storage implementation usage instead of `StorageFactory`\n- `var` where an explicit type belongs. Floci reproduces AWS wire contracts, so the\n  concrete type at a call site is usually what a reviewer needs to see. The one\n  exception is a record deconstruction pattern.\n- Fully-qualified class names written inline instead of imported, such as\n  `new java.util.ArrayList<>()`. A genuine name clash inside one file is fine.\n  Checkstyle catches this except in the files it still suppresses.\n- Wildcard imports in `src/main`. Static wildcards in tests are fine.\n- An empty `catch` block. A tolerated exception is logged, or named `ignored` or\n  `expected` with a comment saying why swallowing is safe.\n\n## Architecture Expectations\n\nFloci follows a layered design:\n\n- Controllers / handlers parse AWS protocol input and produce AWS-compatible responses\n- Services contain business logic and should throw `AwsException`\n- Models hold domain data\n\nCore infrastructure commonly relevant in reviews:\n\n- `EmulatorConfig`\n- `ServiceRegistry`\n- `StorageFactory`\n- `AwsQueryController`\n- `AwsJson11Controller`\n- `AwsException`\n- `AwsExceptionMapper`\n- `EmulatorLifecycle`\n\nCheck that controllers stay thin, business logic remains in services, and new changes fit existing repository patterns.\n\n## Protocol Review Rules\n\nFloci implements real AWS wire protocols. Review protocol-affecting changes carefully.\n\n- Query services should keep form-encoded POST requests with `Action` and XML responses\n- JSON 1.1 services should keep `X-Amz-Target` requests and AWS-style JSON responses\n- REST JSON and REST XML services should stay aligned with AWS path and payload conventions\n- TCP-based services should not drift into HTTP-style abstractions\n\nPay extra attention to these cases:\n\n- CloudWatch Metrics supports both Query and JSON 1.1 and both paths must stay aligned\n- SQS and SNS may have multiple compatibility paths that must not drift\n- Cognito well-known endpoints are OIDC REST JSON endpoints, not AWS management APIs\n- Management APIs should ideally be validated with AWS SDK clients, not only handcrafted HTTP\n\n## XML and JSON Rules\n\nFlag PRs that:\n\n- Ignore `AwsNamespaces` constants\n- Return JSON errors that do not follow AWS error structures\n- Change controller return types in ways that may break reflection or native-image compatibility\n\n## Config and Storage Review\n\nWhen a PR changes configuration or persistence behavior, verify the change is wired consistently.\n\nCheck for updates to:\n\n- `EmulatorConfig`\n- main `application.yml`\n- test `application.yml`\n- `StorageFactory`\n- lifecycle hooks when relevant\n\nSupported storage modes include:\n\n- `memory`\n- `persistent`\n- `hybrid`\n- `wal`\n\nTreat repository YAML as the source of truth for runtime behavior unless the PR explicitly changes configuration semantics.\n\n## Testing Expectations\n\nExpect automated coverage for changes that affect:\n\n- request parsing\n- response shape\n- error handling\n- persistence semantics\n- URL generation\n- service enablement\n\nPrefer:\n\n- AWS SDK-based validation over raw HTTP-only testing\n- integration tests for compatibility-sensitive behavior\n- existing naming conventions such as `*ServiceTest.java` and `*IntegrationTest.java`\n\nIf behavior changes without automated coverage, call that out explicitly.\n\n## Review Checklist\n\nWhen analyzing a PR, check:\n\n- Is the change focused?\n- Does it preserve AWS-compatible wire behavior?\n- Does it reuse an existing Floci pattern?\n- Are controllers thin and services responsible for domain logic?\n- Are `AwsException` and existing error-mapping patterns used correctly?\n- Are config and YAML updates complete?\n- Are storage changes wired through `StorageFactory`?\n- Are tests added or updated where compatibility is affected?\n- Are docs updated when user-facing behavior changes?\n- Does new code follow the AGENTS.md Code Style rules: explicit types over `var`,\n  imported classes over inline fully-qualified names, no wildcard imports in\n  `src/main`, braces in conditionals, constructor injection?\n\n## How to Write Feedback\n\nWrite review comments that are:\n\n- specific\n- repository-aware\n- grounded in AWS compatibility risk\n\nUse severity when helpful:\n\n- `high`: likely breaks AWS SDK / CLI compatibility or protocol behavior\n- `medium`: inconsistent with Floci architecture, wiring, or testing expectations\n- `low`: maintainability, clarity, or minor convention issue\n\nPrefer comments that explain:\n\n- what is risky\n- why it matters in Floci\n- which existing pattern should be followed instead\n\n## If Behavior Is Unclear\n\nUse this fallback order:\n\n1. Prefer AWS behavior\n2. Then existing Floci behavior\n3. Then compatibility test expectations\n\nIf correctness would require a broader architectural change, call out the tradeoff instead of suggesting blind refactoring.\n","isInternal":false,"tokens":1179,"sizeBytes":5683}],"systemPromptSnippet":"<yakaai_skills repo=\"floci-io/floci\">\n<!-- File: AGENTS.md (Tokens: ~5828 | Category: root-instruction) -->\nGuidance for AI coding agents working in the Floci repository.\n\nThis file defines repository-specific operating rules for autonomous or semi-autonomous coding agents. Follow these instructions unless a maintainer explicitly tells you otherwise.\n\n---\n\n## Project Overview\n\nFloci is a Java-based local AWS emulator built on Quarkus.\n\nIts goal is full AWS SDK and AWS CLI compatibility through real AWS wire protocols, not convenience APIs or simplified abstractions.\n\nFloci acts as an open-source alternative to LocalStack Community.\n\n- Port: 4566\n- Stack:\n  - Java 25\n  - Quarkus 3.40.1\n  - JUnit 5\n  - RestAssured\n  - Jackson\n  - Docker integrations for Lambda, RDS, and ElastiCache\n\n---\n\n## First Principles\n\nWhen making changes, follow these priorities:\n\n1. Preserve AWS protocol compatibility\n2. Match AWS SDK and CLI behavior\n3. Reuse existing Floci patterns\n4. Prefer correctness over convenience\n5. Keep changes narrow and testable\n\nCritical rules:\n\n- Do not introduce custom endpoint shapes\n- Do not change request or response formats for convenience\n- Do not perform broad refactors unless the task explicitly requires them\n- Keep behavior aligned with AWS expectations and existing Floci conventions\n\n---\n\n## Architecture\n\nFloci follows a layered design:\n\n- **Controller / Handler**\n  - Parses AWS protocol input\n  - Produces AWS-compatible responses\n\n- **Service**\n  - Contains business logic\n  - Throws `AwsException`\n\n- **Model**\n  - Domain objects\n\n### Core Infrastructure\n\n- `EmulatorConfig`\n- `ServiceRegistry`\n- `StorageBackend` + `StorageFactory`\n- `AwsJson11Controller`\n- `AwsQueryController`\n- `AwsException` + `AwsExceptionMapper`\n- `EmulatorLifecycle`\n\n---\n\n## Package Layout\n\n- `io.github.hectorvent.floci.config`\n- `io.github.hectorvent.floci.core.common`\n- `io.github.hectorvent.floci.core.storage`\n- `io.github.hectorvent.floci.lifecycle`\n- `io.github.hectorvent.floci.services.<service>`\n\nTypical service structure:\n\n- `services/<svc>/`\n  - `*Controller.java` (REST JSON / REST XML services, via JAX-RS)\n  - `*QueryHandler.java` (AWS Query/XML protocol services)\n  - `*JsonHandler.java` (AWS JSON 1.1 protocol services)\n  - `*Service.java`\n  - `model/`\n\nThe entry-point class name depends on the service's AWS protocol (see \"AWS Protocol\nRules\" below); `Controller` is not universal.\n\nRule:\nCopy an existing service pattern before introducing a new one.\n\n---\n\n## AWS Protocol Rules\n\nFloci must implement real AWS wire protocols.\n\n| Protocol | Services | Request Format | Response Format | Implementation |\n|----------|----------|----------------|-----------------|----------------|\n| Query | SQS, SNS, IAM, STS, RDS, ElastiCache, CloudFormation, CloudWatch Metrics | form-encoded POST + `Action` | XML | `AwsQueryController` |\n| JSON 1.1 | SSM, EventBridge, CloudWatch Logs, Kinesis, KMS, Cognito, Secrets Manager, ACM | POST + `X-Amz-Target` | JSON | `AwsJson11Controller` |\n| REST JSON | Lambda, API Gateway, SES V2 | REST paths | JSON | JAX-RS |\n| REST XML | S3 | REST paths | XML | JAX-RS |\n| TCP | ElastiCache, RDS | raw protocol | native | proxies |\n\n### Important exceptions\n\n- CloudWatch Metrics supports both Query and JSON 1.1; handlers must remain aligned\n- SQS and SNS may expose multiple compatibility paths; do not let them drift\n- Cognito well-known endpoints are OIDC REST JSON endpoints, not AWS management APIs\n- Data-plane protocols may use raw TCP sockets\n- Management APIs should be validated with AWS SDK clients, not only handcrafted HTTP requests\n\n---\n\n### Partition literals\n\nFloci serves every AWS partition, not only the commercial one, so a literal that bakes\n`aws` into a code path is a bug in a GovCloud, China, ISO or EUSC deployment even when it\nprints correctly in `us-east-1`. `make partition-check` (CI: Partition Literals) inventories\n`src/main/java` for `arn:aws:` prefixes, `amazonaws.com` hosts, Route 53 hosted-zone ids,\nhand-rolled `arn:aws[a-z-]*:` regexes, `Arn.of(...)` calls with a blank region and comparisons\nwith the literal `\"aws\"` against `tools/partition/baseline.tsv`, per file. The baseline is empty, so any new one fails.\nA literal in a new file fails, growth in an existing file fails, and a drop fails until you\nrun `make partition-baseline` and commit the smaller baseline.\n\n- Mint ARNs through `AwsArnUtils.Arn.of(...)` / `RegionResolver.buildArn(...)`, recognise\n  them with `AwsArnUtils.isArn` / `PARTITION_REGEX`, and derive hosts from the region's\n  DNS suffix (`AwsRegions.dnsSuffixFor`).\n- A regionless ARN (IAM, STS, CloudFront, Organizations, an S3 bucket) never goes through\n  `Arn.of(service, \"\", ...)`, which silently means `aws`: use `RegionResolver.buildGlobalArn`\n  for the request's partition, or `Arn.global(partition, ...)` with the resource's own\n  (`AwsRegions.partitionFor(region)` for a bucket or a stack's region).\n- Never compare a partition with a literal: `\"aws\".equals(arn.partition())` is commercial-only\n  logic that fails silently in every other partition. Compare with the partition the resource\n  belongs to, `AwsRegions.partitionFor(region)` for a regional resource or the stored ARN's own\n  partition for a global one, and accept any published partition with `AwsPartitions.find(...)`\n  when only the shape matters. `\"aws\"` as an account (managed policies) or a command name is not\n  a partition: end that line with `// partition-literal: <reason>`.\n- A service principal is `ServicePrincipals.of(\"<service>\")`, never a literal: it is\n  `<service>.amazonaws.com` in every partition, and `ServicePrincipals.canonical` folds the\n  legacy per-partition forms a policy may still use before matching.\n- A literal that is genuinely partition-invariant (an XML namespace URI, an S3 canned-ACL\n  grantee URI) goes in `tools/partition/allowlist.yaml` with a reason, or ends its line\n  with `// partition-literal: <reason>`. Both are printed by `make partition-audit`.\n- Tests are not scanned; only assert a partition-dependent value when the test is about\n  partitions.\n\n## XML / JSON Rules\n\n- Use `XmlBuilder` for XML responses\n- Use `XmlParser` for XML parsing; do not use regex\n- Use `AwsNamespaces` constants\n- JSON errors must follow AWS error structures\n- Types returned directly from controllers must remain compatible with native-image reflection requirements\n\n---\n\n## Storage Rules\n\nSupported storage modes:\n\n- `memory`\n- `persistent`\n- `hybrid`\n- `wal`\n\nRules:\n\n- Always use `StorageFactory`\n- Do not instantiate storage implementations directly inside services\n- Respect lifecycle hooks for load and flush behavior\n\nImportant nuance:\n\nConfiguration interfaces may declare fallback defaults, but `application.yml` defines effective runtime behavior. Treat repository YAML as the source of truth unless a task explicitly changes configuration semantics.\n\nWhen adding storage-related behavior:\n\n1. Update `EmulatorConfig`\n2. Update main `application.yml`\n3. Update test `application.yml`\n4. Wire through `StorageFactory`\n5. Verify lifecycle integration\n\n---\n\n## Configuration Rules\n\nConfiguration lives under `floci.*`.\n\nWhen adding config:\n\n1. Add it to `EmulatorConfig`\n2. Add it to main `application.yml`\n3. Add it to test `application.yml` if needed\n4. Update documentation if user-facing\n5. Follow `FLOCI_*` environment variable conventions\n\nCritical areas:\n\n- `base-url`\n- `hostname`\n- region and account defaults\n- port ranges\n- persistence paths\n- Docker networking\n\n---\n\n## Build & Run\n\n    ./mvnw quarkus:dev\n    ./mvnw test\n    ./mvnw clean package\n    ./mvnw clean package -DskipTests\n\n`make help` lists the Makefile shortcuts. The native binary and image: `make native`\nthen `make native-image` (CI's flags, staged in `native/<arch>/`), `make native-host` for a\nbinary built on this machine with the installed GraalVM, `make native-up` to run the image,\n`make compat SUITES=\"sdk-test-java compat-cdk\"` for compatibility suites against it.\n\n### Focused tests\n\n    ./mvnw test -Dtest=SsmIntegrationTest\n    ./mvnw test -Dtest=SsmIntegrationTest#putParameter\n\n---\n\n## Compatibility Project\n\nCompatibility test suite: `./compatibility-tests/`\n\nGuidelines:\n\n- Prefer AWS SDK clients over raw HTTP for management-plane validation\n- Use this suite when changes may affect real SDK behavior\n\n---\n\n## Testing Rules\n\n### Conventions\n\n- Unit tests: `*ServiceTest.java`\n- Integration tests: `*IntegrationTest.java`\n- Prefer package-private constructors for testability\n- Integration tests may use ordered execution when stateful behavior requires it\n\n### Expectations\n\n- Test any behavior affecting AWS compatibility\n- Do not rely only on manual HTTP testing\n- Prefer SDK-based validation where possible\n\n### When touching protocol behavior\n\nIf a change affects request parsing, response shape, error handling, persistence semantics, URL generation, or service enablement:\n\n1. Add or update automated tests\n2. Prefer SDK-based verification where possible\n3. Check compatibility across alternate protocol paths\n4. Document intentional deviations clearly\n\n---\n\n## Error Handling\n\n- Services should throw `AwsException`\n- Query and REST XML flows should use `AwsExceptionMapper`\n- JSON 1.1 flows should return structured AWS error responses where required\n- Controller return types must remain reflection-safe\n\n---\n\n## Service Implementation Pattern\n\nWhen adding functionality:\n\n1. Identify the AWS protocol\n2. Reuse an existing service pattern\n3. Keep controllers thin\n4. Use `AwsException` for domain errors\n5. Reuse shared utilities\n6. Update config, storage, docs, and tests together\n7. Validate behavior against AWS SDK expectations\n\n---\n\n## Adding a New AWS Service\n\n1. Create a package under `services/<svc>/` with a Controller or protocol-specific handler,\n   a Service, and `model/`\n2. Add a `<Svc>ServiceConfig` interface and its accessor on `ServicesConfig` in `EmulatorConfig`\n3. Add one `descriptor(...)` entry in `ResolvedServiceCatalog`. This is the registration point;\n   `ServiceRegistry` only reads the catalog and has no registration API\n4. Add `floci.services.<key>.enabled` to both `src/main/resources/application.yml` and\n   `src/test/resources/application.yml`\n5. JSON 1.1 only: inject the `<Svc>JsonHandler` in `AwsJson11Controller`\n6. Obtain storage through `StorageFactory` and implement `Resettable`\n7. List any static `Random` or `SecureRandom` field under `--initialize-at-run-time` in\n   `application.yml`\n8. Check every timestamp member you emit for a `TimestampFormatTrait` before using the\n   epoch-seconds idiom. It is the awsJson1.1 default, but a model can override it per\n   member, and the mismatch is invisible to the AWS CLI because botocore coerces the\n   value, while strict SDKs (Go, Java) reject the whole response. `javap -c` on the SDK\n   model class shows the traits on each `SdkField`\n9. Add `<Svc>ServiceTest` and `<Svc>IntegrationTest`\n10. If the service mints ARNs or AWS hosts (`buildArn`, `Arn.of`, `new AwsArnUtils.Arn`, `dnsSuffixFor`, `AwsEndpoints`),\n   add a case to `PartitionCrossServiceSmokeIntegrationTest` and its `COVERED_PACKAGES`, or a row\n   with a reason to `src/test/resources/partition/smoke-exemptions.tsv`;\n   `PartitionSmokeInventoryTest` fails until one of the two exists\n11. Document it: `docs/services/<svc>.md`, a `mkdocs.yml` nav entry, a Service Matrix row in\n   `docs/services/index.md`, and a row in the README category table\n12. Register the handler in `tools/docs/services.yaml`, then run `make docs-sync` and\n    `make docs-check`\n13. Add a `TestFixtures` client factory and a `<Svc>Test` in `compatibility-tests/sdk-test-java`\n\n---\n\n## Adding a CloudFormation Resource Type\n\n**Every type is served by a per-service provisioner under\n`services/cloudformation/provisioners/`.** `CfnResourceDispatcher` only routes a resource to\nthe registry and stubs what nothing serves; never add type-specific code to it.\n\n1. Add the type to the existing `<Service>CfnProvisioner`, or create one:\n   `@ApplicationScoped`, injecting only the service it wraps. CDI discovery via\n   `CloudFormationResourceRegistry` handles registration: no manual wiring, but a\n   missing `@ApplicationScoped` silently means the type is never provisioned.\n2. `resourceTypes()` lists the `AWS::*` types; `provision(resource, props, ctx)`\n   does the work, switching on `resource.getResourceType()` when it serves several.\n3. Set **both** reference mechanisms. They are separate:\n   - `resource.setPhysicalId(...)` backs `Ref`\n   - `resource.getAttributes().put(...)` backs `Fn::GetAtt`, one entry per attribute\n   Omitting an attribute does not fail; `Fn::GetAtt` resolves to the literal\n   `\"LogicalId.Attr\"`. Source the attribute names from the type's registry schema in\n   `local/aws/cfn-resource-schemas/us-east-1/` (`readOnlyProperties`), and validate\n   `required` from the same file.\n4. **`provision` serves create *and* update.** On `UpdateStack` it is re-invoked with\n   the prior physical id and attributes already populated on the resource. Branch with\n   `ctx.isUpdate()` / `ctx.priorPhysicalId()`, not by reading the id off the resource:\n   `provision` assigns the new id as it runs, so a resource-derived check flips\n   mid-method.\n5. Override `delete(...)` when the type has a backing delete; tolerate already-deleted\n   via `CfnDeletes.safeDelete`, passing the specific \"already gone\" error codes. Never\n   a catch-all: a real failure such as `BucketNotEmpty` must propagate so the stack\n   reports `DELETE_FAILED`. When the delete needs a create-time attribute rather than\n   just the physical id, override `delete(StackResource, String)`.\n6. **Register in `src/test/resources/cloudformation/supported-resource-types.tsv`**\n   (`type<TAB>Owner`). `CfnResourceInventoryTest` diffs that file against the\n   CDI-resolved registry, so it also catches a missing `@ApplicationScoped`.\n7. **Add the provisioner to `CfnProvisionerFixture.inferredProvisioners()`** when it\n   takes a single service, or a fixture test naming that service silently falls through\n   to the stub arm.\n8. Tests: focused unit test mocking one service (`SqsCfnProvisioner`'s test is the\n   pattern) plus an integration test asserting the **exact `Fn::GetAtt` keys**. An\n   unmapped type is stubbed as `CREATE_COMPLETE` with a fake ARN, so asserting status\n   alone cannot detect a type that was never wired. Note the engine's constructor is\n   package-private, so tests in `provisioners/` mock it.\n9. Run `make docs-sync` and commit the result. The resource-type table in\n   `docs/services/cloudformation.md` is **generated** from the step-6 inventory; hand\n   edits fail `docs-check`. Labels, ordering and notes live in\n   `tools/docs/cfn_resource_types.yaml`.\n10. A schema `readOnlyProperties` entry you cannot set goes in\n    `src/test/resources/cloudformation/getatt-attribute-gaps.tsv` with a reason;\n    `CfnSchemaCoverageTest` requires every unset attribute to be fixed or recorded.\n\nReferences: `SqsCfnProvisioner` (smallest), `Ec2LaunchTemplateCfnProvisioner`\n(update-in-place and replacement), `LogsCfnProvisioner` (reconcile-vs-replace update).\n\n---\n\n## Sidecars\n\nA dependency too heavy for the native image (a native runtime, a large engine, another language\necosystem) ships as a sidecar: a stateless HTTP service in its own container that Floci starts\nlazily over the Docker socket. Sidecars live in\n[floci-io/floci-sidecars](https://github.com/floci-io/floci-sidecars), one directory per sidecar\non the shared `sidecar-core`, and are published as `floci/floci-sidecar-<name>:<semver>` on\nDocker Hub. They implement that repository's `docs/contract.md` and carry no AWS vocabulary: the\nsidecar answers a generic question, Floci maps its service semantics onto the answer.\n\nFloci-side rules:\n\n- Never add a `sidecars/` directory to this repository, never publish a `floci/floci:<tag>-<name>`\n  suffix, and never default an image to `:latest` (`ImageCacheService` never re-pulls a cached tag).\n- The consuming service owns two config knobs, `<name>-image` (an exact version) and `<name>-url`\n  (skip container management), and a `<Name>SidecarManager` plus `<Name>SidecarClient` pair.\n  `CedarSidecarManager` is the reference: it reads the contract JSON from `/health` and fails fast\n  on a contract-major mismatch.\n- The sidecar releases before the Floci PR that needs it, as a final or an `X.Y.Z-rc.N` tag; the\n  Floci PR pins that tag. A PR that needs an unreleased sidecar cannot pass CI.\n- Tests: the client's wire contract is covered by a JDK `HttpServer` fake; one Docker-gated\n  `@QuarkusTest` runs the pinned image through the real manager under its own\n  `floci.docker.resource-namespace`. A Floci test imports only Floci classes; the sidecar's own\n  behaviour is tested in its repository.\n- A sidecar that is a stock upstream image plus a script from the classpath needs no repository\n  but still goes through a manager with the same two knobs.\n\n## Code Style\n\n### Checkstyle\n\n- Java code must pass `./mvnw checkstyle:check` (CI: Checkstyle). The rules live in\n  `tools/checkstyle/checkstyle.xml`. Code excluded in `tools/checkstyle/suppressions.xml`\n  is not checked yet, but the rules below still apply to it.\n\n### General\n\n- Use constructor injection\n- Prefer self-explanatory code over comments\n- Avoid unnecessary comments\n- Always use braces in conditionals\n- Never leave a `catch` block empty. If an exception is intentionally tolerated, log it with enough context to diagnose it later.\n  When swallowing really is correct and logging would be noise, name the variable\n  `ignored` or `expected` and say in a comment why it is safe. A bare\n  `catch (Exception e) {}` is never acceptable.\n- Follow existing project patterns\n- Use modern Java features only when they improve clarity\n\n### Types and names\n\n- **Do not use `var`. Write the explicit type.** Floci reproduces AWS wire\n  contracts, so the concrete type at a call site is usually the thing under\n  review: whether a value is a `LinkedHashMap` or a `Map`, an AWS model type or a\n  JDK one, is exactly what a reviewer needs to see. This covers local\n  declarations, enhanced-for (`for (Tag tag : tags)`), classic for-init, and\n  try-with-resources. The one exception is a record deconstruction pattern\n  (`case Node(var left, var right) ->`), where naming the component types is pure\n  noise.\n- **Import the classes you use. Do not write fully-qualified names inline.**\n  `new ArrayList<>()`, never `new java.util.ArrayList<>()`. Qualify inline only for\n  a genuine name clash inside one file: import the type used more often, qualify the\n  other, and leave a short comment naming the clash. Checkstyle (`NoFqcn`) enforces\n  this except in the files listed in `tools/checkstyle/suppressions.xml`.\n\n### Imports\n\n- No wildcard imports in `src/main`. Static wildcards stay fine in tests, where\n  `Assertions.*`, `Mockito.*` and `Matchers.*` are the established idiom.\n- Import order: non-`java`/`javax` imports, then `javax.*`, then `java.*`, then\n  static imports. Alphabetize each group. Separate the non-JDK, JDK, and static\n  sections with a blank line; no blank line is needed between `javax` and `java`.\n  This follows IntelliJ's default layout and is enforced by Checkstyle.\n\n### Conventions the codebase already follows\n\nWritten down so they stay true. New code should match them without thinking.\nA handful of files predate them; a violation you find in the tree is a straggler,\nnot a precedent.\n\n- 4-space indentation, K&R braces. Never indent with a tab.\n- JBoss Logging, in a field named `LOG`, using the parameterized `...v()` form.\n  No string concatenation in log calls.\n- No `printStackTrace`, anywhere. No `System.out` or `System.err` in `src/main`; the\n  one exception is the CLI entry point `io.github.hectorvent.floci.tools.ami.AmiImageTool`,\n  where stdout is the program's output. A few tests print a failure repro just before\n  failing, which is the only good reason to print from a test: an assertion message\n  usually says it better.\n- `java.time` for everything Floci owns. `Calendar` and `SimpleDateFormat` appear\n  nowhere and must not be introduced. A `Date` survives only at a third-party boundary\n  that forces one: the BouncyCastle certificate builder, the JAX-RS\n  `HttpHeaders.getDate()` override, and JDBC's `java.sql.Date` in the RDS Data mapper.\n  Convert at that boundary with `Date.from(instant)` and keep `java.time` on Floci's\n  side of it.\n- Constructor injection in `src/main`. Field injection is fine in tests, and\n  `Instance<T>` field injection is a legitimate CDI pattern.\n- `Optional` as a return type, and never as a field: there are none, keep it that way.\n  It reaches a parameter only where a Quarkus `@ConfigProperty Optional<T>` is threaded\n  through; do not introduce it as a parameter for anything else.\n- Switch expressions over switch statements. Pattern-matching `instanceof` over\n  cast-after-check.\n- `AwsException` for domain errors.\n- `final` on service fields, but not on locals or parameters.\n\n### Tests\n\nThese describe `src/test`. `compatibility-tests` is a separate module with the opposite\nidiom, AssertJ and `@DisplayName` in nearly every file. Follow the module you are in.\n\n- Name test methods either as a camelCase sentence (`putAndGetFromMemory`) or as\n  `method_scenario_expectation`. Both are established. `testX` names are common in\n  older tests and are not the pattern to copy.\n- JUnit 5 assertions with Hamcrest and RestAssured matchers. AssertJ is a declared test\n  dependency, used by the Lambda launcher tests; prefer the established matchers\n  everywhere else.\n- `@DisplayName` is not used here. The method name carries the intent.\n\n---\n\n## Documentation Style\n\n- No em-dashes anywhere, in any content. Use colons, commas, or periods.\n\n## Logging\n\n- Use JBoss Logging\n- Keep logs structured\n- Avoid noisy logs in hot paths\n\n---\n\n## Pull Request Guidelines\n\n- Keep changes focused\n- Avoid unrelated refactors\n- Preserve behavior unless the task explicitly requires change\n- Update docs when necessary\n- Explain missing tests when behavior changed but no automated coverage was added\n\nConventional commits:\n\n- `feat:`\n- `fix:`\n- `perf:`\n- `docs:`\n- `chore:`\n\nDo not add `Co-Authored-By` trailers for AI tools in commit messages. Keep attribution limited to human contributors.\n\n---\n\n## Release Awareness\n\n- Changes merged into `main` do not automatically imply a stable release\n- Releases are cut from `main` via the \"Release Cut\" workflow (`workflow_dispatch`\n  on `.github/workflows/release-cut.yml`), which runs semantic-release: it bumps\n  `pom.xml`, writes `CHANGELOG.md`, commits, tags, and creates the GitHub Release\n- `release/x.y.x` branches are retired for now\n- Tags still trigger the publishing workflows (`release.yml`)\n\nTreat release workflows as critical infrastructure.\n\n---\n\n## Agent Workflow\n\n### Before editing\n\n1. Identify service and protocol\n2. Locate an existing implementation to mirror\n3. Check config impact\n4. Check storage impact\n5. Check documentation impact\n6. Define the minimal useful test plan\n\n### Before finishing\n\n1. Run relevant tests\n2. Validate protocol behavior\n3. Ensure no custom endpoints were introduced\n4. Verify config and docs updates\n\n---\n\n## Common Mistakes\n\n- Creating non-AWS endpoints\n- Bypassing `StorageFactory`\n- Changing wire formats without tests\n- Forgetting YAML updates\n- Producing inconsistent URLs or ARNs\n- Testing only with raw HTTP\n- Introducing unnecessary new patterns\n- Adding type-specific code to `CfnResourceDispatcher` instead of a per-service\n  provisioner\n- Setting a CloudFormation resource's physical id but not its `Fn::GetAtt`\n  attributes (they are two separate mechanisms, and the miss is silent)\n- Hand-editing the resource-type table in `docs/services/cloudformation.md`, which is\n  generated, run `make docs-sync` instead\n- Adding a CloudFormation provisioner without a row in\n  `supported-resource-types.tsv` or an entry in `CfnProvisionerFixture`, either of\n  which leaves a type quietly served by the stub arm\n\n---\n\n## Human Handoff\n\nIf behavior is unclear:\n\n1. Prefer AWS behavior\n2. Then existing Floci behavior\n3. Then compatibility test expectations\n\nIf a task would require broad architectural changes, stop and surface the tradeoffs instead of refactoring across services blindly.\n\n<!-- File: tools/docs/services.yaml (Tokens: ~6829 | Category: anthropic-skill) -->\n# Registry of services whose Supported Actions tables are auto-generated.\n#\n# The action LIST comes from handler source (mode: switch|rest); the Description\n# column is hand-written and PRESERVED across regen, keyed by action name.\n# Per-service overrides: rename_actions (method->canonical), exclude_actions.\n#\n# Phase 1 covers single-table switch-mode services. REST controllers, multi-table\n# docs, mixed-mode (ses), S3 query-string dispatch, and non-tabular docs are tracked\n# as follow-ups; their switch handlers are acknowledged in deferred_handlers below so\n# the new-service sentinel only fires on genuinely unregistered handlers.\n\nservices:\n  - service: account\n    doc: docs/services/account.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/account/AccountController.java, mode: rest }\n  - service: accessanalyzer\n    doc: docs/services/access-analyzer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/accessanalyzer/AccessAnalyzerController.java, mode: rest }\n  - service: acm\n    doc: docs/services/acm.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/acm/AcmJsonHandler.java, mode: switch }\n  - service: applicationautoscaling\n    doc: docs/services/applicationautoscaling.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ApplicationAutoScalingJsonHandler.java, mode: switch }\n  - service: athena\n    doc: docs/services/athena.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/athena/AthenaJsonHandler.java, mode: switch }\n  - service: bedrock\n    doc: docs/services/bedrock.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrock/BedrockController.java, mode: rest }\n  - service: bedrock-agentcore-control\n    doc: docs/services/bedrock-agentcore.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreControlController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreIdentityController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreGatewayController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreMemoryController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreToolsController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreGatewayRuleController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/bedrockagentcorecontrol/BedrockAgentCoreResourcePolicyController.java, mode: rest }\n    rename_actions:\n      CreateEndpoint: CreateAgentRuntimeEndpoint\n      GetEndpoint: GetAgentRuntimeEndpoint\n      UpdateEndpoint: UpdateAgentRuntimeEndpoint\n      DeleteEndpoint: DeleteAgentRuntimeEndpoint\n      ListEndpoints: ListAgentRuntimeEndpoints\n      Create: CreateWorkloadIdentity\n      Get: GetWorkloadIdentity\n      Update: UpdateWorkloadIdentity\n      Delete: DeleteWorkloadIdentity\n      List: ListWorkloadIdentities\n      CreateTarget: CreateGatewayTarget\n      GetTarget: GetGatewayTarget\n      UpdateTarget: UpdateGatewayTarget\n      DeleteTarget: DeleteGatewayTarget\n      ListTargets: ListGatewayTargets\n  - service: ce\n    doc: docs/services/ce.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ce/CostExplorerJsonHandler.java, mode: switch }\n  - service: cloudformation\n    doc: docs/services/cloudformation.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationQueryHandler.java, mode: switch }\n  - service: cloudhsmv2\n    doc: docs/services/cloudhsmv2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudhsmv2/CloudHsmV2JsonHandler.java, mode: switch }\n  - service: cloudtrail\n    doc: docs/services/cloudtrail.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cloudtrail/CloudTrailJsonHandler.java, mode: switch }\n  - service: codeartifact\n    doc: docs/services/codeartifact.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/codeartifact/CodeArtifactController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/V1TagsController.java, mode: rest }\n    rename_actions:\n      ListTagsForResourceByQuery: ListTagsForResource\n    exclude_actions:\n      - ListTags\n      - TagResourcePost\n      - TagResourcePut\n  - service: codegurureviewer\n    doc: docs/services/codegurureviewer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/codegurureviewer/CodeGuruReviewerController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: lambdaweb\n    doc: docs/services/lambda-web-functions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/lambdaweb/LambdaWebController.java, mode: rest }\n  - service: marketplace\n    doc: docs/services/marketplace.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceCatalogController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceAgreementService.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceEntitlementController.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceDeploymentController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceReportingController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceMeteringService.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/marketplace/MarketplaceDiscoveryController.java, mode: rest }\n    exclude_actions:\n      - AgreementType\n      - Status\n      - AcceptorAccountId\n      - OfferId\n      - OfferSetId\n      - ResourceIdentifier\n      - ResourceType\n      - PartyType\n      - BeforeStartTime\n      - AfterStartTime\n      - BeforeEndTime\n      - AfterEndTime\n      - BeforeLastUpdateTime\n      - AfterLastUpdateTime\n      - EndTimeBehaviorType\n      - EndTimeBehaviorReasonCode\n      - InitialAgreementId\n      - LicenseArn\n      - StartTime\n      - LastUpdateTime\n      - EndTime\n      - ASCENDING\n      - DESCENDING\n  - service: comprehend\n    doc: docs/services/comprehend.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/comprehend/ComprehendJsonHandler.java, mode: switch }\n  - service: controlcatalog\n    doc: docs/services/controlcatalog.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/controlcatalog/ControlCatalogController.java, mode: rest }\n    rename_actions:\n      GetControl: GetControl\n      ListControls: ListControls\n  - service: appintegrations\n    doc: docs/services/appintegrations.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/appintegrations/AppIntegrationsController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: dlm\n    doc: docs/services/dlm.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dlm/DlmController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: connect\n    doc: docs/services/connect.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/connect/ConnectController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: rekognition\n    doc: docs/services/rekognition.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/rekognition/RekognitionJsonHandler.java, mode: switch }\n  - service: translate\n    doc: docs/services/translate.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/translate/TranslateJsonHandler.java, mode: switch }\n  - service: detective\n    doc: docs/services/detective.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/detective/DetectiveController.java, mode: rest }\n  - service: dms\n    doc: docs/services/dms.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dms/DmsJsonHandler.java, mode: switch }\n  - service: docdb\n    doc: docs/services/docdb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/docdb/DocDbQueryHandler.java, mode: switch }\n  - service: dsql\n    doc: docs/services/dsql.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/dsql/DsqlController.java, mode: rest }\n  - service: efs\n    doc: docs/services/efs.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/efs/EfsController.java, mode: rest }\n  - service: organizations\n    doc: docs/services/organizations.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/organizations/OrganizationsJsonHandler.java, mode: switch }\n  - service: elasticache\n    doc: docs/services/elasticache.md\n    exclude_actions: [SubnetIds, SecurityGroupIds, SnapshotArnsToRestore]\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/elasticache/ElastiCacheQueryHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/elasticache/ElastiCacheServerlessQueryHandler.java, mode: switch }\n  - service: emr\n    doc: docs/services/emr.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/emr/EmrHandler.java, mode: switch }\n  - service: eventbridge\n    doc: docs/services/eventbridge.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/eventbridge/EventBridgeHandler.java, mode: switch }\n  - service: firehose\n    doc: docs/services/firehose.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/firehose/FirehoseJsonHandler.java, mode: switch }\n  - service: resource-explorer-2\n    doc: docs/services/resource-explorer.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/resourceexplorer2/ResourceExplorer2Controller.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: fis\n    doc: docs/services/fis.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/fis/FisController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: kinesis\n    doc: docs/services/kinesis.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kinesis/KinesisJsonHandler.java, mode: switch }\n  - service: kinesisanalytics\n    doc: docs/services/kinesisanalytics.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kinesisanalytics/KinesisAnalyticsV2JsonHandler.java, mode: switch }\n  - service: kms\n    doc: docs/services/kms.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/kms/KmsJsonHandler.java, mode: switch }\n  - service: memorydb\n    doc: docs/services/memorydb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/memorydb/MemoryDbHandler.java, mode: switch }\n  - service: macie2\n    doc: docs/services/macie2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/macie2/MacieController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/services/securityadmin/SecurityAdminController.java, mode: rest }\n    rename_actions:\n      ListAdmin: ListOrganizationAdminAccounts\n  - service: resource-groups\n    doc: docs/services/resource-groups.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/resourcegroups/ResourceGroupsController.java, mode: rest }\n  - service: inspector2\n    doc: docs/services/inspector2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/inspector2/Inspector2Controller.java, mode: rest }\n  - service: neptune\n    doc: docs/services/neptune.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/neptune/NeptuneQueryHandler.java, mode: switch }\n  - service: networkfirewall\n    doc: docs/services/network-firewall.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/networkfirewall/NetworkFirewallJsonHandler.java, mode: switch }\n  - service: ram\n    doc: docs/services/ram.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ram/RamController.java, mode: rest }\n  - service: rds\n    doc: docs/services/rds.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/rds/RdsQueryHandler.java, mode: switch }\n    exclude_actions: [SubnetIds, VpcSecurityGroupIds, OptionsToRemove, ValuesToAdd, ValuesToRemove]\n  - service: redshift\n    doc: docs/services/redshift.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshift/RedshiftQueryHandler.java, mode: switch }\n    # SubnetIds / VpcSecurityGroupIds / IamRoles / AddIamRoles / RemoveIamRoles / TagKeys / TagValues /\n    # DbGroups / ScheduleDefinitions are labels of an inner member-name switch (memberKeyRegex), not\n    # actions, same shape as the rds exclusions above.\n    exclude_actions: [SubnetIds, VpcSecurityGroupIds, IamRoles, AddIamRoles, RemoveIamRoles, TagKeys, TagValues, DbGroups, ScheduleDefinitions]\n  - service: redshiftdata\n    doc: docs/services/redshift-data.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftdata/RedshiftDataJsonHandler.java, mode: switch }\n    # ExecuteSql / BatchExecuteSql are the deprecated pre-2020 operations: the handler\n    # recognizes them only to return a ValidationException, they are not supported actions.\n    exclude_actions: [ExecuteSql, BatchExecuteSql]\n  - service: redshiftserverless\n    doc: docs/services/redshift-serverless.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftserverless/RedshiftServerlessJsonHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/redshiftserverless/RedshiftServerlessAdminJsonHandler.java, mode: switch }\n  - service: cognitoidentity\n    doc: docs/services/cognitoidentity.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/cognitoidentity/CognitoIdentityJsonHandler.java, mode: switch }\n  - service: globalaccelerator\n    doc: docs/services/globalaccelerator.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/globalaccelerator/GlobalAcceleratorJsonHandler.java, mode: switch }\n  - service: datasync\n    doc: docs/services/datasync.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/datasync/DataSyncJsonHandler.java, mode: switch }\n  - service: route53\n    doc: docs/services/route53.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/route53/Route53Controller.java, mode: rest }\n    # JAX-RS method names use Java casing; map them onto the canonical Route 53 action names.\n    rename_actions:\n      AssociateVpcWithHostedZone: AssociateVPCWithHostedZone\n      DisassociateVpcFromHostedZone: DisassociateVPCFromHostedZone\n      CreateVpcAssociationAuthorization: CreateVPCAssociationAuthorization\n      DeleteVpcAssociationAuthorization: DeleteVPCAssociationAuthorization\n      ListVpcAssociationAuthorizations: ListVPCAssociationAuthorizations\n      ListHostedZonesByVpc: ListHostedZonesByVPC\n      GetDnssec: GetDNSSEC\n  - service: route53resolver\n    doc: docs/services/route53resolver.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/route53resolver/Route53ResolverJsonHandler.java, mode: switch }\n  - service: sagemaker\n    doc: docs/services/sagemaker.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/sagemaker/SageMakerJsonHandler.java, mode: switch }\n  - service: securityhub\n    doc: docs/services/securityhub.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/securityhub/SecurityHubController.java, mode: rest }\n      - { path: src/main/java/io/github/hectorvent/floci/core/common/SharedTagsController.java, mode: rest }\n    rename_actions:\n      ListTags: ListTagsForResource\n      TagResourcePost: TagResource\n    exclude_actions:\n      - ListTagsByQuery\n      - TagResourceByBody\n      - TagResourcePut\n      - UntagResourceByQuery\n  - service: secretsmanager\n    doc: docs/services/secrets-manager.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/secretsmanager/SecretsManagerJsonHandler.java, mode: switch }\n  - service: servicecatalog\n    doc: docs/services/service-catalog.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/servicecatalog/ServiceCatalogJsonHandler.java, mode: switch }\n  - service: servicequotas\n    doc: docs/services/servicequotas.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/servicequotas/ServiceQuotasJsonHandler.java, mode: switch }\n  - service: ssoadmin\n    doc: docs/services/ssoadmin.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/ssoadmin/SsoAdminJsonHandler.java, mode: switch }\n  - service: identitystore\n    doc: docs/services/identitystore.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/identitystore/IdentityStoreJsonHandler.java, mode: switch }\n  - service: budgets\n    doc: docs/services/budgets.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/budgets/BudgetsJsonHandler.java, mode: switch }\n  - service: verifiedpermissions\n    doc: docs/services/verifiedpermissions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/verifiedpermissions/VerifiedPermissionsJsonHandler.java, mode: switch }\n  - service: timestream-influxdb\n    doc: docs/services/timestream-influxdb.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/timestreaminfluxdb/TimestreamInfluxDbJsonHandler.java, mode: switch }\n  - service: sqs\n    doc: docs/services/sqs.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/sqs/SqsJsonHandler.java, mode: switch }\n      - { path: src/main/java/io/github/hectorvent/floci/services/sqs/SqsQueryHandler.java, mode: switch }\n  - service: stepfunctions\n    doc: docs/services/step-functions.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/stepfunctions/StepFunctionsJsonHandler.java, mode: switch }\n  - service: swf\n    doc: docs/services/swf.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/swf/SwfJsonHandler.java, mode: switch }\n  - service: wafv2\n    doc: docs/services/wafv2.md\n    sources:\n      - { path: src/main/java/io/github/hectorvent/floci/services/wafv2/WafV2Handler.java, mode: switch }\n\n# Switch handlers that exist but are intentionally not yet covered (see reasons).\n# A NEW switch handler not listed here and not registered above fails `--strict`.\ndeferred_handlers:\n  - { path: src/main/java/io/github/hectorvent/floci/services/apigatewayv2/ApiGatewayV2JsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ScalingPolicyAlarmActionHandler.java }  # not a JSON/Query action dispatcher — its `case \"X\" ->` arms are the StepScaling AdjustmentType switch, not AWS actions\n  - { path: src/main/java/io/github/hectorvent/floci/services/autoscaling/AutoScalingQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/bcmpricingcalculator/BcmPricingCalculatorJsonHandler.java }  # focused workload-estimate surface\n  - { path: src/main/java/io/github/hectorvent/floci/services/bcmdataexports/BcmDataExportsJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudcontrol/CloudControlJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudmap/CloudMapHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/logs/CloudWatchLogsHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/logs/CloudWatchLogsMetricFilterHandler.java }  # multi-table doc (nested handler, reached through CloudWatchLogsHandler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/metrics/CloudWatchMetricsJsonHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cloudwatch/metrics/CloudWatchMetricsQueryHandler.java }  # multi-table doc (nested handler)\n  - { path: src/main/java/io/github/hectorvent/floci/services/codebuild/CodeBuildJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/codedeploy/CodeDeployJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cognito/CognitoJsonHandler.java }  # non-tabular doc (grouped/inline); separate docs effort\n  - { path: src/main/java/io/github/hectorvent/floci/services/configservice/ConfigServiceJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/cur/CurJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/dynamodb/NativeDynamoDbJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/dynamodb/NativeDynamoDbStreamsJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ec2/Ec2QueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/ecr/EcrJsonHandler.java }  # doc packs multiple actions per row (`A` / `B`) + REST endpoints; needs de-grouping\n  - { path: src/main/java/io/github/hectorvent/floci/services/ecs/EcsJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elasticbeanstalk/ElasticBeanstalkQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elb/ElbClassicQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2QueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/glue/GlueJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/iam/IamQueryHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/iam/StsQueryHandler.java }  # sts (lives in iam package); single-table, registerable next\n  - { path: src/main/java/io/github/hectorvent/floci/services/lakeformation/LakeFormationJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/lightsail/LightsailJsonHandler.java }  # non-tabular doc (grouped/inline); standalone action table to be added\n  - { path: src/main/java/io/github/hectorvent/floci/services/pricing/PricingJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/resourcegroupstagging/ResourceGroupsTaggingJsonHandler.java }  # non-tabular doc (grouped/inline)\n  - { path: src/main/java/io/github/hectorvent/floci/services/ses/SesQueryHandler.java }  # mixed-mode (Query + REST surface)\n  - { path: src/main/java/io/github/hectorvent/floci/services/sns/SnsJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/sns/SnsQueryHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ssm/Ec2MessagesJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/ssm/SsmJsonHandler.java }  # multi-table doc\n  - { path: src/main/java/io/github/hectorvent/floci/services/textract/TextractJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/transcribe/TranscribeJsonHandler.java }  # no standalone action table yet\n  - { path: src/main/java/io/github/hectorvent/floci/services/transfer/TransferHandler.java }  # multi-table doc\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~1179 | Category: copilot-instructions) -->\n# Copilot Instructions for Pull Request Review\n\nReview pull requests in the Floci repository with AWS compatibility as the primary concern.\n\nFloci is a Java-based local AWS emulator built on Quarkus. Its goal is to match AWS SDK and AWS CLI behavior through real AWS wire protocols, not convenience APIs or custom abstractions.\n\n## Review Priorities\n\nEvaluate changes in this order:\n\n1. Preserve AWS protocol compatibility\n2. Match AWS SDK and AWS CLI behavior\n3. Reuse existing Floci patterns\n4. Prefer correctness over convenience\n5. Keep changes focused and testable\n\n## What to Flag\n\nRaise concerns when a PR introduces any of the following without strong justification:\n\n- Non-AWS endpoint shapes\n- Request or response format changes made for convenience\n- Broad refactors unrelated to the PR goal\n- New service patterns where an existing Floci pattern should be reused\n- Direct storage implementation usage instead of `StorageFactory`\n- `var` where an explicit type belongs. Floci reproduces AWS wire contracts, so the\n  concrete type at a call site is usually what a reviewer needs to see. The one\n  exception is a record deconstruction pattern.\n- Fully-qualified class names written inline instead of imported, such as\n  `new java.util.ArrayList<>()`. A genuine name clash inside one file is fine.\n  Checkstyle catches this except in the files it still suppresses.\n- Wildcard imports in `src/main`. Static wildcards in tests are fine.\n- An empty `catch` block. A tolerated exception is logged, or named `ignored` or\n  `expected` with a comment saying why swallowing is safe.\n\n## Architecture Expectations\n\nFloci follows a layered design:\n\n- Controllers / handlers parse AWS protocol input and produce AWS-compatible responses\n- Services contain business logic and should throw `AwsException`\n- Models hold domain data\n\nCore infrastructure commonly relevant in reviews:\n\n- `EmulatorConfig`\n- `ServiceRegistry`\n- `StorageFactory`\n- `AwsQueryController`\n- `AwsJson11Controller`\n- `AwsException`\n- `AwsExceptionMapper`\n- `EmulatorLifecycle`\n\nCheck that controllers stay thin, business logic remains in services, and new changes fit existing repository patterns.\n\n## Protocol Review Rules\n\nFloci implements real AWS wire protocols. Review protocol-affecting changes carefully.\n\n- Query services should keep form-encoded POST requests with `Action` and XML responses\n- JSON 1.1 services should keep `X-Amz-Target` requests and AWS-style JSON responses\n- REST JSON and REST XML services should stay aligned with AWS path and payload conventions\n- TCP-based services should not drift into HTTP-style abstractions\n\nPay extra attention to these cases:\n\n- CloudWatch Metrics supports both Query and JSON 1.1 and both paths must stay aligned\n- SQS and SNS may have multiple compatibility paths that must not drift\n- Cognito well-known endpoints are OIDC REST JSON endpoints, not AWS management APIs\n- Management APIs should ideally be validated with AWS SDK clients, not only handcrafted HTTP\n\n## XML and JSON Rules\n\nFlag PRs that:\n\n- Ignore `AwsNamespaces` constants\n- Return JSON errors that do not follow AWS error structures\n- Change controller return types in ways that may break reflection or native-image compatibility\n\n## Config and Storage Review\n\nWhen a PR changes configuration or persistence behavior, verify the change is wired consistently.\n\nCheck for updates to:\n\n- `EmulatorConfig`\n- main `application.yml`\n- test `application.yml`\n- `StorageFactory`\n- lifecycle hooks when relevant\n\nSupported storage modes include:\n\n- `memory`\n- `persistent`\n- `hybrid`\n- `wal`\n\nTreat repository YAML as the source of truth for runtime behavior unless the PR explicitly changes configuration semantics.\n\n## Testing Expectations\n\nExpect automated coverage for changes that affect:\n\n- request parsing\n- response shape\n- error handling\n- persistence semantics\n- URL generation\n- service enablement\n\nPrefer:\n\n- AWS SDK-based validation over raw HTTP-only testing\n- integration tests for compatibility-sensitive behavior\n- existing naming conventions such as `*ServiceTest.java` and `*IntegrationTest.java`\n\nIf behavior changes without automated coverage, call that out explicitly.\n\n## Review Checklist\n\nWhen analyzing a PR, check:\n\n- Is the change focused?\n- Does it preserve AWS-compatible wire behavior?\n- Does it reuse an existing Floci pattern?\n- Are controllers thin and services responsible for domain logic?\n- Are `AwsException` and existing error-mapping patterns used correctly?\n- Are config and YAML updates complete?\n- Are storage changes wired through `StorageFactory`?\n- Are tests added or updated where compatibility is affected?\n- Are docs updated when user-facing behavior changes?\n- Does new code follow the AGENTS.md Code Style rules: explicit types over `var`,\n  imported classes over inline fully-qualified names, no wildcard imports in\n  `src/main`, braces in conditionals, constructor injection?\n\n## How to Write Feedback\n\nWrite review comments that are:\n\n- specific\n- repository-aware\n- grounded in AWS compatibility risk\n\nUse severity when helpful:\n\n- `high`: likely breaks AWS SDK / CLI compatibility or protocol behavior\n- `medium`: inconsistent with Floci architecture, wiring, or testing expectations\n- `low`: maintainability, clarity, or minor convention issue\n\nPrefer comments that explain:\n\n- what is risky\n- why it matters in Floci\n- which existing pattern should be followed instead\n\n## If Behavior Is Unclear\n\nUse this fallback order:\n\n1. Prefer AWS behavior\n2. Then existing Floci behavior\n3. Then compatibility test expectations\n\nIf correctness would require a broader architectural change, call out the tradeoff instead of suggesting blind refactoring.\n</yakaai_skills>"}