From 55b8f67ede13d5aa7e2ada8a8a51b077d22072bd Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Mon, 29 Jun 2026 11:04:34 -0700 Subject: [PATCH] askrene-migration: contrib script robustness + de-flake coalesce test Three fixes from the 2026-06-29 CodeRabbit pass on PR #320. clboss-askrene-layer-summary: load_layers() now catches OSError and JSONDecodeError on the --input replay path and exits with a message instead of crashing with a traceback on a missing file or malformed JSON. This is the offline mode the help text advertises. clboss-askrene-layer-summary: reject --json together with --top. The combination previously printed a JSON object and then appended a human-readable top section, producing invalid mixed-format output. The top list is human-readable only. test_askrene_layer: pin the coalescing bucket window astronomically large in test_coalesce_drops_dominated so both writes always land in the same Ev::now()/window bucket. Previously a run straddling a bucket boundary re-emitted the dominated write as a keep-alive and failed the sentinel assertion. It is the final test before Shutdown, so the global window needs no restore. --- contrib/clboss-askrene-layer-summary | 15 +++++++++++++-- tests/boss/test_askrene_layer.cpp | 10 ++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/contrib/clboss-askrene-layer-summary b/contrib/clboss-askrene-layer-summary index d5b4bac..699bf3a 100755 --- a/contrib/clboss-askrene-layer-summary +++ b/contrib/clboss-askrene-layer-summary @@ -128,8 +128,15 @@ def percentile(sorted_vals, q): def load_layers(args, network_option): if args.input is not None: - raw = sys.stdin.read() if args.input == "-" else open(args.input).read() - return json.loads(raw).get("layers", []) + try: + if args.input == "-": + raw = sys.stdin.read() + else: + with open(args.input) as f: + raw = f.read() + return json.loads(raw).get("layers", []) + except (OSError, json.JSONDecodeError) as e: + sys.exit(f"failed to load --input data: {e}") res = run_lightning_cli_command( args.lightning_dir, network_option, "askrene-listlayers", args.layer ) @@ -239,6 +246,10 @@ def main(): s = summarize(target) depth = s.pop("_depth") + if args.json and args.top > 0: + sys.exit("--json cannot be combined with --top " + "(the top list is human-readable only)") + if args.json: print(json.dumps(s, indent=2)) else: diff --git a/tests/boss/test_askrene_layer.cpp b/tests/boss/test_askrene_layer.cpp index 8d0230c..7d96fcd 100644 --- a/tests/boss/test_askrene_layer.cpp +++ b/tests/boss/test_askrene_layer.cpp @@ -320,6 +320,16 @@ test_coalesce_drops_dominated( MockRpcServer& server ) { auto const layer = std::string("coalesce-layer"); + /* Pin the coalescing bucket astronomically large so both writes + * below always land in the same Ev::now()/window bucket regardless + * of when the test runs; otherwise a run straddling a bucket + * boundary re-emits the dominated write as a keep-alive and + * assert_sentinel fires spuriously. This is the final test before + * Shutdown, so the global window needs no restore. */ + Boss::Mod::AskreneLayer::set_aging_window_secs( + std::uint64_t(1000) * 365 * 24 * 60 * 60 + ); + auto assert_first = [](Jsmn::Object const& req) { auto id = assert_method(req, "askrene-inform-channel"); auto params = req["params"];