mirror of
https://github.com/gastownhall/beads.git
synced 2026-09-14 20:17:24 +08:00
362eecc6bf
* test(migration): seed and assert the wisp plane on the v1.1.2 upgrade lane The migration-test harness has never seeded a wisp. Its shared fixture is three issues, a dependency, a label and a comment, and thirteen version lanes assert its exact `length == 3` shape — so the plane that 1.3.0's chain actually rewrites (wisps, wisp_dependencies, wisp_comments, leases, events and the ignored-migration cursor) reaches the candidate empty, and every migration in main 0054-0066 plus ignored 0012-0025 runs as DDL over zero rows. Add a separate `run_wisp_plane_upgrade` lane in its own workspace, dispatched after the shared embedded lane the way the v0.55.4 second lane already is, so no existing assertion changes. It seeds two issues and five wisps covering the open, closed, blocked-by-issue, blocked-by-wisp and conditional-blocks classes, plus a wisp comment, a wisp label, an issue-blocked-by-wisp edge through dependencies.depends_on_wisp_id, and a file:// Dolt remote published with the old binary. There is no `bd dolt sql`, so plane state is read with the already-pinned external Dolt v2.1.8 CLI as a read-only oracle. `dolt sql -q` can exit 0 on failure, so every read is decided by its CSV payload rather than its status. Assertions and the trap each one guards: - wisp survival and row counts gc must not collect a fresh closed wisp - updated_at stability 0059/ignored-0015 derived-state restamp - is_blocked truth table + bd blocked the wisp-aware blocker predicate - dolt_ignore superset the #5947 read-before-write clobber - dolt_status clean migrations must not leave dirty tables - events row count across 0062 the drop/commit/recreate flip - leases + granted_node present the #5366 contradicted-cursor replay - unblock on close blocks vs conditional-blocks release - journal shape + wisp writability 0064/0066 and 0060 storage_class - full-fingerprint second open the ignored track, which `Schema already at vN` cannot see at all That last one matters most: MigrateUp reports `applied` from the main track only and never adds appliedIgnored, so the harness's existing no-op check is structurally blind to an ignored-track replay. The fingerprint is the only guard there is. Extend the pinned-runtime gate and the workflow's Dolt install step to the wisp-capable versions, which now need that binary as an oracle. Cache keys are untouched: corpora are seeded fresh per run and the cache holds only release binaries keyed by asset checksum. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(migration): extend the wisp lane to v1.1.0 and the pre-split v1.0.1 regime v1.1.2 alone only proves the post-split regime. v1.0.1 is the one source in the corpus that still stores wisp dependencies in the legacy single-target shape (issue_id, depends_on_id) and has no ignored-migration cursor at all, so it is the only lane where the candidate has to perform the ignored/0003-0005 split, the 0047 delegate repair and the 0058 heal on populated rows, and bootstrap the ignored cursor from nothing rather than find both already done. Add verify_wisp_dependency_split: the three split target columns exist, the legacy column is gone, and each seeded edge lands on the right target — wisp-on-issue and conditional-blocks in depends_on_issue_id, wisp-on-wisp in depends_on_wisp_id, and the issue-on-wisp edge in the main dependencies table's depends_on_wisp_id. Mis-homing those rows is the "duplicate primary key given" hazard class in migrations/README.md, and it is invisible to every count-based check. Make verify_no_restamp regime-aware, because which migrations replay is a property of the source cursor. 0059 and ignored/0015 self-assign `updated_at = updated_at`; their predecessors 0046, 0047 and ignored/0007 do not. A v1.0.1 store replays all of them, so every non-closed row legitimately moves. A v1.1.x store has 0046/0047 behind its main cursor, so issues must not move at all — but its ignored cursor says 0011 while leases.granted_node does not exist, the sentinel disbelieves the cursor, and the ignored series replays anyway (#5366), dragging in unguarded ignored/0007. What is gated in both regimes is the part that is unconditionally true: closed rows never move, because every recompute is scoped `WHERE status NOT IN ('closed', 'pinned')`. That bites the moment one forgets its scope. The tighter guard on everything else is the idempotence fingerprint, which requires a second open to move nothing at all. Record the per-version capability probe results as a table rather than leaving them as assumptions, and run bd doctor quick on every wisp lane instead of only the ones with a remote. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(smoke): add a tolerant wisp-plane scenario to the release gate cross-version-smoke.yml runs upgrade-smoke-test.sh as the release gate, and all six of its scenarios work entirely in the versioned plane. The wisp tables therefore reach the candidate empty, and 0054-0066 plus ignored 0012-0025 only ever run as DDL over zero rows — the same blind spot Scenario 6 was written to close for the dependencies split. Scenario 7 seeds a wisp with the old binary, gives it a blocker edge so the is_blocked recompute has real work to do, upgrades, and then checks the surfaces that break when the replay is wrong: the wisp is still listed, the blocker queries stay wisp-aware without erroring, the wisp is still writable through the migrated storage_class default, and doctor quick is happy. The matrix reaches back thirty releases on a tag push, so a binary that cannot create a wisp or cannot produce a parseable id passes with a SKIP instead of failing. Same tolerant, grep-based, dependency-free style as the rest of the file: no jq, no Dolt CLI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(migration): cover the #5816 dirty tracked wisp-table state #5816 is still open: an ignored-named table that is tracked at HEAD *and* dirty, with ignored migrations pending, hard-locks the open. Nothing in either upgrade harness has ever constructed that state, so the containment surface is untested. It is not reachable from clean OSS lineage — every release since v0.63.3 registers the dolt_ignore patterns before creating the tables — so the scenario builds it deliberately: force-add and commit the wisp tables through the pinned Dolt CLI, then dirty one again with an ordinary old-binary wisp write. That is the shape real clones from forks and older-behavior lineages arrive in, and it is the only place this file issues DML through the oracle. Because the bug is open, the scenario deliberately does not gate on whether the candidate accepts or refuses. It gates on what must hold either way: the command terminates rather than hard-locking, and no wisp rows are lost. A refusal additionally has to name the dirty tables instead of failing opaquely. Carries a TODO(#5816) to tighten to must-succeed once the fix lands. Observed on the current candidate: a clean refusal in about a second naming `wisps`, with all rows intact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(migration): add v1.2.2 to the corpus and make the lane dispatch array-driven v1.2.2 is the tag field upgrades actually start from — the recovery release that superseded the accidental v1.2.1 — and it has never been in this corpus. It is the v1.1.2 tree re-tagged, so it adds no new storage regime; it closes the "the exact tag in the field was never tested" gap for the price of one matrix job. Adding it exposed a latent trap: the dispatch loop carried its own hardcoded copy of the embedded-Dolt version list, so a version added to EMBEDDED_DOLT_VERSIONS in lib/versions.sh was silently routed into the server-Dolt bridge lane instead, and failed there with a confusing "candidate list did not refuse with explicit migration is required". Dispatch on membership of the array that already declares the corpus, so the list exists once. Asset checksum verified against the published beads_1.2.2_linux_amd64.tar.gz. No existing cache-identity string changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
127 lines
5.6 KiB
Bash
Executable File
127 lines
5.6 KiB
Bash
Executable File
#!/bin/bash
|
|
# Storage era definitions and upgrade path matrix.
|
|
|
|
# The explicit export/import bridges for reviewed classic SQLite layouts. They
|
|
# are not in-place schema migrations: each SQLite source remains a rollback copy.
|
|
readonly CLASSIC_SQLITE_VERSION="v0.49.6"
|
|
readonly CONFIGURED_SQLITE_VERSION="v0.50.3"
|
|
readonly PRE_CANONICAL_SQLITE_VERSION="v0.17.0"
|
|
# This source-tag-qualified lane deliberately has no release asset.
|
|
readonly SOURCE_TAG_SQLITE_VERSION="v0.9.1"
|
|
readonly SOURCE_TAG_SQLITE_MODULE="github.com/steveyegge/beads"
|
|
readonly SOURCE_TAG_SQLITE_MODULE_SUM="h1:2KRCRCEtHvfMzmhYrpZzWl2rk2Y1Wq9p2awTdQIK9yY="
|
|
readonly SOURCE_TAG_SQLITE_GOMOD_SUM="h1:CNV215xONrxBs18j3BXx/xvl1qyCO2SeQRqiTN213Vg="
|
|
readonly SOURCE_TAG_SQLITE_COMMIT="e3c8554fa2c3e4b9caf7e296e9c8abbe24211a72"
|
|
readonly SOURCE_TAG_SQLITE_REF="refs/tags/v0.9.1"
|
|
readonly SOURCE_TAG_SQLITE_GO_TOOLCHAIN="go1.25.0"
|
|
|
|
# Representative, public release binaries for the historical-Dolt E2E test.
|
|
# These are deliberately a small, reviewed compatibility corpus rather than
|
|
# a discovery mechanism or an exhaustive tag matrix.
|
|
declare -ar HISTORICAL_DOLT_VERSIONS=(
|
|
"v0.55.4"
|
|
"v0.56.1"
|
|
"v0.57.0"
|
|
"v0.62.0"
|
|
)
|
|
|
|
# Reviewed direct embedded-Dolt sources. Unlike the server corpus, their own
|
|
# embedded data is opened and migrated in place by the candidate.
|
|
declare -ar EMBEDDED_DOLT_VERSIONS=(
|
|
"v0.63.3"
|
|
"v1.0.0"
|
|
"v1.0.1"
|
|
"v1.1.0"
|
|
"v1.1.2"
|
|
"v1.2.2"
|
|
)
|
|
|
|
# Embedded-Dolt sources whose wisp plane is close enough to the 1.3.0 input
|
|
# regimes to be worth seeding: v1.0.1 is the legacy single-target
|
|
# wisp_dependencies shape with no ignored-migration cursor, while v1.1.0,
|
|
# v1.1.2 and v1.2.2 are the post-split shape with the cursor parked at
|
|
# ignored-0011. Both regimes are replayed over populated rows by the candidate's
|
|
# main 0054-0066 + ignored 0012-0025 chain. v1.2.2 is the v1.1.2 tree re-tagged
|
|
# by the recovery release, so it adds no new regime — it is here because it is
|
|
# the tag people in the field actually upgrade from. Also gates the external
|
|
# Dolt oracle download in migration-test.yml.
|
|
declare -ar WISP_PLANE_VERSIONS=(
|
|
"v1.0.1"
|
|
"v1.1.0"
|
|
"v1.1.2"
|
|
"v1.2.2"
|
|
)
|
|
|
|
# The test server is an external compatibility fixture, not the runner's
|
|
# ambient Dolt installation. CI downloads this exact linux/amd64 archive.
|
|
readonly DOLT_TEST_RUNTIME_VERSION="v2.1.8"
|
|
readonly DOLT_TEST_RUNTIME_SHA256="f66318f08ed66e409fc39363ae0fff8ce6fbf6dba9f5bac632b91527b9632a74"
|
|
|
|
# Classic SQLite files that must be retained byte-for-byte for rollback before
|
|
# the manual bridge removes the active copies.
|
|
declare -ar CLASSIC_SQLITE_ROLLBACK_FILES=(
|
|
"beads.db"
|
|
"beads.db-wal"
|
|
"beads.db-shm"
|
|
"metadata.json"
|
|
"config.json"
|
|
"config.yaml"
|
|
"issues.jsonl"
|
|
)
|
|
|
|
# Legacy Dolt-server metadata that must be retained with the dolt/ directory
|
|
# before the manual bridge clears the active source.
|
|
declare -ar LEGACY_DOLT_ROLLBACK_FILES=(
|
|
"metadata.json"
|
|
"config.json"
|
|
"config.yaml"
|
|
"issues.jsonl"
|
|
)
|
|
|
|
# Release artifacts and expected qualification outcomes for strict CI lanes.
|
|
# Strict entries are deliberately explicit: adding a historical lane requires
|
|
# reviewing the official asset, checksum, source capabilities, and supported
|
|
# migration outcome instead of silently discovering them at runtime.
|
|
declare -Ar STRICT_RELEASE_ASSETS=(
|
|
["v0.17.0|linux|amd64"]="beads_0.17.0_linux_amd64.tar.gz"
|
|
["v0.49.6|linux|amd64"]="beads_0.49.6_linux_amd64.tar.gz"
|
|
["v0.50.3|linux|amd64"]="beads_0.50.3_linux_amd64.tar.gz"
|
|
["v0.55.4|linux|amd64"]="beads_0.55.4_linux_amd64.tar.gz"
|
|
["v0.56.1|linux|amd64"]="beads_0.56.1_linux_amd64.tar.gz"
|
|
["v0.57.0|linux|amd64"]="beads_0.57.0_linux_amd64.tar.gz"
|
|
["v0.62.0|linux|amd64"]="beads_0.62.0_linux_amd64.tar.gz"
|
|
["v0.63.3|linux|amd64"]="beads_0.63.3_linux_amd64.tar.gz"
|
|
["v1.0.0|linux|amd64"]="beads_1.0.0_linux_amd64.tar.gz"
|
|
["v1.0.1|linux|amd64"]="beads_1.0.1_linux_amd64.tar.gz"
|
|
["v1.1.0|linux|amd64"]="beads_1.1.0_linux_amd64.tar.gz"
|
|
["v1.1.2|linux|amd64"]="beads_1.1.2_linux_amd64.tar.gz"
|
|
["v1.2.2|linux|amd64"]="beads_1.2.2_linux_amd64.tar.gz"
|
|
)
|
|
declare -Ar STRICT_RELEASE_SHA256=(
|
|
["v0.17.0|linux|amd64"]="d4d08617a324c85b45c9628bc519d659a9ff9c7c37da67aa48727e0af7f19a75"
|
|
["v0.49.6|linux|amd64"]="8546dc9a47e11dc31ac2bc9a0224a9c690975e91850932cbb62623053fbb7db8"
|
|
["v0.50.3|linux|amd64"]="e94b09e0b6a9324bbc0e81ea36bccaaa42172a926bfedfb389e9a26dedb63184"
|
|
["v0.55.4|linux|amd64"]="e0fa25456dd82890230eef17653448a0bf995104c78864be91c5ed84426a5f49"
|
|
["v0.56.1|linux|amd64"]="4f9f6cc44465a11613ff529009901eaaf841c6b1f91c15e002b0ecda2015a15c"
|
|
["v0.57.0|linux|amd64"]="f8629d5627bed7d25f06f92334addc171d679f9aed9d08c5d42a9684205dc04b"
|
|
["v0.62.0|linux|amd64"]="4cca7265b22e5c3ca8d62ab0b9752bec31f68b7f5fa636282a4c7e5454c35535"
|
|
["v0.63.3|linux|amd64"]="5f4efd2e010209b3f381dbcd783b2a3a652f50ea72f40ef04c8ba434d408bf9e"
|
|
["v1.0.0|linux|amd64"]="7057db1e92428fcf5c08d5dc6b07ead57e588b262cba78b9a26893d55bd29fdb"
|
|
["v1.0.1|linux|amd64"]="1d2364d5d7083a4634a9e734ca87822fb79c2b6625988f9f791e3376313b1b77"
|
|
["v1.1.0|linux|amd64"]="b0f3dd607c3fb989ee08d0a6854fba80d0402971eb108f9af6170bc14d491a34"
|
|
["v1.1.2|linux|amd64"]="a72d71ed374955dc9f83a0f90b54bd7b6a0016709dd1676ae2e368651ed401c2"
|
|
["v1.2.2|linux|amd64"]="8140098a51d3b81d5548d1c5e6db1a2d9930e5d141efe2a4bff7d079c4d321e8"
|
|
)
|
|
|
|
strict_release_asset() {
|
|
local value="${STRICT_RELEASE_ASSETS["$1|$2|$3"]:-}"
|
|
[ -n "$value" ] || return 1
|
|
printf '%s\n' "$value"
|
|
}
|
|
|
|
strict_release_sha256() {
|
|
local value="${STRICT_RELEASE_SHA256["$1|$2|$3"]:-}"
|
|
[ -n "$value" ] || return 1
|
|
printf '%s\n' "$value"
|
|
}
|