Skip to content

fix(core): ambient identity is per region, not per account - #1425

Merged
lex00 merged 1 commit into
mainfrom
fix/1416-ambient-region-identity
Aug 3, 2026
Merged

fix(core): ambient identity is per region, not per account#1425
lex00 merged 1 commit into
mainfrom
fix/1416-ambient-region-identity

Conversation

@lex00

@lex00 lex00 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Ambient (#1278) and dependency (#1273) resources are keyed by physical id and merged across stacks, because the account's default VPC route table is one resource however many stacks route through it. Right for a resource that really is account-level; wrong for a regional one. A VPC, subnet or security group in us-west-1 is a different resource from one in us-east-1, and merging them by id drops all but the first sighting.

On AWS it cannot be observed, since VPC, subnet and security-group ids are globally unique. Against a Floci emulator that names the default VPC vpc-default and its subnets subnet-default-a/b/c in every region (lex00/floci#21), a three-region estate's nine default subnets replayed as three, and "how many of my subnets are empty" came back 2 instead of 8.

The rule

Not a table of regional kinds. A lexicon stamps a region on a resource when region is part of that resource's identity — the AWS lexicon does it in stampRegion — and unqualifiedKey reads that stamp back. A resource carrying no region is account-level as far as core can tell and merges exactly as before, which is why dependencies, never stamped, are untouched by this.

Qualification happens where results are merged, not where they are recorded: collectAmbient across stacks, replaySnapshots across recorded stacks, each only when there is more than one to merge. One region is not a merge, so a single-region project's ids do not move — and the first snapshot taken after this lands does not diff as "every ambient resource replaced".

Managed resources are excluded outright: they join the declared canvas, which qualifies by stack or not at all, so re-keying web to us-east-1::web would unjoin it from its own declaration.

Two things the issue did not have

The live path had the same collapse. collectAmbient merges across stacks with found[id] ??= meta, so chant search --live lost the same regions --at did. #1416 named only replay.ts; fixing that alone would have left the two paths disagreeing, which is the failure mode search --at was built to avoid.

Ambient and dependency keys stopped colliding. Once ambient keys carry a region and dependency keys do not, a resource that is both — the default VPC an instance sits in — is no longer deduped by key and would be counted twice. observeResources now matches those on physical id.

One thing from the issue was dropped: it proposed adding a region field to LifecycleSnapshot. Not needed — stampRegion already puts the region on the ambient metadata, which is the second option the issue offered, and it works for old snapshots too.

Verification

replay.ts had no test file, which is part of why this survived. It has one now. Every assertion that asserts the fix fails without it, checked by neutralising unqualifiedKey and re-running: 3 of 6 fail, and the 3 that pass are the regression guards that should pass either way.

Full suite: 10465 passed, 18 skipped, 0 failed. Typecheck and lint clean.

Note for anyone running the suite on a fresh clone: 108 tests fail until just _ensure-gen has run, which is #1419 and not this.

Closes #1416

🤖 Generated with Claude Code

https://claude.ai/code/session_014KoduiMJRyLqjFYBUbMskE

Ambient (#1278) and dependency (#1273) resources are keyed by physical id and
merged across stacks, on the reasoning that the default VPC's route table is one
resource however many stacks route through it. That is right for a resource that
really is account-level and wrong for a regional one: a VPC, subnet or security
group in us-west-1 is a different resource from one in us-east-1, and merging
them by id drops all but the first sighting.

On AWS it cannot be observed — VPC, subnet and security-group ids are globally
unique, so keying by id and keying by (region, id) give the same answer. Against
a Floci emulator, which names the default VPC `vpc-default` and its subnets
`subnet-default-a/b/c` in every region, a three-region estate's nine default
subnets replayed as three and "how many subnets are empty" came back 2 instead
of 8.

The rule is not a table of regional kinds. A lexicon stamps a region on a
resource when region is part of that resource's identity — the AWS lexicon does
it in `stampRegion` — and `unqualifiedKey` reads that stamp back. A resource
carrying no region is account-level as far as anything here can tell, and merges
exactly as it did, which is why dependencies (never stamped) are untouched.

Qualification happens where results are merged, not where they are recorded:
`collectAmbient` across stacks and `replaySnapshots` across recorded stacks,
each only when there is more than one to merge. One region is not a merge, so a
single-region project's ids do not move and the first snapshot taken after this
lands does not diff as "every ambient resource replaced".

Managed resources are excluded outright. They join the declared canvas, which
qualifies by stack or not at all, so re-keying `web` to `us-east-1::web` would
unjoin it from its own declaration.

Two things found while fixing it, both covered by the tests:

- The live path had the same collapse, in `collectAmbient`, which #1416 did not
  mention. Fixing only the replay would have made `search --at` and
  `search --live` disagree.
- Once ambient keys carry a region and dependency keys do not, a resource that
  is both stops colliding by key and would be counted twice. `observeResources`
  now matches those on physical id.

`replay.ts` had no test file, which is part of why this survived. It has one now,
and each assertion fails without the fix — checked by neutralising
`unqualifiedKey` and re-running.

Closes #1416

Claude-Session: https://claude.ai/code/session_014KoduiMJRyLqjFYBUbMskE

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
lex00 added a commit to INTENTIUS/chant-bench that referenced this pull request Aug 3, 2026
The chant arm vendored 0.33.1. It now carries INTENTIUS/chant#1425 and #1429, so
its published g-series describes a build the arm no longer runs: different
harness commit, different workspace fingerprint. By the rule method.md states,
that is a different experiment.

Three replicates rather than one. A single new run would have joined a set whose
other two members are the old build, and the board ranks on the MEDIAN of three
— so the figure would have described neither build. The set is now homogeneous:

    chant-r3  24/24   workspace 3912387ce569  harness 4f3ab36-dirty
    chant-r4  22/24   workspace 3912387ce569  harness 4f3ab36-dirty
    chant-r5  22/24   workspace 3912387ce569  harness 4f3ab36-dirty

Median 0.9167, which is exactly what the g-series median was. **The two fixes do
not move the board**, and that is the expected result rather than a
disappointment: the arm's 0.33.1 read the estate through the CLI, which passed
`--region` and signed it, so it already saw all three regions. #1429 is a
regression against a build the board never ran.

Where they do move the number is the negative set, which asks the question the
board does not: 0/6 to 3/6.

All three runs answered with zero account reads, entirely from the recorded
snapshot, and all three passed the audit — 6/6, 24/24 and 24/24 trials using
chant's own tooling, at 3%, 5% and 1% invocation failure rates.

Two earlier attempts are not published. Both were voided for the same reason,
before lex00/aws-bench#11 put the arm's CLI on PATH where a trial could see it.

Claude-Session: https://claude.ai/code/session_014KoduiMJRyLqjFYBUbMskE

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lex00
lex00 merged commit f67edf3 into main Aug 3, 2026
5 checks passed
@lex00
lex00 deleted the fix/1416-ambient-region-identity branch August 3, 2026 21:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ambient regional resources are deduped account-wide, so a region's copy is lost

1 participant