Skip to content

[gauntlet:plan_approval] #903 re-measure: all 27 estates with stage 12 active - #939

Merged
github-actions[bot] merged 1 commit into
mainfrom
live/plan-approval-remeasure-903
Sep 7, 2026
Merged

[gauntlet:plan_approval] #903 re-measure: all 27 estates with stage 12 active#939
github-actions[bot] merged 1 commit into
mainfrom
live/plan-approval-remeasure-903

Conversation

@lex00

@lex00 lex00 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

The catch-up re-measure #903 asks for, and the one #936's flip needed behind it.
Artifact and rendered docs only: no script, no Go file, no stages.go.

The board

Read from bash scripts/pickup.sh, not from the runner's own summary:

Before, at #936 (70e2722) - every one of the 27 rows still carried
plan_approval: not_run from before the flip:

core  0/26 clear    all 0/27 clear

After, this commit:

core  26/26 clear    all 27/27 clear

Runner's own summary line, the parallel run:

core 26 of 26 clear, all 27 of 27 clear, 1 script(s) exited non-zero
REGRESSION: reference-ec2-vpc: day2_crash regressed from pass to fail with no matching entry in live/gauntlet/regressions.json

Wall clock: 44m11s for all 27 at -parallel 4 (20:16:39 -> 21:00:50),
plus 4m08s for the one solo re-run below. Emulator
ghcr.io/lex00/floci@sha256:a39185cc, oracle terraform 1.15.8 / tofu 1.12.5.

The 27 verdicts

Every estate in the manifest, verdict=pass. Each line's detail= runs 1-3 KB
and is in live/gauntlet.json and live/gauntlet/logs/<estate>.log verbatim;
elided here for length.

corpus-alb-complete            GAUNTLET stage=plan_approval verdict=pass duration_s=22
corpus-autoscaling-complete    GAUNTLET stage=plan_approval verdict=pass duration_s=21
corpus-dynamodb-table-basic    GAUNTLET stage=plan_approval verdict=pass duration_s=13
corpus-ec2-instance-complete   GAUNTLET stage=plan_approval verdict=pass duration_s=17
corpus-ecs-fargate             GAUNTLET stage=plan_approval verdict=pass duration_s=27
corpus-eks-basic               GAUNTLET stage=plan_approval verdict=pass duration_s=100
corpus-evoteum-modules         GAUNTLET stage=plan_approval verdict=pass duration_s=12
corpus-giantswarm-crossplane   GAUNTLET stage=plan_approval verdict=pass duration_s=11
corpus-hongbomiao-harbor       GAUNTLET stage=plan_approval verdict=pass duration_s=11
corpus-hongbomiao-labelbox     GAUNTLET stage=plan_approval verdict=pass duration_s=14
corpus-hongbomiao-storage      GAUNTLET stage=plan_approval verdict=pass duration_s=15
corpus-iam-policy              GAUNTLET stage=plan_approval verdict=pass duration_s=12
corpus-iam-read-only-policy    GAUNTLET stage=plan_approval verdict=pass duration_s=19
corpus-lambda-simple           GAUNTLET stage=plan_approval verdict=pass duration_s=15
corpus-leynos-monitoring       GAUNTLET stage=plan_approval verdict=pass duration_s=9
corpus-mastino-dns             GAUNTLET stage=plan_approval verdict=pass duration_s=28
corpus-overture-tiles          GAUNTLET stage=plan_approval verdict=pass duration_s=13
corpus-rds-complete-postgres   GAUNTLET stage=plan_approval verdict=pass duration_s=20
corpus-s3-bucket-complete      GAUNTLET stage=plan_approval verdict=pass duration_s=35
corpus-security-group-complete GAUNTLET stage=plan_approval verdict=pass duration_s=18
corpus-simpleinfra-dns         GAUNTLET stage=plan_approval verdict=pass duration_s=45
corpus-sqs-basic               GAUNTLET stage=plan_approval verdict=pass duration_s=13
corpus-sumaform-aws            GAUNTLET stage=plan_approval verdict=pass duration_s=54
corpus-vpc-complete            GAUNTLET stage=plan_approval verdict=pass duration_s=18
corpus-xancloud-iac            GAUNTLET stage=plan_approval verdict=pass duration_s=13
reference-ec2-vpc              GAUNTLET stage=plan_approval verdict=pass duration_s=11
terralith-scale                GAUNTLET stage=plan_approval verdict=pass duration_s=13

Diffing every stage of every row against the artifact at 70e2722, rather
than trusting the summary: 27 rows before and after, plan_approval not-pass
on none of them, clear: false on none of them, and exactly one stage moved
backwards.

The one estate that moved backwards

reference-ec2-vpc, day2_crash (stage 10, StatusPlanned, so the two bars
are unaffected and the row still reads clear: true): pass -> fail,
detail=the post-recovery plan exited 1. Its plan_approval passed on the
same run.

Not a flake, and not this change's doing. It reproduced twice on fresh
instance ids - once in the parallel run, once in a solo
go run ./tools/gauntlet run reference-ec2-vpc - and the row this PR carries
is the solo re-run's. The row it replaced was measured at eec6fb4 on
2026-09-06T01:27Z, so the cause is in main between there and 70e2722.

The log, from the recovery apply through the plan that refuses:

=== H2. the next plan recovers on its own: destroy the deposed object, nothing else ===
  choudoufu: exactly one destroy - the deposed object (i-65a7966e4fd5a1a42), nothing else
  i-65a7966e4fd5a1a42 terminated - confirmed via the AWS CLI, not through choudoufu's own report
  record: the deposed entry is cleared, current identity is unchanged (i-1fbf304b2bb047c88)
