OverviewFilesCommitsIssuesDocsReleasesPeers

pgh commits / 718c66c

0.1.133: add `pgh repo retire` / `unretire` / `list-retired`
The daemon has had retire-repo, unretire-repo and list-retired-repos since the tombstone work, with NO CLI surface — `pgh repo retire <x>` fell through to the bare-name path and reported `repo "retire" not found`, treating the subcommand as a repo name. Retirement is not optional bookkeeping. Deleting a bare repo by hand does not stick: pear-git-v3/lib/services/retired-repos.js documents four verified resurrection paths — cross-peer re-pull, boot rescan, directory watcher, and residual references in issue-registry-index.json / tracker autobases. The tombstone must replicate to every peer BEFORE anyone removes bytes, and there was no way to create one from the CLI. unretire is included deliberately: a later op wins by sequence number, so a mistaken retire is reversible — worth having to hand before retiring a dozen repos, not after. Neither command deletes anything, matching the daemon: a tombstone changes what is LISTED, SERVED, PULLED and RE-CREATED; removing bytes stays a manual per-peer operator action. ⚠ KNOWN GAP, found while testing this and NOT fixed here: retire-repo calls assertValidRepoName, which rejects `--help` ("must not start with -") and `undefined` ("looks like a stringified value"). Both exist as real repos on the fleet. So the two most obviously-junk repos cannot be tombstoned — the validation that stops them being CREATED also stops them being CLEANED UP. Retire is the cleanup path and arguably should accept any name that already exists; that is a daemon-side change and wants its own decision. Verified: retire/unretire/list-retired dispatch to the right RPCs, `retired` works as an alias, and a missing name errors with usage instead of being forwarded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
718c66c1bc04c2c386ef5bd789e30d51be25f303
by plankatron <fleeky@gmail.com> · 2026-07-30T22:12:25.000Z

Changed files (2) +31 -1

lib/commands/repo.js +30 -0
……@@ -27,6 +27,14 @@ export const usage = `pgh repo — browse repos served by the daemon
2727 pgh repo writers <name> List writers
2828 pgh repo grant-writer <name> --peer <url> [--role owner|writer]
2929 Admin: grant a peer push authority
30 pgh repo retire <name> Tombstone a repo: stop listing/serving/
31 re-creating it fleet-wide. Bytes are
32 NOT removed — remove ~/git-repos/
33 <name>.git by hand on each peer AFTER
34 the tombstone has replicated. Admin,
35 primary only.
36 pgh repo unretire <name> Undo a retire (a later op wins).
37 pgh repo list-retired Show the replicated tombstone set.
3038 pgh repo set-head <name> <branch> Set the repo's DEFAULT BRANCH fleet-wide
3139 (replicated; admin; run on the tracker
3240 owner). Accepts main or
……@@ -82,6 +90,28 @@ export async function run (client, args) {
8290 // public saw whichever the load balancer hit; it is replicated now, but a
8391 // repo whose nodes already disagree gets seeded by whichever writer
8492 // reconciles first. This makes that a deliberate choice rather than a race.
93 // Retirement. Deleting a bare repo by hand does NOT stick: four paths
94 // resurrect it (cross-peer re-pull, boot rescan, directory watcher, residual
95 // references in issue-registry-index.json / tracker autobases). The tombstone
96 // must replicate to every peer BEFORE anyone removes bytes. See
97 // pear-git-v3/lib/services/retired-repos.js, which never deletes anything and
98 // is explicit that removing bytes is a manual per-peer operator action.
99 if (sub === 'retire') {
100 const name = args[1]
101 if (!name) throw new Error('usage: pgh repo retire <name>')
102 return client.call('retire-repo', { name })
103 }
104
105 if (sub === 'unretire') {
106 const name = args[1]
107 if (!name) throw new Error('usage: pgh repo unretire <name>')
108 return client.call('unretire-repo', { name })
109 }
110
111 if (sub === 'list-retired' || sub === 'retired') {
112 return client.call('list-retired-repos', {})
113 }
114
85115 if (sub === 'set-head') {
86116 const name = args[1]
87117 const ref = args[2]
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "pgh",
3 "version": "0.1.132",
3 "version": "0.1.133",
44 "description": "pgh — P2P content-addressed package manager and app launcher",
55 "type": "module",
66 "main": "index.js",