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
21 changes: 12 additions & 9 deletions agents/conductors/intake/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,18 @@ schema — light structure over free-form prose.
| **reconcile --repo** | `intake reconcile --repo <target> [prefix]` | **also** read the target repo's source for identifiers the prompts name — the one signal that sees a prompt with no Mind-side trace. Opt-in; the default path is offline |

**Recent** is the one section laid out by *date* rather than by state: the 20
newest task events merged across every bucket — issued, parked, filed,
completed — sitting between the Backlog and the Epics. Every other section
answers "what should I do now?"; recency is orthogonal to state, so none of
them can answer "what has been happening?". Dates come from the registry key
that names the event (`issued:` / `parked:` / `filed:`, PyAutoMind REFERENCE.md
"Task dates"), a prompt's own `Issued:` header, or a record's `completed:`.
Live work is selected first and completions fill the rest — on a Mind that
ships two hundred records a month a straight date sort is twenty receipts and
no work.
newest events on the **work in hand** — issued, parked, filed — merged across
the live buckets and sitting between the Backlog and the Epics. Every other
section answers "what should I do now?"; recency is orthogonal to state, so
none of them can answer "what has been happening?". Dates come from the
registry key that names the event (`issued:` / `parked:` / `filed:`, PyAutoMind
REFERENCE.md "Task dates") or a prompt's own `Issued:` header.

Shipped work is deliberately **not** in the feed and `complete/` is never
opened to render it: the ledger is a thousand records deep and takes ~200 a
month, so including it made the table a list of receipts — twenty things nobody
can act on, on the page whose whole job is work in hand. `complete/index.md` is
where shipped work is read.

Census/dashboard are the Mind *backlog* view — deliberately distinct from
Heart's `/health status` health view (see "must never do"). The prompt-taxonomy
Expand Down
149 changes: 34 additions & 115 deletions agents/conductors/intake/_intake.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,142 +492,61 @@ def _clip(text: str, limit: int = 130) -> str:

# --- the recent feed -----------------------------------------------------------
# The dashboard answers "what should I do now?" everywhere else on the page.
# This one section answers "what has been happening?" — the single question a
# task page laid out by STATE cannot answer, because a task's recency is
# orthogonal to which bucket it sits in. Merging the buckets by date puts the
# week's issuing, parking and shipping in one place, which is also the fastest
# way to spot a Mind that has drifted (three weeks of `filed` and nothing
# `completed` is visible at a glance and invisible section by section).
# This one section answers "what has been happening to the work in hand?" — the
# single question a task page laid out by STATE cannot answer, because a task's
# recency is orthogonal to which bucket it sits in. Merging the buckets by date
# puts the week's issuing, parking and filing in one place.
#
# Live work ONLY. The `complete/` ledger is not in this feed: it is a thousand
# records deep and ships ~200 a month, so including it made the table a list of
# receipts — twenty things nobody can act on, on the page whose whole job is
# work in hand. `complete/index.md` is where shipped work is read.
RECENT_MAX = 20

# How many month folders of `complete/` to open. The records are the bulk of
# the Mind (1000+ files), and only the newest handful can reach a 20-row feed,
# so the scan is bounded by folder rather than reading the whole ledger on
# every render. Two months is comfortably more than 20 records in practice;
# the scan widens on its own if they are not.
RECENT_MONTHS = 2

# Records open with their fields (`- completed: …`) but the ledger is a decade
# of hand-written prose, so a fifth of them say it another way. Read the head
# of the file only: a date deep in a narrative is something the task mentions,
# not when it shipped.
_RECORD_HEAD_BYTES = 4096
_RECORD_DATE = re.compile(r"^-\s+(completed|shipped):\s*(.*)$", re.MULTILINE)

# The verb each event reads as in the feed. Past tense throughout — every row
# is something that already happened.
EVENT_LABEL = {"issued": "issued", "registered": "issued", "started": "started",
"planned": "planned", "filed": "filed", "parked": "parked",
"found": "found", "completed": "completed", "shipped": "completed"}
"found": "found"}


def _anchor(heading: str) -> str:
"""GitHub's heading anchor for an `## <slug>` registry entry."""
return re.sub(r"[^a-z0-9-]+", "-", heading.lower()).strip("-")


def _record_date(path: Path, text: str) -> str:
"""The day a completion record's task shipped.

`completed:`/`shipped:` when the record states one; otherwise the record's
own `complete/<YYYY>/<MM>/` folder, which the lifecycle engine files it
into by completion month — coarse, but never wrong about the month, and
the alternative is dropping a fifth of the ledger out of the feed."""
m = _RECORD_DATE.search(text)
if m:
d = _ISO_DATE.search(m.group(2))
if d:
return d.group(1)
parts = path.parts
if len(parts) >= 3 and parts[-3].isdigit() and parts[-2].isdigit():
return f"{parts[-3]}-{parts[-2]}"
return ""


