Close the drift
Someone changes the database by hand. The operator notices and puts it back.
What it shows. What drift detection reads, and what it does about what it finds.
A replay of a verified run, not a live cluster. 6 checks held while it was recorded. Play types it out in the frame below; the transcript is the same session, already there.
# Converged, and the operator says so with a reason rather than a timestamp.$ kubectl -n "$NAMESPACE" get ptahschema storefront -o json \ | jq -r '.status.conditions[] | select(.type == "DriftDetected" or .type == "InSync") | "\(.type)\t\(.status)\t\(.reason)"'DriftDetected False ScopedConvergedInSync True ScopedConverged # Now change the database behind its back, the way an incident does.$ kubectl -n "$NAMESPACE" exec deploy/demo-psql -- psql -c "ALTER TABLE customers DROP COLUMN signed_up_at"ALTER TABLE # The column is gone. The declaration still asks for it.$ kubectl -n "$NAMESPACE" exec deploy/demo-psql -- psql -c "\d customers" Table "public.customers" Column | Type | Collation | Nullable | Default--------+--------+-----------+----------+--------- id | bigint | | not null | email | text | | not null |Indexes: "customers_pkey" PRIMARY KEY, btree (id)Referenced by: TABLE "orders" CONSTRAINT "fk_orders_customer_id" FOREIGN KEY (customer_id) REFERENCES customers(id) # Nobody is asked. The operator observes on its interval and closes it.$ kubectl -n "$NAMESPACE" exec deploy/demo-psql -- psql -c "\d customers" Table "public.customers" Column | Type | Collation | Nullable | Default--------------+--------------------------+-----------+----------+--------- id | bigint | | not null | email | text | | not null | signed_up_at | timestamp with time zone | | |Indexes: "customers_pkey" PRIMARY KEY, btree (id)Referenced by: TABLE "orders" CONSTRAINT "fk_orders_customer_id" FOREIGN KEY (customer_id) REFERENCES customers(id) # It closed the drift with a plan, the same way it does everything else.$ kubectl -n "$NAMESPACE" get ptahschemaplanNAME SCHEMA FINGERPRINT STATEMENTS DESTRUCTIVE AGEptah-plan-5b18ea4a0dba32b2f833e785 storefront sha256:5b18ea4a0dba32b2f833e785a430f249a6cc92d18511d53da5831c9045b46f90 3 false 2m5sptah-plan-ea24297faf3b1dd3895f4e14 storefront sha256:ea24297faf3b1dd3895f4e140a2a1675e4eb272ff27be536042561abf34deda0 1 false 23s
What was checked
Every step states what has to hold before its output may be published. A condition is read as its type, status and reason on the live object, never as a phrase in a message.
| Step | Claim | Held |
|---|---|---|
| 1 | exits 0, prints "ScopedConverged" | yes |
| 2 | exits 0, prints "ALTER TABLE" | yes |
| 3 | exits 0, never prints "signed_up_at" | yes |
| 4 | exits 0, prints "signed_up_at" | yes |
| 5 | ptahschema/storefront: DriftDetected=False (ScopedConverged) | yes |
| 5 | exits 0, prints "storefront" | yes |
What it ran against
- Kubernetes1.37.0
- Operator8c7fe187707f
- Ptahv0.6.0
- Executorsha256:91ec0a6fa27f
- Recorded2026-09-17
- Scenarios at8c7fe187707f
What the commands read
A session is repeated, not admired, so every name in it is one your own environment can carry. There is nothing else: no helper of ours stands between a command and the cluster, and the recorder refuses a step that reads a name this list does not hold. The commands arekubectl, ptah and kubectl ptah, each of which you install once; the last one is the operator’s own read-only client.
- KUBECONFIGthe cluster the commands run against
- NAMESPACEthe namespace the schema and its database credentials live in
- OPERATOR_NAMESPACEwhere the chart installed the operator
- CONTROLLERthe controller Deployment’s name, from that release
- REGISTRY_IN_CLUSTERthe registry address a Pod in the cluster resolves
- PTAH_OCI_REGISTRYthe registry address the push goes to
- PTAH_OCI_USERNAMEthe account that push uses
- PTAH_OCI_PASSWORDits password, which no command prints
A session also uses demo/bin/lab in two places, and both are the lab handing over what it generated rather than doing the demonstration’s work:lab credentials prints the three registry values above, and lab manifestfills the published template with values the step states. Rundemo/acceptance/reproduce.sh to watch the same scenario repeated from a directory where neither exists, by an account that may not create a Job.
The scenario is demo/scenarios/drift.yaml, and make demo replays the whole set against a lab of your own. All recorded runs.