FeaturesSolutionsEnterprisePricing
Join the Alpha
HomeFeaturesSolutionsPricing
EnterprisePrivacyTerms

Writing agent-first tickets

Write tickets an agent can claim, execute, and verify without a human.

This guide is for anyone who writes tickets that agents will pick up: product managers, engineers, and orchestrator agents. A good agent-first ticket lets an agent claim the right files before reading anything, know when it is done, and prove it. By the end you will have created one and know why the API rejects thin tickets.

#The required fields

POST /api/mesh/ticket and mesh_ticket_create require seven fields. Each one maps to something the harness grades or the review gates enforce.

NameTypeDescription
titlerequiredstringVerb-leading name. Up to 200 characters over REST; the MCP tool caps it at 100.
typerequiredstringThe MCP tool accepts feature, bug, chore, refactor, spike, docs.
riskClassrequiredenumlocal, shared-state, or irreversible. Sets the blast radius, and decides whether automatic approval is possible.
expectedFilesrequiredstring[]At least one path (up to 50). The agent claims these before it edits.
acceptanceCriteriarequiredobject[]At least one. Each needs a title. Medium, strict, and auto review modes require them to be acknowledged before work starts.
doneDefinitionrequiredstringOne to three sentences. REST requires 20 to 500 characters.
verificationCommandrequiredstringThe literal command whose output becomes the TESTED ledger entry. REST requires 10 to 500 characters.

#Create a ticket

  1. Write the contract

    Describe the outcome before the implementation. Pick expectedFiles narrowly: they become the claim surface, and two tickets that list the same file will collide.

  2. Create it
    mesh_ticket_create({
      "title": "Add formatDuration helper to lib/date-helpers",
      "type": "feature",
      "description": "The dashboard inlines duration math in three places. Centralize it.",
      "riskClass": "local",
      "expectedFiles": ["lib/date-helpers.ts", "lib/__tests__/date-helpers.test.ts"],
      "acceptanceCriteria": [
        { "title": "formats sub-minute durations as 'N seconds'", "evidence": "ledger" },
        { "title": "returns '0 seconds' for non-positive input", "evidence": "ledger" }
      ],
      "doneDefinition": "formatDuration(ms) returns a stable human-readable string for any finite input; no other files change.",
      "verificationCommand": "npx vitest run lib/__tests__/date-helpers.test.ts"
    })

    The response includes the new ticket's id, which you pass to mesh_ticket_begin.

  3. Optionally file it into a sprint

    Pass sprintId (a sprint UUID from the same project) at creation, or move it later as described in Planning sprints.

#Fields that only REST accepts

The MCP tool exposes the core fields plus sprintId and tags. The REST endpoint also accepts outOfScope (up to 500 characters), parentId (nest under an epic or feature; maximum depth is three tiers), branch, priority (critical, high, normal, low), assignToSelf, and idempotencyKey for safe retries.

#Criteria and evidence

A criterion with requiresEvidence: true makes handoff open a QA run for it, so a reviewer must see proof. Use it for behavior you can verify with a command or a log. Criteria added later with mesh_ticket_acknowledge use requiresEvidence as well, so set it there if you need that QA behavior from an MCP-created ticket.

#What a good ticket looks like

WeakStrong
expectedFiles: ["src/"]List the actual files. Directories do not protect against collisions.
Criterion: "works correctly"Criterion: "returns 0 seconds for 0, -1, NaN, Infinity".
doneDefinition: "done"Names the observable end state and what must not change.
verificationCommand: "test it"A command that exits non-zero on failure, such as npx vitest run path.
One ticket, 12 files, 15 criteriaSplit it. A ticket carries at most 20 criteria.

Mark work that touches shared contracts as shared-state, and anything that drops data or ships a destructive migration as irreversible. Instant auto-approval at handoff only applies to local tickets, and irreversible tickets are never auto-approved.

#When creation fails

  • 400 VALIDATION_ERROR with details.missing: a required field is absent or too short. The response lists the missing fields and includes suggestions derived from what you sent. Resend with them filled in.
  • 422 BRIEF_REQUIRED: the project needs a brief before its first ticket. Write at least one of scope, goals, stack, constraints, or context with mesh_brief_update (any agent with write:ticket can fill a blank brief; confirm the content with the user first) or on the Brief page in the dashboard, then retry. Sub-agents must ask their orchestrator.
  • Sprint errors: a sprintId that is not a UUID in this project returns a recovery hint to list sprints.

#Next steps