Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 22 additions & 32 deletions scripts/board.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
#: holds everything that was missed — git history does, which is exactly what
#: ``scripts/catch_up.py`` reads back.
CATCH_UP_DAYS = 7
CATCH_UP_LYRIC = "…so… I've been lost for a while" # Fred again..
CATCH_UP_CMD = "Read PyAutoMemory/skills/catch_up/SKILL.md and follow the catch_up skill with arguments: lensing."

# The family look lives once, in the Brain (``board/_theme.py``): the
Expand Down Expand Up @@ -701,27 +700,23 @@ def _catch_up_due(snapshot: dict) -> int | None:
return model["age_days"] if model["state"] == "stale" else None


def _catch_up_md(snapshot: dict) -> list[str]:
def _catch_up_summary(snapshot: dict) -> str:
model = _lensing_catch_up(snapshot)
if model["state"] == "healthy":
return []
title = "Strong-lensing catch-up due" if model["state"] == "stale" else "Strong-lensing freshness unknown"
return [f"> **{title}**", f"> {model['reason']}",
f"> Next: `{CATCH_UP_CMD}` — human-reviewed paper selection.", ""]
if model["state"] == "unknown":
return "Catch-up start date unknown."
due = model["due_at"][:10]
status = "due" if model["state"] == "stale" else "next due"
return f"Catch up from {model['last_activity']} · {status} {due}."


def _catch_up_md(snapshot: dict) -> list[str]:
return ["## Catch up", "", _catch_up_summary(snapshot), "",
f"`{CATCH_UP_CMD}`", ""]


def _catch_up_html(snapshot: dict) -> str:
model = _lensing_catch_up(snapshot)
if model["state"] == "healthy":
return ""
title = "Strong-lensing catch-up due" if model["state"] == "stale" else "Strong-lensing freshness unknown"
lyric = (f"<p class='lyric'><em>{_html.escape(CATCH_UP_LYRIC)}</em></p>"
if model["state"] == "stale" else "")
return (f"<section class='catchup'>{lyric}<p><strong>{title}</strong></p>"
f"<p>{_html.escape(model['reason'])}</p>"
f"<p><code>{_html.escape(CATCH_UP_CMD)}</code> "
f"{_copy_btn(CATCH_UP_CMD, 'copy: catch up on strong lensing')} "
"<span class='muted'>Human-reviewed paper selection</span></p></section>")
return (f"<p>{_html.escape(_catch_up_summary(snapshot))}</p>"
f"{_copy_btn(CATCH_UP_CMD, 'Copy catch-up prompt')}")


