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
11 changes: 6 additions & 5 deletions agents/conductors/cortex/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ note`).
| `checkin [--dry-run \| --apply] [--push \| --no-push] [--project KEY]` | **Where is my science?** | the door — the sequence below, ending in a summary keyed by project; reaches no cluster; exit **1** when the tree does not check |
| `census [--json]` | What is the Cortex holding? | per project: status, summary, runs by state, log length, last update; totals (active projects, runs open, runs running); the check-in stamp; the problems `cortex.py check` would report |
| `dashboard --check` | Are the committed pages current? | exit 0 current · **1 stale** · 2 no checkout · 3 unreadable tree |
| `dashboard --apply` | — | writes `dashboard.md` + `dashboard.html` + `state.json` — the organ-cockpit feed (`board/_state.py` v1): red on `cortex.py check` problems, yellow while a run is running or the check-in is stale (the page script's 180-min rule, judged at render), items carry `checkin_payload` / `resume_payload`; `--check` ignores its `updated` |
| `dashboard --apply` | — | writes `dashboard.md` + `dashboard.html` + `state.json` — the organ-cockpit feed (`board/_state.py` v1): red on `cortex.py check` problems, yellow while a run is running or the check-in is stale (the feed's 180-min rule, judged at render), items carry `checkin_payload` / `resume_payload`; `--check` ignores its `updated` |
| `issue [--project KEY] [--apply]` | What sits at the top of each project's issue? | the fenced ledger block (`cortex.py`'s `issue_block`) per project with an `Issue:`; `--apply` writes it into the issue body through `gh` (replacing the block between the markers, or prepending it); never creates an issue; exit **1** without `gh` |

```
Expand Down Expand Up @@ -78,8 +78,8 @@ The page opens on the door: a counts table (`Running`, `Open`, `Projects` —
what `board/_board.py` reads for the Brain board's Cortex strip), the
**check-in chip** — two lines, because the check-in has two halves that do
not run on the same machine: `pyauto-brain cortex pull` on the laptop, then
`pyauto-brain cortex checkin --apply --push` anywhere — and the **last
check-in** stamp under it. Then **Summary**: one row per active project, four
`pyauto-brain cortex checkin --apply --push` anywhere — and the shared
**Last updated / Update** footer. Then **Summary**: one row per active project, four
narrow columns (`Project | Running | Open | Last update`) so it fits a phone.
Then **Projects**: a `### <key> — <summary>` card per project with a ledger —
active first in `projects.yaml` order, then planned/dormant, with the retired
Expand All @@ -98,8 +98,9 @@ chips anywhere — retiring is `scripts/cortex.py retire`, a typed verb.
**`checkin.yaml`** is the one-key file (`refreshed: <UTC ISO 8601>`) the
door writes before rendering. `census()` reads it into `c["checkin"]`;
missing means "never checked in", unparseable is a `problems` line. The
stamp means *last check-in*, never last render, and the HTML twin computes
its age on the **viewer's** clock and reddens it once stale.
stamp means *last check-in*, never last render. The cockpit feed keeps its
180-minute check-in freshness rule; the HTML uses the shared panel refresh
footer and omits the old standalone check-in widget.

`--check` compares the pages with the generation comment **and** the visible
`Last updated` banner stripped, so a re-render on a new date is not drift.
Expand Down
50 changes: 6 additions & 44 deletions agents/conductors/cortex/_cortex.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,39 +595,11 @@ def render_dashboard(c: dict) -> str:
"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{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)}"
".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}"
".pathchip{display:inline-block;background:var(--accent);color:#fff;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.88em;padding:.12em .5em;border-radius:.3rem;overflow-wrap:anywhere}"
"@media(prefers-color-scheme:dark){.pathchip{background:color-mix(in srgb,var(--accent) 55%,#000)}}"
".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, 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'),"
"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>"
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:
"""The one-tap-copy twin GitHub Pages serves — same content, real buttons."""
Expand Down Expand Up @@ -656,32 +628,23 @@ def render_dashboard_html(c: dict) -> str:
{"label": "Running", "href": "#summary", "count": n["running"]},
{"label": "Open", "href": "#summary", "count": n["open"]},
{"label": "Projects", "href": "#projects", "count": n["active"]},
{"label": "Check in", "href": "#checkin-box"},
{"label": "Check in", "href": "#orchestration-cortex"},
]),
orchestration_panel(
"cortex", "Check in on the science",
"Review projects and runs together, then record what you have learned.",
checkin_payload(c), work_links=work_links, copy_label=CHECKIN_LABEL, organ="cortex",
checkin_payload(c), work_links=work_links, organ="cortex",
refreshed_at=c.get("refreshed_at") if not c.get("problems") else None,
refresh_url=home + "/actions/workflows/dashboard_refresh.yml" if home else None),
]
if home:
H.append(f'<p class="muted mdsrc"><a href="{_attr(blob + "dashboard.md")}">'
f'markdown version</a> · <a href="{_attr(blob + "README.md")}">'
"GitHub Page</a></p>")
f'markdown version</a></p>')
if c["problems"]:
H += ['<p>⚠️ <b>The tree does not check</b> — '
"<code>scripts/cortex.py check</code> reports:</p>", "<ul>"]
H += [f"<li><code>{_esc(p)}</code></li>" for p in c["problems"][:10]]
H += ["</ul>"]
stamp = c.get("checkin") or ""
H += [f'<div class="checkin" id="checkin-box">'
f'<span class="mark" id="checkin-mark"></span><div>'
f'<span class="label">Last check-in</span>'
f'<time id="checkin" datetime="{_attr(stamp)}">'
f'{_esc(stamp or CHECKIN_NEVER)}</time>'
f'<span class="note" id="checkin-note"></span></div></div>']

