chore(deps): update dependency gastownhall/beads to v1.3.0 - #56
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
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.
This PR contains the following updates:
1.2.2→1.3.0Release Notes
gastownhall/beads (gastownhall/beads)
v1.3.0Compare Source
The first tested release off
mainsince the 1.1 line. [1.2.2] was a recoveryrelease that re-shipped the v1.1.2 code under a higher version number, so a
v1.2.2 user is running 1.1-era code and meets everything below at once:
the changes listed here, plus the [1.2.1] changes that release deliberately
withheld. Read the upgrade notes before installing.
Upgrade notes
On an embedded or local store, the first invocation migrates your schema, in
place, from v53 to v66. A v1.2.2 (or any 1.1.x) database sits at main-series
schema v53; this binary knows v66, so the first command that opens the store
applies 13 main-series migrations,
0054_add_lease_columnsthrough0066_add_events_journal_actor. Two of those passes rewrite rows rather thanjust reshaping tables — the aux-row id rekey and the
eventsdolt_ignore flipdescribed under Changed — so on a large store the first invocation is
noticeably slower than the ones after it. It is crash-resumable and picks up
where it left off, but do not interrupt it if you can avoid it.
A shared Dolt sql-server is never auto-migrated (#5920, #6048): migrating it
promotes the schema for every client at once, so it waits for explicit consent
via
bd migrate schema. Remote-backed stores keep their existingdesignated-migrator gate. Upgrade all of that server's clients first — see
Shared servers.
The counter restarts partway through, and that is not a loop. The
clone-local (dolt_ignored) series runs after the main one, through the same
printer and its own numbering, and it moves 0011 → 0026 on this upgrade. So the
run is about 28 migrations, not 13, and what you see on stderr is 13 lines
counting up to 0066 followed by 15 lines starting again at 0012:
A counter that jumps backwards is the signature operators kill runs over. Let
it finish.
Progress prints only when stderr is a terminal. Piped and CI runs see nothing
at all, which is deliberate — a silent-looking CI upgrade is not a stuck one.
Back up first, with the binary you have now. Take the backup before you
install 1.3.0. Under the new binary
bd exporttriggers the auto-migrationbefore it exports, so a snapshot taken afterwards is a post-migration snapshot
and cannot protect you against the migration going wrong. On a remote-backed
store, finish syncing with the old binary too: once 1.3.0 is installed the
pending-migration gate refuses
bd dolt pushandbd dolt pullas well, notjust
bd migrate.A JSONL export is cheap, issue-complete, and importable by any bd version. If
you want a Dolt-native snapshot that keeps history and config, configure a
destination and sync it — bare
bd backuptakes no backup, it is a commandgroup that prints help and exits 0:
Upgrade every client that shares a store, together. The forward schema-skew
guard means an older co-resident binary — a second
bdearlier inPATH, along-running
bd serve, another clone's cron job — refuses a database migratedpast what it knows, rather than proceeding blind. That is the guard working, not
a bug, but it makes a mixed-version fleet a broken fleet: one machine running
1.3.0 takes the whole store forward and every 1.2.2 client stops. Run
which -a bdafter installing, and on a remote-backed store follow the existingdesignated-migrator procedure (one clone migrates and pushes; the rest pull or
re-clone). See
Upgrading for the
per-install-method recipes and the multi-clone flow.
If you need to go back, the rollback is a schema-cursor rollback, not a
downgrade of the data: the procedure is written up in the
recovery runbook
(repo copy:
docs/recovery/accidental-1-2-1-release.md). Its worked example isthe v53↔v65 case from the accidental 1.2.1 release; the steps are the same for
v66, with the version numbers adjusted.
Breaking changes for v1.2.2 users
This is a highlights list, not the complete set. A v1.2.2 user is crossing
two releases at once, and the breaks below are the ones most likely to stop a
script or a service. The full set is the
[1.2.1]section further down plus theChanged section here — read
[1.2.1]in full before upgrading, since[1.2.2] withheld it and v1.2.1 itself was pulled, so nobody on the supported
line has seen it.
After upgrading,
bd upgrade reviewprints exactly the entries between theversion you were running and this one. (Prefer it to
bd info --whats-new,which dumps the entire release history.)
Carried from [1.2.1] — never shipped to v1.2.2 users:
bd update --status <done-status>now enforces close policy. Moving anissue to
closed(or any configured done-category status) viabd update,bd batch update, or the issueops facade refuses when the issue has openchildren or a live direct blocker, matching
bd close. Override withbd update --force(update <id> status=closed force=trueinbd batch;UpdateRequest.ForceClosePolicyfor facade consumers). An unforced refusalrolls back the entire batch.
bd searchincludes closed issues by default (bd-t5yex). The dominantquery is "was this already found/filed/fixed?", where excluding closed issues
produced a false "no". Narrow with
--status opento get the old behavior.bd listkeeps its open-only default.bd update/bd closeisinteractive-only (bd-m00pb,
#4839). A scripted
bd update $ID …with an empty$IDnow refuses in argument validationinstead of mutating whatever was touched last. The fallback requires a
terminal stdin;
BD_NON_INTERACTIVE=1andCI=1/truealso disable it. SetBD_LAST_TOUCHED_FALLBACK=1if a script genuinely relied on it.bd human listhides done/frozen and pinned beads by default, andvalidates
--status(#5332). It previously passed--statusthroughunvalidated and showed closed beads. A
--statustypo is now an error ratherthan an empty list. Every bead type still shows.
bd dolt pushandbd syncno longer adopt a git-origin-derived Doltremote without consent (#5068). Both used to silently derive a remote from
git remote get-url origin, persistsync.remote, commit that config changeunder your git identity, and upload the full issue history — which published
a whole issue database to a public origin on a command the user believed
targeted an already-configured remote. Adoption now prompts (defaulting to
no) and fails closed non-interactively.
--yes/-yconsents ahead of time.bd --readonly serveis refused instead of binding a server that cannot dowhat it advertises. On a Dolt SQL-server workspace the flag was previously a
silent no-op and the server came up fully writable, so anything scripted as a
"safe" read-only server does not start after this upgrade — drop the flag.
Worth checking before you restart a long-running
bd serveas part of thefleet upgrade above.
bd config listandGET /v0/beads/configno longer enumerate thekv.plane, which is where
bd remembermemories live. That closed anunauthenticated
GET /v0/beads/confighanding out every stored memory. Ascript that read memories out of
bd config listnow gets empty output ratherthan an error; use
bd kv/bd rememberinstead.backendpackage drops orphan handling (bd-gwryr).backend.OrphanHandlingand its four constants, thestorageoriginals theyaliased,
BatchCreateOptions.OrphanHandlingandissueops.CheckOrphanareremoved. They never did anything except cost a query, but a Go consumer that
names them no longer compiles.
New in 1.3.0 (full entries under Changed):
BEADS_DOLT_PORTenvironment variable.--run to/instead of collapsing it to ageneric separator, so
gastown--mayormatchesgastown/mayorand stopsmatching
gastown__mayor.Added
The events journal records WHO performed each mutation.
bd_events_journalgains an
actorcolumn (migration 0066 plus its ignored-series twin 0025, soupgraded workspaces and fresh clones converge on the same shape), stamped
inside the mutating transaction with the same identity the audit-events table
resolves; on a
commentrow it is the comment's author.bd events tail/bd events exportJSON gains an additive, omit-when-emptyactorfield —empty means the path had no actor (derived maintenance, deletes, rows older
than the column), never a user. The input for same-field / different-actor
conflict measurement over the journal.
bdwarns when it stores a label containing a space(#5813). Such a label is a
legitimate thing to ask for —
-l 'good first issue'— and bd stores it asasked rather than re-splitting a boundary the shell already decided. But it is
also exactly what a missed comma looks like, and that case cannot be told
apart from the deliberate one at the point of writing. A line on stderr
catches it at the keystroke instead of months later:
⚠ Stored "auth backend" as ONE label — it contains a space.It is advice, not an error; stdout isuntouched, so
--silentstill prints just the ID, and--quietsilences it.Not emitted for
--remove-label, since removing such a label is how thedamage gets repaired.
bd doctorgains aLabel Whitespacecheck for finding damage already ina database (#5813). It
reports labels that contain whitespace or differ from their trimmed form, and
bd doctor --fixrepairs them. The check is warn-only: a database with legacydamage still exits 0. As with the rest of bare
bd doctor, it does not yetrun in embedded mode (GH#3794).
bd staleandbd blockedgain--label,--label-anyand--exclude-label(#5822),with the same meanings and the same flag names they already have on
bd listand
bd ready:--labelis AND (repeatable, must have all),--label-anyisOR,
--exclude-labeldrops anything carrying one. They were the two listingcommands with no label-scoped output at all, so a theme-scoped stale sweep had
to pull the whole list as JSON and post-filter it on the labels array.
Filtering happens in SQL ahead of
LIMIT, not on the hydrated rows, so--label x --limit 10returns ten matching issues rather than whateversurvives filtering the first ten. No
--themeflag: beads has no themeconcept, themes are labels here, and
--label theme:xis the native way tosay it.
New
*.gate.lockfiles appear next to and inside.beads(#5046,#5093). A two-level
cooperative gate now serializes the operations that cannot safely overlap —
ordinary commands take it shared, maintenance like
bd backup restoretakesit exclusive. It is
flock-based, so the file is the lock's name, not itsstate: the gate is created once (mode
0600) and deliberately never deleted,because deleting it is how two processes end up holding two different locks
for the same resource. Each gate sits beside what it guards, never inside
it: the workspace gate for
<dir>/.beadsis<dir>/.beads.gate.lockin theproject root, and the physical-root gate is
.beads/embeddeddolt.gate.lockinthe default embedded mode (
.beads/dolt.gate.lockunder a server layout;under the shared server it lives beside
~/.beads/shared-server/doltandnever enters the workspace at all; a remote Dolt host has no physical gate).
Both are covered by the
*.gate.lock*patternbd doctormaintains in theproject and
.beadsgitignores, so an upgraded workspace picks up the ignorerule on the next
bd doctor --fix; a workspace with no.beadsdirectory isnot gated at all, rather than scattering lock files into whatever directory
bdwas run from.sortonGET /v0/beads/issues. The listing served one order —(created_at DESC, id ASC)— because the cursor is a keyset position in it,and the spec said so in its own words: "the sort order is welded to the cursor
contract, and a new order needs new surface." The cost of that welding fell on
every client that wanted
bd list's ordering, because the only way to get itwas to page the whole result set and re-sort locally. Measured over a
1400-row store at the 200-row page an HTTP client actually uses, that is
7 requests plus a client-side comparator;
?sort=priorityis 1(
TestProxiedServerListSortRetiresTheWalk, which runs both strategies againsta real
bd serveand checks both againstbd list --jsonrow for row). It isthe one cost on this surface that got worse as a project grew.
Two values, and the set is closed.
createdis the existing order, nowspellable.
priorityis(priority ASC, created_at DESC, id ASC)—bd list's flagless ordering, which is also whatbd list --sort priorityproduces, so one served order retires the walk for both. The other seven
orders
bd list --sorttakes are not offered: each value here is a cursorcontract needing a key proven total, and
idis a natural-numeric order nodatabase expresses,
updatedmoves on every write,closedis nullable, andstatus/title/type/assigneeare mutable and unindexed.Absent
sortstill meanscreated, permanently. It is the compatibilitycontract for every client written before the parameter existed; changing it
would alter which rows a truncated page contains, with no error to notice it
by. A server that predates the parameter answers
param: "sort"withreason: "unknown_parameter", which is the per-parameter capability probe aclient dispatches on to fall back.
The cursor now carries its order, and a mismatch is refused. This is the
half that makes the parameter safe rather than merely useful. The token was
base64 of
{t,i}with no binding, so acreated-order cursor replayed undersort=prioritywould have decoded perfectly and been read as a position in adifferent total order — a page that both skips and duplicates rows, served
with a 200, undetectable by the client. Tokens are now
v2with an explicitorder member and decode refuses any token whose order differs from the
request's, as
invalid_cursor(documented recovery: restart paging).Outstanding
v1tokens stay readable as thecreated-order positions theyare, so no traversal in flight has to restart. This does not contradict
"the token carries no filters": filters select the set, the order decides what
the position means.
priorityis a mutable key, whichcreated_atis not, and the spec saysso: under
createdonly new rows move relative to a walk, while a priorityupdate moves an existing row too, so it can be seen twice or missed. That is
the already-documented "a cursor pins a position, not a snapshot" caveat
reached by a second route, not a new class of error — unchanged data never
skips or repeats under either order.
Changed
Shared Dolt databases (sql-server mode) no longer auto-apply schema
migrations on a version bump. Migrating a shared database promotes the
schema for every connected bd client at once and locks out clients still
on an older binary, so bd now refuses without explicit consent: run
bd migrate schemaonce after upgrading every client, or setBD_ALLOW_REMOTE_MIGRATE=1in scripted use. Reads keep working on thecurrent schema meanwhile. Embedded (single-user) databases still
auto-migrate silently, unchanged. (#5920)
The smart migration gate's "auto-migrate as safe first-mover" and
auto-fast-forward arms are now embedded-only. On a shared server the gate
always stops for consent, because neither argument can observe the other
clients attached to that server. (#5920, #4516)
The newly exported Go packages (
backend,backend/conformance,beadserrors,issueops,journalops,memoryops,schema,test/conformance) are markedEXPERIMENTAL: they are not yet covered by the project's compatibility promise and
may change in a minor release. Pin an exact beads version and re-run the
conformance suite on every bump.
The optimistic-concurrency token is now a JSON string — responses carry
revisionas an opaque decimal string andexpected_versionmust be sent asthat string. JSON-number tokens exceeded JavaScript's 2^53 integer precision.
This member never shipped in a tagged release.
CI: migration-test and upgrade-smoke now seed and assert wisp-plane data
(wisps, wisp deps/comments, leases, events, ignored-track cursor) across
v1.0.1/v1.1.x/v1.2.2 → candidate upgrades.
--profileis now--cpu-profile, with no alias(#5126, bd-ugz). The
persistent flag that writes a CPU profile is spelled
--cpu-profile; the old--profilespelling is gone rather than deprecated, so it fails as an unknownflag instead of silently doing nothing.
--profileis the conventional namefor a named configuration, not for pprof output, and holding it hostage to
a debugging aid nobody passes twice a year was the wrong trade. Scripts and
aliases that profile bd need the new spelling.
Migration 0061 rekeys every events, comments, snapshot and
compaction-snapshot row to a content-derived id
(#5150, bd-ri8bd). Those
four aux tables minted random UUIDv7 ids, which is fine for a single writer
and wrong for replication: unversioned newest-wins replication has no merge
pass, so two clones recording the same fact produced two rows that never
converged. Ids are now UUIDv5 over a SHA-256 digest of the row's own content
(frozen column order, plus the table name and a duplicate-disambiguating
ordinal), so the same fact derives the same id everywhere and convergence is
a property of the key rather than of a reconciliation step. New rows derive
their id at insert; existing rows are converted by a one-time bulk rekey
on the first open after upgrade, which is the main reason that invocation is
slower than the ones after it. The pass is crash-resumable through a
local_metadatasentinel and records completion clone-locally, so it runsonce per clone and is not repeated on the next command.
wisp_eventsandwisp_commentsare deliberately excluded — they are clone-local and nevermerge. Any external reference to an event, comment or snapshot id taken
before the upgrade will not resolve afterwards.
The
eventsaudit table is now clone-local(#5162, bd-red8u).
Migration 0062 moves
eventsonto the dolt_ignored plane in a self-committingdrop/commit/recreate that preserves every row. Events are single-writer audit
rows with low cross-machine value, and minting a versioned Dolt commit for
each one was the dominant source of commit churn on a busy store. The
consequence is the point:
eventsrows no longer replicate. They keepfull SQL durability locally and
bd history <id> --eventsstill reads them,but a pull no longer brings another machine's events over, and a push no
longer sends yours. Events already committed before the flip stay in the
remote's history until the next squash window. Comments stay versioned.
The
interactions.jsonlaudit sidecar is opt-in(#4688, fixes
#4687).
audit.enablednow defaults to false and
bd initno longer creates the file, sostatus/assignee/priority field-change logging writes nothing unless you ask
for it. Set
audit.enabled=true(orBD_AUDIT_ENABLED=1) to restore the oldbehavior. The database-backed replacement is
bd history <id> --events, whichneeds no sidecar. Anything that read
.beads/interactions.jsonlon theassumption it would exist needs the config key set.
bd importrefuses a redirected stdin rather than quietly ignoring it(#5171, bd-axluy). Bare
bd importimports the default JSONL, andbd import -imports stdin; abd import < file.jsonllooked like the second and behaved like the first,importing a different file than the one on the command line with no way to
notice. With stdin redirected, no
-, and no named source,bd importnowerrors and names both fixes.
/dev/nullis a character device, so a scriptedbare
bd importunder a subprocess still imports the default file as before.Auto-backup defaults to off under a Dolt sql-server (wy-zrmqr). The
automatic backup used to switch itself on whenever a git remote existed,
regardless of storage mode — which on one shared sql-server meant ~31 clients
independently deciding to back up the same database, pinning its CPU. The
default is now off in server, proxied-server and shared-server modes (and in
nocgo builds, which are always server-backed); embedded mode is unchanged and
still enables it when a git remote is present. An explicitly configured
backup.enabled(orBD_BACKUP_ENABLED) always wins over the default, andbd config get backup.enablednow prints the effective value with its source—
default (auto: off in sql-server mode)— rather than a barefalsethatcould not be told apart from a configured one.
bd hooks install --chainand--forceare accepted no-ops(#5284, bd-5vdt8). Managed
BEGIN/END BEADS INTEGRATIONmarker sections made both flags meaningless:an install now replaces only the content between its own markers, so content
outside them is always preserved (what
--forceused to negate) and anexisting hook always keeps running alongside the bd section (what
--chainused to request). The flags remain registered so existing invocations do not
break, and
--jsonstill echoes them, but neither changes what is written.A hook with no markers is handled by shape: a legacy bd hook is replaced, and
a foreign one gets a one-time
<hook>.backupsidecar before the section isinjected. Symlinked and git-tracked hook paths are refused outright, before
anything is written.
Write commands refuse to run while a migration freeze marker is present
(dc-6jaq). When a file named
MIGRATION-FREEZEexists in the workspacedirectory, the working directory, or any ancestor of either — or at the
path named by
BD_MIGRATION_FREEZE_FILE, which is authoritative when set—
bd create/update/close/remember/importand every other writecommand (~120 call sites,
bd qincluded), plus the destructivebd init --reinit-localandbd bootstrap, print⛔ workspace is frozen for migrationnaming the marker's path and exit 14, a distinct codescripts can branch on, instead of writing to a store mid-migration.
Operators freeze a workspace by creating the file (optionally
operator<TAB>RFC3339 timestamp<TAB>reasonon one line, echoed back inthe refusal) and thaw it by removing the file. Read commands (
list,show,ready, …) keep working during a freeze, and bd's own maintenancestands down with them — version tracking, auto-migration, JSONL
auto-import, Dolt auto-commit, auto-backup, auto-export and auto-push are
all skipped, so a frozen store is never rewritten just because someone ran
a read. A running
bd serveis not gated: stop it before freezing. If bdcannot tell whether a marker is present, it refuses rather than assuming
the workspace is open.
bd dep addnames the implicittype=blocksdefault, but only to aninteractive operator (#5854). Creating an edge with no
-t/--typesilentlyproduces a
blocksedge, which drops the dependent out ofbd ready— theusual surprise when someone meant structural parent/child linkage.
bd dep addnow says so on stderr. Becauseblocksis the documented default andthe majority-correct case, the note is gated: it is emitted only when stderr
is a TTY, so scripted and agent callers (and CI, and
bd dep add 2>log)never see it and are not trained to ignore stderr.
--quietsilences it, asit does the other non-error stderr notices, and
BD_NO_DEP_TYPE_WARNING=1turns it off for operators who have internalised the default.
--jsonoutputis unchanged.
bd showlabelscreated_byasCreated by:, notOwner:(be-ss66).The text view rendered
created_byunder a label naming a differentconcept.
owneris a separate field holding a separate value — the gitauthor email kept for CV attribution — exposed as
ownerin--jsonandnever rendered in the text view at all. Two identities, one label, and the
label named the one that was not on screen. This is a correctness fix and
not polish: a reader asking "whose bead is this" reasonably reaches for
what the label promises, and two defect analyses were filed off exactly
that mistake in one evening, both measuring provenance from the wrong
column and both retracted after another agent disproved them.
bd shownow prints
Created by: <identity>. The--jsonpayload is unchanged, anda repo-wide audit found no in-tree parser of the old
Owner:text — theonly other producer,
bd github's repo-config display, names an actualrepository owner and keeps its label.
beads_dirandrepo_rootare now OPTIONAL members ofContextResponseon
GET /v0/beads/context. They are the only two members of the identityhandshake that describe the SERVER's filesystem rather than the workspace's
logical identity, and an absolute host path is not something a remote client
can act on — it cannot open it. The document already called publishing them
a cost "an operator accepts when binding beyond loopback"; while they were
required, accepting it was the only conforming option. A deployment that
does not want to disclose its filesystem layout can now withhold them and
still serve a conforming body.
No body changes.
bd servepublishes both, always, exactly as before —the relaxation is a promise to clients, not a switch on the server. What
changes is what a client may assume: it MUST tolerate their absence, and
MUST identify a workspace by
project_id,databaseandbackend, whichstay required. In Go,
apigen.ContextResponse.BeadsDirand.RepoRootbecome
*string.An explicitly configured Dolt server port now outranks the ambient
BEADS_DOLT_SERVER_PORT/BEADS_DOLT_PORTenvironment variables(be-wf9a.1, be-9tju, GH#4052).
ServerPortresolution now carries asource — which step of the chain produced the port — and the env read only
applies when that source is not an assertion by the user or by tooling acting
for them. Two deliberate behavior changes follow. First, a port set by the
caller before the store opens (a library caller filling in
Config.ServerPort,or
bd init --server-port) is no longer silently replaced by whatever thesurrounding shell exported; previously the env var won unconditionally.
Second, the legacy
BEADS_DOLT_PORTspelling no longer overrides a portresolved from
.beads/config.yaml(dolt.port), Dolt's ownconfig.yaml(
listener.port), ormetadata.json(dolt_server_port). The currentBEADS_DOLT_SERVER_PORTspelling is unaffected — it sits first in theresolution chain and still wins over all of them.
Compatibility: a workspace that relied on exporting
BEADS_DOLT_PORTtoredirect away from a port pinned in config now connects to the pinned port
instead. Switch to
BEADS_DOLT_SERVER_PORT, or change the pinned value. Portsthat bd resolved for itself — the gitignored
.beads/dolt-server.port, andthe shared-server default 3308 — stay non-authoritative and remain overridable
by either spelling, which is the case that kept working throughout.
A related failure goes away with it: because bd's own bookkeeping is no longer
mislabeled as user configuration, a stale
.beads/dolt-server.portleft by acrashed server no longer makes auto-start refuse to start with "bd will not
silently use a different port than the one you configured" when nothing was
configured. Auto-start warns and retargets, as it did before. Shared-server
mode still fails closed there — a repo-local auto-start is a different
database, not a port refresh — but now says so in its own words.
bd reclaimsummarizes the leases its replica guard declined instead ofnaming every one, every run (wy-sp2l4). A lease granted by another replica
is by construction never reclaimed here, so the audit was not a one-off: it
repeated identically on every run, one stderr line per stranded remote lease.
A supervisor on a 1-minute timer against a federated store with 47 of them
printed 47 lines a minute, indefinitely. The default is now ONE line —
reclaim: skipped 47 stale leases granted by other replicas — "mini" (30), "mini2" (17), not this node ("laptop")…— naming at most three replicas andcollapsing the rest into a count, with the exact total preserved.
bd -v(or
BD_DEBUG=1) expands it to the per-lease lines, itself capped at 20 witha collapsed tail. Still stderr-only:
bd reclaim --jsonowns stdout and itspayload is unchanged. Under
--quietthe audit now skips its queriesentirely rather than running them to discard the output.
Actor matching decodes an exact
--run to/instead of collapsing it toa generic separator (be-p7dzx, bundled be-vc51). Identity comparison — used
by
ExpectedAssigneeon release and update, and by claim/close/unclaimauthorization — canonicalizes a run of
.,_or-to one separator so thesame identity spelled under different layers' conventions compares equal. An
exact two-byte
--run is now excluded from that collapse: it is gascity'ssession-name encoding of a rig-qualified agent's
/, so it decodes to/.Compatibility:
gastown--mayornow matchesgastown/mayor, and stopsmatching
gastown__mayorandgastown-mayor. That is the intended fix ratherthan a side effect —
gastown--mayoris the agentmayoron riggastownwhile
gastown__mayoris the dotted aliasgastown.mayor, so matching themwas a widening — but any stored
--spelling that is NOT a gascity slashencoding changes equivalence class silently, with no error to notice. Longer
or mixed runs,
__and---included, are unaffected and still collapse.bd ready --claimnow refuses a row cap under--proxied-server. Theproxied ready role cannot enforce
--max-rows/BEADS_MAX_ROWS, so bd failsloudly rather than silently dropping the limit;
--claimno longer exemptsthe command. Agent rigs that set the cap globally must unset it for proxied
bd ready --claim. Direct mode is unchanged, and a claim there stillsucceeds against a ready pool larger than the cap. (#6269)
Fixed
Server-mode issue mutations no longer revert concurrent writers' committed
rows (#5740). The
issue-mutation path ran
DOLT_ADD/DOLT_COMMITinside its still-open SQLtransaction;
DOLT_ADDstages the whole table as of the transaction'sBEGIN-time root, and the Dolt commit was minted before the commit-time merge
that reconciles concurrent writers — so a mutation could silently write every
row another session had committed during its window back to that session's
BEGIN-time value, seen in production as claims reverting minutes after they
were made. The Dolt commit now runs after the SQL transaction commits, against
the post-merge working set. Its failure is logged rather than returned: the
data is durably committed by then and rides the next Dolt commit, and treating
an applied mutation as failed is what made retries double-apply and claim
verification disown claims the caller held. Reported and fixed by
@nova-submodules.
Removed-backend rejection now detects an existing Dolt database and gives
the exact
metadata.jsonheal (change"backend"to"dolt", or delete thefield) instead of a destructive export/reinitialize path. Workspaces that bd
v1.2.x opened as Dolt despite a stale
"backend"value are one edit fromhealthy, and the message now says so. Rejection remains fail-closed and
read-only — no storage database is opened or modified — and still exits 1.
(Detection helper ported from @steveyegge's #4740.)
bd migrateno longer aborts with a duplicate-primary-key error when the samedependency edge exists in two typed target columns; duplicate edges are now
merged deterministically (issue-ref wins over wisp/external), renaming an issue
no longer leaves its dependency rows on a stale primary key, and databases left
half-rekeyed by earlier binaries are detected and repaired on the next
migration pass (#5268).
Legacy tracked
ignored_schema_migrationsself-heals by untracking it atopen time, unwedging
bd dolt pullon upgraded databases (#4356).A v1.1.x/v1.2.x ignored-migration cursor is believed when
leases.granted_nodeis merely not yet migrated, so upgrades apply only thepending ignored tail instead of replaying
ignored/0007and silentlyrestamping
wisps.updated_at(#5981 class, #5366 follow-up).Auto-export heals after
bd deleteinstead of permanently refusing — theorphan guard proves deletions against the store's own history (embedded and
server modes), stores wedged by earlier binaries recover on first run, and
export-state.jsonis now versioned (#5896).bd flattenandbd compactnow finish with a full Dolt GC (allgenerations), so re-running them on a previously-GC'd store actually reclaims
the orphaned history and retires orphaned
--as-ofaddresses;bd gcgains--fulland hints when a default pass reclaims little (#5907).--storage-classis honored on the proxied-server and--fileroutes andrejected as a plan-wide
--graphflag; it was silently ignored there.Delete and cascade
dep_removejournal rows are attributed to therequesting actor (#5985);
bd events export/tailnow say when the journalis disabled.
Truthful errors when a server-scoped credential cannot reach the requested
database (missing / denied / wrong-session attribution);
--init-if-missingis honored in proxied-server mode.
The aux row re-key survives dolt#11131-class encoding drift instead of
failing mid-upgrade with a raw
invalid hash lengtherror. Drifted tables areskipped with a warning, recorded clone-locally in
aux_row_rekey_drifted, andre-keyed on a later pass, rather than leaving the database unopenable. This
already reached 1.1.x and v1.2.2 users — it is the [1.1.2] fix — and lands on
the main line here, so a store upgrading from v1.2.2 is not newly exposed
(#5064, #4380). Diagnosis and fix by @marcodelpin, carried and reworked by
@maphew.
Server mode honors
Config.LenientOpen, so the dirty-table guard'sdocumented recovery is executable. The refusal tells you to run
bd dolt commit/bd vc commit, but against an external Dolt server thosecommands could not open the store to do it — the flag reached server mode and
newServerModenever read it, while the field's own doc comment said "Ignoredin server mode". That left the #4566 deadlock intact for exactly the case with
no way out: embedded mode can move the database directory aside and
bd bootstrap, and there is no equivalent against a shared server. Fixed by@Toady00 (#5783, #5781).
bd bootstrapprobes git remotes for Dolt data instead of rejecting themoutright (#6037, #5743, #5663).
bd initdeliberately persists agit-origin-derived
sync.remoteinto the tracked.beads/config.yaml— a gitorigin is a valid Dolt remote before
refs/dolt/dataexists, because the firstbd dolt pushcreates that ref on the same remote — but bootstrap classifiedthose URLs as code-repo URLs and returned before the origin auto-detect that
would have hydrated the clone,
git+https://andgit+ssh://included.Committed
sync.remotevalues from earlier releases hydrate again. The failurewas silent because one outcome code covered both "a database already exists,
nothing to do" and "I refused and left you with nothing": bootstrap printed
✓ Database already exists, exited 0, and created nothing, after whichbd init --reinit-localpointed back atbd bootstrapin a closed loop.Bootstrap now exits non-zero whenever it declines to set up a database.
--set-metadataagain stores numbers/booleans/null as typed JSON scalars(v1.2.2 behavior); string-forcing lives on
--metadata-json. Values written byin-window dev builds are left as strings; re-apply the flag to retype a key.
bd create --json(andPOST /v0/beads/issues) again echo sub-secondRFC3339Nano timestamps; reads (
show/list) remain second-precision as inv1.2.2.
bd purgeandbd pruneselect candidates by TIER, so a typed wisp isephemeral however it was minted
(#5995). The two sweeps
split the world between them —
bd purgeclears the ephemeral tier,bd prunethe durable one — but membership was decided by the rawephemeralcolumn, and a wisp minted before that column was set carried a
wisp_typewith
ephemeral = 0. Such a bead belonged to neither sweep:bd purgereported "No closed ephemeral beads to purge" while
bd pruneskipped it asa wisp. One production database had 858 such rows / 7.3 MB that no sweep
could ever reach. The tiers are now complementary predicates over both
columns — ephemeral is
ephemeral = 1 OR wisp_typeis non-empty, durable isits exact complement — evaluated across both planes rather than taking the
wisps-plane fast path, because legacy typed wisps live in the
issuestable.A non-empty
wisp_typenow also implies ephemeral at mint, on every createpath, so no new bead can land in the gap. A previously unreachable typed
wisp is now a
bd purgecandidate, so the first purge after upgrade mayclear considerably more than usual. NoHistory beads (wisps plane, no
wisp_type) stay durable-tier, unchanged.bd primesays when it could NOT read the memory plane(#5877). A broken or
unreachable store made prime omit the memory section entirely, so a session
that woke with zero recall looked exactly like a workspace that simply has no
memories — same output, same exit 0, no way for a fleet operator to tell them
apart. Prime still never fails a session-start hook, but a memory read it
cannot serve now renders
Skipped: beads storage unavailable (<error>) — persistent memories were NOT injected this session, alongside the timeoutbanner that already existed for a deadline. Both the classic and the
proxied-server route share the banner. A workspace-less directory, and a
healthy store with no memories, stay silent as before.
bdno longer serializes every invocation behind the schema-init advisorylock. The schema-init probe ran entirely inside the database-scoped
GET_LOCK('bd_schema_init:<db>', 5), so on a shared Dolt server everybdprocess queued behind every other one to discover that it had nothing to
migrate. Measured on an 18-seat rig: 1500
is_free_locksamples over 14sfound the lock held 96.7% of the time, held runs had a 673ms median and a
4.2s max, and free gaps had a 0ms median — the lock was handed straight from
holder to holder. That queue was 0.4-2.4s of pure waiting on every claim,
heartbeat, list, comment and mail check.
MigrateUpWithLocknow answers thesteady-state question first, without the lock — it puts the session on the
target database (proving the database exists before issuing any statement
that could fail, so a fresh bootstrap still falls through to the locked
CREATE DATABASE; callers whose pool already names the database injectnothing and the probe simply declines a session that is elsewhere), then
asks whether both migration cursors are at this
binary's latest with the content-hash column and custom-status/type backfills
done, whether all the canonical
dolt_ignorepatterns are present, andfinally whether the migration lock is free — and takes
GET_LOCKonly on thepath that can actually migrate. That last term is what keeps the probe honest
while a peer is mid-pass: the cursors land before the backfills and rekeys
finish, so a held lock, not the cursors alone, is the proof that nobody is
rewriting the tables underneath this caller. The probe issues no writes —
its one session mutation is the
USEthat pins the target database — and failsclosed: any error, any unreadable state, and anything short of provably
converged falls through to the locked path unchanged, as does any caller
carrying fresh-bootstrap heal authority.
Incremental auto-export now actually takes the incremental path
(#5806). Change detection
compared
GetStateHash()values — a hash of the entire database plusworking set (
DOLT_HASHOF_DB()), not a commit — butChangedIssueIDsfeedsboth endpoints straight into
dolt_diff(), which only accepts real commithashes or the literal
WORKING. Every incremental attempt therefore failedto resolve a diff and silently fell back to a full export: correct output,
but the incremental short-circuit this feature exists for never actually
ran. The "to" endpoint is now
WORKING(dolt_diff's own literal for thelive working set) paired with the previous export's real commit hash as
"from", so the incremental path resolves and fires as designed.
Three format/scope regressions surfaced alongside the dead code path, since
nothing had ever exercised it end-to-end: the incremental patch could leak
memories into the auto-export output (full export filters them; the
patch path did not), could include the configured owner's own issues
where full export excludes them, and omitted the
_typediscriminator fieldfull export always writes. All three are now pinned by tests that diff a
patched run's output against a from-scratch full export of the same state.
The patch write is also now atomic (write-temp + rename), matching the
full-export path's existing guarantee.
The owner and
_typefixes match full export exactly. Memory recordsdeliberately do not, and the difference is worth stating: where the full
path refuses to overwrite an
issues.jsonlthat already contains memorylines (
guardAutoExportOverwrite), the incremental path preserves themverbatim and proceeds. Preserving is the better behavior — a manually
seeded export survives patching byte-for-byte instead of wedging — so the
incremental path keeps it rather than adopting the refusal.
Scope: this is a server-mode (
DiffStore) improvement only.EmbeddedDoltStoreimplements neitherDiffStorenorStateHasher, so inthe default embedded mode the incremental path is inert (
incremental skipped — store does not implement DiffStore) and every cycle is still afull export. The deletion-proof guard below started out server-mode only for
the same reason; #5896 is
now closed for embedded mode too — see the store-history heal entry below.
Caveat: the diff anchor only advances on real commits. In server mode
dolt auto-commit is off, so with no commits being made the anchor stays put
and the diff range grows with every cycle. That is correct but not free:
once the change set crosses the 5000-id threshold, every export falls back
to a full rewrite until something commits and the anchor moves forward.
bd deleteno longer wedges auto-export in server mode(#5806, part of
#5896). Auto-export's
orphan guard refuses to overwrite an
issues.jsonlholding issue recordsabsent from the local store — the #4988 protection against a JSONL that has
run ahead of the database. A normal
bd deleteproduced exactly that shape,so a single delete left
issues.jsonlpermanently stale, with a "refusingto overwrite" warning on every later command. The guard now asks
dolt_diffwhether each JSONL-only id was genuinely removed since the last export's
anchor and, when proven, stops treating it as corruption.
The proof is deliberately narrow. It requires
diff_type='removed'on theissues table itself, so a label-, dependency- or comment-only change, a
delete-then-recreate, a wisp, or any diff error all still refuse. It also
requires the anchor to still be reachable from
HEAD: after aDOLT_RESET --hard, a branch checkout, or any other data-dir rewind, a rowreads as "removed" without anyone having deleted it, and honoring that
would drop a live record. Both conditions must hold, or the guard refuses as
before.
Disabling telemetry no longer strands the queued eventsData backlog
forever (GH#5712).
bd send-metricsearly-returned on disabled metricsbefore its prune step, and the spawn gate refused to schedule the child at
all when disabled — so the one machine whose queue can never again drain by
upload (the one that just opted out) kept every queued batch and orphaned
emitter temp indefinitely (2M+ files / 15.8GB observed on one control VM).
With metrics disabled, bd now still spawns the detached child under the same
marker-first throttle, and the child prunes by the normal TTL/cap policy and
exits without POSTing anything. Until that pre-existing backlog ages out, a
host that just opted out while its queue still holds batches younger than the
7-day TTL (and under the file/size caps) keeps forking the throttled,
detached, network-free prune child every flush interval — reclaiming nothing
until those batches age past the TTL — so new prune-only
bd send-metricsprocesses on a freshly disabled host are expected during that bounded window
and never POST. Once the backlog has decayed empty, spawns stop entirely; a
machine that never enabled telemetry has no queue directory and never forks.
bd showon a deleted or purged issue no longer prints text identical toan ID that never existed (ga-m6inyb).
bd deleteand wisppurgebothremove a row (and its
events) from the live tables with no trace leftbehind, so "not found" was collapsing two states that call for opposite
responses — archived (success) and never-existed (possibly lost data) —
into one observable. This had a real cost: a gate-reviewer session showed
a legitimate issue successfully, hit an unrelated mysql i/o timeout on a
retry moments later, got the identical "not found" text for the same ID,
and reasonably concluded data had been lost — when the issue had simply
been archived in between.
bd showon a missing ID now adds a secondline —
Hint: this ID may have never existed, or may reference a deleted/purged record with no trace left in the live database — try 'bd history <id>'— pointing at the existing, opt-inbd history <id>foranyone who wants to check further on a non-wisp issue (wisps are never
committed to history, purged or not, so there is no signal left to
recover for them). JSON output gains an additive
hintfield; theexisting
errortext is unchanged. Direct-mode text output changes forone path: the single-ID not-found branch's first line changes from
Error fetching <id>: no issue found matching "<id>"toIssue <id> not found, matching the wording the multi-ID and proxied-mode paths alreadyused (those two are unchanged aside from gaining the new second
Hint:line).
Explicit commits now sweep the entire working set and report no-ops
honestly. In server mode,
bd vc commitandbd dolt commitpreviouslyrouted through the config-excluding automatic-commit path, so out-of-band
config changes could remain dirty even though the command exited
successfully. Both commands now include config while continuing to exclude
ignored wisp tables. A clean or otherwise non-committable working set reports
Nothing to commit(committed: falsein JSON) instead of attributing anunchanged HEAD to a new commit.
Every CLI label write now normalizes its input, as every label filter
already did (#5813,
fixes #5812).
utils.NormalizeLabels(trim, drop empty, dedupe) was applied tolist,search,ready,orphans,countand workapi — and to no write path atall. The filter trimmed its input and the store did not, so a label could not
match its own filter:
bd create --labels 'theme:a, theme:b'storedtheme:bwith pflag's leading space intact, andbd list --label theme:bthen returned nothing. The failure is silent in the worst way — a filtered
list quietly short of rows is indistinguishable from a complete one. One real
database had 150 such rows across 111 issues, found months later only because
theme-by-theme counts refused to reconcile.
create,quick,update(
--add-label,--remove-label,--set-labels) andtagnow normalize onthe way in.
--remove-labelmatters as much as the additive flags: anuntrimmed
' theme:a'silently removed nothing.bd tagis included deliberately. Its help calls it "Shorthand forbd update <id> --add-label <label>", and it was the last CLI door stillstoring a positional verbatim. Normalization happens before the direct/proxied
route split, so the two routes cannot disagree. A label that is only
whitespace is now refused rather than dropped: the plural flags can discard an
empty element and still honor the rest of the request, but
bd taghas onelabel to add, so dropping it would report success having written nothing.
Import and batch ingest are deliberately excluded and keep round-trip
fidelity: what was exported is what is restored.
bd showno longer corrupts shell globs and other quoted wildcards in adescription or notes
(#5799). CommonMark lets a
*or_run act as both an opener and a closer when the charactersflanking it fall in the same class, and a quoted glob is exactly that shape —
the
*in'*.captured'sits between'and.. Two such runs thereforepaired, including across lines, since goldmark joins a paragraph's lines
before parsing. Everything between them rendered as emphasis: with color the
asterisks were consumed and silently vanished from the command, leaving a
still-plausible but different one on screen; without color, glamour's notty
style wrote a literal
**into the middle of the text. Storage was neveraffected —
bd show --jsonalways returned the body byte-for-byte — but therendered text is what a reader acts on, and what an agent reads when it takes
its instructions from a bead. Runs that can act as both opener and closer are
now hidden from the parser and restored afterwards, so
bd showprints whatwas stored.
The known trade, in two narrow shapes, both pinned by test: an authored
closer that is itself both-flanking no longer pairs, so
see *"quoted"*, nextrenders literally rather than italicized (color path only — nottyalready printed it literally); and a backslash escape reaching for a
both-flanking delimiter keeps its backslash, so
\*.capturedrenders as\*.captured. An escape whose delimiter is not both-flanking, such asa \*literal\* b, is unchanged. Both are shapes CommonMark itself treats asambiguous, and for a bead body — stored plain text, not authored markdown —
showing the stored characters is the better of the two failures. Ordinary
emphasis (
**bold**,*italic*),SELECT * FROM t, code spans and fencedblocks all render exactly as before.
bd blockedsilently ignored the label filters it already accepted(#5822).
types.WorkFilterhas carriedLabels,LabelsAnyandExcludeLabelsallalong, and
bd blockedbuilt one, butGetBlockedIssuesInTxonly ever readParentID. Programmatic callers that set them got unfiltered results back andno error —
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.