| name | task-api |
| description | Programmatic interaction with the agent-taskboard task API over HTTP - create, move, triage, reissue, and reorder tasks when clicking through the board UI would be too slow. |
| trigger | user |
| mutates_code | false |
| mutates_queue | true |
| sentinel | TASKBOARD-SKILL-TASK-API |
Skill: Task-API
Sentinel: TASKBOARD-SKILL-TASK-API. Echo this string in your first reply when the orchestrator probes whether the skill loaded.
Programmatic interaction with the agent-taskboard task API. Read this before
creating or moving tasks via HTTP, especially when the task you are doing
includes "add to the queue", "reissue this task", "drop the task to archive",
"sort the task to the top", or "create a follow-up task".
The board mostly drives state via the UI, but agents (including you, future-me)
need a reliable scripted path because:
- The board cannot create 30 follow-up tasks in a row efficiently.
- Triage of 100+ items belongs in a script, not a click marathon.
- Codex, Claude Code, Copilot, and Gemini all need to be able to do this
identically; the convention lives here so every CLI picks it up.
When to invoke
- The user asks for "lege eine Aufgabe an" / "create a task" / "queue a follow-up".
- A triage step needs to move many tasks between lanes.
- You want to surface a finding as a new ticket the orchestrator will pick up.
- You need to promote a hot bug to the top of the queue.
Server contract
Stable backend listens on http://127.0.0.1:5031. Dev backend on
http://127.0.0.1:5030 (when running). The two have separate workspaces; pick
the one whose project list contains the project you are targeting.
Every mutating request must carry the X-Client-Id: local-default header.
The ClientIdentityMiddleware rejects mutations without it as 401. Read
requests do not need the header but it is harmless to include.
Use /api/tasks. The route is canonical; the former /api/jobs
compatibility alias has been removed (see
ADR-0057).
The raw watchPath key and the path-versus-shortCode direction are explained in
../../../docs/wiki/concepts/api-project-identity-and-watchpath.md.
Common pitfall: the watchPath quirk
Every mutation that targets a specific task needs a watchPath query parameter.
This is not the project's source-tree path; it is the resolved task-folder
root that GET /api/watch-paths returns under the path field.
For the agent-taskboard project the call is GET /api/watch-paths:
[
{
"name": "Runbook",
"path": "C:\\Projects\\agent-taskboard-workspace\\projects\\runbook",
"rootPath": "C:\\Projects\\Runbook\\App",
"repositoryPath": "C:\\Projects\\Runbook"
},
{
"name": "Agent Task Processor",
"path": "C:\\Projects\\agent-taskboard-workspace\\projects\\agent-taskboard",
"rootPath": "C:\\Projects\\agent-taskboard-devspace\\agent-taskboard-dev"
}
]
Use the path field, not rootPath. The server resolves tasks against that
path; using rootPath returns 409 Job already exists or invalid input even
when the slug is unique.
For self-contained projects (no .orchestrator.yml pointer) the path is
<rootPath>\.orchestrator\jobs. Always read the live response; never hard-code.
Hard rules
- Always include
X-Client-Id. Even on GETs. The Angular interceptor
adds it for browser requests; the drift rule frontend-fetch-xclientid
exists because three sites forgot it.
- Always read
/api/watch-paths first if you do not already know the
exact path for your project. Caching it in a script run is fine.
- Use Node.js, not curl. Windows backslashes in JSON bodies break shell
quoting. The reference scripts in
scripts/ handle this; copy
them rather than hand-rolling curl.
- One task per request. No bulk-create endpoint. Loop in your script.
- Slugs are stable. Do not include timestamps in
id unless you actually
want a fresh per-attempt artifact. Stable slugs keep history linkable.
- Lane targets use the full lane name, e.g.
2-ready or 6-completed
(not ready / completed). See references/states.md.
- Failed-pickup orphans are not regular tasks. Do not move them with the
state API if they have no
job.json. Use a dedicated API recovery/delete
path when one exists; otherwise stop and ask for an explicit operator
decision before filesystem cleanup.
agent and cliType must be real CLI values. Use claude, codex,
copilot, or gemini, and keep them aligned unless you are deliberately
testing drift handling. Do not use agent: "human" to keep a card visible;
park visible non-running work by choosing an appropriate lane such as
0-backlog or 5-human-review.
Process: finding existing tasks
Before creating a new task, check whether one already exists. The server has
no ?q=-search endpoint today; it returns the full set and you filter
client-side. The full payload is small (~1-2 MB for hundreds of tasks).
node scripts/find-tasks.js
node scripts/find-tasks.js --lane 2-ready
node scripts/find-tasks.js --grep "codex"
node scripts/find-tasks.js --project "Lotta"
node scripts/find-tasks.js --lane 4-auto-review --grep "session"
Output: <lane>\t<project>\t<slug> - <title>, one line per match. Pipe to
grep for further narrowing.
Underlying call: GET /api/tasks/grouped. Returns
{ backlog: [], preparation: [], ready: [], progress: [], ... }, each value
an array of JobInfo-shaped objects with id, title, projectName,
state, cliType, etc.
Process: creating one task
const http = require('http');
const watchPath = 'C:\\Projects\\agent-taskboard-workspace\\projects\\agent-taskboard';
const task = {
id: 'my-stable-slug',
title: 'Human-readable title (shown on the card)',
targetState: '2-ready',
order: 0,
taskType: 'bug',
agent: 'codex',
cliType: 'codex',
watchPath,
promptMarkdown: [
'## Context',
'',
'Markdown lines as a string array, joined with \\n.',
].join('\n'),
};
const body = JSON.stringify(task);
const req = http.request({
hostname: '127.0.0.1', port: 5031, path: '/api/tasks/', method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Client-Id': 'local-default',
'Content-Length': Buffer.byteLength(body),
},
}, res => {
let d = '';
res.on('data', c => d += c);
res.on('end', () => console.log('status:', res.statusCode, '| body:', d.slice(0, 200)));
});
req.on('error', e => console.log('ERR', e.message));
req.write(body); req.end();
A 200 with {"id":"..."} means success. A 409 with "Job already exists or invalid input" is the most common failure; nine times out of ten the cause is
the watchPath not matching /api/watch-paths.
The full template is in scripts/create-task.js.
Process: moving a task between lanes
POST /api/tasks/{jobId}/move?watchPath=... with body {"targetState":"6-completed"}.
const body = JSON.stringify({ targetState: '6-completed' });
const path = `/api/tasks/${encodeURIComponent(jobId)}/move` +
`?watchPath=${encodeURIComponent(watchPath)}`;
Common targets:
6-completed (logically completed; physically lands under 7-archive)
2-ready (re-queue / reissue)
5-human-review (parking lot for "I need your call")
7-archive (drop without ceremony)
The full template is in scripts/move-state.js.
Process: promoting to the top of 2-ready
POST /api/tasks/{jobId}/move-to-top?watchPath=... with no body.
Use this when a hot bug needs to be picked up before the existing queue, but
the task is already in 2-ready or 0-backlog. The state machine handles the
atomic reorder on disk.
Reference: scripts/move-to-top.js.
Process: bulk triage
Triage rolls do not fit in one mutation. Pattern:
- Read the folder contents via
fs.readdirSync, classify each entry
(status.md + aspect-*.md) into action buckets.
- For each bucket, loop the mutation script.
- Idempotent prompt-append: when reissuing, check if the prompt.md already
contains your "Human Review Note" marker so re-running the script does not
stack duplicate notes.
The full reusable triage script is in scripts/triage-lane.js.
Output expectations
When you run any of these scripts, report the count of successes + failures
and any unusual statuses. A silent 200 is fine; a 409 or 404 needs to be
shown to the user with the offending slug so they can dedupe.
For larger triage operations, also produce a one-line summary per bucket
("moved 45 to completed, 12 reissued to ready, 3 unclear").
Anti-patterns
- Curl with inline JSON strings. Backslashes in Windows paths break shell
quoting. The two times I tried it in the same session both failed; the Node
template never failed.
- Hard-coding
rootPath as watchPath. Returns 409 every time.
- Forgetting
X-Client-Id. The 401 returns no body, and the error message
is dropped on the floor in some calling code paths.
- Bypassing the API by editing folders directly. The in-memory cache stays
stale until invalidation; only do this for shell folders that the API
cannot see (e.g. an orphan with no
job.json).
- Hand-rolling YAML escapes for prompt content. Use the
[...].join('\n')
pattern so multiline markdown stays readable in source.
- Creating duplicate slugs for "test" tasks. They land as 409 conflicts.
Add a suffix only if you want a fresh attempt artifact, never as a habit.
Reference