def _totals(snapshot: dict) -> dict:
Expand Down Expand Up @@ -1163,9 +1158,6 @@ def _bar(statuses: dict) -> str:
color:var(--bg);opacity:0;transition:opacity .2s;pointer-events:none;z-index:9}
#toast.show{opacity:1}
#toast.bad{background:var(--warn)}
.catchup{border-left:3px solid var(--warn);padding:.4em .9em;margin:0 0 1em}
.catchup p{margin:.25em 0}
.catchup .lyric{color:var(--warn)}
"""

# The shared copy handler is delegated, so a chip inside a <summary> would
Expand Down Expand Up @@ -1592,9 +1584,7 @@ def _fresh_span(fresh: dict, now_text: str, stale_text: str,
filing_items.append(
f"<li>{issue_link} <span class='meta'>"
f"{_html.escape(f['branch'])}</span>{pr}</li>")
# Above everything else when present, absent otherwise: it is the one
# section that is a human's merge, and an empty "nothing waiting" line
# would only teach the eye to skip it.
# Keep pending filings in their own navigable section when present.
filings_block = (
f"<h2 id='filings'>Filings awaiting merge <span class=\"muted\">({len(filing_items)} "
f"on a branch, not yet in memory — merge the PR to finish the "
Expand All @@ -1608,10 +1598,9 @@ def _fresh_span(fresh: dict, now_text: str, stale_text: str,
{"href": "#citation-work", "label": "To cite", "count": t["todo"]},
{"href": "#sub-wikis", "label": "Pages", "count": t["pages"], "context": f"{pct}% cited"},
]
if _catch_up_html(snapshot):
navigation.insert(0, {"href": "#catch-up", "label": "Catch up"})
navigation.append({"href": "#catch-up", "label": "Catch up"})
if filing_items:
navigation.insert(0, {"href": "#filings", "label": "Filings awaiting merge", "count": len(filing_items)})
navigation.append({"href": "#filings", "label": "Filings awaiting merge", "count": len(filing_items)})
hero = t_.hero(BOARD_KEY, "Dashboard", _LEDE, navigation=navigation)
repo_url = _repo_url(snapshot)
panel = t_.orchestration_panel(
Expand All @@ -1637,12 +1626,9 @@ def _fresh_span(fresh: dict, now_text: str, stale_text: str,
<body{body_attrs}>
{hero}
{panel}
<section id="catch-up">{_catch_up_html(snapshot)}</section>
<p class="muted mdsrc"><a href="dashboard.md">markdown version</a>{onetap}</p>
<div id="toast"></div>
{filings_block}
<h2 id='arxiv-inbox'>arXiv inbox <span class="muted">(suggested overnight — un-acted papers
lapse after {inbox_actions.INBOX_WINDOW_DAYS} days)</span></h2>
<div class="inbox-tools">{onetap.removeprefix(" · ")}</div>
{inbox_block}
<h2 id='arxiv-interests'>arXiv interests <span class="muted">(everything that is not strong
lensing — one day's ten at a time; \U0001f9f9 clears the day and shows the
Expand All @@ -1658,8 +1644,12 @@ def _fresh_span(fresh: dict, now_text: str, stale_text: str,
<h2 id='sub-wikis'>Sub-wikis <span class="muted">({snapshot.get('bib_entries', 0)} bibliography
entries · {snapshot.get('links', {}).get('wanted', 0)} wanted pages)</span></h2>
<table class="recent">{''.join(wiki_rows)}</table>
<h2 id="catch-up">Catch up</h2>
{_catch_up_html(snapshot)}
{filings_block}
{_boards_nav(snapshot)}
<footer>Rendered by <code>scripts/board.py</code> from the checkout —
<div id="toast"></div>
<footer><a href="dashboard.md">markdown version</a> · Rendered by <code>scripts/board.py</code> from the checkout —
nothing here is committed; generated
{_html.escape(str(snapshot.get('generated') or '?'))}.</footer>
<script>{t_.JS}{_EXTRA_JS}</script>
Expand Down
42 changes: 27 additions & 15 deletions tests/test_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,8 +752,8 @@ def boom(*a, **k):
assert board._pending_filings() == []


def test_pending_filings_render_above_the_inbox_with_a_pr_link(tmp_path):
"""A filing on a branch is not in memory: the board says so, first, and
def test_pending_filings_render_in_their_own_section_with_a_pr_link(tmp_path):
"""A filing on a branch is not in memory: the board says so and
hands out the one tap that finishes it."""
snap = _snap_with_remote(tmp_path)
snap["filings"] = [{"issue": 91, "branch": "queue-filing/issue-91"}]
Expand All @@ -762,7 +762,7 @@ def test_pending_filings_render_above_the_inbox_with_a_pr_link(tmp_path):
assert "compare/main...queue-filing/issue-91?expand=1" in text
assert "/issues/91" in text
if '<!doctype html>' in text:
assert text.index("id='filings'") < text.index("id='arxiv-inbox'")
assert text.index("id='filings'") > text.index("id='arxiv-inbox'")
else:
assert text.index("Filings awaiting merge") < text.index("arXiv inbox")

Expand Down Expand Up @@ -946,41 +946,41 @@ def test_last_ingested_is_collected_from_a_scope_section(tmp_path):
assert board.collect(root)["last_ingested"] == "2026-09-01"


def test_no_banner_under_the_threshold(tmp_path):
def test_catchup_remains_available_under_the_threshold(tmp_path):
snap = _banner_snap(tmp_path, "2026-09-24") # 6 days
assert board._days_since_ingest(snap) == 6
assert "lost for a while" not in board.render(snap, "md")
assert "class='catchup'" not in board.render(snap, "html")
assert "Catch up from 2026-09-24 · next due 2026-10-01." in board.render(snap, "html")
assert 'href="#catch-up"' in board.render(snap, "html")
assert board.to_state(snap)["days_since_ingest"] == 6


