Conversation
This was referenced Sep 15, 2026
aparajon
force-pushed
the
armand/storage-schema-guide
branch
from
September 15, 2026 22:50
95191f4 to
10aa468
Compare
aparajon
force-pushed
the
armand/storage-apply-named-release
branch
from
September 15, 2026 22:53
28171d0 to
cf405fa
Compare
aparajon
force-pushed
the
armand/storage-schema-guide
branch
7 times, most recently
from
September 17, 2026 16:36
590ae1f to
fe55852
Compare
… one answering storage apply took no schema selector: a convergence ran the schema embedded in the binary answering it, so the storage a release needs could not be in place until that release was already deployed. That is backwards for the workflow the command exists for, where an operator applies the next release's storage ahead of the rollout. --release and --schema-dir now work on apply the same way they work on plan, and the files they name are what converges. The schema is resolved once and threaded through the preview, the confirmation, and the convergence, so a moved tag cannot have an operator approve one file set and run another. Two gates sit in front of it. Naming a release is refused with --auto-approve, so a cross-release convergence is never reachable unattended, and the confirmation states the consequence a plan cannot show: until the named release is deployed, every pod that boots the running one converges the difference back, and it does so asymmetrically. A surplus table or column is refused as destructive and survives; a surplus index loses no data, so it is removed without asking. A release read over a plaintext $GITHUB_API_URL is now refused on a convergence rather than warned about. The rewrite that costs a plan its authority costs a convergence the storage database, since whatever DDL arrives is what runs, having parsed exactly as a real schema would. AV-9 is rewritten for this: which schema a convergence runs is the operator's to name. What it still refuses is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four comments and a test assertion still described the retired rule, in the layers underneath rather than in the ones this branch rewrote. "apply is what a boot does" was doing two jobs: an apply runs under the same policy a boot runs under, which is still true and is the only thing the three comments citing it were about, and an apply runs the same schema a boot runs, which it no longer does. Each now names the policy property directly, so none of them rests on half a claim. The converged-plan test asserted the absence of the old hint's wording, which pinned a phrase the tree no longer has anywhere. It asserts the absence of a next step instead, which is what the case is about. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ts target once Review feedback on the storage schema commands, in three parts. A refused `storage apply --json` printed a human plan on the surface a program parses — or, on the destructive gate, which prints nothing and returns silently, nothing at all. Both gates now stay quiet under `--json` and the refusal comes back as the same response shape a convergence does, both halves carrying the report: nothing ran, so everything the plan found is still outstanding. The storage target is resolved once per run and passed along. Every resolution of a config using `storage.dsn_from` is a fresh read of secret references, so up to three of them meant three audited reads of the storage credential, and a window where a value that changed in between had the convergence run against a database its own preview never looked at. The resolution now sits on the target flags themselves, so the copy an apply hands its preview carries the database it already resolved, and a completeness test over the package pins the single call site. The destructive gate's doc comment had run into the manual gate's, leaving one gate wearing the other's AV-9 rationale and the other with no doc.
aparajon
force-pushed
the
armand/storage-apply-named-release
branch
from
September 17, 2026 18:37
28ff40f to
00d0a35
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why this matters
The storage a release needs has to exist before the first pod of that release starts — and the pod that would converge it is the one that cannot start until it is there. That is the whole reason
storage applyexists, and until now it could not do it: a convergence ran the schema embedded in the binary answering the command, so an operator holding a CLI on the deployed release could only ever converge the deployed release's schema. Pre-applying the next release's storage, which is the workflow the command was built for, was the one thing it refused.On a long-lived database this is not a convenience. A storage index added in a new release runs as a table copy on every pod in the roll, under a five-minute startup budget; converging it once ahead of time is the difference between a deploy and an outage.
What it does
--releaseand--schema-dirnow work onstorage applyexactly as they work onstorage plan, and the files they name are what converges. An operator runsschemabot storage apply --release v1.4.0from whatever CLI they have to hand.An operator on CLI v1.2.3, fleet deployed on v1.2.3, converging v1.4.0's storage (which adds
applies.driver_note) ahead of the roll:The schema is resolved once, in
Run, and threaded through the preview, the confirmation, and the convergence. A tag that moves between the plan and the apply cannot have an operator approve one file set and run another.Two gates sit in front of it:
--auto-approveis refused with a named schema. A cross-release convergence is never reachable unattended. A pre-deploy job can still converge the answering binary's own schema with-yand no selector — that path is unchanged.A named schema gets no more permission than a boot: a
DROP TABLEorDROP COLUMNis refused exactly as it is at startup, which is also what protects the storage from a hand-assembled--schema-dirthat is missing half its tables — the missing tables diff as destructive statements and the whole set is refused.One tightening comes with it: a release read from a plaintext
$GITHUB_API_URLis now refused on a convergence, where a plan still only warns. The rewrite that costs a plan its authority costs a convergence the storage database, since whatever DDL arrives is what runs — and it arrives having parsed exactly as a real schema would, so nothing downstream has anything to catch. A mirror on loopback is exempt, as everywhere else in this file.storage apply --release v1.4.0 (new)
storage apply --release v1.4.0 (previous)
storage apply --release v1.4.0 --auto-approve (new refusal)
Two fixes on the same commands, from review
--jsonconvergence answers in JSON. Both gates printed a human plan on the surface a program parses — and the destructive gate, which prints nothing and returns silently, printed nothing at all, so a caller could not tell a refusal from a crash. They stay quiet under--jsonnow, and the refusal comes back as the response shape a convergence uses, both halves carrying the report: nothing ran, so everything the plan found is still outstanding.storage.dsn_fromis a fresh read of secret references, so the three call sites meant three audited reads of the storage credential, and a window where a value that changed in between had the convergence run against a database its own preview never looked at. The resolution now sits on the target flags, so the copy an apply hands its preview carries the database it already resolved, and a completeness test over the package pins the single call site.Invariants
Destructiveand survives anEnsureSchema.Note
storage planandstorage applyhave not shipped, so theApplyStorageSchema/PlanStorageSchemasignature change breaks no importer.Opened by Claude Code (Opus 5).