H += ['<a id="summary"></a><h2>Summary</h2>', '<table class="map">',
"<tr><th>Project</th><th>Running</th><th>Open</th>"
"<th>Last update</th></tr>"]
Expand Down Expand Up @@ -720,8 +683,7 @@ def render_dashboard_html(c: dict) -> str:
footer = boards_footer(dict(_board_links(home, THEME_ORGAN)), THEME_ORGAN)
if footer:
H.append(footer)
H += [f"<script>{_THEME_JS}</script>",
f"<script>{_CHECKIN_JS}</script>", "</body>", "</html>"]
H += [f"<script>{_THEME_JS}</script>", "</body>", "</html>"]
return section_layout("\n".join(H) + "\n")


Expand Down Expand Up @@ -752,9 +714,9 @@ def _now() -> _dt.datetime:


def checkin_stale(stamp: str, now: _dt.datetime | None = None) -> bool:
"""The page script's freshness rule (`_CHECKIN_JS`), in Python: a missing
"""The cockpit feed's check-in freshness rule: a missing
or unparseable stamp, or one older than CHECKIN_FRESH_MINUTES, is stale.
The page judges on the reader's clock at load; the feed judges at render."""
The feed judges at render; the panel refresh clock is independent."""
if not stamp:
return True
try:
Expand Down
7 changes: 2 additions & 5 deletions board/_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -1612,13 +1612,10 @@ def render_html(data):
# page. `–` where a source was unreachable: an absent count is never a
# zero here, the same contract the rows keep.
# The sibling boards' header line: the markdown twin (written beside
# index.html by --apply, so the relative href resolves on Pages) and the
# way back to the repository front door on github.com.
# index.html by --apply, so the relative href resolves on Pages).
if data.get("org") and data.get("repo"):
gh = (f'https://github.com/{data["org"]}/{data["repo"]}'
"/blob/main/README.md")
H.append('<p class="muted mdsrc"><a href="board.md">markdown '
f'version</a> · <a href="{_attr(gh)}">GitHub Page</a></p>')
f'version</a></p>')
H.append("<a id=\"morning\"></a><h2>⌨ Morning sync (local)</h2>")
H.append(_row(
"Sync every repo to main + clean generated cruft — run in a terminal "
Expand Down
6 changes: 5 additions & 1 deletion board/_theme.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,8 +929,12 @@ def orchestration_panel(key, title, description, prompt, *, work_links=(),
# Labels are also plain text in the prompt; disallow line injection.
if not label.strip() or any(c in label for c in "\r\n"):
raise ValueError("Work destination labels must be nonempty, single-line text")
# Repository buttons use the destination's name; companion surfaces
# such as the organization Community Hub keep their descriptive label.
parts = parsed.path.strip("/").split("/")
display_label = parts[1] if len(parts) >= 2 and parts[0] not in {"orgs", "users"} else label
links.append(f'<a href="{_html.escape(url, quote=True)}">'
f'{_html.escape(label)}</a>')
f'{_html.escape(display_label)}</a>')
context.append(f"- {label}: {url}")
base = portable_prompt(prompt).replace("\r\n", "\n").replace("\r", "\n")
if context:
Expand Down
9 changes: 8 additions & 1 deletion docs/board-orchestration.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ truncate words. Omit explanatory subtitles and preserve work links/copy controls

`work_links` is an ordered list of mappings with `label` and `href`. Destinations
must be HTTPS GitHub links. These owner-supplied links appear beside the controls
and are appended under **Work on GitHub** in the portable prompt. They are fixed
and are appended under **Work on GitHub** in the portable prompt. Repository
buttons display the repository name from the destination URL, without the owner
prefix or “Open”/“repository” filler. Non-repository companion destinations keep
their supplied labels; portable prompt metadata retains its original labels.
Every primary panel copy button uses **Copy check-in prompt**. They are fixed
metadata; typing a focus does not rewrite them. Avoid putting the same URL list
in the base prompt as well. Missing metadata displays an explicit unavailable
message, never an invented destination.
Expand Down Expand Up @@ -61,6 +65,9 @@ Different panels must not overwrite one another's input, preview or feedback.

## Last updated footer

Omit redundant **GitHub Page** links and the legacy standalone Cortex
last-check-in/stale widget. Keep underlying check-in records and feed semantics.

Every panel has the same small footer beneath the copy controls: **Read the
prompt** on the left, **Last updated …** and **↻ Update** on the right. It wraps
below the preview control on narrow screens. Brain owns the markup, typography,
Expand Down
6 changes: 2 additions & 4 deletions tests/test_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,11 +541,9 @@ def test_html_is_self_contained_with_one_tap_payloads(tmp_path):
assert 'data-cmd="bash PyAutoBrain/bin/morning.sh"' in page
# The doors roster is on the page.
assert 'data-cmd="Use the intake skill."' in page
# The header line every sibling board carries: the markdown twin and the
# way back to the repository front door on github.com.
# Keep the Markdown source without the redundant repository link.
assert '<a href="board.md" class="board-source-link" title="Markdown version" aria-label="Markdown version"><svg' in page
assert ('<a href="https://github.com/ExampleOrg/PyAutoBrain/blob/main/'
'README.md">GitHub Page</a>') in page
assert 'GitHub Page</a>' not in page


# --------------------------------------------------------------- the look --
Expand Down
3 changes: 2 additions & 1 deletion tests/test_board_theme.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,11 +468,12 @@ def test_orchestration_panel_keeps_work_links_in_visible_and_portable_context():
work_links=[{'label': 'Open work repo', 'href': 'https://github.com/Example/Work'},
{'label': 'Community hub', 'href': 'https://github.com/orgs/Example/discussions'}])
assert 'Review &lt;work&gt;' in page and 'A &amp; B' not in page
assert '<a href="https://github.com/Example/Work">Open work repo</a>' in page
assert '<a href="https://github.com/Example/Work">Work</a>' in page
preview = unescape(re.search(r'data-orchestration-prompt[^>]*>(.*?)</textarea>', page, re.S)[1])
assert preview == ('Use the board skill. check\n\nWork on GitHub:\n'
'- Open work repo: https://github.com/Example/Work\n'
'- Community hub: https://github.com/orgs/Example/discussions')
assert '<a href="https://github.com/orgs/Example/discussions">Community hub</a>' in page
assert 'readonly' in page and 'aria-live="polite"' in page


Expand Down
7 changes: 5 additions & 2 deletions tests/test_cortex_conductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,11 @@ def test_the_html_twin_wears_the_theme_with_real_copy_buttons(skeleton):
assert "On the laptop, use the Cortex pull procedure" in html
assert "Follow the Cortex check-in procedure to refresh the board" in html
assert "Submit compute only when I explicitly ask" in html
assert '<time id="checkin" datetime="2026-09-02T09:00Z">2026-09-02T09:00Z</time>' in html
assert "fresh-bad" in html and _cortex._CHECKIN_JS in html
assert 'id="checkin-box"' not in html
assert 'stale, paste the check-in' not in html
assert 'Copy check-in prompt' in html
assert 'data-freshness-stamp' in html
assert 'GitHub Page</a>' not in html
assert "<span class=\"board-nav-count\">1</span><span class=\"board-nav-label\">Running</span>" in html
assert "<span class=\"board-nav-count\">1</span><span class=\"board-nav-label\">Open</span>" in html
assert "<span class=\"board-nav-count\">2</span><span class=\"board-nav-label\">Projects</span>" in html
Expand Down
Loading