Monorepo management for opam overlays
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Skip past days with existing files, verify admin via delivery_email

monopam:
- Make --aggregate the default for daily changes (add --no-aggregate to skip)
- Skip past days entirely if per-day file exists to avoid redundant Claude calls
- For today, still check if entry exists before regenerating
- Simplify Zulip format headers ("Updates for..." instead of "## Changes for...")

poe:
- Fix admin verification to use delivery_email from Zulip API
- Fallback to sender_email if API call fails

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+52 -22
+13 -8
bin/main.ml
··· 409 409 "Changes are stored in the .changes directory at the monorepo root:"; 410 410 `I (".changes/<repo>.json", "Weekly changelog entries"); 411 411 `I (".changes/<repo>-daily.json", "Daily changelog entries"); 412 - `I (".changes/YYYYMMDD.json", "Aggregated daily entries (with --aggregate)"); 412 + `I (".changes/YYYYMMDD.json", "Aggregated daily entries (default with --daily)"); 413 413 `P 414 414 "Also generates aggregated markdown files at the monorepo root:"; 415 415 `I ("CHANGES.md", "Aggregated weekly changelog"); ··· 426 426 "Repositories with no user-facing changes will have blank entries \ 427 427 (empty summary and changes) rather than 'no changes' text."; 428 428 `P 429 - "With --aggregate, also generates a structured JSON file suitable for \ 430 - the poe Zulip bot broadcasting system."; 429 + "When using --daily, an aggregated JSON file is generated by default \ 430 + for the poe Zulip bot broadcasting system. Use --no-aggregate to skip."; 431 + `P 432 + "If a per-repo-per-day JSON file already exists for a past day, that \ 433 + repo is skipped for that day to avoid redundant Claude API calls."; 431 434 ] 432 435 in 433 436 let info = Cmd.info "changes" ~doc ~man in ··· 451 454 let doc = "Preview changes without writing files" in 452 455 Arg.(value & flag & info [ "dry-run"; "n" ] ~doc) 453 456 in 454 - let aggregate = 455 - let doc = "Also generate .changes/YYYYMMDD.json aggregated file (only with --daily)" in 456 - Arg.(value & flag & info [ "aggregate"; "a" ] ~doc) 457 + let no_aggregate = 458 + let doc = "Skip generating .changes/YYYYMMDD.json aggregated file (--daily generates it by default)" in 459 + Arg.(value & flag & info [ "no-aggregate" ] ~doc) 457 460 in 458 - let run config_file package daily weeks days history dry_run aggregate () = 461 + let run config_file package daily weeks days history dry_run no_aggregate () = 459 462 Eio_main.run @@ fun env -> 460 463 with_config env config_file @@ fun config -> 461 464 let fs = Eio.Stdenv.fs env in ··· 465 468 if daily then begin 466 469 (* Use 30 as default history for daily if not explicitly set *) 467 470 let history = if history = 12 then 30 else history in 471 + (* Aggregate by default for daily, unless --no-aggregate is passed *) 472 + let aggregate = not no_aggregate in 468 473 Monopam.changes_daily ~proc ~fs ~config ~clock ?package ~days ~history ~dry_run ~aggregate () 469 474 end 470 475 else ··· 484 489 Term.( 485 490 ret 486 491 (const run $ config_file_arg $ package_arg $ daily $ weeks $ days $ history $ dry_run 487 - $ aggregate $ logging_term)) 492 + $ no_aggregate $ logging_term)) 488 493 489 494 (* Main command group *) 490 495
+9
lib/changes.ml
··· 146 146 let daily_filename repo_name date = 147 147 repo_name ^ "-" ^ date ^ ".json" 148 148 149 + (* Check if daily file exists on disk *) 150 + let daily_exists ~fs ~monorepo ~date repo_name = 151 + let filename = daily_filename repo_name date in 152 + let file_path = Eio.Path.(fs / Fpath.to_string monorepo / ".changes" / filename) in 153 + match Eio.Path.kind ~follow:true file_path with 154 + | `Regular_file -> true 155 + | _ -> false 156 + | exception Eio.Io _ -> false 157 + 149 158 (* Load daily changes from .changes/<repo>-<date>.json in monorepo *) 150 159 let load_daily ~fs ~monorepo ~date repo_name = 151 160 let filename = daily_filename repo_name date in
+4
lib/changes.mli
··· 78 78 val save : fs:_ Eio.Path.t -> monorepo:Fpath.t -> changes_file -> (unit, string) result 79 79 (** [save ~fs ~monorepo cf] saves the changes file to .changes/<repo_name>.json. *) 80 80 81 + val daily_exists : fs:_ Eio.Path.t -> monorepo:Fpath.t -> date:string -> string -> bool 82 + (** [daily_exists ~fs ~monorepo ~date repo_name] checks if a daily changes file exists. 83 + @param date Date in YYYY-MM-DD format *) 84 + 81 85 val load_daily : fs:_ Eio.Path.t -> monorepo:Fpath.t -> date:string -> string -> (daily_changes_file, string) result 82 86 (** [load_daily ~fs ~monorepo ~date repo_name] loads daily changes from .changes/<repo_name>-<date>.json. 83 87 Returns an empty changes file if the file does not exist.
+24 -12
lib/monopam.ml
··· 1052 1052 | None -> now_ptime 1053 1053 in 1054 1054 let date = Changes.date_of_ptime day_time in 1055 + let is_today = day_offset = 0 in 1055 1056 1056 - (* Load existing daily changes from .changes/<repo>-<date>.json *) 1057 - match Changes.load_daily ~fs:fs_t ~monorepo ~date repo_name with 1058 - | Error e -> Error (Claude_error e) 1059 - | Ok changes_file -> 1060 - (* Skip if day already has an entry *) 1061 - if Changes.has_day changes_file ~date then begin 1062 - Log.info (fun m -> m " Day %s already has entry, skipping" date); 1063 - all_changes_files := changes_file :: !all_changes_files; 1064 - process_days (day_offset + 1) 1065 - end 1066 - else begin 1057 + (* For past days, skip if file exists at all (already analyzed) *) 1058 + (* For today, skip only if file has entries (may need to catch new commits) *) 1059 + let should_skip = 1060 + if is_today then 1061 + Changes.daily_exists ~fs:fs_t ~monorepo ~date repo_name && 1062 + (match Changes.load_daily ~fs:fs_t ~monorepo ~date repo_name with 1063 + | Ok cf -> Changes.has_day cf ~date 1064 + | Error _ -> false) 1065 + else 1066 + Changes.daily_exists ~fs:fs_t ~monorepo ~date repo_name 1067 + in 1068 + if should_skip then begin 1069 + Log.info (fun m -> m " Day %s already processed, skipping" date); 1070 + (match Changes.load_daily ~fs:fs_t ~monorepo ~date repo_name with 1071 + | Ok cf -> all_changes_files := cf :: !all_changes_files 1072 + | Error _ -> ()); 1073 + process_days (day_offset + 1) 1074 + end 1075 + else 1076 + (* Load existing daily changes from .changes/<repo>-<date>.json *) 1077 + match Changes.load_daily ~fs:fs_t ~monorepo ~date repo_name with 1078 + | Error e -> Error (Claude_error e) 1079 + | Ok changes_file -> 1067 1080 (* Get commits for this day *) 1068 1081 let since = date ^ " 00:00:00" in 1069 1082 let until = date ^ " 23:59:59" in ··· 1143 1156 process_days (day_offset + 1) 1144 1157 end 1145 1158 end 1146 - end 1147 1159 end 1148 1160 in 1149 1161 match process_days 0 with
+2 -2
lib_changes/query.ml
··· 44 44 let buf = Buffer.create 1024 in 45 45 if include_date then begin 46 46 match date with 47 - | Some d -> Buffer.add_string buf (Printf.sprintf "## Changes for %s\n\n" d) 48 - | None -> Buffer.add_string buf "## Recent Changes\n\n" 47 + | Some d -> Buffer.add_string buf (Printf.sprintf "Updates for %s:\n\n" d) 48 + | None -> Buffer.add_string buf "Recent updates:\n\n" 49 49 end; 50 50 (* Group by change type *) 51 51 let by_type = [