What this change is
Task 15 stages a credential reference. It writes a pointer to a 1Password vault item into the cluster and stops there. Everything that would prove the credential is usable — and everything that would actually deliver an alert — belongs to later, separately gated tasks.
- Adds exactly one Terraform resource: a
OnePasswordItemcustom resource in namespacekube-prometheus-stack. - Points it at a single vault item path. The manifest carries a path and nothing else.
- Adds a source validator that pins the manifest to an exact literal, requires
specto be exactly{itemPath}, and rejects four concrete mutations. - Adds a separate key-only contract that binds a materialized Secret by UID and resourceVersion, asserts the five key names, and structurally forbids any value field.
- Adds a narrowly scoped exemption so the existing no-delivery guard tolerates the new resource's own name.
- It does not show the credentials are valid. Nothing authenticates against Matrix anywhere in this change.
- It does not show Matrix delivery is active. No Matrix receiver, route, or webhook exists, and every alert still routes to the built-in
nullsink. - It does not yet confirm the real Secret's key names. The contract that checks them has run only against synthetic self-tests, because no Secret exists to inspect.
- It was not applied. No merge, no
terraform apply, no Secret read, no workflow dispatch.
The staged object
The entire resource added to tf/modules/prometheus-stack/main.tf, unchanged across both revisions of this pull request. The validator asserts the manifest is byte-for-byte this, has exactly the four keys apiVersion, kind, metadata, spec, and — as a separate assertion over the parsed configuration — that spec's key set is exactly {itemPath}, so no field-mapping block can be smuggled in.
resource "kubernetes_manifest" "matrix_alertmanager_receiver" { manifest = { apiVersion = "onepassword.com/v1" kind = "OnePasswordItem" metadata = { name = "matrix-alertmanager-receiver" namespace = "kube-prometheus-stack" } spec = { itemPath = "vaults/kubernetes-vault/items/alertmanager_matrix_receiver" } } }
module.prometheus_stack
.kubernetes_manifest
.matrix_alertmanager_receiver
vaults/kubernetes-vault
/items/alertmanager_matrix_receiver
No fields block, no value, no depends_on, no lifecycle. The validator requires the resource body to hold the single key manifest, and a negative case proves that adding spec.fields is rejected.
The five-key map
Five 1Password field labels are intended to materialize as five identically named keys on the resulting Secret. A dedicated contract checks those names against real post-apply evidence — never against their values.
Change map — staging, materialization, and the gate
Solid lanes are created or enforced by this PR. Dashed lanes are runtime behaviour or future work that this PR deliberately does not perform. Hover a lane to isolate it.
Declares the pointer and nothing else. The manifest carries a path, not a value, so there is no credential for Terraform to render or persist; the reviewed plan artifact is redacted to structure only.
spec to {itemPath}; 4 negative casesBehaviour of the already-installed operator once the custom resource exists. This is where values first appear, entirely outside Terraform's view.
Added by this change and bound to one exact Secret revision. It judges names, never values — and it is built so that a value cannot even be submitted to it.
extra="forbid"; evidence carrying data is rejected outrightEverything above concerns shape: a path, a namespace, a set of key names, a Secret revision. Even a fully satisfied key-only contract proves only that the right names arrived — never that the values behind them are correct, current, or accepted by anything. No semantic claim crosses this line.
The sole place where the credentials are actually judged. It binds its verdict to the specific Secret it inspected, by UID and resourceVersion, so a later rotation cannot silently inherit a stale pass.
Zero Matrix delivery resources exist today. Alertmanager still declares a single built-in null receiver and routes every alert to it, which is the standing no-delivery safeguard. The Task 9 contract fails the build if core tries to add real delivery, and that guard is unchanged outside one path-scoped exemption.
Plan outcome
The reviewed artifact is a redacted plan manifest — structure without values. Its schema is asserted, not assumed.
# module.prometheus_stack # .kubernetes_manifest # .matrix_alertmanager_receiver actions = ["create"] delete_count = 0 replace_paths = []
- Exactly one non-
no-opresource change is permitted; more than one fails the contract. - The validator accepts only
["create"]or["update"]. The observed plan is create. - Any
deleteaction or any replacement path fails closed — one of the three negative test cases proves it.
The manifest may contain exactly three top-level keys, and each resource change exactly nine. The absent keys are the point:
# allowed per change address, module_address, mode, type, name, actions, replace_paths, before_sensitive, after_sensitive # absent by assertion — this is the redaction before, after
Because before and after are rejected outright, the reviewed artifact structurally cannot carry a value, rather than merely happening not to.
- Workflow run
- 32178432037 · attempt 1 · success
- Artifact
- 9339927303
terraform-speculative-plan-7bd591f7… - tfplan sha256
- 12d567ea66f90900efcea0074fdd65289a2dcf29a012761f097032960b899d75
- plan manifest sha256
- 5221b470126d4fafab540ff7973534e2db5ec4b2576f7983efbd860dfe4f61d5
- Remote run
- run-difbTWYh4s9k25sW
- Artifact expires
- 2026-08-19T19:47:35Z
File by file
Five files, all inside tf/modules/prometheus-stack. Nothing else in the repository is touched. Only the first carries infrastructure; the rest are contracts and CI wiring.
The staged resource itself, added above the existing Grafana password resource. It is the only Matrix-related resource in a 444-line module whose other twenty resources are Grafana, Prometheus, Pushgateway, ingresses and dashboard ConfigMaps.
Why here: the Secret must land in the same namespace as the Alertmanager that will eventually consume it, so the reference belongs to the Prometheus Stack module rather than a separate one.
A standalone source validator. It parses main.tf with python-hcl2, requires the staging resource to be the sole one of its name, and compares the manifest against an expected literal — so any drift in path, name, namespace, or shape fails.
It then runs four negative cases, each a deep copy mutated and required to be rejected: a wrong itemPath, an added depends_on, an added spec.fields block, and a plan whose action is delete. Testing that the guard refuses bad input is what gives the positive assertion its weight.
What changed in the second round: the earlier FIELD_MAPPING constant and the assertion over it were removed outright. In their place the validator now asserts something it can actually observe — that spec's key set is exactly {itemPath}. Key names moved to the contract below, where they are compared against real evidence instead of against themselves.
The key-only materialization contract, and the substantive addition of this round. It models the evidence as frozen Pydantic types: an OnePasswordItemMetadata that must match the exact apiVersion, name, namespace and item path, and a SecretMetadata carrying name, namespace, a non-empty uid, a non-empty resource_version, and the tuple of key names.
Values are excluded structurally rather than by convention. Every model is declared extra="forbid" and none has a field for secret data, so evidence containing a data block fails validation instead of being silently dropped — and one of the four negative cases exists purely to prove that.
Note on invocation: the self-tests run unconditionally, but the real comparison happens only when an evidence file is passed as an argument. At this head none has been, because nothing has been applied.
The existing no-delivery guard scans every walked location for the tokens matrix and /alerts/, plus the five Task 15 field names and the item-name token alertmanager_matrix_receiver. The new resource necessarily trips it: its Terraform name contains matrix and its itemPath contains alertmanager_matrix_receiver.
The fix is a path-scoped skip, not a token removal — and it suppresses only forbidden-string scanning. The sensitive-resource-type and manifest-kind checks still run over the new resource, which passes them on its own merits because OnePasswordItem is not a sensitive kind. Webhook-key detection and the alternate-config-source checks are likewise untouched, though it is worth being precise that those read the Helm values and the rendered Alertmanager config rather than the Terraform resource, so they never covered this resource to begin with.
CI plumbing, not Task 15 semantics. Two pinned expectations in the workflow contract move: the apply gate now also accepts the remote run status planned_and_saved alongside planned, cost_estimated and policy_checked; and the expected actions/upload-artifact commit pin is bumped to a new SHA.
Why it appears here: the contract test asserts the workflow file byte-for-byte, so an unrelated workflow change cannot merge without this file moving with it. Worth separating mentally from the credential-staging story when reviewing.
Weak links and reviewer notes
The one substantive finding from the first review round was resolved in code rather than argued away. One new finding replaces it, surfaced by checking what the pipeline actually executes. The rest are seams a reviewer should hold in mind — each a consequence of Task 15 deliberately deferring meaning to Task 16.
MediumNone of these contracts are executed by CI+
task15, task9, pytest, uv, or any .py path. The jobs that ran on this pull request are Terraform format, init, validate and a Trivy config scan — plus a kubeconform step that explicitly reports itself as skipped, since no static manifests can be rendered without credentials in fork-safe CI — followed by plan, manifest projection, hashing, artifact upload and evidence publication. No Python contract executes at any point.spec.fields block, or widen the guard, and every check on the pull request would still report green.32178432037, cross-checked against the workflow file at head 7bd591f7.LowThe key-only contract has never seen a real Secret+
LowThe guard exemption is a substring match on the location path+
.kubernetes_manifest.matrix_alertmanager_receiver appears anywhere in a walked location, so every nested location under that resource is exempt from forbidden-token scanning.spec to exactly {itemPath}. A token the Task 9 guard ignores would still have to survive both equality checks.policy.py pointing at the pinning assertions would keep the pair from drifting apart.LowThe no-Secret rule guards the Terraform surface, not the cluster surface+
resource_failures blocks core from declaring a kind: Secret manifest, and OnePasswordItem is deliberately absent from the sensitive-kind set — so the new resource passes that check on its own merits, without needing the exemption.InfoResolved since the first revision: the self-referential key assertion is gone+
FIELD_MAPPING constant of five identical pairs and asserted that its left column equalled its right column. Both sides derived from the same literal, so the check was true by construction and could never fail — it validated a constant, not the configuration.spec's key set is exactly {itemPath} — a genuine statement about the parsed configuration — and a new negative case proves that adding spec.fields is rejected.BLOCKED decision — noting that no independent Terraform or rendered-plan evidence proved the five labels — before the second commit landed.InfoAn unrelated CI-contract change rides along in the diff+
workflow_contract_check.py moves two pinned expectations: the apply gate additionally accepts the remote run status planned_and_saved, and the expected actions/upload-artifact pin is bumped to a new commit SHA.InfoTwo staging records exist, and only the newer one binds this head+
task-15-metadata-staging-binding.json, names the first head 052418a0 with that round's artifact and digests. A later post-correction packet, task-15-release-refresh-20260818T194740Z.json, binds the current head 7bd591f7, tree 606f6801, run 32178432037, artifact 9339927303 and both digests. Evidence for this head does exist on disk.runtime_semantic_claim set to false.7bd591f7 and the matching digest packet. The flag records a moment in time, not a defect in what is now published.9339927303 expires 2026-08-19T19:47:35Z. If the hold outlasts it, the digests can no longer be re-verified against a retrievable artifact and a fresh speculative plan is required.InfoMerging introduces no content change, and the plan shape did not move+
7bd591f7 and merge-preview commit 976fa949 resolve to the same tree, 606f6801.5221b470… is unchanged from the first revision even though the head moved, which is independent confirmation that the second round touched test code only and left the planned infrastructure untouched.Status
Every automated check that ran on the frozen head passed — Apply and Main Plan were skipped by design, not failed — and the change is tightly scoped to what Task 15 authorises. The hold is not a symptom of a failure; it is the design. Task 16 remains the sole gate that may assert anything about the credentials themselves, and no step in this pull request attempts that assertion.
Read the row below carefully. The green ticks cover Terraform and the plan; the contracts this change adds are not among them.