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
14 changes: 13 additions & 1 deletion agents/conductors/profiling/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,19 @@ as malformed — only a record missing *some* of its key fields is corruption.
(`triage`) stays the only judge of what a timing or a drift means, with the
human. The projects keep their producers, results, pins and drift policy.
Same split as Heart ↔ vitals and Eyes ↔ the Eyes conductor. (Organ row
born in PyAutoMind#463; its reader and board arrive in phase 2.)
born in PyAutoMind#463; its reader and board arrived in phase 2.)
- **Where drift candidates are read** — the
[Pulse board](https://pyautolabs.github.io/PyAutoPulse/) (strip on the
Brain board's Resume section, card on the organ cockpit) is where
cross-project drift candidates and their provenance (the resolved commit
per project, the pair of records, the evidence paths) are read.
`autolens_profiling` owns the measurement semantics: its producers, pins
and drift policy. A `/profiling triage <comparison_key>` prompt copied from
a Pulse drift item names one comparison row on the Pulse board: open that
row, read the pair of records and their evidence paths, then judge with the
`triage` verb. The key is a pointer for the human/agent reading the board —
`triage` itself takes no target argument and still sweeps every pinned
finding.
- **vs build** — campaigns are not releases; `profile.yml`'s on-release runs
stay CI/Build territory.
- **release-validation script cost is NOT ours.** The compile axis was
Expand Down
2 changes: 1 addition & 1 deletion board/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ copy-for-Claude payload:
| ⏱ Test performance | the Heart board's published `performance` block — rendered verbatim, never re-derived | each row's own prompt |
| 🏷️ Version consistency | the coupled-set stamps (`board: version_stamps`) | `/bug version drift: …` |
| 💬 Community | the Ears (`community scan`, reused wholesale) — every open conversation gets a row | `/community`, `/community triage <ref>` |
| 🔄 Resume | the Mind's registry + generated counts; pending-release PRs | `/start_dev …`, `/prm <url>` |
| 🔄 Resume | the Mind's registry + generated counts; the Cortex, Eyes and Pulse strips (each organ's own `dashboard.md` head counts, display-only, never in `state.json`); pending-release PRs | `/start_dev …`, `/prm <url>` |
| 🧹 Upkeep | open-issue count; the cleanup doors | `/issue_cleanup`, `/hygiene`, `/repo_cleanup` |
| 🧼 Hygiene | the hygiene conductor's own `--json` pre-scan, run IN this render (BOARD_HYGIENE_SCAN=1; brain_board.yml checks out the body-map scan set blobless+sparse first) — no machine involved | each row's own delegate door |
| 🖥️ Dev box | `state/devbox_board.json` — worktree state (unpushed/dirty/stashes — the one thing only the dev box can see; its hygiene rows render only when no cloud scan ran), pushed by `board publish` (morning.sh's last step); age-stamped, stale at 48h, dropped after 7d | — |
Expand Down
29 changes: 29 additions & 0 deletions board/_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,21 @@ def collect_eyes(repo="PyAutoEyes"):
return _dashboard_counts(repo, head_only=True)


def collect_pulse(repo="PyAutoPulse"):
"""The Pulse's own generated counts (projects, records, comparisons,
drifted, refused pairs, cached, failed — the counts table at the head of
its `dashboard.md`), or None.

Composed exactly as the Eyes strip is: the organ's renderer decides the
numbers, the Brain board only shows them — a drifted count is the
producer's say-so, never a Brain verdict (judgment is the profiling
conductor's `triage`). Only the head of the page is read, because the
body carries one table per project. No Pulse checkout, or no rendered
board, is not a degraded morning either — the strip just does not appear.
"""
return _dashboard_counts(repo, head_only=True)


def collect_open_issues(org, degraded):
"""Total open issues across the org — the /issue_cleanup pointer count
(the audit itself stays that skill's confirmation-gated job)."""
Expand Down Expand Up @@ -932,6 +947,7 @@ def collect():
resume = collect_resume(org, degraded)
cortex = collect_cortex()
eyes = collect_eyes(board_family.get("eyes", "PyAutoEyes"))
pulse = collect_pulse(board_family.get("pulse", "PyAutoPulse"))
open_issues = collect_open_issues(org, degraded)
boards = {name: f"{pages_base}/{repo}/"
for name, repo in board_family.items()}
Expand All @@ -952,6 +968,7 @@ def collect():
"resume": resume,
"cortex": cortex,
"eyes": eyes,
"pulse": pulse,
"open_issues": open_issues,
"hygiene": collect_hygiene(degraded),
"devbox": collect_devbox(),
Expand Down Expand Up @@ -1343,6 +1360,11 @@ def render_md(data):
+ " · ".join(f"{k.lower()} {n}"
for k, n in data["eyes"].items())
+ f" — [Eyes board]({data['boards'].get('eyes', '')})")
if data.get("pulse"):
L.append("- Pulse: "
+ " · ".join(f"{k.lower()} {n}"
for k, n in data["pulse"].items())
+ f" — [Pulse board]({data['boards'].get('pulse', '')})")
for t in data["resume"]["tasks"]:
L.append(f" - `Use the start-dev skill. {t['path']}` — {t['title'][:70]}")
pending = data["resume"]["pending_prs"]
Expand Down Expand Up @@ -1727,6 +1749,13 @@ def community_row(e, note, tone):
"board \u2197</a>"
+ pills(*[(f"{k.lower()} {n}", "" if i == 0 else "n")
for i, (k, n) in enumerate(data["eyes"].items())])))
if data.get("pulse"):
pulse_url = data["boards"].get("pulse", "")
H.append(_plain(
f'Run times on the <a href="{_attr(pulse_url)}">Pulse '
"board \u2197</a>"
+ pills(*[(f"{k.lower()} {n}", "" if i == 0 else "n")
for i, (k, n) in enumerate(data["pulse"].items())])))
for t in data["resume"]["tasks"]:
# An in-flight task wears the header facets the Mind gave it, so it
# looks like itself on both pages — same pills, same order.
Expand Down
14 changes: 13 additions & 1 deletion docs/organs/pulse.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ the Eyes: the Eyes show what the software *shows*; the Pulse feels how fast it
**Repo:** [PyAutoLabs/PyAutoPulse](https://github.com/PyAutoLabs/PyAutoPulse)

**Status:** organ row registered (phase 0 of the `profiling-organ-birth`
epic); the registry, reader, receipts, board and workflows arrive in phase 2.
epic); the registry, reader, receipts, board and workflows arrived in phase 2;
phase 3 puts the organ on the Brain board and the organ cockpit.
Design: `docs/research/profiling_inference_organs.md` in the Brain repo.

