AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified BSD-3-Clause Self-run

Witan Task

skill-mitodl-agent-kit-witan-task · by mitodl

>

— No reviews yet
0 installs
3 views
0.0% view→install

Install

$ agentstack add skill-mitodl-agent-kit-witan-task

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ✓ Environment & secrets No
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-mitodl-agent-kit-witan-task)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● today

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Witan Task? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Task Manager

Interactive entry point for the dependency-aware task tracker. Tasks live in the same graph as workflow projects and memory, so they can roll up to a project (project_slug), nest under an epic (parent), block one another (blocked_by), and reference code-graph symbols (symbol_refs).

The repo key is auto-detected from .git/config (the canonical HTTPS URI) — you rarely pass repo explicitly.

Claim before you work it

Any task you actually start gets claimed first — task_claim(slug="tk-...") before the first edit, every time. This is not limited to tasks you reached through this skill. It applies just as much to one you picked off the ready list in your session's injected context, one named in a witan project run prompt, or one a human mentioned by slug.

The claim is the only signal that anybody is on it. in_progress with a live lease is what makes task_ready hide it from the next session and what makes task_claim refuse a second holder — an unclaimed task being actively worked looks exactly like one nobody has touched. Two sessions have already written the same fix for the same task on the same day, each unaware of the other, because neither claimed it first.

So:

  • Claim it when you decide to work it, not when you finish. A claim after

the fact records history; it prevents nothing.

  • Take the refusal seriously. {"claimed": false, "held_by": ...} means

someone is on it. Pick another task — do not force past a live lease without a reason you can state.

  • Pass session_id, not assignee. task_claim(slug=..., session_id=...)