=== H3. one more plan: fully converged, nothing left to propose ===

Warning: Live resource displaced from the address it is marked for

A aws_instance with identity "i-49b443856f409d5a7" carries estate
"ec2-reference" and the address "aws_instance.main", but the estate's own
record for aws_instance.main names "i-1fbf304b2bb047c88" as the live resource
that address owns right now, and records this one as destroyed by an earlier
apply of this estate. [...] Nothing is proposed for this resource

Error: Two live resources claiming one address

2 live aws_instance resources carry estate "ec2-reference" and address
"aws_instance.main" at once: i-1fbf304b2bb047c88, i-65a7966e4fd5a1a42. A
human has to resolve the collision before this estate can be planned; see
live/MARKERS.md, "Ownership semantics".
FAIL: the post-recovery plan exited 1
GAUNTLET stage=day2_crash verdict=fail duration_s=31 detail=the post-recovery plan exited 1

Two objects, one plan, opposite treatment, and the difference between them is
whether anything ever recorded them as destroyed: i-49b4438... (an earlier
replace's old object) has a tombstone and draws the warning; i-65a7966...
(the deposed object H2 destroyed 30 seconds earlier) has none and draws the
refusal. Both are terminated instances whose marker tags EC2 still serves.

Where the tombstone went: #920 (issue #901) taught the write-back not to
tombstone a deposed object whose destroy leg had not completed - correctly,
since at H1 that object is superseded at the address and alive, and
writeback.go's own log line says as much ("That object is NOT recorded as
destroyed, because it is alive and the next apply's job is to destroy it").
But the next apply then destroys it and records nothing:
supersedeIdentity has exactly one call site,
internal/live/projection/writeback.go:821, on the replace path. Nothing
writes a tombstone when a deposed object's outstanding destroy finally runs.
Before #920 the (at-that-moment wrong) H1 tombstone happened to cover H3;
removing it exposed the missing write half. That is the read-half-without-
its-write-half shape HANDOFF names, pointed the other way, and it lands in
the table's first row: choudoufu refuses where nothing is wrong with the
world.

Left exactly as the runner wrote it, and deliberately not entered in
live/gauntlet/regressions.json: that ledger is a human acknowledging a
regression their own change earns, and this is a defect for its own unit -
signing it off here would hide it. gauntlet run will keep exiting nonzero
on this estate until it is fixed; the artifact is written and rendered either
way, which is the ratchet behaving as live/GAUNTLET.md describes.

Tests

Scoped to what this change touches, per the brief:

$ env -u PWD go test -count=1 ./tools/gauntlet/ ./live/
ok  	github.com/intentius/choudoufu/tools/gauntlet	7.300s
ok  	github.com/intentius/choudoufu/live	48.430s

$ env -u PWD go run ./tools/gauntlet check
rendered files are current

Both re-run after the rebase onto origin/main (which was clean -
live/gauntlet.json was untouched on main since 70e2722, so no
merge-artifact was needed).

Reproduce

just corpus-fetch
env -u PWD go build -o .bin/choudoufu ./cmd/choudoufu
TOFU_BIN=$PWD/.bin/choudoufu env -u PWD go run ./tools/gauntlet run -set all -parallel 4
env -u PWD go run ./tools/gauntlet render

The one regression alone:

TOFU_BIN=$PWD/.bin/choudoufu env -u PWD go run ./tools/gauntlet run reference-ec2-vpc

Closes the measurement half of #903.

🤖 Generated with Claude Code

https://claude.ai/code/session_018QSftBd7j3Dp4mnB6feRKL

The runner rewrote every row. `go run ./tools/gauntlet run -set all
-parallel 4` at 70e2722, against the pinned emulator
ghcr.io/lex00/floci@sha256:a39185cc, 44m11s wall for the 27 estates.

Board: core 0/26 clear, all 0/27 clear (every row carried
plan_approval=not_run behind #936's flip) -> core 26/26 clear, all 27/27
clear. Every estate reports `GAUNTLET stage=plan_approval verdict=pass`;
no estate lost a stage that was pass before.

One stage moved backwards, and it is not plan_approval and not this
change's doing: reference-ec2-vpc's day2_crash (stage 10, StatusPlanned,
so the bars are unaffected) went pass -> fail. It reproduces
deterministically - twice, on fresh instance ids, once in the parallel
run and once in a solo `gauntlet run reference-ec2-vpc` - and the shape
points at #920 (issue #901) landing the suppress half of a tombstone
without the write half:

  H1 crashes a create_before_destroy replace; the old object is deposed
     and alive, and #920 correctly writes NO tombstone for it.
  H2's recovery apply destroys that deposed object (terminated,
     confirmed through the AWS CLI) and clears the deposed record entry -
     but supersedeIdentity is called from exactly one site
     (internal/live/projection/writeback.go:821, the replace path), so
     nothing records the object this apply just destroyed.
  H3's plan then sees the terminated instance's marker still readable
     with no tombstone covering it and refuses: "Two live aws_instance
     resources carry estate ec2-reference and address aws_instance.main
     at once". The same plan WARNS rather than errors about an earlier
     replace's old object, which does have a tombstone - the two
     objects differ only in whether anything recorded them.

Left as the runner wrote it, unacknowledged in
live/gauntlet/regressions.json: that ledger is for a human acknowledging
a regression their own change earns, and this one is a defect to fix, not
one to sign off. Reported in the PR body and on #903 instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QSftBd7j3Dp4mnB6feRKL
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.

1 participant