Skip to content

Commit f428b4f

Browse files
sfncoreclaude
andauthored
Add dolt-snapshots plugin: lifecycle-aware convoy snapshots (#2324)
* Add dolt-snapshots plugin: lifecycle-aware convoy snapshots Tags Dolt databases at convoy lifecycle boundaries for audit, diff, and rollback. Two snapshots per convoy: - open/ tag: pre-work baseline (when issues are wired up) - staged/ tag + branch: launch baseline (after staging/dispatch) Handles fast convoys that complete before the plugin runs by including recently-closed convoys (24h window). Only snapshots convoys with tracked issues to avoid capturing incomplete state. Features: - Immutable tags for audit trail - Mutable branches for review/dry-run - Per-rig database discovery via dependencies - Stale branch cleanup with escalation - SQL-safe convoy title handling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Use env vars with defaults for Dolt connection config DOLT_HOST, DOLT_PORT, DOLT_USER, ROUTES_FILE now read from environment with sensible defaults, so the plugin works across different setups without editing the file. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Replace auto-delete of epic branches with report-only epic/* branches are no longer auto-deleted. Users may have epic branches they're actively using. The plugin now reports them without taking action — manual review required. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Rewrite dolt-snapshots plugin as Go binary with parameterized SQL Addresses all review blockers from PR #2324: 1. SQL injection: All queries use parameterized ? placeholders via database/sql. DB names sanitized through allowlist. No shell variable interpolation in SQL strings. 2. Subshell counter bug: Go binary tracks stats in struct fields. No bash pipelines or subshell variable scoping issues. 3. Auto-commit dirty state: Tags HEAD as-is. No DOLT_COMMIT calls. Snapshot captures current state without write side effects. Additional fixes: - Inline Python for routes replaced with pure Go loadRoutes() - Substring grep for DB detection replaced with exact match - Cooldown gate replaced with event gate on convoy.staged - Cross-rig external:rig:id dependency format handled correctly - 17 unit tests covering sanitization, routing, dependency resolution, config resolution, and snapshot decision logic The plugin.md is now a thin wrapper that builds the Go binary on-demand and delegates all Dolt operations to it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Split dolt-snapshots into three event-gated plugins One plugin per convoy lifecycle event, all sharing the same Go binary: - dolt-snapshots: convoy.created → open/ tags (pre-work baseline) - dolt-snapshots-staged: convoy.staged → staged/ tags + branches - dolt-snapshots-launched: convoy.launched → staged/ tags + branches Replaces the 5-minute cooldown poll with precise event triggers. The binary is idempotent — each invocation creates whichever tags/branches are missing for all eligible convoys. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: include staged_warnings convoy status in snapshot query Convoys staged with warnings get status 'staged_warnings' instead of 'staged_ready'. The snapshot query now matches both statuses so tags and branches are created for all staged convoys. Discovered during cross-rig verification test (hq-cv-mjz). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add --watch mode for sub-second convoy snapshot response Replace the three separate event-gated plugins (dolt-snapshots, dolt-snapshots-staged, dolt-snapshots-launched) with a single binary that tails ~/.events.jsonl in watch mode. Responds to convoy.created, convoy.staged, convoy.launched, and convoy.closed events in <1 second, eliminating the ~60s deacon patrol polling latency that was too slow for convoy.launched (agents start writing to databases immediately). The plugin starts the watcher as a background process with pidfile-based idempotency. Deacon patrol re-dispatches are no-ops when the watcher is already running. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * bd: backup 2026-03-06 22:37 --------- Co-authored-by: mayor <noreply@anthropic.com>
1 parent 01983a8 commit f428b4f

13 files changed

Lines changed: 3034 additions & 4833 deletions

File tree

.beads/backup/backup_state.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
2-
"last_dolt_commit": "dgoou26f6q7b4s9m6e6jvkikkjme735m",
2+
"last_dolt_commit": "oesj5vr69kjl6gqn7jprc93migsbolhm",
33
"last_event_id": 7382,
4-
"timestamp": "2026-03-06T01:17:11.531011Z",
4+
"timestamp": "2026-03-06T22:37:06.84298432Z",
55
"counts": {
6-
"issues": 677,
7-
"events": 3643,
8-
"comments": 14,
9-
"dependencies": 315,
10-
"labels": 169,
11-
"config": 17
6+
"issues": 702,
7+
"events": 120,
8+
"comments": 0,
9+
"dependencies": 981,
10+
"labels": 7,
11+
"config": 12
1212
}
1313
}

.beads/backup/comments.jsonl

Lines changed: 0 additions & 14 deletions
Large diffs are not rendered by default.

.beads/backup/config.jsonl

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
{"key":"auto_compact_enabled","value":"false"}
22
{"key":"compact_batch_size","value":"50"}
3-
{"key":"compact_model","value":"claude-haiku-4-5-20251001"}
43
{"key":"compact_parallel_workers","value":"5"}
54
{"key":"compact_tier1_days","value":"30"}
65
{"key":"compact_tier1_dep_levels","value":"2"}
76
{"key":"compact_tier2_commits","value":"100"}
87
{"key":"compact_tier2_days","value":"90"}
98
{"key":"compact_tier2_dep_levels","value":"5"}
109
{"key":"compaction_enabled","value":"false"}
11-
{"key":"issue_prefix","value":"gt"}
12-
{"key":"kv.memory.formula-toml-syntax","value":"Formula files are TOML in internal/formula/formulas/. All {{variable}} placeholders must be declared in [vars] section. Computed variables need default=\"\" so they are not required as input. [vars] entries must be TOML tables ([vars.name]) NOT bare strings. Template keywords filtered by isHandlebarsKeyword(). Tests read from BOTH source (formulas/) AND deployed (.beads/formulas/) dirs."}
13-
{"key":"kv.memory.hooks-package-structure","value":"Hooks system: internal/hooks/ package. config.go = data model + file I/O (HooksConfig, SettingsJSON, Target types). merge.go = MergeHooks (base + role override + rig/role override). discover.go = DiscoverTargets (finds all worktrees needing settings.json). Base config: ~/.gt/hooks-base.json. Overrides: ~/.gt/hooks-overrides/{target}.json (/ replaced with __)."}
14-
{"key":"kv.memory.refinery-worktree-merge-flow","value":"Refinery works from a git worktree — cannot checkout main (checked out by mayor). Use 'git push origin temp:main' for fast-forward pushes. Merge flow: 1) git fetch origin main \u003cbranch\u003e, 2) git reset --hard origin/\u003cbranch\u003e, 3) git rebase origin/main, 4) go test ./..., 5) git push origin temp:main, 6) gt mail send witness MERGED, 7) bd update MR --status=closed, 8) git push origin --delete \u003cbranch\u003e"}
15-
{"key":"kv.memory.style-and-workspace","value":"Styling via internal/style/ package — lipgloss wrappers: Success, Warning, Error, Dim, Bold, Info. Workspace discovery via internal/workspace.FindFromCwd(). Commands use cobra in internal/cmd/."}
10+
{"key":"issue_prefix","value":"st"}
1611
{"key":"schema_version","value":"6"}
17-
{"key":"types.custom","value":"molecule,gate,convoy,merge-request,slot,agent,role,rig,message"}
12+
{"key":"types.custom","value":"agent,role,rig,convoy,slot,queue,event,message,molecule,gate,merge-request"}

.beads/backup/dependencies.jsonl

Lines changed: 981 additions & 315 deletions
Large diffs are not rendered by default.

.beads/backup/events.jsonl

Lines changed: 120 additions & 3643 deletions
Large diffs are not rendered by default.

.beads/backup/issues.jsonl

Lines changed: 702 additions & 677 deletions
Large diffs are not rendered by default.

.beads/backup/labels.jsonl

Lines changed: 7 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,7 @@
1-
{"issue_id":"gt-071h","label":"gt:improvement"}
2-
{"issue_id":"gt-0jj1l","label":"worker-lifecycle"}
3-
{"issue_id":"gt-16k","label":"reconciliation"}
4-
{"issue_id":"gt-16k","label":"zfc"}
5-
{"issue_id":"gt-1emx","label":"duplication"}
6-
{"issue_id":"gt-1fje","label":"formula"}
7-
{"issue_id":"gt-2e5q","label":"formula"}
8-
{"issue_id":"gt-3hki","label":"from:dog"}
9-
{"issue_id":"gt-3hki","label":"gt:message"}
10-
{"issue_id":"gt-3hki","label":"plugin:dolt-backup"}
11-
{"issue_id":"gt-3hki","label":"result:skip"}
12-
{"issue_id":"gt-3vr5","label":"api"}
13-
{"issue_id":"gt-3vr5","label":"heartbeat"}
14-
{"issue_id":"gt-3vr5","label":"witness"}
15-
{"issue_id":"gt-3vr5","label":"zfc"}
16-
{"issue_id":"gt-4d7p","label":"zfc-violation"}
17-
{"issue_id":"gt-4k12","label":"zfc-violation"}
18-
{"issue_id":"gt-4unmo","label":"worker-lifecycle"}
19-
{"issue_id":"gt-5rne","label":"zfc-violation"}
20-
{"issue_id":"gt-5sh6","label":"zfc-violation"}
21-
{"issue_id":"gt-5v29","label":"desire-path"}
22-
{"issue_id":"gt-5zs8","label":"api"}
23-
{"issue_id":"gt-5zs8","label":"design"}
24-
{"issue_id":"gt-5zs8","label":"pinned"}
25-
{"issue_id":"gt-6oio","label":"plugin:rebuild-gt"}
26-
{"issue_id":"gt-6oio","label":"result:success"}
27-
{"issue_id":"gt-6oio","label":"rig:gastown"}
28-
{"issue_id":"gt-7r3c","label":"gt:improvement"}
29-
{"issue_id":"gt-7w6cq","label":"desire-path"}
30-
{"issue_id":"gt-87s6","label":"plugin:rebuild-gt"}
31-
{"issue_id":"gt-87s6","label":"result:success"}
32-
{"issue_id":"gt-87s6","label":"rig:gastown"}
33-
{"issue_id":"gt-8ik","label":"reconciliation"}
34-
{"issue_id":"gt-8ik","label":"zfc"}
35-
{"issue_id":"gt-8l3w","label":"formula"}
36-
{"issue_id":"gt-8neb","label":"architecture"}
37-
{"issue_id":"gt-8neb","label":"desire-path"}
38-
{"issue_id":"gt-94i9","label":"pre-existing"}
39-
{"issue_id":"gt-94i9","label":"test-failure"}
40-
{"issue_id":"gt-9mwl","label":"zfc-violation"}
41-
{"issue_id":"gt-9p8h5","label":"desire-path"}
42-
{"issue_id":"gt-9xbg","label":"zfc-violation"}
43-
{"issue_id":"gt-bmho","label":"zfc-violation"}
44-
{"issue_id":"gt-bxwjr","label":"design"}
45-
{"issue_id":"gt-bxwjr","label":"placeholder"}
46-
{"issue_id":"gt-c5vvu","label":"gt"}
47-
{"issue_id":"gt-crew-role","label":"migrated-to:hq-crew-role"}
48-
{"issue_id":"gt-d9ed","label":"zfc-violation"}
49-
{"issue_id":"gt-deacon","label":"migrated-to:hq-deacon"}
50-
{"issue_id":"gt-edu3","label":"zfc"}
51-
{"issue_id":"gt-ef2t","label":"enhancement"}
52-
{"issue_id":"gt-eo8d","label":"flaky"}
53-
{"issue_id":"gt-eo8d","label":"pre-existing"}
54-
{"issue_id":"gt-eo8d","label":"test-failure"}
55-
{"issue_id":"gt-f6mkz","label":"design"}
56-
{"issue_id":"gt-f6mkz","label":"prerequisite"}
57-
{"issue_id":"gt-fj87","label":"zfc"}
58-
{"issue_id":"gt-gastown-crew-dennis","label":"gt:agent"}
59-
{"issue_id":"gt-gastown-crew-george","label":"gt:agent"}
60-
{"issue_id":"gt-gastown-crew-gus","label":"gt:agent"}
61-
{"issue_id":"gt-gastown-crew-jack","label":"gt:agent"}
62-
{"issue_id":"gt-gastown-crew-joe","label":"gt:agent"}
63-
{"issue_id":"gt-gastown-crew-max","label":"gt:agent"}
64-
{"issue_id":"gt-gastown-crew-mel","label":"gt:agent"}
65-
{"issue_id":"gt-gastown-crew-tom","label":"gt:agent"}
66-
{"issue_id":"gt-gastown-polecat-Toast","label":"gt:agent"}
67-
{"issue_id":"gt-gastown-polecat-capable","label":"gt:agent"}
68-
{"issue_id":"gt-gastown-polecat-cheedo","label":"gt:agent"}
69-
{"issue_id":"gt-gastown-polecat-coma","label":"gt:agent"}
70-
{"issue_id":"gt-gastown-polecat-dag","label":"gt:agent"}
71-
{"issue_id":"gt-gastown-polecat-dementus","label":"gt:agent"}
72-
{"issue_id":"gt-gastown-polecat-furiosa","label":"gt:agent"}
73-
{"issue_id":"gt-gastown-polecat-keeper","label":"gt:agent"}
74-
{"issue_id":"gt-gastown-polecat-max","label":"gt:agent"}
75-
{"issue_id":"gt-gastown-polecat-nux","label":"gt:agent"}
76-
{"issue_id":"gt-gastown-polecat-rictus","label":"gt:agent"}
77-
{"issue_id":"gt-gastown-polecat-slit","label":"gt:agent"}
78-
{"issue_id":"gt-gastown-polecat-toast","label":"gt:agent"}
79-
{"issue_id":"gt-gastown-polecat-valkyrie","label":"gt:agent"}
80-
{"issue_id":"gt-gastown-refinery","label":"gt:agent"}
81-
{"issue_id":"gt-gastown-refinery","label":"idle:2"}
82-
{"issue_id":"gt-gastown-witness","label":"gt:agent"}
83-
{"issue_id":"gt-gow8b","label":"design"}
84-
{"issue_id":"gt-gow8b","label":"hooks"}
85-
{"issue_id":"gt-gow8b","label":"infrastructure"}
86-
{"issue_id":"gt-group-test-group","label":"gt:group"}
87-
{"issue_id":"gt-group-test-group-debug","label":"gt:group"}
88-
{"issue_id":"gt-group-test-groupX","label":"gt:group"}
89-
{"issue_id":"gt-group-test-groupY","label":"gt:group"}
90-
{"issue_id":"gt-group-trace-test","label":"gt:group"}
91-
{"issue_id":"gt-gw1","label":"reconciliation"}
92-
{"issue_id":"gt-gw1","label":"zfc"}
93-
{"issue_id":"gt-h7s","label":"reconciliation"}
94-
{"issue_id":"gt-h7s","label":"zfc"}
95-
{"issue_id":"gt-idrl","label":"zfc-violation"}
96-
{"issue_id":"gt-j1m5v","label":"design"}
97-
{"issue_id":"gt-j1m5v","label":"feature"}
98-
{"issue_id":"gt-j30x","label":"plugin:rebuild-gt"}
99-
{"issue_id":"gt-j30x","label":"result:success"}
100-
{"issue_id":"gt-j30x","label":"rig:gastown"}
101-
{"issue_id":"gt-j30x","label":"type:plugin-run"}
102-
{"issue_id":"gt-j3cx","label":"idle:3"}
103-
{"issue_id":"gt-l5js","label":"bug"}
104-
{"issue_id":"gt-l5js","label":"p1"}
105-
{"issue_id":"gt-l7uq","label":"zfc-violation"}
106-
{"issue_id":"gt-mayor","label":"migrated-to:hq-mayor"}
107-
{"issue_id":"gt-mxqc","label":"zfc"}
108-
{"issue_id":"gt-mzxs","label":"zfc"}
109-
{"issue_id":"gt-npatu9","label":"beads"}
110-
{"issue_id":"gt-npatu9","label":"cleanup"}
111-
{"issue_id":"gt-npatu9","label":"code-review"}
112-
{"issue_id":"gt-obp2","label":"test-coverage"}
113-
{"issue_id":"gt-oetc","label":"zfc"}
114-
{"issue_id":"gt-ohqi","label":"zfc-violation"}
115-
{"issue_id":"gt-olb4","label":"infrastructure"}
116-
{"issue_id":"gt-p7uq","label":"refactor"}
117-
{"issue_id":"gt-p7uq","label":"refinery"}
118-
{"issue_id":"gt-p7uq","label":"zfc"}
119-
{"issue_id":"gt-p7uq.1","label":"refactor"}
120-
{"issue_id":"gt-p7uq.1","label":"refinery"}
121-
{"issue_id":"gt-p7uq.1","label":"zfc"}
122-
{"issue_id":"gt-p7uq.2","label":"refactor"}
123-
{"issue_id":"gt-p7uq.2","label":"refinery"}
124-
{"issue_id":"gt-p7uq.2","label":"zfc"}
125-
{"issue_id":"gt-p7uq.3","label":"refactor"}
126-
{"issue_id":"gt-p7uq.3","label":"refinery"}
127-
{"issue_id":"gt-p7uq.3","label":"zfc"}
128-
{"issue_id":"gt-ph6q","label":"zfc-violation"}
129-
{"issue_id":"gt-pimh","label":"formula"}
130-
{"issue_id":"gt-pisa8","label":"desire-path"}
131-
{"issue_id":"gt-polecat-role","label":"migrated-to:hq-polecat-role"}
132-
{"issue_id":"gt-pr-sheriff","label":"pinned"}
133-
{"issue_id":"gt-qago","label":"zfc-violation"}
134-
{"issue_id":"gt-qjtq","label":"zfc-violation"}
135-
{"issue_id":"gt-qmsx","label":"zfc-violation"}
136-
{"issue_id":"gt-qzjb","label":"cleanup"}
137-
{"issue_id":"gt-rcrt","label":"zfc-violation"}
138-
{"issue_id":"gt-re2y","label":"zfc-violation"}
139-
{"issue_id":"gt-rig-gastown","label":"gt:rig"}
140-
{"issue_id":"gt-s4fcs","label":"seance"}
141-
{"issue_id":"gt-s4fcs","label":"ux"}
142-
{"issue_id":"gt-sk5u","label":"zfc-violation"}
143-
{"issue_id":"gt-sm59","label":"enhancement"}
144-
{"issue_id":"gt-td6p","label":"formula"}
145-
{"issue_id":"gt-tit8","label":"design"}
146-
{"issue_id":"gt-tit8","label":"memory"}
147-
{"issue_id":"gt-tsut","label":"zfc-violation"}
148-
{"issue_id":"gt-utbjv","label":"worker-lifecycle"}
149-
{"issue_id":"gt-v3z5","label":"desire-path"}
150-
{"issue_id":"gt-witness-role","label":"migrated-to:hq-witness-role"}
151-
{"issue_id":"gt-wrdz","label":"cleanup"}
152-
{"issue_id":"gt-xpgh8","label":"desire-path"}
153-
{"issue_id":"gt-xu9o","label":"plugin:dolt-janitor"}
154-
{"issue_id":"gt-xu9o","label":"result:success"}
155-
{"issue_id":"gt-xu9o","label":"rig:gastown"}
156-
{"issue_id":"gt-xu9o","label":"type:plugin-run"}
157-
{"issue_id":"gt-xzbk","label":"plugin:dolt-backup"}
158-
{"issue_id":"gt-xzbk","label":"result:skip"}
159-
{"issue_id":"gt-xzbk","label":"type:plugin-run"}
160-
{"issue_id":"gt-y1uvb","label":"architecture"}
161-
{"issue_id":"gt-y1uvb","label":"design"}
162-
{"issue_id":"gt-yyod","label":"plugin:dolt-archive"}
163-
{"issue_id":"gt-yyod","label":"result:success"}
164-
{"issue_id":"gt-yyod","label":"rig:gastown"}
165-
{"issue_id":"gt-yyod","label":"type:plugin-run"}
166-
{"issue_id":"gt-zhc4","label":"plugin:rebuild-gt"}
167-
{"issue_id":"gt-zhc4","label":"result:success"}
168-
{"issue_id":"gt-zhc4","label":"rig:gastown"}
169-
{"issue_id":"gt-zhc4","label":"type:plugin-run"}
1+
{"issue_id":"st-rig-sfgastown","label":"gt:rig"}
2+
{"issue_id":"st-rig-sfgastown","label":"status:docked"}
3+
{"issue_id":"st-sfgastown-crew-dunks","label":"gt:agent"}
4+
{"issue_id":"st-sfgastown-crew-test","label":"gt:agent"}
5+
{"issue_id":"st-sfgastown-refinery","label":"gt:agent"}
6+
{"issue_id":"st-sfgastown-witness","label":"gt:agent"}
7+
{"issue_id":"st-sfgastown-witness","label":"idle:0"}

plugins/dolt-snapshots/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
snapshot

plugins/dolt-snapshots/go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module github.com/steveyegge/gastown/plugins/dolt-snapshots
2+
3+
go 1.23
4+
5+
require github.com/go-sql-driver/mysql v1.8.1
6+
7+
require filippo.io/edwards25519 v1.1.0 // indirect

plugins/dolt-snapshots/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
2+
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
3+
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
4+
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=

0 commit comments

Comments
 (0)