From e7609fdd46b6078f3594f69abf385f14c71063f8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 23 Aug 2026 15:25:37 +0000 Subject: [PATCH] intake: keep shipped work out of the dashboard's Recent feed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent shipped listing the `complete/` ledger alongside live work. On this Mind that made it a list of receipts: ~200 records a month means the newest dates are almost all completions, and the section whose whole job is the work in hand was mostly things nobody can act on. The feed is now live work only — issued, parked, filed — and `complete/` is never opened to render it, rather than being read and filtered afterwards. That drops the bounded month-walk, the record date parsing and the live-first selection rule that only existed to stop completions crowding live rows out; a plain date sort is now correct. `complete/index.md` stays where shipped work is read. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01L4HPWjv5rdzBAkKpfW1SbR --- agents/conductors/intake/AGENTS.md | 21 ++-- agents/conductors/intake/_intake.py | 149 +++++++--------------------- tests/test_intake_dashboard.py | 59 ++++------- 3 files changed, 68 insertions(+), 161 deletions(-) diff --git a/agents/conductors/intake/AGENTS.md b/agents/conductors/intake/AGENTS.md index 1ef6dd9..9d9205a 100644 --- a/agents/conductors/intake/AGENTS.md +++ b/agents/conductors/intake/AGENTS.md @@ -89,15 +89,18 @@ schema — light structure over free-form prose. | **reconcile --repo** | `intake reconcile --repo [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 diff --git a/agents/conductors/intake/_intake.py b/agents/conductors/intake/_intake.py index 06b7893..c7bb1b7 100755 --- a/agents/conductors/intake/_intake.py +++ b/agents/conductors/intake/_intake.py @@ -492,33 +492,22 @@ 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: @@ -526,108 +515,38 @@ def _anchor(heading: str) -> str: 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///` 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///` 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: @@ -749,7 +668,6 @@ def _count(key): "hygiene": hygiene, "drift": drift, } - c["completed"] = completed_records(mind) c["recent"] = recent_events(c) return c @@ -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: @@ -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 diff --git a/tests/test_intake_dashboard.py b/tests/test_intake_dashboard.py index 4781d53..fcd32ea 100644 --- a/tests/test_intake_dashboard.py +++ b/tests/test_intake_dashboard.py @@ -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.""" @@ -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.""" @@ -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 "

Recent" in html @@ -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