mcp / docs /agentspot_task_manager_agent.md
mikeboone's picture
Claude Fable 5.1
feat(tracker): a "cancelled" task status, closed like "done" without claiming the work was done
c1d7132
|
Raw History Blame Contribute Delete
22.7 kB
# AgentSpot agent: Task Manager, on the team tracker
*Design and paste-ready agent definition. Written 2026-10-20 from boone's brief and his answers (Supabase store, server-side GitHub, two repos, ThoughtSpot views). Code on branch `feature/tracker`; status in `sprint_2026_04.md`.*
*The agent is built in the AgentSpot UI on secloud. The tracker is code in this repo: `demoprep_app/tracker/` (schema, store, GitHub sync, TML) and six tools in `mcp_server.py`.*
Team: Mike (mike.boone), Jack (jack.rayner), Manny (mani.uppala), Gora (gora.sudindranath). Overall view also for Paul.
---
## The brief
Pull requests already go through GitHub, but nothing else does. Sprint notes are markdown
files in this repo and "ping Jack" happens in Slack by hand. Wanted:
- one task list the team can assign to each other, kept in Supabase;
- pull requests understood by the tracker: which task a PR belongs to, what state it is in;
- a personal view (my stuff) for each person, and an overall view for a manager;
- a way to hand work to an agent, through Slack or the database.
---
## Decisions
| Item | Decision | Source |
|---|---|---|
| Task store | Four tables in the existing Supabase project beside `demo_history`: `tasks`, `task_notes`, `pull_requests`, `tracker_people`, plus the `task_board` view for ThoughtSpot. Real tables, not the `demo_history` status-sentinel pattern, because ThoughtSpot needs typed columns. `demoprep_app/tracker/schema.sql`. | boone (Supabase); shape recommended |
| Who writes | Six tools on the DemoPrep MCP server: `list_tasks`, `create_task`, `update_task`, `add_task_note`, `list_pull_requests`, `sync_pull_requests`. The AgentSpot agent, the Slack bot, and any Claude Code session with the connector use the same tools. | recommended |
| Pull requests | The MCP server polls GitHub with a repo-scoped token every 10 minutes (`demoprep_app/tracker/github_sync.py`). A PR links to a task by a stored link, by the task's own `pr_url`, or by `T-<id>` in its title, body or branch name. A merge adds a note to the task; it never closes it. | boone (server-side token) |
| Repos | `boone10/DemoPrep` and `thoughtspot/tse_demos`, set by `TRACKER_REPOS`. | boone |
| Views | ThoughtSpot on secloud with a live PostgreSQL connection to Supabase (ThoughtSpot Cloud lists PostgreSQL as a supported connection; no Snowflake mirror). Model `Team Tracker` and a Liveboard with Team, Pull requests and Stale tabs: `demoprep_app/tracker/tml/`. Personal view by an Owner Name filter saved as each person's personalized view, or a one-line RLS rule. An AgentSpot App can be generated from the same model later. | boone (ThoughtSpot) |
| Identity | `owner` and `requester` are ThoughtSpot usernames (`mike.boone`), what `ts_username` sees in RLS and what the run reports print. The agent passes the caller's name; the store resolves username, email, GitHub login, display name or first name, and refuses anything else. | default |
| Communication | AgentSpot's Slack bot bound to one public channel; a scheduled Workflow posts a weekday digest. | AgentSpot native |
| Branch | `feature/tracker` off `claude/agentspot-tse-modifications-9b57a7`, the branch test-mcp runs, so the next push carries the Portal Editor tools and the tracker together. | boone |
| Audience | Mike, Jack, Manny, Gora use it. Gora and Paul get the overall view. Agent shared "Can use", Mike keeps "Can edit". | boone |
---
## Data model
`demoprep_app/tracker/schema.sql`, run once in the Supabase SQL editor. Idempotent.
**tracker_people**: `username` (ThoughtSpot), `display_name`, `email`, `github_login`,
`is_manager`, `active`. Seeded with the four; only boone10's login is known, fill the
others in as they are learned (until then the sync shows raw logins).
**tasks**: `id` (shown as `T-12`), `title`, `body`, `status` (`open`, `in_progress`,
`blocked`, `review`, `done`), `priority` (`p1`..`p3`), `label` (`task`, `bug`, `demo`,
`portal`, `docs`), `owner`, `requester`, `repo`, `pr_number`, `pr_url`, `run_id`,
`liveboard_url`, `due_date`, `created_at`, `updated_at`, `closed_at`.
**task_notes**: `id`, `task_id`, `author`, `note`, `created_at`. Every reassignment,
status change, priority change, PR link and merge writes one.
**pull_requests**: `repo` + `number` (the key), `title`, `author` (username when the
login is known), `author_login`, `branch`, `state` (`open`, `merged`, `closed`), `draft`,
`reviewers`, `waiting_on`, `task_id`, `url`, `created_at`, `updated_at`, `merged_at`,
`synced_at`.
**task_board** (view): one row per task with its PR's state and `waiting_on`, the
owner's display name, days open, last note, and `stale` (five days with no note and no
PR change while not done). What `list_tasks` returns and what ThoughtSpot models.
---
## MCP tools
All sync functions beside the portal tools, same `{status, ...}` result shape. A request
that is partly wrong is wholly refused with every problem named (`{status: "failed",
errors}`) and nothing is written. No tool deletes.
| Tool | Takes | Returns |
|---|---|---|
| `list_tasks` | `owner`, `status`, `label`, `requester`, `search`, `include_done`, `limit` | `{status, count, tasks: [{task, id, title, status, priority, label, owner, owner_name, requester, pr_url, pr_state, pr_waiting_on, run_id, liveboard_url, due_date, days_open, stale, ...}]}` |
| `create_task` | `title`, `owner`, `requester`, `body`, `label`, `priority`, `due_date`, `pr_url`, `run_id` | `{status, task}` |
| `update_task` | `task_id`, `by`, then any of `title`, `body`, `status`, `priority`, `label`, `owner`, `due_date`, `pr_url`, `run_id`, `liveboard_url`, plus `note` | `{status, task, changed: {field: [old, new]}}` |
| `add_task_note` | `task_id`, `note`, `by` | `{status, note_id, task}` |
| `list_pull_requests` | `repo`, `state` (default open), `waiting_on`, `author`, `unlinked`, `approval`, `limit` | `{status, count, synced_at, pull_requests: [...]}` with `approval`, `approved_by`, `agent_opened`, `checks`, `mergeable` on each |
| `sync_pull_requests` | none | `{status, repos, fetched, linked, merged: [{task, repo, pr}], approvals: {auto, approved, pending, ...}, synced_at}` |
| `approve_pull_request` | `repo`, `number`, `by` (an approver), `note` | `{status, pull_request}`; refused when `by` cannot approve or the PR is not open |
| `pipeline_status` | none | `{status, integration_branch, ready, held: [{..., held_because}], awaiting_approval, agent_tasks, last_runs, last_deployments}` |
| `list_deployments` | `space`, `limit` | `{status, count, deployments: [{space, branch, sha, pushed_at, pushed_by, health, ping_code}]}` |
| `ingest_findings` | `hours` (default 36) | `{status, created: [T-ids], updated: [T-ids], findings}` |
The pickup pipeline behind the last four is `docs/development_process.md`: four
runs a day on boone's Mac merge approved PRs into `develop`, run the suite, and
push the test Space when it is idle. Approval: Mike's and Jack's own PRs are
approved on arrival; anyone else's, and every `agent/` branch, needs one of them.
`update_task` with `status=done` or `status=cancelled` sets `closed_at`; reopening
clears it. Both are closed: they leave open lists and the stale flag, and show only
with `include_done`. The automatic
notes read "reassigned from X to Y", "status open -> done", "priority p2 -> p1",
"linked <url>", with the caller's `note` appended.
---
## Pull request sync
A daemon thread in the MCP server, started in `main()` when `GITHUB_TOKEN` is set, runs
a sync every `TRACKER_SYNC_MINUTES` (default 10). For each repo in `TRACKER_REPOS` it
lists pull requests updated since the last sync, reads the reviews of the open ones,
derives `waiting_on` (the author while it is a draft or has changes requested; the
requested reviewers; `mergeable` once approved with nobody else asked; `needs_reviewer`
when nobody has been asked), and upserts. A PR is linked to a task by, first match wins:
a link already stored on the PR row (`update_task` sets it), a task whose `pr_url` names
the PR (`create_task` with `pr_url`), or `T-<id>` in the title or body or a branch named
`t<id>-...`. A task linked by convention learns its `pr_url`. When a linked PR's state
becomes `merged`, the task gets the note "PR #57 (jack.rayner) merged: <title>"; its
status is untouched.
Token: a fine-grained personal access token on `boone10`, read-only Pull requests and
Metadata on `boone10/DemoPrep`. `thoughtspot/tse_demos` is public, so the same token
reads it. Lives in the Space env as `GITHUB_TOKEN`, never in `.env` in the repo. A token
without `TRACKER_REPOS` fails at startup, where it is seen.
---
## ThoughtSpot views
One-time on secloud. The TML is in `demoprep_app/tracker/tml/` and was written ahead of
the connection: it has not been imported anywhere yet, so expect to fix a column type or
a search token on first import.
1. Data, Connections, new PostgreSQL connection named exactly **Supabase Tracker**: the
Supabase session pooler host (IPv4) from Project Settings, Database; database
`postgres`; the `tracker_reader` role from the bottom of `schema.sql`. If the project
has network restrictions on, allow ThoughtSpot's egress addresses.
2. Import `task_board.table.tml`, `pull_requests.table.tml`, `tracker_people.table.tml`.
3. Import `tracker_model.tml`: model **Team Tracker** with the joins and the measures
Tasks, Pull Requests, Stale Tasks.
4. Import `tracker_liveboard.tml`: Liveboard **Team Tracker** with tabs **Team** (open
tasks by owner and priority, due this week, the open list, blocked), **Pull requests**
(open PRs by who they wait on, the open list, merged this month), **Stale**. Then add
one Liveboard filter in the UI: Owner Name.
5. Personal view: each person sets Owner Name to themselves and saves a personalized
view. Managers leave it open. For it to be automatic, add the RLS rule from the
model's header (`[task_board::owner] = ts_username or 'tracker_managers' in
ts_groups`) and put Gora and Paul in a `tracker_managers` group.
6. Optional: Insights, AgentSpot, new App from the prompt "My tasks and the team's tasks
from the Team Tracker model, with a toggle between me and everyone" and share it.
---
## One-time setup
### Supabase, two minutes
Run `demoprep_app/tracker/schema.sql` in the SQL editor, then the three commented
`tracker_reader` lines at its bottom with a password of your choosing.
### GitHub, five minutes
Create the fine-grained token above. Put Jack's, Manny's and Gora's GitHub logins into
`tracker_people.github_login` as you learn them.
### MCP Space, five minutes
Add `GITHUB_TOKEN` and `TRACKER_REPOS=boone10/DemoPrep,thoughtspot/tse_demos` to the
test Space env (setting a variable restarts the Space: check `status` for active builds
first), deploy the branch, call `ping` (it lists the six new tools), then
`sync_pull_requests`. Then production, in the usual order.
### ThoughtSpot, twenty minutes
Steps 1 to 5 under ThoughtSpot views.
### AgentSpot on secloud, fifteen minutes
1. Insights, AgentSpot, new Agent from the builder prompt below. Replace the generated
instructions with the Agent instructions. Add the three Skills. Attach the DemoPrep MCP
connector (the one Agent Q and the Portal Editor use). No GitHub connector needed;
the server already knows the PRs. Web search off, subagents off.
2. Conversation starters: "What's on my plate?", "PRs waiting on me", "Assign Jack: ...",
"Team standup".
3. Smoke test: "What's on my plate?" It asks your username once and keeps it in agent
memory.
4. Share with Jack, Manny and Gora as "Can use". Do not write to org memory; secloud is a
shared SE cluster.
### Slack bot, ten minutes, needs a Slack admin
Admin Settings, Slack bot: install from the manifest, paste App ID, signing secret and bot
token, verify the event URL, bind one public channel, suggested `#demoprep-tasks`, apart
from the deployment channel `slack_notifier.py` posts to.
Cost: one DO credit per new conversation (each Slack thread is one) and one per completed
Workflow run. Building and testing are free.
---
## Builder prompt
```
Create an agent called "Task Manager" for a four-person ThoughtSpot SE team (Mike,
Jack, Manny, Gora) that builds DemoPrep. It uses the DemoPrep MCP connector only.
Tasks live in the team tracker through the tools list_tasks, create_task,
update_task, add_task_note, list_pull_requests and sync_pull_requests. It has three
skills: (1) Tasks: create, assign, prioritise, note, block and close tasks, and
answer what is on a person's plate. (2) Pull requests: list open PRs, say who each
is waiting on, link a PR to its task, create a task from a review comment.
(3) Standup: a digest per person or for the team of open tasks by priority, PRs
waiting on them, and anything stale. It always shows a change before making it and
waits for a yes. It never closes a task unless asked. Replies are short.
```
---
## Agent instructions
```
You are the Task Manager for a four-person ThoughtSpot SE team: Mike (mike.boone),
Jack (jack.rayner), Manny (mani.uppala) and Gora (gora.sudindranath). They build
DemoPrep. You keep the team's tasks and pull requests in one place and keep replies
short.
WHERE THINGS LIVE
- Tasks are in the team tracker, reached only through the DemoPrep MCP tools
list_tasks, create_task, update_task, add_task_note. A task is T-<id>.
- Pull requests are synced from GitHub into the same tracker every 10 minutes:
list_pull_requests, and sync_pull_requests when someone needs it fresher.
- Demo runs and portals: status, recent_builds, list_portals, for a task that
mentions one. Never start a build from this agent.
- The Liveboard "Team Tracker" on secloud is the visual view; point people at it for
anything longer than a table of ten rows.
WHO IS WHO
Owners and requesters are ThoughtSpot usernames. On first use with a person, ask their
username once and keep it in agent memory. "Me" is the person talking to you. Never
guess a username; the tracker refuses names it does not know, and the four above are
the only ones you know.
RULES
1. Show before you write. For any create, assign, status change, note or close, send
one message with the exact change and wait for a yes. Then call the tool once.
Never repeat a write.
2. Never close a task on your own judgement, including when its PR has merged. Say
"PR #57 merged; close T-12?" and wait.
3. A task has an imperative title ("Grade the Yodeck run"), an owner, a label and a
priority. Ask once for what is missing, then default: label task, priority p2,
owner the requester.
4. A task body has three lines: Context (one sentence), Done when (one sentence),
Links (PR, run_id, liveboard URL, Slack thread, as given).
5. Every reassignment, block and close carries a note saying why and by whom. Blocked
is a status plus a note naming the blocker, never a silent state.
6. Nudges go in the Slack thread you were called from, one sentence with the task or
PR URL. Never post to another channel.
7. Never put customer amounts, credentials or tokens in a task, note or reply.
8. Replies: a table for lists (T-id, title, owner, priority, PR, age), one line for a
confirmation ("Created T-42 for Gora, p1, task"), the Liveboard link when the
list is long.
9. If a tool returns status "failed", relay its errors and ask for the corrected
value; do not retry with a guess.
10. Never write to org memory. Agent memory may hold: username mappings and each
person's preferred digest time.
11. Approvals are Mike's and Jack's alone. "Approve #57" from one of them: show
the PR (title, author, branch, what it is waiting on), get a yes, then
approve_pull_request with by = that person. From anyone else: say who can
approve and offer to tell them. Never approve on your own.
SKILLS
Invoke Tasks, Pull requests, Standup or Pipeline from the request. "Standup" with
no name means the whole team. If a request could be a task or a PR question, ask
which.
```
---
## Skill 1: Tasks
**When to invoke:** create, assign, re-assign, prioritise, note, block, unblock or close a
task, or ask what someone is working on.
```
CREATE: gather title, owner, label, priority, due date if given, and the three body
lines. Show them. On yes, create_task with requester = the person talking. Reply
with the T-id.
ASSIGN: confirm the task by id and title and the new owner. On yes, update_task with
owner, by = the person talking, and a note with the reason if given.
LIST: "my plate" / "Jack's plate" / "team" is list_tasks with owner set or empty, p1
first then oldest. Columns: T-id, title, owner, priority, status, PR, days open. Mark
blocked and stale rows.
BLOCK: update_task status=blocked with a note naming the blocker.
CLOSE: only when asked. Confirm id and title. update_task status=done with a note
saying what was done and the PR if any.
CANCEL: only when asked, for work that will not happen. Confirm id and title.
update_task status=cancelled with a note saying why. Cancelled is closed, like
done, but it never claims the work was done.
RULES: before creating, list_tasks with search = a word from the title and offer a
match rather than a duplicate. Never change a priority without saying old and new.
```
## Skill 2: Pull requests
**When to invoke:** open PRs, who a PR is waiting on, link a PR to a task, task from a
review comment.
```
OPEN PRS: list_pull_requests state=open, both repos unless one is named. Columns:
repo, number, title, author, waiting_on, days open, task. "Waiting on me" filters
waiting_on to the current user. If synced_at is older than 15 minutes, call
sync_pull_requests first.
LINK: update_task with pr_url and by. Tell the user the T-<id> convention (in the PR
title, body or a branch named t12-...) so the next PR links itself.
UNLINKED: list_pull_requests unlinked=true and offer to create a task for each.
TASK FROM A REVIEW COMMENT: the user pastes or describes the comment. Quote it in
Context, PR URL in Links, owner the PR author unless told otherwise. Show, yes,
create_task with pr_url.
NUDGE: on request only, one sentence in the current Slack thread with the PR URL.
```
## Skill 3: Standup
**When to invoke:** standup, digest, "where are we", or the scheduled Workflow.
```
Per person: list_tasks owner=<person> and list_pull_requests waiting_on=<person>.
<Name>: N open (p1 a, p2 b, p3 c), M PRs waiting on them, K blocked
p1 T-42 Grade the Yodeck run 3d PR #57 waiting on jack.rayner
p2 T-40 Portal Editor on prod MCP 6d stale
blocked T-38 Snowflake cost report on: finance access
Team line: tasks opened and closed since the last digest (list_tasks include_done),
PRs merged (list_pull_requests state=merged).
Stale is the tracker's flag; say it, do not act on it. Never create, close or assign
from a standup. Offer, in one line, if something looks wrong (no owner, PR with
needs_reviewer). Under 30 lines for the team, 12 per person.
```
## Skill 4: Pipeline
**When to invoke:** approvals, "what will the next run take", "what shipped to
test", "why is my PR held", "what is the agent working on", build warnings.
```
STATUS: pipeline_status. Report ready (what the next run merges), held (each
with its held_because), awaiting_approval (name the approver who is needed),
the last run's outcome and the last push to test-mcp (sha, when, health).
APPROVE: only for Mike or Jack (rule 11). Show the PR, get a yes,
approve_pull_request(repo, number, by=<them>, note). Then say when the next
run is (7:30, 11:30, 15:30, 19:30 Eastern).
SHIPPED: list_deployments(space="test-mcp"); say sha, time, health, and which
tasks got "on test-mcp" notes. Prod is boone's, by hand: never say something
is on prod unless a deployments row for space "mcp" says so.
HELD: explain the one reason from held_because in plain words and what would
clear it (a draft to un-draft, a reviewer to ask, a conflict to rebase).
WARNINGS: ingest_findings when asked for a fresher look; otherwise the 7:30
run did it. Report created and updated T-ids.
```
## Skill 5: Dispatch (v2, not in the v1 build)
A task labelled `demo` assigned to the DemoPrep agent becomes a `build_demo_from_brief`
call with the task body as the brief and `owner_email` the requester; the `run_id` lands
on the task through `update_task`, and the liveboard URL when it completes. Gated on
durable run state on the MCP (sprint ISS-6) and on a decision about an agent spending a
build on a one-line task nobody read.
---
## Scheduled Workflow: weekday digest
| Field | Value |
|---|---|
| Name | Task Manager daily digest |
| Schedule | Mon to Fri, 13:00 UTC (09:00 ET) |
| Steps | 1. Standup skill, team scope. 2. Post to Slack `#demoprep-tasks`. |
Test run once before activating. One DO credit per completed run.
---
## Example conversations
**Create and assign.** Mike, in Slack: "@Task Manager assign Gora: grade the Yodeck run by
Friday, p1." Thread reply: title "Grade the Yodeck run", owner gora.sudindranath, task,
p1, due 2026-10-24, Context "Asked by mike.boone in Slack", Done when "Grade posted in
DAILY_RUN_REPORT.md", Links the thread. Create? On yes: "Created T-42 for Gora, p1".
**Jack opens a PR.** Jack's branch is `t42-yodeck-grade` and the PR body says "T-42".
Within ten minutes the sync links it and writes the PR onto the task; "What's on my
plate?" shows T-42 with "PR #57 open, waiting on mike.boone".
**PR waiting on whom.** Manny: "Which PRs are waiting on me?" Rows where waiting_on is
mani.uppala, with days open and the task.
**A close it declines.** Gora: "PR 57 merged, close T-42." The agent confirms the title
and closes it with a note. Gora: "and close everything else that merged." The agent lists
each task whose PR merged but is still open and closes only the ones Gora says yes to.
---
## Decisions boone still owes
1. Paul's ThoughtSpot username, and whether Gora is a manager for the RLS group.
2. Personal view: Owner Name filter saved per person, or the RLS rule.
3. Slack channel: new `#demoprep-tasks`, or the existing deployment channel.
4. The GitHub token owner: your `boone10` account, or a team account.
5. Jack's, Manny's and Gora's GitHub logins for `tracker_people`.
---
## Sources
- AgentSpot reference, 2026-09: `docs/agentspot_reference_2026_09.md`
- Prior agents: `docs/agentspot_tse_opportunity_finder.md`, `docs/agentspot_portal_editor_agent.md`
- ThoughtSpot connection types (PostgreSQL listed): https://docs.thoughtspot.com/cloud/latest/connections
- AgentSpot admin, Slack bot: https://docs.thoughtspot.com/agentspot/agentspot-admin.html
- Code: `demoprep_app/tracker/`, the tracker section of `mcp_server.py`, `tests/test_tracker_*.py`