def completed_records(mind: Path, months: int = RECENT_MONTHS,
want: int = RECENT_MAX) -> list:
"""The newest completion records, newest first — `[{date, slug, path}]`.

Walks `complete/<YYYY>/<MM>/` newest-first and stops one month after it has
enough, so the whole ledger is never read to render a 20-row table. A month
is opened whole: records inside one are unordered, and a record can carry a
date from the month before it was filed under."""
root = mind / "complete"
if not root.is_dir():
return []
folders = sorted(
(d for y in root.iterdir() if y.is_dir() and y.name.isdigit()
for d in y.iterdir() if d.is_dir() and d.name.isdigit()),
key=lambda d: (d.parent.name, d.name), reverse=True)
out, opened = [], 0
for folder in folders:
opened += 1
for f in folder.glob("*.md"):
text = f.read_text(encoding="utf-8", errors="replace")[:_RECORD_HEAD_BYTES]
out.append({"date": _record_date(f, text),
"slug": f.stem,
"path": str(f.relative_to(mind))})
if opened >= months and len(out) >= want:
break
out.sort(key=lambda r: r["date"], reverse=True)
return out


def recent_events(c: dict, limit: int = RECENT_MAX) -> list:
"""The Mind's newest task events, newest first — `[{date, event, title, …}]`.
"""The newest events on the work in hand, newest first.

One row per task, not per event: a task that was filed and later issued
appears once, on its latest date (which is what `_entry_date` already picks
per entry).

**Live work is never crowded out by shipped work.** A straight date sort
would be all completions — this Mind ships ~200 records a month, so the
twenty newest dates in it are twenty records — and the one thing the feed
must show is the work still in hand. So the rows are *selected* live-first
(in flight, parked, planned), completions fill whatever room is left, and
the selected set is then sorted by date like any other feed. On a quiet
Mind that changes nothing; on a busy one it is the difference between a
table of work and a table of receipts.

