Repository navigation
feat: tell a quiet arXiv inbox apart from a broken one - #60
Merged
Merged
Conversation
498e1a8 committed .claude/ (the session-start hook + settings) without adding it to ALLOWED_TOP_DIRS, so `make validate` and `make test` have both been red on main ever since — validate_structure only considers git-tracked top-level names, and .claude/ is tracked. Unrelated to the inbox work on this branch; committed separately so it can be read, or reverted, on its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ex9YARe5kDjsT3Ug7LKQ1r
The board rendered an empty inbox as "nothing waiting — the nightly arXiv
digest fills this" for two opposite states: arXiv genuinely announced no
strong-lensing papers, or the digest ran and the filing failed so the papers
were lost. On 2026-08-25 it was the second, and nothing on the page said so.
arxiv_papers.yml already killed this ambiguity on the Slack side — empty days
still post a heartbeat, so silence in #papers always means a broken run. The
board had no equivalent.
inbox_actions.py now owns a `last digest: <date>` line in arxiv-inbox.md,
rewritten on every digest run whether or not papers arrive. One line does both
jobs the prompt scoped: dated today over an empty inbox it is the quiet-day
marker, making absence positive evidence; days old it is the "last filled"
signal. Being replaced rather than accumulated, the sweep has nothing to age
out — which is what the explicit per-day marker line would have cost.
The staleness verdict is computed in the reader's browser, not at render time.
knowledge_board.yml republishes on pushes to arxiv-inbox.md, which is exactly
what stops happening when filing breaks: the page freezes at its last good
render, so a warning baked in server-side would sit at "0 weekdays" forever
and never fire in the one failure it exists for. The date is a fact and
survives the freeze; the verdict is recomputed against the reader's clock. A
page that was already stale when rendered warns without JS too.
Threshold is 2 weekdays, in inbox_actions.py beside INBOX_WINDOW_DAYS. Not 1:
the digest fires at a nominal 02:00 UTC and GitHub cron only ever jitters
later, so a board rendered early can see yesterday's stamp with nothing wrong.
Weekdays, not days, because the digest is Mon-Fri — a Monday board reading
Friday's stamp is three days but zero missed runs.
A missing stamp reads neutrally ("no run recorded yet"), never as a warning:
spawn.py empties arxiv-inbox.md for the template repos, so a freshly spawned
Memory has never run a digest and has nothing to be late for.
Seeds the stamp at 2026-08-25, the day the backfill dispatch filed the two
papers now in the inbox.
23 new tests (92 total, was 69), covering the three empty states, the weekday
arithmetic across a weekend, and the render/browser handshake. The shipped JS
was separately cross-checked against the Python for 30 consecutive stamp dates.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ex9YARe5kDjsT3Ug7LKQ1r
4 tasks done
main landed the same .claude/ allowlist fix in #59 (93a0b6b) while this branch was in flight. Same one-line change, different comment wording — resolved in main's favour, so 05aa803 here now contributes nothing and the file is byte-identical to main. Kept as a merge rather than a rebase: the branch is pushed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ex9YARe5kDjsT3Ug7LKQ1r
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #58.
The board rendered an empty arXiv inbox as
nothing waiting — the nightly arXiv digest fills thisfor two opposite states: arXiv genuinely announced no strong-lensing papers, or the digest ran and the filing failed so the papers were lost. On 2026-08-25 it was the second, and nothing on the page said so.arxiv_papers.ymlalready killed this ambiguity on the Slack side — empty days still post a heartbeat, so silence in#papersalways means a broken run. The board had no equivalent.inbox_actions.pynow owns alast digest: <date>line inarxiv-inbox.md, rewritten on every digest run whether or not papers arrive. One line does both jobs the prompt scoped: dated today over an empty inbox it is the quiet-day marker, making absence positive evidence; days old it is the "last filled" signal. Being replaced rather than accumulated, the sweep has nothing to age out — which is what an explicit per-day marker line would have cost.The board now reads:
Two design calls worth a reviewer's attention.
The warning is computed in the browser, not at render time.
knowledge_board.ymlrepublishes on pushes toarxiv-inbox.md— which is precisely what stops happening when filing breaks. The page then freezes at its last good render, so a warning baked in server-side would sit at "0 weekdays" forever and never fire in the one failure it exists for. The rendered date is a fact and survives the freeze; the verdict is recomputed against the reader's clock. A page that was already stale when rendered warns without JS too.The threshold is 2 weekdays, not 1 day. The digest fires at a nominal 02:00 UTC and GitHub cron only ever jitters later, so a board rendered early can see yesterday's stamp with nothing wrong. Weekdays rather than days because the digest is Mon–Fri: a Monday board reading Friday's stamp is three calendar days but zero missed runs.
A missing stamp reads neutrally (
no run recorded yet), never as a warning —spawn.pyemptiesarxiv-inbox.mdfor the template repos, so a freshly spawned Memory has never run a digest and has nothing to be late for.The first commit on this branch is unrelated:
mainhas been red onmake validateandmake testsince498e1a8committed a tracked.claude/without adding it toALLOWED_TOP_DIRS. It is one line, kept as its own commit so it can be read or reverted separately.The companion PyAutoMind PR makes the digest write the stamp on quiet days. Merge this one first —
arxiv_papers.ymlcallsinbox_actions.py stamp, so the other way round leaves a window where the nightly run fails on a subcommand that does not exist yet.API Changes
New public surface in
scripts/inbox_actions.py, all additive —last_digest(),set_last_digest(),weekdays_since(),is_stale(), theINBOX_STALE_WEEKDAYSconstant and astampsubcommand.board.py's snapshot gains one key,inbox_last_digest. Nothing was removed, renamed, or had its signature changed; the inbox line format, the 7-day window and all four transitions are untouched. See full details below.Test Plan
make test— 92 passing, 23 new (was 69)make validate— structure and citation metadata both greenboard.py, not retyped) cross-checked against the Python weekday arithmetic over 30 consecutive stamp dates, weekends included — no mismatchesarxiv_papers.yml: quiet day/same date (no commit), quiet day/new date (heartbeat commit), paper day (paper lands below the stamp), quiet day sweeping lapsed papersFull API Changes (for automation & release notes)
Added
inbox_actions.INBOX_STALE_WEEKDAYS— weekdays of silence before the board calls a digest broken (2)inbox_actions.last_digest(text)— the date of the last recorded digest run, orNoneinbox_actions.set_last_digest(inbox_text, date)— record a run, replacing any existing stampinbox_actions.weekdays_since(stamp, today)— weekdays elapsed, skipping weekendsinbox_actions.is_stale(stamp, today)— the staleness verdict; a missing stamp is never staleinbox_actions.STAMP_RE,inbox_actions.SEPARATOR— the stamp grammar and the header separatorscripts/inbox_actions.py stamp— CLI subcommand, printsstamped:<date>board.collect()→snapshot["inbox_last_digest"], carried through--jsonboard._inbox_freshness(),board._inbox_empty_note()— internal render helpersChanged Behaviour
board.py's inbox section now carries a freshness clause in both the populated and empty branches; the empty branch's wording is no longer a fixed stringdata-last-digest/data-stale-weekdays/data-stale-textand afreshness()script that recomputes the verdict client-sideMigration
None — every addition is new surface. Existing callers of
papers(),append(),sweep(),remove()andadd_to_queue()are unaffected, and an inbox with no stamp parses exactly as before.Generated by the PyAutoLabs agent workflow.
Generated by Claude Code