docs(plans): record !2411, correct S04-A's ordering rule, Files lists and seams

What this does

Six edits to the per-format credential columns plan.

Records Step 2's MR. Row 2 was the last empty cell among the three add-columns steps; Steps 1, 6 and 9 are already filled. It now points at !2411 (merged) (:908).

Corrects the migration ordering rule. Step 2's Shared seams paragraph said the requirement over the six migration steps is "mutual exclusion rather than a sequence: any order is fine". That is false, and !2411 (merged) is the counterexample.

internal/datastore/migrations/migrations.go sets goose.WithAllowOutofOrder(false). A step whose migration versions sort below a version the database has already applied is refused outright rather than applied late: Up returns a missing-migration error and applies nothing, Runner.Start calls it at pod startup, and Runner.Check stays pending on failure. So the versions are ordered, not merely mutually exclusive, and whichever of the six lands second has to renumber above the head at that moment rather than only re-deriving knownHeadVersion.

Mutual exclusion over knownHeadVersion is still required; the ordering obligation is added beside it.

Both halves are recorded once, in ## Dependencies (:767-814), the shape this plan already uses for Row counts (:739-747), the lock window (:748-766) and the two merged-before gates (:827 and :838). The six steps the rule binds cite that line from their Shared seams field (:351, :519, :548, :621, :639, :706), and Step 2 keeps only its own knownHeadVersion seam. The paragraph introducing the mermaid diagram carried the same claim and pointed readers at Shared seams for the rule; it now names the two constraints mermaid cannot draw and cites the same line (:274).

Says what CI does not enforce. "Either way it is caught before deploy" held for the two cases the bullet names, a pipeline created before a sibling merged and a sibling that has already merged, and not for two of the six riding one merge train. Both halves of lint:migration-ordering's comparison read the target branch, target_latest from a git ls-tree -r of it (.gitlab-ci.yml:530-533) and the added set from a three-dot git diff <target>...HEAD (:539), so on the second car's pipeline the car ahead is invisible twice: its timestamp is not in target_latest, and its migration file lands inside the second car's own added set. Every timestamp there sorts after the target's latest, the loop at :548-553 finds nothing, and both cars go green and merge out of order. lint:comment-caps already records the same blindness for its own base selection (:870-872), so the clause has a citation in the repo rather than only a derivation. It lands in both places the fact lives, the bullet (:796-807) and the graph note (:275-278), so a reader who finds only one does not relax the constraint on the grounds that CI would catch the violation.

Drops a borrowed justification that argued the other way. "That is the conclusion the package-id FK-check index plan reached on the same two files" attached to both the mutual exclusion and the second-lander rule, and that plan reached neither: it answered the same overlap by serializing, "they run serially rather than in parallel" (:332-333) and "Step 2 opens after Step 1 merges" (:499-500). Serialization fixes one sequence, which this plan's graph note says these constraints do not do, so the borrow argued the opposite of what it was cited for. The passage now says what that plan did and why this rule is weaker (:772-781), and the genuine support was already two sentences later in AllowOutofOrder(false). The two-files half of the claim held and is kept: structure.sql, and the migrations_checksum_test.go that carries knownHeadVersion. The sibling borrow in the ADR-007 gate (:838) was checked too and stands, so it is left alone.

Completes Steps 4 and 7's Files lists. Step 4 named internal/datastore/maven_remote_repositories_errors.md but not the .go beside it, and Step 7 had the same gap for Container. Both classifiers carry the code half of their sidecar's PgError.Detail rule, and the step that encrypts the columns is what falsifies that reason, since the failing row no longer echoes plaintext credentials. Step 10 already listed npm's pair (:665-672), so Maven (:434-441) and Container (:573-582) now read the same way and no format is left split. Container's entry says one site rather than Maven's two: pgErrorCarriesRowData is declared in npm_remote_repositories_errors.go and described in maven_remote_repositories_errors.go, and the container file carries neither.

Moves the CountRowsForVersion residual to Step 3. Step 4 carried an entry for internal/datastore/namespace_encryption_keys.go whose whole content was a residual, "what this step owns is the comment's claim about what the function reports", and the split was empty. Guardrail 17 (AGENTS.md:516) obliges Step 3 to leave the comment true when it changes the body, so the rewrite that survives Step 3, "counts registered tables", is still true once Step 4's writes create the first Maven rows. The one Step 3 rewrite that would leave a residual is a present-tense "still reports 0" clause, and A Claim Time Will Falsify Names What It Is True At rules that shape out. Step 3's entry (:372-387) now covers the body and the comment's claim about what the function reports; Step 4 lists the file nowhere. Both Shared seams fields name the path: Step 3's (:401-405) as the only step in this plan that edits it, Step 4's (:471-478) to say it edits none of it, since that is what a reviewer of either MR opens the field for and the Depends on: Step 3 edge gives the mechanism without the path.

The Files-list and seam edits ride in this MR rather than on separate branches touching the same file: guardrail 4 (AGENTS.md:387) gives collision between parallel edits as the reason the Status table takes a single writer, and guardrail 23 (AGENTS.md:565) says an overlapping file conflicts whichever lands second with no pipeline reporting it. !2411 (merged) sits on this plan's Status row and this MR is unmerged, so any further branch would be the next one queued behind them.

Plan line numbers are measured at 54749e159. The namespace_encryption_keys.go citations inside the plan stay at ee9e5063c, the basis its Research Findings pins; the .gitlab-ci.yml and AGENTS.md lines cited here are identical at both.

How this was reached

Step 6 merged with 20260904140000 and 20260904140100 while Step 2 sat on an older base carrying 20260904131400 and 20260904131500. Following the plan as written, Step 2 would have re-derived the constant, resolved the one conflicting line, and gone red on the merge train. It renumbered instead.

lint:migration-ordering is what catches it, outside the merge-train blind spot. The job fetches the target branch and reads its latest migration timestamp when it runs (.gitlab-ci.yml:525 and :530-533), and the added-file set comes from a three-dot diff against the target (:539), so the merged-results setting is not what makes the check current. A pipeline created before a sibling merged still passes, and the violation appears on the next pipeline the branch gets: push or re-run after the sibling lands and it is a red job before review; leave it, and the merge train's own pipeline is the first to see it, as a dropped train. The local suites see nothing, since every suite migrates from zero, where version order is self-consistent, and scripts/ci/check-migration-immutability.sh reads in-place edits rather than ordering. Re-running the job after a sibling lands, and renumbering above the head it reports, is what avoids the round trip.

Steps 5, 8 and 11 each drop a plaintext column and carry the same obligation, which is why this belongs in the plan rather than only in !2411 (merged)'s description.

Scope

Plan text only. No code, no schema, no behaviour. 113 insertions and 25 deletions against the merge base b5287abe3, all in the one plan file.

Related to #417 (closed)

Edited by Dzmitry (Dima) Meshcharakou

Merge request reports

Loading
Loading