- Asserts
- The key set joins to exactly
hostname,remote_plan_format,run_id;remote_plan_format == 1; the host equals the pinnedTF_CLOUD_HOSTNAME; and the run reference matches^run-[A-Za-z0-9]{16}$. - Publishes
- Host and run id as step outputs and into the job summary, so a reviewer can read which remote run the apply is about to touch.
- Locked by
- apply-pointer-step-absent, apply-pointer-keys-unchecked, apply-pointer-host-unchecked, plus four executable fixtures.
01What was reviewed and what actually ran
Five files, +512 / -10. One workflow gains three fail-closed steps between artifact download and apply; the checked-in contract, its failure fixtures and a new executable harness lock every one. The finding came from a pre-apply deploy review, not from a production failure.
Bound by this change new
- Reviewed policy to executed change set. The redacted manifest is re-derived from the saved
pointer at apply time through the checked-in
plan-review-manifest.jqprojection and must equal the dispatchedplan_manifest_sha256. - Pointer shape and host. Exactly three keys,
remote_plan_format == 1, the pinned HCP host, a run reference matching^run-[A-Za-z0-9]{16}$. - Remote run lifecycle. Must resolve to workspace
kubernetes-infra, be reported confirmable by HCP, and hold a status in the allowlistplanned,cost_estimated,policy_checked. - CLI to workspace.
TF_VERSIONreconciled1.9.0→1.10.2, the version the workspace reports; all foursetup-terraformsteps resolve that single pin. - Reviewability. Both plan-producing jobs publish the remote
run_idbeside their digests.
Explicitly not asserted said so in the job summary
- Any approval recorded outside this workflow. The gate reads HCP for run state; it does not treat an out-of-band sign-off as authority to apply.
- Any workspace change made after this read. The run is confirmable when queried; a later mutation is outside the window this gate can close.
- The pin regression on
origin/main. Pre-existing, from another team's merged PR, left unfixed deliberately - see section 08. - Nothing is retained on disk. The projection streams into
jqandsha256sum; the contract rejects any raw plan JSON intermediate.
02The artifact the gate was hashing
tf/terraform.tf declares a cloud {} block bound to organization
ekho-labs and workspace kubernetes-infra. Plans therefore execute
remotely, and terraform plan -out=tfplan does not write a self-contained plan. It
writes this, and nothing else.
// tf/tfplan — 86 bytes, run reference redacted {"remote_plan_format":1,"run_id":"run-XXXXXXXXXXXXXXXX","hostname":"app.terraform.io"}
- remote_plan_format
- Number
1. The only structural marker that this file is a pointer rather than a plan. - run_id
- A 20-character HCP run reference. The entire change set lives behind it, on HCP, and never enters this file.
- hostname
app.terraform.io. Which HCP instance resolves the reference at apply time.
Shape taken from the pointer the main-push plan run actually produced, not from
documentation. The live PR evidence comment now carries a real run_id of the same
pattern.
What tfplan_sha256 certified the gap
That these 86 bytes were unaltered. A genuine control, and it is kept. But the digest pins a run reference, so it can match byte for byte while the remote run behind it planned something else entirely.
The reviewed plan-review-manifest.json
was a sibling produced at plan time and carried in the same zip. It was compared against its own
recorded digest and never re-derived from the remote run at apply time. Nothing tied reviewed
policy to the change set that would execute.
Corroborating signal from the same pass: the workflow pinned
TF_VERSION: "1.9.0" while the workspace ran 1.10.2 - a silent CLI/workspace
split that only a remote-execution reading explains.
03The apply gate, before and after
Same dispatch, same artifact, same digests. The difference is what has to be true between downloading the plan and running it. Hover or focus a step to trace its path; click to pin, Escape to release.
- Permitted — asserted before advancing
- Unbound — advances on a run reference alone
- Fail closed — no apply
Before — origin/main digest-bound, policy-unbound
Four steps, and not one of them asks what the referenced run actually plans. Every assertion here is about the bytes, the artifact and the branch - never about the change set.
After — PR #87 three fail-closed steps, then apply
Both lanes end at the identical terraform apply of the identical saved
pointer. Nothing was added to the apply itself; what changed is which runs can reach it.
04The hardened apply sequence, step by step
Steps 01-03 and 07 are unchanged and stated inline; expand a new step, 04 to 06, for exactly
what it asserts and which checked-in fixture fails if someone removes it. The contract also requires
this order, requires every new step to begin set -euo pipefail, forbids
continue-on-error and || true inside them, and requires apply to
be the last step in the job.
- 01Bound manual dispatchretained ·
workflow_dispatchonrefs/heads/main, four required inputs,environment: production, concurrency groupterraform-production-apply. Unchanged by #87. - 02Source-run provenanceretained · repository, workflow path, event
push, refrefs/heads/main, head SHA equal tocommit_sha, conclusion success, exactly one unexpired artifact of the exact name. Detail in section 06. - 03Exact artifact set and both digestsretained · exactly
tfplanandplan-review-manifest.jsonas regular files, both matching the dispatched digests. The pointer digest is a real integrity control; it is just not a policy control. - Asserts
- The run resolves to the reviewed workspace
kubernetes-infra, HCP reportsis-confirmabletrue, and the status is one ofplanned,cost_estimated,policy_checked. A discarded, errored, already-applied or foreign-workspace run is rejected. - Does not assert
- Any approval recorded outside this workflow, and any workspace change made after this read. The job summary says so in those words rather than implying more coverage than exists.
- Locked by
- apply-remote-run-state-absent, apply-remote-run-confirmable-unchecked, and the run-discarded / run-wrong-workspace executable fixtures.
- Re-derives
terraform show -json "$PLAN_DIRECTORY/tfplan" \ | jq -S -f modules/prometheus-stack/tests/plan-review-manifest.jq \ | sha256sum | cut -d ' ' -f 1
- Asserts
- That digest equals the dispatched
plan_manifest_sha256.terraform show -jsonresolves the pointer against the live remote run, so the projection is taken from the change set that is about to execute, not from the sibling file that travelled in the zip. - Never writes
- Raw plan JSON. The contract rejects any redirect in this step other than the summary append, so no intermediate can be retained.
- Locked by
- apply-manifest-rebind-step-absent, apply-manifest-rederivation-absent, apply-rederived-manifest-uncompared, apply-manifest-binding-fail-open, apply-manifest-binding-continue-on-error, apply-gate-not-fail-closed, and the decisive manifest-drift fixture.
- 07Recheck main, then apply the saved planretained · remote
refs/heads/mainmust still equal the dispatched commit, immediately followed by the apply and nothing else. The contract compares this step's active lines to an exact three-line list, locked by mutable-step-after-main-recheck and unbound-second-apply.
05The test that separates what a digest cannot
The apply gate's real shell steps run against synthetic pointers with
terraform and curl replaced by fixture shims, so each control is proven by
behaviour rather than by a string match on the workflow file. Two fixtures hold the pointer bytes
constant and move only the remote change set.
valid accepted
Pointer digest 9be342a86d5e1abfe5aea899409959876e4f9693f3123e3830cce74b364f64cd
The re-derived manifest equals the dispatched
plan_manifest_sha256. The run clears pointer,
run_state and rebind, and reaches apply.
manifest-drift rejected
Pointer digest 9be342a86d5e1abfe5aea899409959876e4f9693f3123e3830cce74b364f64cd
Byte-identical to the accepted case. Behind it the
first resource change has become a delete. tfplan_sha256 cannot tell them
apart; the re-derived manifest does, and the job exits at rebind.
Those digests are the harness's synthetic pointer, not a production run. That is the point: the two cases are constructed to be indistinguishable to the old control.
All eight executable apply-gate fixtures
Each is rejected at its own step for its own reason, so one over-broad check cannot be mistaken for five specific ones. Identical on both independent green passes.
| Fixture | Outcome | Stops at | Rejected because |
|---|---|---|---|
| valid | applied | none | Every assertion holds. |
| extra-key | rejected | pointer | Key set does not carry exactly the three reviewed keys. |
| missing-key | rejected | pointer | Key set does not carry exactly the three reviewed keys. |
| renamed-key | rejected | pointer | Key set does not carry exactly the three reviewed keys. |
| wrong-hostname | rejected | pointer | Host is not the expected HCP host. |
| run-discarded | rejected | run_state | HCP does not report the run confirmable. |
| run-wrong-workspace | rejected | run_state | Run belongs to a workspace other than the reviewed one. |
| manifest-drift | rejected | rebind | Re-derived manifest does not match the reviewed plan_manifest_sha256. |
06Controls that were already there and stayed there
No existing gate was weakened to make room for the new ones. Each is still asserted by the same checked-in contract, and each still has a failure fixture that turns red if it is removed.
Speculative plans stay ineligible retained
A PR plan uploads under terraform-speculative-plan-<head sha> on the pull_request event. Apply accepts only a push source run, so a PR artifact can never be applied.
Apply material comes from main retained
The deployable artifact is produced only by a successful push to refs/heads/main, named terraform-plan-<github.sha>, holding exactly the pointer and the redacted manifest.
Four bound dispatch inputs retained
source_run_id, commit_sha, tfplan_sha256, plan_manifest_sha256, all required. The new rebind step consumes the fourth rather than adding a fifth.
Source-run provenance retained
Repository, workflow path, event, ref, head SHA, conclusion and exactly one unexpired artifact are verified before anything is downloaded.
Digest verification retained
Exact regular-file set equality, then both SHA-256 digests. Pointer integrity is still checked - now the first of four assertions instead of the only one.
Main recheck and concurrency retained
Remote main is re-read immediately before apply, and applies serialize in group terraform-production-apply with cancel-in-progress: false. Non-dispatch runs stay cancellable.
Also unchanged: no plan contents, values, outputs or variables are ever published. The redacted manifest carries only format and Terraform versions and, per resource, address, module, mode, type, name, actions, replace paths and sensitivity booleans.
07Evidence
Every control is locked by a test that failed before the change. Red proof was measured
against origin/main's workflow at
sha256 2cacbef5…; the green suite ran twice, independently.
- 10
- distinct static-contract failures the extended checker reports against
origin/main- the missing version pin, the missing host pin, four unrecorded-run-id assertions, and the three absent gate steps. - 8 / 8
- executable apply-gate fixtures fail against
origin/mainwith apply job is missing bound gate steps: pointer, run_state, rebind. - 14 / 15
- new negative fixtures have no marker to mutate on
origin/main, because the code they would break does not exist there yet. - 48
- failure fixtures in the Task 3 suite after this change, all parsing and all rejected as intended.
- 2
- independent green passes, identical: contract checker, legacy characterization still correctly failing, 3 runtime source-run fixtures, 8 apply-gate fixtures, the Task 3, 5, 6, 7 and 9 suites, amtool, both temporary-root suites.
- 0 / 0
terraform fmtandvalidatefindings; actionlint reports 0 on main and 0 on the branch; basedpyright reports 0 errors on both sides.- 31924407290
- CI run, attempt 1, event
pull_request, conclusion success. Security Check, Static Checks and Terraform Plan pass; Main Plan and Apply correctly skipped. - run-…
- the live PR evidence comment now carries a real
remote_run_idmatching^run-[A-Za-z0-9]{16}$- independent confirmation that the shape the gate asserts is the shape the workspace emits.
Merge state at review: no conflicts, merged tree 145ec015. All eight apply-gate controls added here hold on that merged tree; its single failure is the pre-existing pin regression described in section 08.
08Residual risk
Severity is the reviewer's calibration of what is still exposed with this change open and unapplied. Two of these are not defects in PR #87 at all; they are conditions it does not remove.
Showing all 6 risks.
- Detail
- PR #87 is open and unmerged, so an apply dispatched today still runs the pre-hardening gate: it hashes the pointer without re-deriving the manifest. An apply 31923590875 dispatched during the task window ran exactly that path; it was not dispatched by this work and was left untouched.
- Mitigation
- Merging closes the window. Until then a reviewer can bind a review to a specific remote run by reading the
remote_run_idnow published beside the digests.
- Detail
- Merged commit 8f3ff17 (PR #36, security hardening rollout) downgraded two invocations from immutable SHA pins to mutable tags,
actions/checkout@v4andhashicorp/setup-terraform@v3. Main's own checked-in checker already fails on main for this reason, and a mutable tag can be repointed upstream. - Disposition
- Found in passing, out of this task's scope, deliberately not fixed here: it belongs to another team's merged PR. It is the sole failure on the merged tree and needs a decision, not a silent patch.
- Detail
- HCP is asked once whether the run is confirmable and belongs to the reviewed workspace. A workspace change made after that read is outside the window the step can close, and the job summary says so rather than implying otherwise.
- Mitigation
- rebind runs after the read and re-derives policy from the pointer, so a substitution between the two would still have to survive the manifest comparison.
- Detail
- The gate asserts run state and policy equality. It does not assert that any human approval recorded outside this workflow took place, and does not attempt to.
- Mitigation
- Published as a non-assertion in the job summary, so a reviewer cannot mistake the check for an approval record. The two-reviewer release gate remains the place that decides.
- Detail
- run_state reads the HCP API and rebind resolves the pointer against the remote run, so an HCP outage stops the apply.
- Why acceptable
- It fails in the correct direction. A plan that cannot be re-derived cannot be certified, and a remote apply could not execute in that state anyway.
- Detail
TF_VERSIONis1.10.2because that is what the workspace reports today. A workspace upgrade re-opens the split this change closed.- Mitigation
- Asserted by the contract and by the terraform-cli-version-stale-pin and terraform-cli-workspace-version-split fixtures, so drift fails loudly instead of silently.