— your $CLAUDE_SESSION_ID on Claude Code, $PI_SESSION_ID on Pi (read your own platform's variable with echo, not a fallback chain: an agent launched from the other one can inherit its id), any stable per-run id elsewhere. It qualifies the holder as # so your own parallel sessions are told apart; without it they all claim under one name, the contention check cannot separate them, and the second session silently renews the first's lease and is told it claimed. A deployed witan cannot infer the id (a pod has no $CLAUDE_SESSION_ID, and MCP carries no session state), so an agent talking to it directly has to send it — the CLI's proxy does this for you, an MCP client does not. A success with "qualified": false and a "warning" is the server telling you this happened. Do not put the session id in assignee: that replaces the holder outright, so the claim records a session and no person. Reserve assignee for a genuinely different worker identity (a CI job, a named runner).

  • Release what you drop. task_release(slug=...) if you claim something

and then move on, so it returns to ready work instead of ageing out.

  • Claim as you go, not in bulk. When handed a list, claim each task as you

reach it. Claiming all of them up front holds leases on work you may never start, which is the same lie in the other direction.

witan task run already claims before it launches the agent, so a session started that way arrives holding its task; its prompt says so.

Read the comments when you claim. task_get(slug=...) returns a comments list alongside the description. A comment is how another agent tells you the task's own text is wrong, so treat it as outranking the description it sits next to — a task that says "add a check that fails during pulumi preview" and carries a comment saying that preview never runs on those pipelines is not a task to execute as written.

Comment on someone else's task

When you find a problem with a task you are not executing, say it on the task:

task_comment(slug="tk-...", text="")

The comment is attributed to you, timestamped, and append-only — it cannot be edited or deleted, and it does not touch the task. It shows up in task_get, and in the holder's injected session context if they are actively working it.

Reach for it instead of the two things that used to be the only options:

  • Not task_update. Rewriting the description destroys another author's

text and leaves no trace of who disagreed or why.

  • Not task_create. A task whose real content is "your premise is wrong"

is not work. It lands in everyone's ready-work list, has to be filed at the parent's priority to sit near it, and closing it means "I read this" — which is not what closing a task means anywhere else.

A comment is read once, by whoever executes that task. If what you found is reusable knowledge about the repo, that is a memory_store (a project fact or lesson) — and it is fine to do both: store the fact, then comment with the correction and a pointer to it.

When to use this vs. your built-in todo list

Use task_* for work that outlives the current session or is shared — items others (or a future session) should see, things with dependencies/blockers, or an epic's sub-issues. For the step-by-step checklist of the task you're doing right now, use your built-in todo list; don't mirror those ephemeral steps into the graph.

Asking the user

Several steps below ask the user to choose or type something. Ask the same way on every agent:

  • If a structured question tool is actually available to you (e.g. Claude

Code's AskUserQuestion, or a question tool a Pi extension registers), use it with the header, question and options given. Where a step also needs free text, use whatever free-text entry that tool offers; if it offers none, ask for the text in a plain message.

  • Otherwise, ask the same question in a normal message — number the

options, say what free-text answer is accepted — and end your turn and wait for the reply. Do not guess an answer or pick a default on the user's behalf.

Never add an option the step does not list just to get a free-text box. Either way, nothing that changes the graph (claiming a task, creating or starting a project or session) happens until the user has answered.

On invocation

Step 1 — Check args.

  • list → go to List tasks.
  • new → go to Create a task.
  • close → go to Close a task.
  • otherwise (no args) → go to Triage ready work.

Triage ready work

Call task_ready() (defaults to the current repo, ordered by priority). If the MCP call fails, tell the user the witan server is not connected and stop.

  • If there are no ready tasks, say so and offer Create a task.
  • Otherwise present the ready tasks as one question (see Asking the user):
  • Header: "Claim task"
  • Question: "Which task do you want to work on?"
  • Options: each ready task (label = title, description = "[priority] slug: {slug}"),

plus "Create a task" and "None".

  • Wait for the answer. Do not claim anything until the user has picked a

task; "None" means claim nothing and stop.

  • On a chosen task: claim it (that is what picking it means — see **Claim

before you work it). Call task_claim(slug="", session_id=""). task_claim sets in_progress with a lease and refuses if someone else holds it ({"claimed": false, "held_by": ...}) — surface that and offer another task instead of overwriting. On success confirm: "Claimed {title}** ({slug}). Close it with /witan-task close, or task_release it if you step away." Note: claims are advisory (a true atomic lock is a tracked follow-up), so a dead worker's claim auto-frees after its lease lapses and reappears in ready work.

Create a task

Ask these together (see Asking the user):

  1. "Task title?" (free text)
  2. "Type?" — options: task, bug, feature, chore, epic.
  3. "Priority?" — options: p2 (default), p0, p1, p3.

Wait for the answers before calling task_create.

Then gather, if relevant: a one-line description, a parent epic slug (parent), blocker slugs (blocked_by), a GitHub issue/PR URL (external_uri), and a project slug (project_slug). Call:

task_create(
    title="",
    description="",
    type="",
    priority="",
    parent="",
    blocked_by=["", ...],         # omit if none
    external_uri="",
    project_slug="",
)

For an epic with sub-issues: create the epic first (type="epic"), then create each child with parent="". Report the new slug(s).

List tasks

Call task_list() (optionally task_list(status="open"), task_list(project_slug=""), or task_list(parent="") to see an epic's children). Print a table: slug, title, type, status, priority, assignee, blockedby, externaluri. Group sub-issues under their parent when a parent filter is used.

Close a task

Ask which task (slug) and an optional resolution note, then:

task_close(slug="", resolution="")

Closing a blocker automatically makes its dependents eligible for task_ready. Confirm and, if useful, run task_ready() again to show what just unblocked.

Linking after the fact

To add a dependency, hierarchy, or provenance link to existing tasks, use task_link(from_slug, to_slug, kind):

  • blocks — from blocks to
  • parent — from (epic) is the parent of to
  • discovered_from — to is the source from was discovered from
  • addresses — from (task) addresses to (a Memory slug)

Undoing a link

task_unlink(from_slug, to_slug, kind) takes the same arguments and removes the link. Reach for it when one was recorded the wrong way round — the usual tell is a task showing as blocked by something it actually blocks.

Removing the last blocks link returns the task from blocked to open, so it shows up in task_ready() again. If the link wasn't there, the call reports removed: False and changes nothing; re-running is safe.

Linking tasks to code symbols

For a task scoped to specific code, attach code-graph symbol ids via symbol_refs so the work is discoverable from the code. Get ids from the witan-code tools — code_find_definition / code_search_symbol return them in the symbol_id field (#::):

task_create(
    title="Refactor Service.run for lazy init",
    description="...",
    symbol_refs=["https://github.com/mitodl/ol-django#app/svc.py::Service.run"],
)

symbol_context(symbol_id) lists the tasks and memories attached to a symbol — call it before editing that code to surface related open work.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.