def test_banner_at_the_threshold_carries_the_day_count(tmp_path):
snap = _banner_snap(tmp_path, "2026-09-23") # exactly CATCH_UP_DAYS
assert board.CATCH_UP_DAYS == 7
md = board.render(snap, "md")
assert "**Strong-lensing catch-up due**" in md
assert "2026-09-23 (7 days ago)" in md
assert "after 7 days" in md
assert "## Catch up" in md
assert "Catch up from 2026-09-23 · due 2026-09-30." in md
assert "`Read PyAutoMemory/skills/catch_up/SKILL.md and follow the catch_up skill with arguments: lensing.`" in md
# Top of the markdown: before the contents line.
assert md.index("Strong-lensing catch-up due") < md.index("_Contents")
assert md.index("## Catch up") < md.index("_Contents")


def test_catchup_html_follows_banner_and_navigation_with_a_copy_button(tmp_path):
snap = _banner_snap(tmp_path, "2026-09-11")
page = board.render(snap, "html")
assert "<em>…so… I&#x27;ve been lost for a while</em>" in page
assert "2026-09-11 (19 days ago)" in page
assert "lost for a while" not in page
assert "Catch up from 2026-09-11 · due 2026-09-18." in page
assert 'data-cmd="Read PyAutoMemory/skills/catch_up/SKILL.md and follow the catch_up skill with arguments: lensing."' in page
assert page.index('class="hero"') < page.index('class="board-nav"') < page.index("class='catchup'")
assert page.index('class="hero"') < page.index('class="board-nav"') < page.index('id="catch-up"')
assert board.to_state(snap)["days_since_ingest"] == 19


def test_unknown_banner_when_nothing_is_on_record(tmp_path):
snap = _banner_snap(tmp_path, None)
assert board._days_since_ingest(snap) is None
assert "lost for a while" not in board.render(snap, "md")
assert "Strong-lensing freshness unknown" in board.render(snap, "html")
assert "Catch-up start date unknown." in board.render(snap, "html")
assert board.to_state(snap)["days_since_ingest"] is None


Expand All @@ -998,8 +998,8 @@ def test_lensing_catch_up_threshold_has_one_model_for_every_surface(tmp_path, ag
assert not rows
else:
assert len(rows) == 1 and rows[0]["reason"] == model["reason"]
assert model["reason"] in board.render(snap, "md")
assert model["reason"] in board.render(snap, "html")
assert board._catch_up_summary(snap) in board.render(snap, "md")
assert board._catch_up_summary(snap) in board.render(snap, "html")
assert rows[0]["actions"][0]["target"] == "Read PyAutoMemory/skills/catch_up/SKILL.md and follow the catch_up skill with arguments: lensing."
assert rows[0]["actions"][0]["safety"] == "scientific_judgement"
assert "requires_human_decision" not in rows[0] # No invented choice.
Expand Down Expand Up @@ -1182,3 +1182,15 @@ def test_panel_refresh_uses_capture_not_ingest_date(tmp_path, monkeypatch):
board.render(snap, "html")
assert calls[0]["refreshed_at"] == snap["generated"]
assert calls[0]["refresh_url"] == "https://github.com/SomeOrg/MemoryWork/actions/workflows/knowledge_board.yml"


def test_inbox_is_first_content_after_checkin(tmp_path):
page = board.render(_snap_with_remote(tmp_path), "html")
between = page.split('id="orchestration-memory"', 1)[1].split("id='arxiv-inbox'", 1)[0]
assert 'id="catch-up"' not in between
assert "mdsrc" not in between
assert "id='onetap'" not in between
assert "id='filings'" not in between
assert page.index("id='arxiv-inbox'") < page.index("id='onetap'") < page.index("id='arxiv-interests'")
assert "<code>Read PyAutoMemory/skills/catch_up" not in page
assert "Copy catch-up prompt" in page
Loading