## The defining function: feeling the pulse
Expand Down Expand Up @@ -41,6 +42,13 @@ The board shows one row per registered project with scope, evidence time, last
fetch, coverage and links, and keeps missing or refused evidence visible beside
valid data. A valid empty feed says "no measurements", never "all passed".

Beside the board, the organ publishes two machine surfaces: `badge.json`, a
one-line headline, and `state.json`, the organ-cockpit feed (contract v1 in
`PyAutoBrain/board/state_schema.json`). The feed is what puts the Pulse on the
[organ cockpit](https://pyautolabs.github.io/cockpit/) alongside the other
organs; the Brain board's Resume section carries a Pulse strip composed from
the head counts of the organ's own `dashboard.md`.

## The driver split

The project repos *produce and hold* timings and the Pulse *shows* them;
Expand Down Expand Up @@ -76,3 +84,7 @@ The organ is being born through the `profiling-organ-birth` epic:
the lens profiling project.
- **Phase 0** ([PyAutoMind#463](https://github.com/PyAutoLabs/PyAutoMind/issues/463))
added the organ row to the body map.
- **Phase 3** ([PyAutoBrain#450](https://github.com/PyAutoLabs/PyAutoBrain/issues/450))
added the Pulse strip to the Brain board and the Pulse card to the organ
cockpit, and named the Pulse board in the profiling conductor's prose as
where drift candidates are read.
8 changes: 8 additions & 0 deletions skills/profiling/profiling.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ and board show how fast each `<lib>_profiling` project runs, but it never
judges. This command's `triage` is the only judge of what a timing or a drift
means; the projects keep their producers, results, pins and drift policy.

The Pulse board (strip on the Brain board, card on the organ cockpit) is where
cross-project drift candidates and their provenance are read; `autolens_profiling`
owns the producers, pins and drift policy. A `/profiling triage <comparison_key>`
prompt copied from a Pulse drift item names one comparison row on the Pulse
board: open that row, read the pair of records and their evidence paths, then
judge with the `triage` verb (which takes no target — the key is where to look,
not an argument).

The Profiling Agent **reasons; it never runs sweeps or edits source.** The
classification is the result for CPU-unusable cells; full timings for those
belong to the A100 rows.
48 changes: 48 additions & 0 deletions tests/test_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
# The Eyes organ's own counts (the head of its dashboard.md), None when
# no Eyes is checked out.
"eyes",
# The Pulse organ's own counts (the head of its dashboard.md), None when
# no Pulse is checked out.
"pulse",
}

AUTONOMY_LOG = """\
Expand Down Expand Up @@ -208,6 +211,26 @@ def _default_fixtures(**overrides):
| [fit.png](https://example.invalid/fit.png) | 5 |
"""

# The head of the Pulse's generated board is its counts table; its body
# carries one table per project, which the strip must not count.
PULSE_DASHBOARD_MD = """\
# PyAutoPulse — profiling dashboard

| Where | Count |
|-------|------:|
| [Projects](#projects) | 1 |
| [Records](#projects) | 159 |
| [Comparisons](#projects) | 145 |
| [Drifted](#projects) | 0 |
| [Refused pairs](#projects) | 0 |
| [Cached](#projects) | 0 |
| [Failed](#projects) | 0 |

## Projects

| [alpha_profiling](#alpha_profiling) | 7 | 9 |
"""


def _fabricate(tmp_path, fixtures, heart_board=None):
"""A PYAUTO_ROOT with a fabricated Mind, file:// sibling-board badges, and
Expand Down Expand Up @@ -243,6 +266,9 @@ def _fabricate(tmp_path, fixtures, heart_board=None):
eyes = tmp_path / "PyAutoEyes"
eyes.mkdir()
(eyes / "dashboard.md").write_text(EYES_DASHBOARD_MD)
pulse = tmp_path / "PyAutoPulse"
pulse.mkdir()
(pulse / "dashboard.md").write_text(PULSE_DASHBOARD_MD)

# One badge per sibling board named in the declared config surface —
# read from policy.yaml so no board repo name is hardcoded here.
Expand Down Expand Up @@ -357,6 +383,28 @@ def test_the_eyes_strip_composes_the_eyes_own_head_counts(tmp_path):
assert json.loads(r.stdout)["eyes"] is None


def test_the_pulse_strip_composes_the_pulse_own_head_counts(tmp_path):
"""The Pulse decides its own numbers; the Brain board reads only the
counts table at the head of its page (the body's per-project tables are
not counts), links its board, and shows nothing when no Pulse is here."""
s, _ = _surface(tmp_path)
assert s["pulse"] == {"Projects": 1, "Records": 159, "Comparisons": 145,
"Drifted": 0, "Refused pairs": 0, "Cached": 0,
"Failed": 0}
assert s["boards"]["pulse"].endswith("/PyAutoPulse/")
stub = _fabricate(tmp_path / "md", _default_fixtures())
md = _run([], tmp_path / "md", stub).stdout
assert ("- Pulse: projects 1 · records 159 · comparisons 145 · "
"drifted 0 · refused pairs 0 · cached 0 · failed 0") in md
page = _run(["--html"], tmp_path / "md", stub)
assert page.returncode == 0, page.stderr
assert "Pulse board" in page.stdout and 'data-organ="pulse"' in page.stdout
stub = _fabricate(tmp_path / "no_pulse", _default_fixtures())
(tmp_path / "no_pulse" / "PyAutoPulse" / "dashboard.md").unlink()
r = _run(["--json"], tmp_path / "no_pulse", stub)
assert json.loads(r.stdout)["pulse"] is None


def test_json_surface_is_complete_and_derives_org(tmp_path):
s, _ = _surface(tmp_path)
assert set(s) == SURFACE_KEYS
Expand Down
Loading