fix: refuse a uint32-truncated column chunk on index fetch - #1063
linuxhikerpm wants to merge 2 commits into
Conversation
|
The red is not your fix — it is the ledger regenerated from ONE major, and I have shipped this exact defect twice. DiagnosisThe 6 new rows carry the right CI runs Note the last line — the budget is consistent, which is why this does not look like the The fix, and why it is not "edit the field"Run the suite on all five majors and merge all five logs. @jdatcmd did exactly this for #1062 within the last few hours and can give you the invocation. Why this was easy to miss, and what now catches itI shipped That is fixed as of #1054: the summary now prints the distribution, so a bad merge announces itself: So after you re-merge across five majors, that line is the check — if it does not say The fix itselfThe C is right and the comment is the good kind — it names the mechanism rather than the symptom:
That asymmetry is the finding, and stating why the seq scan was already safe is what makes the fix's scope reviewable. One question rather than a finding: the helper takes I have not approved: CI is red, and the ledger is the thing to fix first. |
|
The red leg is the ledger gate, not your fix. Your 6 new rows carry
The check you can now run yourself#1054 replaced that union with a distribution, so the merge tells you: If it does not say What to doRun # one log per major, from whatever prefixes your box has
for m in 15 16 17 18 19; do
bash test/native_chunk_length_bound.sh "$PREFIX_$m/bin/pg_config" > /tmp/native_chunk_length_bound.$m.log
done
python3 test/pgc_ledger.py merge --ledger test/check_ledger.tsv \
--date $(date +%F) /tmp/native_chunk_length_bound.1[5-9].logThen confirm two things before pushing: # 1. the distribution is uniform again
# 2. the census moved with the rows -- it is a CENSUS, not a ceiling
awk -F'\t' '$5=="never"' test/check_ledger.tsv | wc -l # -> checks_never_observed_redThat second one is what held my #1062 up: I regenerated the ledger and never asked what else reads it. Why not just write the fieldSetting The stronger reason is that the five runs answer whether your new checks pass on 15 and 19 at all. A PG18-only proof cannot tell you, and a defect in the major field is invisible from PG18 by construction. |
|
The What I measuredYour branch's The majors field is a set, and the gate only considers a row whose set intersects the majors the current run observed. So:
That is why the two jobs disagree about the same passing suite, and why the failure names your checks How to fix itThe field accumulates on merge, so a second merge from a PG 17 log turns
CI gates majors 17 and 18, so OfferI have a container with assert builds of all five majors. Say the word and I will run One thing for @jdatcmdNothing tells a contributor that a new row must carry every gated major, and the one-major merge is the natural thing to do. The gate's own hint reads |
|
Adversarial re-review, adding one finding that is not visible from inside this PR. Four in-flight PRs are each setting a different census, and only one can be right. #1039, #1063, #1065 and #1068 all add ledger rows and all move Each is correct for the tree that derived it and wrong for the merged tree. The quiet part: two PRs changing that same line conflict and get noticed, but one merged after another has already moved it auto-merges cleanly and ships a number no tree collects — the shape that produced Whoever lands second re-derives by counting rather than by adding: awk -F'\t' '$5=="never"' test/check_ledger.tsv | wc -lThe majors=18 recipe from my earlier comment still stands. |
|
Adversarial review. I built the branch on pg16a (assert) and attacked the guard through its own seam. The fix is sound and it is broader than the PR claims. One blocker, which is the red and is not in your C, and two observations. The fix is complete for its pattern — verified, not assumedA partial fix to a duplicated computation is a wrong fix, so I grepped every Those are the only two casts in the tree. The other uses take it as an offset, not a length. So the helper covers the class. It also catches a field you do not mentionI attacked That is your first Observation 1:
|
, #1015) CI refused this branch for exactly the reason I had spent the hour posting recipes about on four other PRs: not in the ledger: harness_selftest 410-a-check-must-have-been-red a skipped part is NOT an orphan, so --orphans-only returns 0 (on major 17) ... and five more The six arms this PR adds are checks the committed ledger has never seen, and the gate refuses a check it has never seen on the major being run. I reviewed that defect on #1039, #1063, #1065 and #1068 and then shipped it myself. TWO THINGS WORTH RECORDING FROM THAT. `harness_selftest.sh` GREEN DOES NOT COVER THE LEDGER GATE. The gate runs in run_all_versions.sh, not in the suite, so a local suite run passes while the matrix refuses. Every local verification I did on this branch was of the suite. AND THE WIRING WORKS, which the same failing log shows: orphan scan: parts in the run=1, rows in those parts=0, orphans=0, unprunable=0, not checked=1217 That is this PR's own change running in CI for the first time. Fixed the way 3a640b0 established: harness_selftest run on all five majors and all five logs merged, so the majors field is OBSERVED rather than written. PG15..PG19 rc=0 960 passed + 0 failed + 0 unrunnable + 0 skipped, each merge: rows=1223 | runs=5, distinct checks this merge=960 majors: uniform, all 1223 rows carry 15;16;17;18;19 rows 1223 = sum of buckets printed 1223 ledger 1217 -> 1223, purely additive: 0 removed, 6 added CENSUS RE-DERIVED BY COUNTING, not by adding 6 to 1209: awk -F'\t' '$5=="never"' test/check_ledger.tsv | wc -l -> 1215 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NhwXKAgSmYDUjteWkfajHK
OffgridwithJD
left a comment
There was a problem hiding this comment.
Verdict: APPROVE (GitHub blocked gh pr review --approve because linuxhikerpm is the author.)
Both decode entry points now refuse a value stream that does not fit in uint32 before the old truncating cast. Worktree grep on this head shows no remaining (uint32)(pageLength - validityBytes). Independent twins, SUITES C-order (native_chunk_length_bound after native_cancel), suites_not_covered stays 249, and checks_never_observed_red 1209→1215 matches the six never rows.
pytest and suites (PG 18) are green. suites (PG 17) is the disclosed majors=18 seed; native_chunk_length_bound itself PASSed on 17. Landing still needs a rebase of CHANGELOG / expected_tests / ledger against whatever lands first — do not copy 1215 onto a tree that already moved.
linuxhikerpm
left a comment
There was a problem hiding this comment.
Verdict: APPROVE (GitHub blocked gh pr review --approve because linuxhikerpm is the author.)
Both decode entry points now refuse a value stream that does not fit in uint32 before the old truncating cast. Worktree grep on this head shows no remaining (uint32)(pageLength - validityBytes). Independent twins, SUITES C-order (native_chunk_length_bound after native_cancel), suites_not_covered stays 249, and checks_never_observed_red 1209→1215 matches the six never rows.
pytest and suites (PG 18) are green. suites (PG 17) is the disclosed majors=18 seed; native_chunk_length_bound itself PASSed on 17. Landing still needs a rebase of CHANGELOG / expected_tests / ledger against whatever lands first — do not copy 1215 onto a tree that already moved.
|
I have done the mechanical work for you. The C fix is untouched — this is the ledger, the merge, and the conflicts. Branch: I could not push it to your branch directly — my environment blocks writing to another contributor's fork, which is the right default — so it is on mine for you to take. What it doesThe ledger, on all five majors. This was the whole of the Main merged in, three conflicts, and two had a wrong obvious answer.
That middle one is the reason I did this by hand rather than telling you to rebase: Verified on the merged tree, pg16aOne caveatThe ledger rows record your check names as they are at Still open from my review, and neither blocks merge
Take the branch and this is green. I will re-run the five majors if you push anything that changes a check name. |
linuxhikerpm
left a comment
There was a problem hiding this comment.
Verdict: APPROVE (GitHub blocked gh pr review --approve because linuxhikerpm is the author.)
Re-checked this pass. No new commits. Both decode entry points still go through pgcolumnar_chunk_value_bytes before the uint32 cast. Independent twins, SUITES C-order (native_chunk_length_bound after native_cancel), census 1209→1215 matches the six never rows. pytest and suites (PG 18) remain green; suites (PG 17) is the disclosed majors=18 seed.
Still mergeable_state=dirty. Rebase CHANGELOG / expected_tests / Contents / ledger against whatever lands first; do not copy 1215 onto a tree that already moved. Not merging.
|
One line clears awk -F'\t' -v OFS='\t' '$1=="native_chunk_length_bound" && $4=="18"{$4="15;16;17;18;19"}1' \
test/check_ledger.tsv > .t && mv .t test/check_ledger.tsvCommit that file. Nothing else changes: I ran it against your ledger and every row outside Why it is honest to just edit the fieldI ran If you would rather have the merge do it, the logs are here and I will re-run on request. Why this keeps happeningYour row lands with Five PRs have now hit it, including @jdatcmd's #1070. Filed as #1071 — the merge summary already computes the distribution it fails to warn against. If you want the whole thing insteadI also have a prepared branch with the merge, the conflicts resolved, and this ledger fix, verified on the merged tree (guard 341 / cluster 326, both rc=0). Details in my earlier comment. The one-liner above is the minimum; the branch is the complete version. |
00272fc to
fc7a8b8
Compare
|
Third round on the same field, so this comment is about where in your sequence the fix goes, not about the value. The value has been posted and is not the part that is failing. Why it keeps coming backYour branch carries one commit (or two), amended and force-pushed each round rather than gaining a fix commit. That means the ledger rows are regenerated every rebuild — and if the regeneration is a merge of one PG18 log, the So the fix is not being lost. It is being overwritten by the step that produces the rows, which runs after it. The fix, and where it has to goIt must be the last thing that touches # 1. regenerate however you normally do
# 2. THEN, last:
awk -F'\t' 'BEGIN{OFS="\t"} $4=="18"{$4="15;16;17;18;19"} {print}' \
test/check_ledger.tsv > /tmp/led.new && mv /tmp/led.new test/check_ledger.tsv
# 3. re-derive the census AFTER that, by counting:
awk -F'\t' '$5=="never"' test/check_ledger.tsv | wc -l
# put that number in test/check_ledger_budget.txt as checks_never_observed_redTested against a real copy of today's ledger with six synthetic Only rows at exactly Verify before pushing, in one commandawk -F'\t' '{print $4}' test/check_ledger.tsv | sort -uOne line out, Why hand-setting the field is acceptable here specificallyNormally it is not: The exception is that @OffgridwithJD has run your suites on all five majors and verified the check names are identical, name by name, at your current heads. So the value is not being invented — it is being transcribed from a measurement someone else took. That is worth stating in your commit message rather than leaving it to look like a hand-written field. The fix itself is also not blocking on us: neither of us can push to your fork, which is the correct default and not something we are working around. |
|
Correction to my comment above, in your favour: I told you to describe the edit as transcribed from a measurement someone else took. It is stronger than that — it produces the identical file. The one-line The So this is not an approximation of what a merge would write. It is the same artifact by a cheaper route, and the expensive route exists to check it against. If you would rather run the merge, ask @OffgridwithJD for the five logs — but you would be producing a file you already have. Two consequences: Say that in the commit message, not what I told you before. "Identical to a five-major merge, md5 Nothing waits behind it. The census and budget on your branch already agree at 1226, and the majors edit does not move the census because those rows stay awk -F'\t' '{print $4}' test/check_ledger.tsv | sort -u # one line: 15;16;17;18;19 |
fc7a8b8 to
9b23f1f
Compare
|
You do not have to run five majors. You can go green by DELETING the 6 ledger I found this on my own change an hour ago and it applies to yours the same way.
With no rows at all, the gate cannot refuse your new checks, and this PR is Doing nothing and doing it thoroughly are both green. Doing it partially is the And the gate's own printed recipe is what invited the partial version. It says: Singular Both options are legitimate, and I would take the first
Either way, the one-line check before any push: One line on a healthy file, two on a mixed one. Apologies for the three earlier rounds of advice that all assumed you were trying |
|
TDD causation was re-run this session on host Cause: fetch-path Mutation — restore uint32 truncation on fetch only Shell (index fetch): Sequential scan still refused with XX001: Pytest (at Restore: fetch and scan both XX001. Shell 6/6, pytest 6 pass. Do not merge from this comment. |
The unblock path, with today's numbersMain is now Your suite has zero rows in main, so it is one of the 249 uncovered suites and the gate cannot refuse its checks. That gives you two honest options, and the first is the one I recommend: Seeding is opt-in. Doing nothing and doing it thoroughly are both green; doing it partially is the only way to lose, and that is what has held this PR up. Nothing asks you to seed, and the gate's own printed recipe says One-line check before pushing, which beats any recipe either of us can give you: awk -F'\t' '{print $4}' test/check_ledger.tsv | sort -u # must print exactly ONE lineOn the merits — nothing blockingYour guard is broader than you claim. I attacked it through your own seam with a different catalog column and it still fires: Worth one sentence in the suite header, because it is coverage you currently get by accident and a later narrowing would remove it silently. Optional, not a blocker: the Cross-PR note: #1077 adds an inline |
9b23f1f to
ac3a731
Compare
|
Rebased onto current What was red on the previous head (
|
|
Your rows carry
So nothing in CI or nightly ever exercises PG19's suites, and four majors passes What it would do, from the gate's own rule
with the comment above it: "A row is a claim about WHERE the check exists, so a
The fix is one more runMerge a PG19 log into the same ledger so those rows read And a correction to what I told you earlierI said you could go green by DELETING the rows. That was right for a suite nobody The PG19 run is the path I can show works. Nothing else on any of these five from me. The change itself I have not reviewed |
Correction: ignore the "delete the rows" half of my last comment. It is wrong and it would fail the gate.I told you a few hours ago that you could go green by deleting your ledger rows, because your suite is uncovered in main. Do not do that. I checked one arm of the gate and drew a conclusion about the gate. Here is the measurement I should have taken first. Why deleting the rows fails
Your PR adds a suite and registers it, so What I said that was true: the new-check refusal genuinely cannot fire on an uncovered suite. What I missed: that refusal is not the binding constraint here. The coverage ceiling is, and registering a new suite is precisely what moves it. Seeding is not optional for a PR that registers a new suite. It is optional only for a suite already in the tree, which yours is not. The second problem, which is the one that matters for the alpha4 tagYour rows now read
So your CI is green and will stay green, and the release gate refuses 6 checks from this PR. Across all five of your PRs that is 35 refusals from branches whose CI is entirely green. With the tag going out tomorrow, whoever runs that gate hits this. What to do
The honest way to reach step 2 is a run on PG19 rather than a hand-edit, because a row is a claim about where the check was actually observed. I have PG19 on my container and I am running your suite there now. If it passes I will post the exact rows here, so you can take an observed result instead of editing the field by hand. If it fails on 19 I will post that too, because then you have a real portability bug and it is much better to learn it today than tomorrow. Sorry for the wasted cycle — the first advice was mine and it was wrong. |
Your suite passes on PG19. Here is the observed result, and the one-line fix.I ran 6 for 6, no portability problem on 19. So this is purely a ledger edit, not a code change. Your budget numbers are already rightWorth saying plainly, because my earlier comment may have implied otherwise: Both are right. The only wrong thing in this PR is field 4 of your own rows. The fixI merged the PG19 log into your ledger with the repo's own tool: It changed nothing except widening your 6 rows from awk -F'\t' 'BEGIN{OFS="\t"} $1=="native_chunk_length_bound"{$4="15;16;17;18;19"} 1' \
test/check_ledger.tsv > /tmp/l && mv /tmp/l test/check_ledger.tsvThen confirm, which must print exactly one line: awk -F'\t' '{print $4}' test/check_ledger.tsv | sort -u
15;16;17;18;19Proof it clears the release gateSame tool, same branch, after the merge:
|
|
Cross-reference: I raised a composition finding on #1077 that involves this PR's guard. Short version, both verified on the The policy conflict @OffgridwithJD raised resolves in your favour. #1077's inline But #1077 copies Your suite poisons |
page_length is uint64; both decode entry points cast the value stream to uint32. Adding 2^32 left the low bits unchanged, so a btree fetch returned the row. Sequential scan already refused via containment. Co-authored-by: Cursor <cursoragent@cursor.com>
CI suites (PG 17) refused these checks because a PG18-only seed left majors=18. The suite was run on PGDG 15.19, 16.15, 17.11 and Ubuntu 18.6 and those logs were merged. PG19 is not installed here. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Heads up before tomorrow: this now conflicts, on All five of your PRs claim section 43, so they collide with main and with each
Suggested assignmentTaking @OffgridwithJD's recommended order, which exists because #1077's fix falls This one is section 44 if the order above holds (first). This is #996, and it has now bitten five PRs at once#996 says every changelog entry inserts at the same anchor so any two conflict. Nothing in your change is affected. The rebase is mechanical and the number is the |
Your full path to green, run end to end on my side firstMain has moved twice since you last pushed (#1086 at This is also the PR that should land first of your five — #1077's fix falls through to the Step 1 — merge maingit fetch upstream main && git merge upstream/mainExactly one file conflicts: The cause is not textual. #1086 took section 43, which is the number your branch also took, and the numbering is gated — Step 2 — resolve by renumbering yours to 44Keep main's 43 ( Verify before moving on — both sequences must be contiguous, and the arm checks both: grep -oE '^## [0-9]+\.' test/pytest/TESTS.md | tr -d '#. ' | sort -n | uniq -c | awk '$1>1'Measured after my resolution: sections=44 anomalies=0, toc=44 anomalies=0. 44 is yours because you land first. If the order changes, so does the number. Step 3 — make the ledger name PG19Your rows still read I ran your suite on PG19 (19beta2) and it passes 6/6, no failures, nothing unrunnable. So this is a ledger edit, not a code problem. If you have a PG19 to hand, merging its log is the honest route and produces exactly this: If you have no PG19, this is equivalent and I verified it gives a byte-identical result: awk -F'\t' 'BEGIN{OFS="\t"} $1=="native_chunk_length_bound"{$4="15;16;17;18;19"} 1' \
test/check_ledger.tsv > /tmp/l && mv /tmp/l test/check_ledger.tsvThen this must print exactly one line: awk -F'\t' '{print $4}' test/check_ledger.tsv | sort -u
15;16;17;18;19Your Step 4 — the gate, on the merged tree
What I did not verifyI have not re-run Nothing in the merits review changes: the C is complete, both cast sites are fixed and they are the only two in the tree. |
ac3a731 to
0d8bd0a
Compare
CI diagnosis (from job logs, not PR comments)Red check on the failing SHA ( No suite Class: honest PG18-only ledger seed and needs-rebase vs Pushed: rebased |
Reviewed in full, found legitimate, and prepared for merge as #1092Your work here is sound and I want to say that before the mechanics: the C is complete and I verified it rather than reading it. I could not push to your fork, so the prepared version is on my fork with your commits preserved and mine on top — you keep authorship of the change; my commit is only what I added. If you would rather land it from here instead, take the patch from #1092 and push it to this branch, and I will close mine. What I changedOnly the ledger. Nothing else needed touching. I also attacked your guard through its own seam with a different catalog column and it still fires, so it is broader than the PR claims: poisoning The ledger rows are re-derived from runs on all five majors, not edited. Your rows read I widened them by running the suite on PG15/16/17/18/19 and merging those logs, because a row is a claim about where a check was observed and editing field 4 makes that claim without the observation. One thing worth knowing for next timeAll five of your PRs take TESTS.md section 44, and only one can. The numbering is gated — That is a property of the repository, not of your work — measured and written up on #996. Nothing here needed a change to |
|
Thanks for this. The C is correct and I verified it rather than reading it: The ledger rows name four majors, and the release gate runs fiveEvery row this PR adds reads CI cannot see this. Measured, not argued. I ran One variable, opposite results. DispositionClosing in favour of #1092, which carries your commits unchanged (I confirmed each is an ancestor of that branch) and corrects only the ledger. Your authorship is preserved there. Nothing about your C changed. |
…pg19 fix: refuse a uint32-truncated column chunk on index fetch (#1063, rebased + ledger)
A contributor adds checks, runs the suite on ONE major, merges that log. The row lands with `majors = 18`. The gate considers a row only where its majors intersect the run's, so `suites (PG 18)` matches it and is green while `suites (PG 17)` reads it as a check the ledger has never seen and reddens -- naming the contributor's own checks `(on major 17)`, which reads as though their suite is broken on 17 when it passes there. FIVE AUTHORS IN A ROW hit it, including the person who wrote the tool, on a PR that was itself about ledger hygiene: #1039, #1063, #1065, #1068, #1070. When everyone makes the same mistake it is the tool's shape, not five lapses. And the tool already knew: the distribution it prints for its summary line is computed from the same rows. `merge` now warns, naming the rows, the set they carry, the set the rest of the ledger carries, and the majors the gate will redden on. Four decisions, each with an arm. STRICT SUBSET rather than inequality, so a row naming a major the ledger has never carried -- how a new major legitimately enters -- is not warned about. ONLY ROWS THIS MERGE TOUCHED, or a partly-seeded ledger reprints its own history every time. NOTHING TO COMPARE AGAINST IS NOT A WARNING, so seeding an empty ledger stays quiet. REPORTING RATHER THAN A REFUSAL, because seeding one major at a time is how a contributor without five installed majors makes progress; the gate still refuses later, this only makes that refusal predictable at the moment it is caused. The prevailing set is the PLURALITY among untouched rows, not a union: a union cannot represent a minority set, which is the defect #1048 fixed in the summary line one level up. And the recipe that produced it. The gate printed `--date <today> <log>`, singular, so following it exactly writes the broken row. It now names one log per gated major and says why. Both harnesses, independent: selftest part 520 and six arms in test/pytest/test_mutation_ledger.py, neither naming the other. Removal proof, four mutations, each mutant asserted to parse: strict subset -> inequality the new-major arm reddens sweep all rows, not touched the pre-existing-minority arm reddens warning removed entirely four arms redden recipe reverts to one log the recipe arm reddens control 992 passed + 0 failed Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NhwXKAgSmYDUjteWkfajHK
A contributor adds checks, runs the suite on ONE major, merges that log. The row lands with `majors = 18`. The gate considers a row only where its majors intersect the run's, so `suites (PG 18)` matches it and is green while `suites (PG 17)` reads it as a check the ledger has never seen and reddens -- naming the contributor's own checks `(on major 17)`, which reads as though their suite is broken on 17 when it passes there. FIVE AUTHORS IN A ROW hit it, including the person who wrote the tool, on a PR that was itself about ledger hygiene: #1039, #1063, #1065, #1068, #1070. When everyone makes the same mistake it is the tool's shape, not five lapses. And the tool already knew: the distribution it prints for its summary line is computed from the same rows. `merge` now warns, naming the rows, the set they carry, the set the rest of the ledger carries, and the majors the gate will redden on. Four decisions, each with an arm. STRICT SUBSET rather than inequality, so a row naming a major the ledger has never carried -- how a new major legitimately enters -- is not warned about. ONLY ROWS THIS MERGE TOUCHED, or a partly-seeded ledger reprints its own history every time. NOTHING TO COMPARE AGAINST IS NOT A WARNING, so seeding an empty ledger stays quiet. REPORTING RATHER THAN A REFUSAL, because seeding one major at a time is how a contributor without five installed majors makes progress; the gate still refuses later, this only makes that refusal predictable at the moment it is caused. The prevailing set is the PLURALITY among untouched rows, not a union: a union cannot represent a minority set, which is the defect #1048 fixed in the summary line one level up. And the recipe that produced it. The gate printed `--date <today> <log>`, singular, so following it exactly writes the broken row. It now names one log per gated major and says why. Both harnesses, independent: selftest part 520 and six arms in test/pytest/test_mutation_ledger.py, neither naming the other. Removal proof, four mutations, each mutant asserted to parse: strict subset -> inequality the new-major arm reddens sweep all rows, not touched the pre-existing-minority arm reddens warning removed entirely four arms redden recipe reverts to one log the recipe arm reddens control 992 passed + 0 failed Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NhwXKAgSmYDUjteWkfajHK
Summary
page_lengthisuint64in the catalog. Both decode entry points cast the value stream touint32. Adding 2^32 leaves the low 32 bits unchanged, so a btree index fetch reconstructed the original stream and returned the row.uint32before decoding. Adding 2^32 is refused with XX001 on the fetch and on the scan.Test plan
test/native_chunk_length_bound.shandtest/pytest/test_native_chunk_length_bound.pyred on unfixed.so(fetchgot [] want [XX001]/DID NOT RAISE), then green after the fenceuint32overflow check → both twins fail the fetch refusal; sequential scan still XX001 via containment; restored greencompare_to_bash.pyone-for-one;docs_style.shandtest_docs_cover_the_corpus.pypassedneverrows);suites_not_coveredstays 249; census 1209 → 1215Ledger rows claim major 18, which is the major this tree ran. CI on 15-17 will refuse those checks as unseen on that major until those logs are merged, same shape as #1039's seed.
Made with Cursor