diff --git a/openadapt/cli.py b/openadapt/cli.py index cb1306fc9e..c1ed2775e2 100644 --- a/openadapt/cli.py +++ b/openadapt/cli.py @@ -1350,12 +1350,23 @@ def verify_ledger() -> None: @verify_ledger.command("list") @_LEDGER_OPTION def verify_ledger_list(ledger_dir: Optional[str]) -> None: - """Show held and open writes: key, state, when it began, outcome.""" + """Show held and open writes: key, state, when it began, outcome. + + Also each provisional CONFIRMED whose recheck is still owed, with its due + time, so a recheck isn't lost when the server that holds it restarts. + """ ledger = _write_ledger(ledger_dir) rows = ledger.unresolved() - if not rows: - click.echo("No held or open writes.") + owed = ledger.rechecks_owed() + if not rows and not owed: + click.echo("No held or open writes. No rechecks owed.") return + for key, attempt in owed: + click.echo( + f"{key} recheck-owed began {attempt.begun.get('at')} " + f"due {attempt.recheck_owed} by {attempt.begun.get('actor_kind')}:" + f"{attempt.begun.get('actor_id')}" + ) for key, attempt in rows: outcome = (attempt.resolved or {}).get("outcome", "-") state = "review" if attempt.review else ledger.state(attempt) diff --git a/tests/test_verify_command.py b/tests/test_verify_command.py index 088b6e00c1..9ce361a041 100644 --- a/tests/test_verify_command.py +++ b/tests/test_verify_command.py @@ -103,3 +103,30 @@ def test_verify_ledger_lists_and_releases_a_held_write(tmp_path) -> None: assert released.exit_code == 0, released.output listed = runner.invoke(main, ["verify", "ledger", "list", "--ledger", str(folder)]) assert "No held or open writes." in listed.output + + +def test_verify_ledger_lists_a_recheck_owed_until_it_runs(tmp_path) -> None: + ledger_mod = pytest.importorskip("openadapt_flow.write_ledger") + folder = tmp_path / "ledger" + ledger = ledger_mod.Ledger(ledger_mod.FileLedger(folder)) + key = "d" * 64 + due = "2026-01-01T00:02:00Z" + ledger.begun(key, "write-2", contract="c" * 64, actor_kind="agent", actor_id="bot") + ledger.resolved( + key, + "write-2", + outcome="CONFIRMED", + retry_safe=False, + delivered=True, + result={"outcome": "CONFIRMED", "provisional": True, "recheck_due_at": due}, + ) + runner = CliRunner() + listed = runner.invoke(main, ["verify", "ledger", "list", "--ledger", str(folder)]) + assert listed.exit_code == 0, listed.output + assert key in listed.output and "recheck-owed" in listed.output + assert f"due {due}" in listed.output + + ledger.rechecked(key, "write-2", outcome="CONFIRMED") + listed = runner.invoke(main, ["verify", "ledger", "list", "--ledger", str(folder)]) + assert "recheck-owed" not in listed.output + assert "No rechecks owed." in listed.output