mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
feat(prompter): make the intake actually use its task-history digest (#592)
The history-digest pipeline (PR #297) injected past-task data but nothing told the intake agent what to do with it: prompter.md now carries an explicit don't-re-propose section (cite duplicates by short id, reference precedent in notes, let history inform depends_on sequencing), and list_recent_for_project excludes cancelled tasks so dead work can't pose as precedent. Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -33,6 +33,16 @@ Keep each unit to one concern. A unit that bundles several unrelated changes is
|
||||
|
||||
Before your first question, use `Read` / `Grep` / `Glob` and the read-only git verbs to learn the real surface. If the CEO says "put it on the Metrics page", open the Metrics page and see what's there. If they mention an endpoint, find it. Read targeted excerpts yourself — you have no subagents, and a broad survey is never worth stalling the interview; skim the few files the request actually names. **Ground every question and every claim in what the code actually shows** — never guess at a surface you could have read.
|
||||
|
||||
## Task history — don't propose what's already been done
|
||||
|
||||
Your context may carry a **`## Task History`** section (recent tasks for this scope, oldest first, `` `short-id` title (status, date) ``) and you always have a `search_past_tasks` tool for ad-hoc lookups mid-conversation. Check both before you draft:
|
||||
|
||||
- **Avoid duplicates.** If the history shows a shipped or in-flight task that already covers what the CEO is describing, say so plainly instead of quietly drafting a duplicate — name it by short id and ask whether this is genuinely new or a follow-up.
|
||||
- **Cite precedent.** When a task extends or follows up on a prior one, name that prior task by short id in `notes` or `what_this_builds` (e.g. "follows up `a1b2c3d4`") so the Main PM / cell PM / dev inherit the continuity you found, not just the CEO's one-liner.
|
||||
- **Sequence with judgment.** In a MegaTask batch, if the history shows this project's work consistently stages a particular way (a migration before the code that needs it, one cell's contract before the cell that consumes it), let that inform your `depends_on` — but a CEO-declared ordering still wins verbatim (see MegaTasks below).
|
||||
|
||||
No history section, or nothing relevant in it? Draft normally — this is informational, never a requirement to manufacture precedent that isn't there.
|
||||
|
||||
## Interview discipline
|
||||
|
||||
- Open by reflecting back, in a sentence or two, what you understand they want — so they can correct course immediately.
|
||||
|
||||
@@ -7384,13 +7384,14 @@ class TaskService(BaseService):
|
||||
async def list_recent_for_project(
|
||||
self, project_id: UUID, limit: int = 15
|
||||
) -> list[TaskTable]:
|
||||
"""Recent tasks for a project, most-recently-active first.
|
||||
"""Recent non-cancelled tasks for a project, most-recently-active first.
|
||||
|
||||
Backs the prompter's history digest: the intake agent gets a compact
|
||||
chronological view of what's already been built/attempted in this repo.
|
||||
"Recent" = highest of completed_at / updated_at / created_at, so a
|
||||
just-touched-but-not-completed task still surfaces ahead of an old
|
||||
completed one.
|
||||
completed one. Cancelled tasks are excluded — abandoned work is not
|
||||
precedent an intake agent should treat as shipped or in-flight.
|
||||
"""
|
||||
activity = func.coalesce(
|
||||
TaskTable.completed_at, TaskTable.updated_at, TaskTable.created_at
|
||||
@@ -7398,6 +7399,7 @@ class TaskService(BaseService):
|
||||
stmt = (
|
||||
select(TaskTable)
|
||||
.where(TaskTable.project_id == project_id)
|
||||
.where(TaskTable.status != TaskStatus.CANCELLED)
|
||||
.order_by(activity.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
|
||||
@@ -321,6 +321,21 @@ async def test_list_recent_for_project_scoped_to_project(
|
||||
assert out_of_scope.id not in ids
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_recent_for_project_excludes_cancelled(task_setup: dict) -> None:
|
||||
svc = task_setup["svc"]
|
||||
db = task_setup["db"]
|
||||
live = await svc.create(_req(task_setup, title="live"))
|
||||
cancelled = await svc.create(_req(task_setup, title="cancelled"))
|
||||
cancelled.status = TaskStatus.CANCELLED
|
||||
await db.flush()
|
||||
|
||||
rows = await svc.list_recent_for_project(task_setup["project_id"])
|
||||
ids = {t.id for t in rows}
|
||||
assert live.id in ids
|
||||
assert cancelled.id not in ids
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Subtask hierarchy
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
"""compose_prompt includes the Task history guidance section for the intake role."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from roboco.agents.factories._base import compose_prompt
|
||||
from roboco.models import AgentRole
|
||||
|
||||
|
||||
def test_task_history_guidance_present_for_prompter() -> None:
|
||||
prompt = compose_prompt(AgentRole.PROMPTER, None, "intake-1")
|
||||
assert "## Task history" in prompt
|
||||
|
||||
|
||||
def test_task_history_guidance_names_the_search_tool_and_duplicate_avoidance() -> None:
|
||||
"""The static guidance names the ambient heading, the search tool, and the
|
||||
three explicit uses the CEO asked for: avoid duplicates, cite precedent by
|
||||
short id, and let history inform sequencing."""
|
||||
prompt = compose_prompt(AgentRole.PROMPTER, None, "intake-1")
|
||||
assert "search_past_tasks" in prompt
|
||||
assert "Avoid duplicates" in prompt
|
||||
assert "Cite precedent" in prompt
|
||||
assert "Sequence with judgment" in prompt
|
||||
Reference in New Issue
Block a user