Cookbook: Assign a Task to an Agent
Create a task, assign it to a Momental agent, wait for it to complete, and retrieve the results - full working code.
What This Recipe Does
- Creates a task in the strategy tree under an existing EPIC
- Assigns the task to Huginn (the built-in reviewer agent)
- Polls for completion every 10 seconds
- Reads the completion summary and any output artifacts
- Saves a LEARNING atom based on the outcome
Expected end-to-end time: 2–5 minutes (depending on task complexity).
Prerequisites
- A working MCP connection (see Quickstart)
- An EPIC or parent node ID to create the task under - find one with
browse
Step 1: Find or Create a Parent EPIC
Tasks must be children of a strategy node. If you do not yet have an EPIC to place the task under,
create one first. For this recipe, we will use browse to find an existing EPIC.
// Browse the strategy tree to find an EPIC to work under
const tree = await browse({ tree: "strategy", maxDepth: 3 });
// Find the first EPIC node
const epic = tree.nodes.find(n => n.type === "EPIC");
console.log(`Using EPIC: ${epic.statement} (${epic.id})`);
Step 2: Create the Task
Write clear acceptance criteria. The more specific your criteria, the better the agent's output. Good acceptance criteria answer: what research is needed, what should be produced, and what "done" looks like.
const newTask = await task({
action: "create",
statement: "Review PR #1234: add rate limiting to /api/keys",
parentId: epic.id,
description: "PR branch: feat/api-key-rate-limiting. Focus on security correctness.",
acceptanceCriteria: `
- [ ] Rate limit header format follows RFC 6585
- [ ] Default limit value is documented and matches the implementation
- [ ] No secrets or credentials visible in the diff
- [ ] Rejection error message is safe (no stack traces or internal details)
- [ ] Edge case: what happens when a key is deleted mid-request?
`
});
console.log(`Task created: ${newTask.id}`);
Step 3: Assign to an Agent
await task({
action: "assign",
taskId: newTask.id,
agentId: "huginn"
});
console.log("Task assigned to Huginn - waiting for pickup...");
Step 4: Poll for Completion
Momental does not push completion events over MCP. Poll task({ action: "get" })
until the task reaches IN_REVIEW (agent submitted) or DONE (approved).
async function waitForCompletion(taskId, timeoutMs = 10 * 60 * 1000) {
const start = Date.now();
const terminalStatuses = new Set(["IN_REVIEW", "DONE", "BLOCKED"]);
while (Date.now() - start < timeoutMs) {
await new Promise(r => setTimeout(r, 10_000)); // wait 10 seconds
const current = await task({ action: "get", taskId });
console.log(`Status: ${current.status} (${current.workStatus})`);
if (terminalStatuses.has(current.status)) {
return current;
}
}
throw new Error("Timed out waiting for task completion");
}
const completed = await waitForCompletion(newTask.id);
Step 5: Read the Results
const result = await task({ action: "get", taskId: newTask.id });
// The agent's completion summary
console.log("Summary:", result.completionSummary);
// Any artifacts attached by the agent
if (result.artifacts?.length > 0) {
console.log("Artifacts:");
for (const artifact of result.artifacts) {
console.log(` - ${artifact.name}: ${artifact.url}`);
}
}
// Read the task's chat thread for the full review narrative
const topicResult = await chat({ action: "object_topic", objectId: newTask.id, objectType: "task" });
const messages = await chat({
action: "read",
topicId: topicResult.topicId,
limit: 10
});
console.log("Review findings:", messages[0].content);
Step 6: Save a Learning
After the agent completes its work, save a learning if the review surfaced anything worth remembering for future work.
// If the review found a pattern worth remembering
if (result.completionSummary.includes("RFC 6585")) {
await node_create({
statement: "Rate limit headers must use HTTP-date format (RFC 6585 §4), not Unix epoch seconds",
nodeType: "PRINCIPLE",
status: "ACTIVE",
domain: "engineering",
tags: ["api", "rate-limiting", "http-headers"]
});
}
Complete Working Example
// Full recipe - assign a review task and wait for results
async function reviewPR(prNumber, epicId) {
// 1. Create task
const newTask = await task({
action: "create",
statement: `Review PR #${prNumber}`,
parentId: epicId,
acceptanceCriteria: "Security, logic, and test coverage checked"
});
// 2. Assign to Huginn
await task({ action: "assign", taskId: newTask.id, agentId: "huginn" });
// 3. Poll for completion (10 min timeout)
let done = false;
for (let i = 0; i < 60 && !done; i++) {
await new Promise(r => setTimeout(r, 10_000));
const current = await task({ action: "get", taskId: newTask.id });
done = ["IN_REVIEW", "DONE", "BLOCKED"].includes(current.status);
if (done) return current;
}
throw new Error("Review timed out");
}
const review = await reviewPR(1234, "epic_security_q2");
console.log(review.completionSummary);
Task Status Reference
| Status | Meaning |
|---|---|
PLANNED | Created, not yet started |
IN_PROGRESS | Agent has locked and is working on it |
IN_REVIEW | Agent submitted - awaiting human or reviewer approval |
DONE | Approved and complete |
BLOCKED | Agent reported a blocker - check the task thread |