Monorepo management for opam overlays
at main 329 lines 9.9 kB view raw view rendered
1# Monopam CLI Improvements Plan 2 3## Design Decisions (Clarified) 4 51. **Verse remotes**: Auto-add on `monopam sync`, remove outdated ones for all verse members 62. **Add/remove commands**: Removed from CLI - use agent skills instead 73. **Doctor output**: Structured JSON with per-repo recommendations, rendered to text by CLI with `--json` option 84. **Verse remote URL**: Point to `src/` checkout (individual repo) 95. **Opam sync direction**: Local metadata always trumps opam-repo metadata 106. **Claude usage**: Always use Claude (via ocaml-claude library) for doctor command 11 12--- 13 14## Current CLI Commands 15 16After removing add/remove: 17- `monopam status` - Show sync status and verse fork analysis 18- `monopam sync [--remote] [--skip-push] [--skip-pull] [package]` - Primary sync command 19- `monopam changes` - Generate changelogs with Claude 20- `monopam verse init` - Initialize workspace 21- `monopam verse members` - List registry members 22- `monopam verse pull` - Pull verse member repos 23- `monopam verse sync` - Sync verse workspace 24 25--- 26 27## Implementation Plan 28 29### Phase 1: Verse Remotes (Auto-managed) 30 31#### Changes to `monopam sync` 32 33Add verse remote management to the sync process: 34 351. **During fetch phase**: For each verse member in registry: 36 - Scan their monorepo for subtrees 37 - For matching subtrees in our `src/`: 38 - Add git remote named `verse/<handle>` pointing to their `src/` checkout 39 - If remote exists but URL changed, update it 40 - Fetch from the remote 41 422. **Cleanup**: Remove verse remotes for: 43 - Members no longer in registry 44 - Repos we no longer have 45 46#### Data Flow 47 48``` 49sync starts 50 ├── push phase (existing) 51 ├── fetch phase (existing) 52 │ └── NEW: for each verse member with matching repos: 53 │ └── ensure git remote in src/<repo> → verse/<member>/src/<repo> 54 │ └── git fetch verse/<handle> 55 ├── merge phase (existing) 56 ├── subtree phase (existing) 57 ├── finalize phase (existing) 58 └── remote phase (existing) 59``` 60 61#### Implementation in monopam.ml 62 63```ocaml 64(* New function to manage verse remotes for a repo *) 65let ensure_verse_remotes ~proc ~fs ~config ~verse_config pkg = 66 let checkouts_root = Config.Paths.checkouts config in 67 let checkout_dir = Package.checkout_dir ~checkouts_root pkg in 68 let repo_name = Package.repo_name pkg in 69 70 (* Get all verse members who have this repo *) 71 let verse_subtrees = Verse.get_verse_subtrees ~proc ~fs ~config:verse_config () in 72 let members_with_repo = 73 Hashtbl.find_opt verse_subtrees repo_name 74 |> Option.value ~default:[] 75 in 76 77 (* For each member, ensure remote exists *) 78 List.iter (fun (handle, verse_mono_path) -> 79 let remote_name = "verse/" ^ handle in 80 let verse_src = Fpath.(verse_mono_path / ".." / "src" / repo_name) in 81 (* Add or update remote *) 82 Git.ensure_remote ~proc ~fs ~name:remote_name ~url:verse_src checkout_dir 83 ) members_with_repo 84``` 85 86--- 87 88### Phase 2: Opam Metadata Sync 89 90#### New Command: `monopam opam sync` 91 92Synchronize `.opam` files from monorepo subtrees to opam-repo. 93 94```bash 95monopam opam sync # Sync all packages 96monopam opam sync eio # Sync specific package 97``` 98 99**Behavior:** 100- For each package in monorepo: 101 - Read `.opam` file from subtree 102 - Compare with opam-repo version 103 - If different, copy monorepo → opam-repo (local always wins) 104 - Stage changes in opam-repo 105 106#### Integration with `monopam sync` 107 108Add `--opam` flag: 109 110```bash 111monopam sync --opam --remote # Full sync including opam metadata 112``` 113 114Or make it part of finalize phase (always sync opam). 115 116#### Implementation 117 118```ocaml 119let sync_opam_files ~proc ~fs ~config pkgs = 120 let monorepo = Config.Paths.monorepo config in 121 let opam_repo = Config.Paths.opam_repo config in 122 123 List.iter (fun pkg -> 124 let name = Package.name pkg in 125 let subtree_opam = Fpath.(monorepo / Package.subtree_prefix pkg / (name ^ ".opam")) in 126 let repo_opam = Fpath.(opam_repo / "packages" / name / (name ^ ".dev") / "opam") in 127 128 (* Read both files *) 129 let subtree_content = read_file_opt ~fs subtree_opam in 130 let repo_content = read_file_opt ~fs repo_opam in 131 132 match subtree_content with 133 | None -> () (* No opam file in subtree, skip *) 134 | Some content when Some content <> repo_content -> 135 (* Copy to opam-repo *) 136 write_file ~fs repo_opam content; 137 Git.add ~proc ~fs opam_repo [Fpath.to_string repo_opam] 138 | _ -> () (* Already in sync *) 139 ) pkgs 140``` 141 142--- 143 144### Phase 3: Doctor Command 145 146#### New Command: `monopam doctor` 147 148Claude-powered workspace health analysis. 149 150```bash 151monopam doctor # Full analysis, text output 152monopam doctor --json # JSON output for tooling 153monopam doctor eio # Analyze specific repo 154``` 155 156#### Output Structure (JSON) 157 158```json 159{ 160 "timestamp": "2026-01-21T12:00:00Z", 161 "workspace": "/home/user/tangled", 162 "summary": { 163 "repos_total": 39, 164 "repos_need_sync": 2, 165 "repos_behind_upstream": 3, 166 "verse_divergences": 5 167 }, 168 "repos": [ 169 { 170 "name": "eio", 171 "local_sync": "in_sync", 172 "remote_sync": { "ahead": 0, "behind": 0 }, 173 "verse_analysis": [ 174 { 175 "handle": "alice.bsky.social", 176 "their_commits": [ 177 { 178 "hash": "abc1234", 179 "subject": "Add Eio.Path.symlink support", 180 "category": "feature", 181 "priority": "medium", 182 "recommendation": "review-first", 183 "conflict_risk": "low", 184 "summary": "Adds symlink creation support to Eio.Path module" 185 }, 186 { 187 "hash": "def5678", 188 "subject": "Fix race condition in Eio.Fiber.fork", 189 "category": "bug-fix", 190 "priority": "high", 191 "recommendation": "merge-now", 192 "conflict_risk": "none", 193 "summary": "Fixes potential deadlock when forking fibers under load" 194 } 195 ], 196 "suggested_action": "git fetch verse/alice.bsky.social && git cherry-pick def5678" 197 } 198 ] 199 } 200 ], 201 "recommendations": [ 202 { 203 "priority": "high", 204 "action": "Merge alice's bug fix for eio (def5678)", 205 "command": "cd src/eio && git cherry-pick def5678" 206 }, 207 { 208 "priority": "medium", 209 "action": "Run monopam sync to resolve local sync issues", 210 "command": "monopam sync" 211 } 212 ], 213 "warnings": [ 214 "opam-repo has uncommitted changes", 215 "verse/alice.bsky.social/ is 10 commits behind" 216 ] 217} 218``` 219 220#### Text Rendering 221 222``` 223=== Monopam Doctor Report === 224Generated: 2026-01-21 12:00:00 225 226Summary: 227 39 repos tracked 228 2 need local sync 229 3 behind upstream 230 5 verse divergences 231 232───────────────────────────────────────── 233 234eio (diverged from alice.bsky.social) 235 236 Their commits (2): 237 238 [HIGH] def5678 Fix race condition in Eio.Fiber.fork 239 Category: bug-fix | Risk: none | Action: merge-now 240 → Fixes potential deadlock when forking fibers under load 241 242 [MED] abc1234 Add Eio.Path.symlink support 243 Category: feature | Risk: low | Action: review-first 244 → Adds symlink creation support to Eio.Path module 245 246 Suggested: git fetch verse/alice.bsky.social && git cherry-pick def5678 247 248───────────────────────────────────────── 249 250Recommendations: 251 1. [HIGH] Merge alice's bug fix for eio (def5678) 252 $ cd src/eio && git cherry-pick def5678 253 254 2. [MED] Run monopam sync to resolve local sync issues 255 $ monopam sync 256 257Warnings: 258 • opam-repo has uncommitted changes 259 • verse/alice.bsky.social/ is 10 commits behind 260``` 261 262#### Claude Integration 263 264Use the existing `claude` OCaml library for analysis: 265 266```ocaml 267module Doctor = struct 268 type commit_analysis = { 269 hash: string; 270 subject: string; 271 category: [`Security_fix | `Bug_fix | `Feature | `Refactor | `Docs | `Test]; 272 priority: [`Critical | `High | `Medium | `Low]; 273 recommendation: [`Merge_now | `Review_first | `Skip | `Needs_discussion]; 274 conflict_risk: [`None | `Low | `Medium | `High]; 275 summary: string; 276 } 277 278 let analyze_commits ~commits ~our_branch ~their_handle = 279 let prompt = Format.asprintf {| 280You are analyzing git commits from a collaborator's repository. 281 282Repository context: 283- Our branch: %s 284- Their handle: %s 285 286Commits to analyze: 287%s 288 289For each commit, provide JSON with: 290- category: security-fix, bug-fix, feature, refactor, docs, test 291- priority: critical, high, medium, low 292- recommendation: merge-now, review-first, skip, needs-discussion 293- conflict_risk: none, low, medium, high 294- summary: one-line description of what the commit does 295 296Respond with a JSON array of analyses. 297|} our_branch their_handle (format_commits commits) in 298 299 Claude.chat ~model:"claude-sonnet-4-20250514" ~messages:[ 300 Claude.Message.user prompt 301 ] () 302 |> parse_analysis_response 303end 304``` 305 306--- 307 308## Implementation Order 309 3101. **Phase 1a**: Add `ensure_verse_remotes` function to monopam.ml 3112. **Phase 1b**: Integrate verse remote management into sync fetch phase 3123. **Phase 2a**: Add `sync_opam_files` function 3134. **Phase 2b**: Add `monopam opam sync` command (or integrate into sync) 3145. **Phase 3a**: Create `doctor.ml` module with types and Claude integration 3156. **Phase 3b**: Add `monopam doctor` command with JSON/text output 316 317--- 318 319## Files to Modify/Create 320 321### Modify 322- `monopam/lib/monopam.ml` - Add verse remote management, opam sync 323- `monopam/lib/monopam.mli` - Export new functions 324- `monopam/bin/main.ml` - Add doctor command, opam subcommand 325 326### Create 327- `monopam/lib/doctor.ml` - Doctor analysis logic 328- `monopam/lib/doctor.mli` - Doctor interface 329- `monopam/lib/opam_sync.ml` - Opam metadata sync logic (optional, could be in monopam.ml)