From 44739f2e06bae12df3cf017adf585a748dd313b7 Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Fri, 4 Sep 2026 15:20:37 -0400 Subject: [PATCH] feat(cortex): the check-in stamp is a box, the map is a map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The board's one freshness fact was a bold line in a paragraph, sitting under two blurbs that repeated what the page already is. It now reads as the first thing on the page and reads on the viewer's own clock. - The check-in stamp is a bordered box: a mark, the label, the stamp at 1.6rem, and a note. The script paints it green (`fresh-ok`, `✓`, `fresh · N min ago`) or red (`fresh-bad`, `✗`, `stale, paste the check-in` / `never checked in`) on load, at 3 h — `CHECKIN_FRESH_MINUTES`, up from an hour. Age is still computed on load and nowhere else: a static page cannot know it is stale at render. The markdown twin renders `### Last check-in: `. - `REFRESH_BLURB` is gone from the Cortex page (the Mind keeps its own): the "only as current as they are" sentence told the reader nothing the box does not now say in their own units. The `Last updated` footnote stays — both twins keep the prefixes `dashboard_body` strips, so `--check` is unchanged. - The Projects blurb is gone too, and every card is wrapped in `
` so a project's name can be a 1.35rem accent-ruled heading rather than the shared card-internal `h3`. Both `h2()` helpers now tolerate an empty blurb with no dangling dash. - Projects with nothing open move out of a fold at the foot of the map and into a `Nothing open` table under the Summary — project, status, phases. Closes #356 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01JYs4cX2wT5vsZScjLNLZif --- agents/conductors/cortex/_cortex.py | 156 +++++++++++++++++----------- tests/test_cortex_conductor.py | 64 ++++++++++-- 2 files changed, 151 insertions(+), 69 deletions(-) diff --git a/agents/conductors/cortex/_cortex.py b/agents/conductors/cortex/_cortex.py index c2d320cd..b69c1e5c 100644 --- a/agents/conductors/cortex/_cortex.py +++ b/agents/conductors/cortex/_cortex.py @@ -227,12 +227,8 @@ def _home_from_git(root: Path) -> str: # One section: (key, title, source path in the repo, blurb). SECTIONS = ( - ("projects", "Projects", "projects.yaml", - "The science body map, active first: where each project lives, what it " - "is holding, and the one phase of it to act on next. The plans and the " - "issues are one click away — this page routes to them rather than " - "reprinting them; `census --by-project` and the check-in printout keep " - "the full per-phase tree."), + # No blurb: the section is the map, and the cards say what they are. + ("projects", "Projects", "projects.yaml", ""), ("awaiting", "Awaiting ruling", "phases/", "Results are in and nothing is running — the human's verdict is the only " "thing outstanding. Ordered failures first, then the phases a ruling is " @@ -251,12 +247,6 @@ def _home_from_git(root: Path) -> str: "Cortex does not exist."), ) -REFRESH_BLURB = ( - "generated from `phases/`, `rulings/` and `projects.yaml`, so it is only " - "as current as they are. `dashboard_refresh.yml` re-renders it on every " - "push to `main`." -) - #: The one-key file the check-in door writes and this page reads back. It #: means *last check-in*, not last render: a doc-only push re-renders the #: board and must not be able to fake freshness. @@ -265,6 +255,11 @@ def _home_from_git(root: Path) -> str: CHECKIN_LABEL = "check in since last time" CHECKIN_NEVER = "never checked in" +#: How old a check-in may be before the board calls it stale, in minutes. It +#: is interpolated into the page's script — never into the rendered stamp, +#: which is a fact and does not age between renders. +CHECKIN_FRESH_MINUTES = 180 + def read_checkin(root: Path) -> tuple[str, list[str]]: """`(stamp, problems)` from `/checkin.yaml`. @@ -950,10 +945,12 @@ def render_dashboard(c: dict) -> str: def h2(key: str) -> list: # Every section links the markdown it is rendered from — the ledger - # file is the record, this page is only the view. - return [f"## {titles[key]}", "", - f"[markdown version]({_src_url(blob, srcs[key])}) — " - f"{blurbs[key]}", ""] + # file is the record, this page is only the view. A section with no + # blurb stops at the link: no dangling dash. + line = f"[markdown version]({_src_url(blob, srcs[key])})" + if blurbs[key]: + line += f" — {blurbs[key]}" + return [f"## {titles[key]}", "", line, ""] L = [ "# PyAutoCortex Dashboard", @@ -973,7 +970,7 @@ def h2(key: str) -> list: "next and what is still gated on development work. The verdict is " "always yours — this page hands you the command, never the ruling.", "", - f"> **Last updated {c['generated']}.** This page is {REFRESH_BLURB}", + f"> **Last updated {c['generated']}.**", "", "| Where | Count |", "|-------|------:|", @@ -990,7 +987,11 @@ def h2(key: str) -> list: # The first row on the page, before any section: the paste that refreshes # everything below it, and the stamp saying when that last happened. L += [_task_row(CHECKIN_LABEL, _checkin_payload(c)), "", - f"**Last check-in:** {c.get('checkin') or CHECKIN_NEVER}", ""] + f"### Last check-in: {c.get('checkin') or CHECKIN_NEVER}", ""] + + # Computed once, read twice: the cards below, and the table of what has + # nothing open that rides under the summary. + shown, folded = by_project_keys(c) L += ["## Summary", "", "One row per active project — the next thing, what it is holding, " @@ -1005,13 +1006,19 @@ def h2(key: str) -> list: L += [""] else: L += ["- _(no active project)_", ""] + if folded: + L += ["#### Nothing open", "", + "| Project | Status | Phases |", "|---|---|---|"] + L += [f"| {_cell(k)} | " + f"{_cell((c['projects'][k].get('status') or '?').strip())} | " + f"{_cell(project_counts(k, c))} |" + for k in project_order(folded, c)] + L += [""] - - # The map, first: active projects, then planned, then dormant. Each card - # is where the project lives, what it holds and the ONE phase to act on + # The map: active projects, then planned, then dormant. Each card is + # where the project lives, what it holds and the ONE phase to act on # next — the plans and the issues ride behind a fold, one click away. L += h2("projects") - shown, folded = by_project_keys(c) for key in project_order(shown, c): row = c["projects"][key] L += [f"### {_summary_label(key)}", ""] @@ -1040,14 +1047,6 @@ def h2(key: str) -> list: if _issues_url(row): links.append(f"[issues]({_issues_url(row)})") L += ["", " · ".join(links), "", "", ""] - if folded: - L += [f"
{len(folded)} project(s) with nothing " - "open", "", - " · ".join( - f"`{_cell(k)}` ({_cell((c['projects'][k].get('status') or '?').strip())}" - f" · {project_counts(k, c)})" for k in project_order(folded, c)), - "", "
", ""] - L += h2("awaiting") awaiting_rows = [] for r in c["awaiting"]: @@ -1116,28 +1115,60 @@ def h2(key: str) -> list: # ------------------------------------------------------------------- html --- -_FRESH_CSS = (".fresh{border-left:3px solid var(--edge);padding:.1rem .9rem;" - "margin:1.2rem 0;background:var(--tint)}" +_FRESH_CSS = (".fresh{padding:.1rem .9rem;margin:1.2rem 0;" + "background:var(--tint)}" ".fresh p{margin:.5rem 0}" "table.map{width:100%;border-collapse:collapse;font-size:.95em}" "table.map th{text-align:left;color:var(--muted);" "font-weight:600}" "table.map td,table.map th{border-bottom:1px solid var(--line);" "padding:.4rem .5rem .4rem 0;vertical-align:top}" - ".checkin{margin:.2rem 0 1.4rem}" + # The one thing a slot has to know before it reads anything + # else, so it is a box and not a line. The mark, the note and + # the two tone classes are painted by the script on load. + ".checkin{display:flex;gap:.9rem;align-items:center;" + "padding:.9rem 1.2rem;border:2px solid var(--line);" + "border-radius:.6rem;margin:.2rem 0 1.4rem}" + ".checkin .mark{font-size:2rem;line-height:1}" + ".checkin .label{display:block;font-size:.75rem;" + "letter-spacing:.06em;text-transform:uppercase;" + "color:var(--muted)}" + ".checkin time{font-size:1.6rem;font-weight:700;" + "font-variant-numeric:tabular-nums;display:block}" + ".checkin .note{font-size:.85rem;color:var(--muted)}" + ".checkin.fresh-ok{border-color:var(--ok);" + "background:color-mix(in srgb,var(--ok) 12%,transparent)}" + ".checkin.fresh-ok .mark{color:var(--ok)}" + ".checkin.fresh-bad{border-color:var(--bad);" + "background:color-mix(in srgb,var(--bad) 12%,transparent)}" + ".checkin.fresh-bad .mark{color:var(--bad)}" + # A project name is what a thumb scrolls for; the shared h3 is + # sized for a card's inner headings, so the map overrides it. + ".project h3{font-size:1.35rem;font-weight:700;" + "padding-bottom:.2rem;border-bottom:2px solid var(--accent);" + "letter-spacing:.01em;margin-top:1.6rem}" ".paths{margin:.2rem 0}.paths b{display:inline-block;" "min-width:3.6rem}" ".stale{color:var(--bad);font-weight:600}") # The page is static, so a stamp is never stale *at render* — it is stale on -# the reader's clock, an hour later, on a different day. So the age is -# computed on load, here, and nowhere else. +# the reader's clock, 3 h later, on a different day. So the age is computed +# on load, here, and nowhere else: the box is neutral until this runs. _CHECKIN_JS = ( - "(function(){var e=document.getElementById('checkin');if(!e)return;" + "(function(){var e=document.getElementById('checkin')," + "b=document.getElementById('checkin-box')," + "m=document.getElementById('checkin-mark')," + "n=document.getElementById('checkin-note');if(!e||!b)return;" "var t=e.getAttribute('datetime'),d=t?new Date(t):null;" - "if(!d||isNaN(d.getTime())||(Date.now()-d.getTime())/60000>60){" - "e.classList.add('stale');" - "e.textContent+=' \\u00b7 stale, paste the check-in';}})();") + "if(!d||isNaN(d.getTime())||(Date.now()-d.getTime())/60000>" + f"{CHECKIN_FRESH_MINUTES}){{" + "b.classList.add('fresh-bad');e.classList.add('stale');" + "if(m)m.textContent='✗';" + "if(n)n.textContent=t?'stale, paste the check-in':'never checked in';" + "}else{b.classList.add('fresh-ok');if(m)m.textContent='✓';" + "var a=Math.round((Date.now()-d.getTime())/60000);" + "if(n)n.textContent='fresh · '+(a<60?a+' min':Math.round(a/60)+' h')" + "+' ago';}})();") def render_dashboard_html(c: dict) -> str: @@ -1154,8 +1185,10 @@ def link(path, text_html): def h2(key): a = (f' ' "markdown version") if blob else "" - return (f'

{titles[key]}{a}

' - f'

{_md_inline(blurbs[key])}

') + head = f'

{titles[key]}{a}

' + if not blurbs[key]: + return head + return head + f'

{_md_inline(blurbs[key])}

' def phase_head(r, tone_pills=()): head = link(r["rel"], _summary_label(r["title"])) @@ -1191,8 +1224,8 @@ def phase_head(r, tone_pills=()): ] # One line, deliberately: the `--check` normaliser drops it whole so a # date change is not drift. - H += [f'

Last updated {c["generated"]}. This ' - f'page is {_md_inline(REFRESH_BLURB)}

'] + H += [f'

Last updated ' + f'{c["generated"]}.

'] if home: H.append(f'

' f'{link("dashboard.md", "markdown version")} · ' @@ -1205,13 +1238,20 @@ def phase_head(r, tone_pills=()): # The first button on the page, before any section: the paste that # refreshes everything below it, then the stamp saying when that last - # happened. The script at the bottom reddens the stamp on the viewer's - # own clock. + # happened. The script at the bottom marks the box green or red on the + # viewer's own clock, 3 h being the line between them. stamp = c.get("checkin") or "" H += [_html_task(_summary_label(CHECKIN_LABEL), _checkin_payload(c)), - f'

Last check-in: ' + f'

' + f'
' + f'Last check-in' f'

'] + f'{_summary_label(stamp or CHECKIN_NEVER)}' + f'
'] + + # Computed once, read twice: the cards below, and the table of what has + # nothing open that rides under the summary. + shown, folded = by_project_keys(c) H += ['

Summary

', '

One row per active project — the next thing, what ' @@ -1228,12 +1268,19 @@ def phase_head(r, tone_pills=()): H.append("") else: H.append('

(no active project)

') - + if folded: + H += ["

Nothing open

", '', + ""] + H += [f"" + for k in project_order(folded, c)] + H.append("
ProjectStatusPhases
{_summary_label(k)}" + f"{_summary_label((c['projects'][k].get('status') or '?').strip())}" + f"{_summary_label(project_counts(k, c))}
") H.append(h2("projects")) - shown, folded = by_project_keys(c) for key in project_order(shown, c): row = c["projects"][key] + H.append('
') H.append(f"

{_summary_label(key)}

") H += [f'

{label} ' f"{_summary_label(path)}

" @@ -1244,7 +1291,7 @@ def phase_head(r, tone_pills=()): nxt = next_open_phase(key, c) if nxt is None: H.append('

(nothing open — every phase of this ' - "project is history)

") + "project is history)

") continue rest = rest[1:] text = phase_head(nxt, ((nxt["state"], "n"),)) @@ -1270,14 +1317,7 @@ def phase_head(r, tone_pills=()): if _issues_url(row): links.append(f'issues') H.append("

" + " · ".join(links) + "

") - if folded: - H.append('

' - + f"{len(folded)} project(s) with nothing open: " - + " · ".join( - f"{_summary_label(k)} " - f"({_summary_label((c['projects'][k].get('status') or '?').strip())}" - f" · {_summary_label(project_counts(k, c))})" - for k in project_order(folded, c)) + "

") + H.append("
") H.append(h2("awaiting")) for r in c["awaiting"]: diff --git a/tests/test_cortex_conductor.py b/tests/test_cortex_conductor.py index 11a175b0..4c039396 100644 --- a/tests/test_cortex_conductor.py +++ b/tests/test_cortex_conductor.py @@ -265,14 +265,14 @@ def test_the_checkin_chip_is_the_first_task_row_on_both_twins(skeleton): def test_the_stamp_is_read_from_checkin_yaml_and_says_never_without_it(tmp_skeleton): c = _cortex.census(tmp_skeleton) assert c["checkin"] == "" and c["problems"] == [] - assert f"**Last check-in:** {_cortex.CHECKIN_NEVER}" in \ + assert f"### Last check-in: {_cortex.CHECKIN_NEVER}" in \ _cortex.render_dashboard(c) assert _cortex.CHECKIN_NEVER in _cortex.render_dashboard_html(c) _cortex.write_checkin(tmp_skeleton, "2026-09-04T14:03:00Z") c = _cortex.census(tmp_skeleton) assert c["checkin"] == "2026-09-04T14:03:00Z" - assert "**Last check-in:** 2026-09-04T14:03:00Z" in _cortex.render_dashboard(c) + assert "### Last check-in: 2026-09-04T14:03:00Z" in _cortex.render_dashboard(c) assert ('