Undated rows are absent rather than sorted to the bottom: `lifecycle.py
dates` is where a missing date gets reported, and padding this table with
unknowns would bury the answer it exists to give.
"""
live, done = [], []
events = []
for r in c.get("in_flight") or []:
if r.get("date"):
live.append({"date": r["date"], "event": r.get("event") or "issued",
"title": r["title"], "path": r["path"], "live": True,
"payload": f"/start_dev {r['path']}"})
events.append({"date": r["date"], "event": r.get("event") or "issued",
"title": r["title"], "path": r["path"],
"payload": f"/start_dev {r['path']}"})
for key, verb in (("parked", "resume"), ("planned", "start")):
for e in c.get(key) or []:
if e.get("date"):
live.append({"date": e["date"],
"event": e.get("event") or key,
"title": e["slug"],
# The entry's own heading anchor — a registry file
# is long enough that landing at its top is not
# the same as landing on the task.
"path": f"{key}.md#{_anchor(e['slug'])}",
"live": True,
"payload": _registry_payload(e, key, verb)})
for r in c.get("completed") or []:
if r.get("date"):
done.append({"date": r["date"], "event": "completed",
"title": r["slug"], "path": r["path"], "live": False,
"payload": f"/memory recall the completed PyAutoMind "
f"task {r['slug']} — its record is "
f"{r['path']}"})

def _newest(rows):
return sorted(rows, key=lambda e: (e["date"], e["title"]), reverse=True)

chosen = _newest(live)[:limit]
chosen += _newest(done)[:max(0, limit - len(chosen))]
chosen = _newest(chosen)
for e in chosen:
events.append({"date": e["date"],
"event": e.get("event") or key,
"title": e["slug"],
# The entry's own heading anchor — a registry
# file is long enough that landing at its top is
# not the same as landing on the task.
"path": f"{key}.md#{_anchor(e['slug'])}",
"payload": _registry_payload(e, key, verb)})
events.sort(key=lambda e: (e["date"], e["title"]), reverse=True)
for e in events:
e["event"] = EVENT_LABEL.get(e["event"], e["event"])
return chosen
return events[:limit]


def census(mind: Path) -> dict:
Expand Down Expand Up @@ -749,7 +668,6 @@ def _count(key):
"hygiene": hygiene,
"drift": drift,
}
c["completed"] = completed_records(mind)
c["recent"] = recent_events(c)
return c

Expand Down Expand Up @@ -895,11 +813,12 @@ def _epic_members(c: dict) -> dict:


RECENT_BLURB = (
"The Mind's most recent {n} task events, newest first — what was issued, "
"parked, filed and shipped, in one place. Every other section on this page "
"is laid out by state, which is exactly why none of them can answer "
"\u201cwhat has been happening?\u201d. Work still in hand is listed "
"first-class here; completed records fill whatever room is left.")
"The {n} newest things to happen to the work in hand, newest first — "
"issued, parked, filed. Every other section on this page is laid out by "
"state, which is exactly why none of them can answer \u201cwhat has been "
"happening?\u201d. Shipped work is not here: it is read from "
"`complete/index.md`, and a thousand records deep it would crowd out "
"everything anyone can still act on.")


def _dated(row: dict) -> str:
Expand Down Expand Up @@ -1056,8 +975,8 @@ def render_dashboard(c: dict) -> str:
"|------|-------|------|"]
L += [f"| {r['date']} | {r['event']} | {_cell(_recent_link(r))} |"
for r in recent]
L += ["", "_Dates come from each task's registry entry and each record's "
"`completed:` — `lifecycle.py dates` reports anything undated._", ""]
L += ["", "_Dates come from each task's registry entry "
"`lifecycle.py dates` reports anything undated._", ""]

# Epics live at the bottom, whole: each epic's resume prompt sits with its
# queued member prompts, grouped and phase-ordered, so nobody picks a
Expand Down
59 changes: 22 additions & 37 deletions tests/test_intake_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,17 +482,34 @@ def _recent_mind(root, n_records=0, month="08"):
})


def test_recent_merges_every_state_into_one_dated_feed(tmp_path):
def test_recent_merges_every_live_state_into_one_dated_feed(tmp_path):
"""Recency is orthogonal to state, so it is the one question no other
section on the page can answer."""
page = _page(_recent_mind(tmp_path, n_records=3))
page = _page(_recent_mind(tmp_path))
recent = page.split("## Recent")[1]
for slug in ("sprocket-calibration", "flywheel-balance", "gearbox-survey",
"shipped-00"):
assert slug in recent or slug.replace("-", " ") in recent.lower()
for slug in ("Sprocket calibration", "flywheel-balance", "gearbox-survey"):
assert slug in recent
assert "| Date | Event | Task |" in recent


def test_shipped_work_is_not_in_the_feed(tmp_path):
"""The `complete/` ledger is a thousand records deep and ships ~200 a
month, so including it made this a list of receipts — twenty things nobody
can act on, on the page whose whole job is work in hand."""
mind = _recent_mind(tmp_path, n_records=40)
rows = _intake.census(mind)["recent"]
assert len(rows) == 3
assert not any("shipped" in r["title"] for r in rows)
assert "shipped-00" not in _page(mind)


def test_the_complete_ledger_is_never_opened(tmp_path):
"""Not merely filtered out afterwards — a 20-row table of live work must
not read a thousand records to render."""
assert not hasattr(_intake, "completed_records")
assert "completed" not in _intake.census(_recent_mind(tmp_path, n_records=5))


def test_recent_names_the_event_each_date_records(tmp_path):
"""A bare date says nothing; `parked` / `issued` / `filed` says what
happened — the whole reason the registry key is the event name."""
Expand All @@ -517,18 +534,6 @@ def test_recent_sits_after_the_backlog_and_before_the_epics(tmp_path):
assert page.index("## Backlog") < page.index("## Recent") < page.index("## Epics")


def test_a_busy_ledger_never_crowds_live_work_out_of_the_feed(tmp_path):
"""A straight date sort on a Mind that ships 200 records a month is 20
receipts and no work. Live tasks are selected first; records fill the
rest — otherwise the dates on active tasks would be invisible on the very
page they were added for."""
rows = _intake.census(_recent_mind(tmp_path, n_records=40))["recent"]
assert len(rows) == _intake.RECENT_MAX
live = [r for r in rows if r["live"]]
assert {r["title"] for r in live} == {"Sprocket calibration",
"flywheel-balance", "gearbox-survey"}


def test_an_undated_task_is_absent_rather_than_sorted_to_the_bottom(tmp_path):
"""`lifecycle.py dates` is where a missing date gets reported; padding the
feed with unknowns would bury the answer it exists to give."""
Expand Down Expand Up @@ -575,15 +580,6 @@ def test_a_date_in_another_fields_prose_does_not_count_as_a_date(tmp_path):
assert _intake.census(mind)["recent"] == []


def test_a_record_without_a_completed_field_still_dates_by_its_folder(tmp_path):
"""A fifth of the ledger states its date in prose. The month folder the
lifecycle engine filed it into is coarse but never wrong about the month,
and the alternative is dropping those records out of the feed."""
mind = _mind(tmp_path, complete={
"2026/07/legacy_record.md": "Shipped one July afternoon.\n"})
assert _intake.census(mind)["recent"][0]["date"] == "2026-07"


def test_the_html_twin_carries_the_same_feed_with_real_copy_buttons(tmp_path):
html = _intake.render_dashboard_html(_intake.census(_recent_mind(tmp_path)))
assert "<h2>Recent" in html
Expand All @@ -593,17 +589,6 @@ def test_the_html_twin_carries_the_same_feed_with_real_copy_buttons(tmp_path):
assert 'data-cmd="/start_dev active/sprocket_calibration.md"' in html


def test_the_completed_scan_does_not_read_the_whole_ledger(tmp_path):
"""1000+ records is the bulk of the Mind; a 20-row table must not open all
of them on every render."""
mind = _mind(tmp_path, complete={
f"2026/{m:02d}/rec-{m}-{i:02d}.md": _record(f"rec-{m}-{i}", f"2026-{m:02d}-01")
for m in range(1, 9) for i in range(30)})
scanned = _intake.completed_records(mind)
assert len(scanned) < 240
assert scanned[0]["date"] == "2026-08-01"


def test_a_live_row_wears_its_date_where_the_task_is(tmp_path):
"""A status line reads very differently against a row issued yesterday
than against one issued in May, so the date rides on the row too — not
Expand Down
Loading