From 34724b1367dca435567e296d095696b8f0942d0e Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 2 Sep 2026 15:30:33 -0700 Subject: [PATCH 1/2] Ask about undeposited notes weekly, not only when metadata changes The push trigger fires on a metadata CHANGE, and the condition it cares about is a STATE: archival, published, holding no record. A note that reached that state before this workflow existed -- or whose metadata has simply not moved since -- is never asked about at all. Retrofitting the boundary-conditions note is what showed it. Nothing in the repository would have offered it a DOI; it had to be asked for by hand, and the only reason it was ever offered before that was an unrelated backfill that happened to touch its metadata. So the same question is asked weekly. That is only safe because of the guard merged in #46: a request already open counts as asked, so a note waiting on a decision is not asked again and does not accumulate drafts. Verified before adding this -- two further runs against the live API reported "already asked" and created nothing. Scheduled an hour before the `outstanding` report, so a request opened on Monday appears in that week's issue rather than waiting for the next. Underworld development team with AI support from Claude Code --- .github/workflows/deposit-ready.yml | 10 ++++++++++ tests/test_migration.py | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/.github/workflows/deposit-ready.yml b/.github/workflows/deposit-ready.yml index 4ecd551..e96d789 100644 --- a/.github/workflows/deposit-ready.yml +++ b/.github/workflows/deposit-ready.yml @@ -14,6 +14,16 @@ on: push: branches: [main] paths: ['articles/**/metadata.yml'] + # A note published before this workflow existed, or one whose metadata has + # not moved since, is never asked about by the push trigger -- it fires on a + # CHANGE, and the condition here is a STATE: archival, published, no record. + # Retrofitting the boundary-conditions note found exactly that; it had to be + # asked for by hand. So the same question is asked weekly, and the guard on + # what is already open means a request left waiting is not asked again. + schedule: + # Mondays, 21:00 UTC -- an hour before the `outstanding` report, so a + # request opened here appears in that week's issue rather than the next. + - cron: '0 21 * * 1' workflow_dispatch: permissions: diff --git a/tests/test_migration.py b/tests/test_migration.py index 84108e7..1d0330a 100644 --- a/tests/test_migration.py +++ b/tests/test_migration.py @@ -2372,3 +2372,29 @@ def test_an_open_request_is_not_asked_for_twice(): "it must match the requests it opens itself" assert "if s not in asked" in ask_step, \ "and it must subtract them from what it asks about" + + +def test_the_deposit_reminder_asks_on_a_schedule_as_well_as_on_a_push(): + """The push trigger fires on a metadata CHANGE; the condition it cares + about is a STATE — archival, published, and holding no record. + + A note published before this workflow existed, or one whose metadata has + not moved since, is therefore never asked about. Retrofitting the + boundary-conditions note found exactly that: it had to be asked for by + hand. The schedule closes it, and the open-request guard is what makes a + repeating trigger safe. + """ + import yaml + ready = yaml.safe_load( + (ROOT / ".github" / "workflows" / "deposit-ready.yml").read_text( + encoding="utf-8")) + on = ready.get(True) or ready.get("on") + assert "schedule" in on, "a state condition needs a repeating trigger" + assert "workflow_dispatch" in on, "and a manual route, for a note in a hurry" + + # A repeating trigger without the guard would mint a fresh DOI every week + # for every request left waiting. The two belong together. + src = (ROOT / ".github" / "workflows" / "deposit-ready.yml").read_text( + encoding="utf-8") + assert "gh pr list --state open" in src, \ + "a scheduled reminder MUST skip what it has already asked about" From 7dec8533be8960fd4338000523588a4861a12d25 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 2 Sep 2026 15:31:10 -0700 Subject: [PATCH 2/2] Read the workflow triggers as text, not YAML pyyaml is not in the test environment, and the triggers are a flat block at the top of the file. I pushed this test failing: the pipe into `tail` masked pytest exit status, so the chain that committed it carried on. Underworld development team with AI support from Claude Code --- tests/test_migration.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_migration.py b/tests/test_migration.py index 1d0330a..952035d 100644 --- a/tests/test_migration.py +++ b/tests/test_migration.py @@ -2384,17 +2384,17 @@ def test_the_deposit_reminder_asks_on_a_schedule_as_well_as_on_a_push(): hand. The schedule closes it, and the open-request guard is what makes a repeating trigger safe. """ - import yaml - ready = yaml.safe_load( - (ROOT / ".github" / "workflows" / "deposit-ready.yml").read_text( - encoding="utf-8")) - on = ready.get(True) or ready.get("on") - assert "schedule" in on, "a state condition needs a repeating trigger" - assert "workflow_dispatch" in on, "and a manual route, for a note in a hurry" + src = (ROOT / ".github" / "workflows" / "deposit-ready.yml").read_text( + encoding="utf-8") + # Read as text rather than YAML: pyyaml is not in the test environment, + # and the triggers are a flat block at the top of the file. + triggers = src.split("jobs:")[0] + assert "schedule:" in triggers, "a state condition needs a repeating trigger" + assert "cron:" in triggers + assert "workflow_dispatch:" in triggers, \ + "and a manual route, for a note in a hurry" # A repeating trigger without the guard would mint a fresh DOI every week # for every request left waiting. The two belong together. - src = (ROOT / ".github" / "workflows" / "deposit-ready.yml").read_text( - encoding="utf-8") assert "gh pr list --state open" in src, \ "a scheduled reminder MUST skip what it has already asked about"