Skip to content

Spec dialogue

Agent Smith isn't only a pipeline you point at finished tickets. It's also the thing you talk to before the ticket exists: a design partner that discusses the work with you, drafts the spec, files the ticket, and then runs it through the same pipelines as everything else.

Where you talk to it

You can hold the conversation in three places, and it behaves the same in all of them:

  • The dashboard. The Work it out page, with a project picker, a ticket search, uploads, a live view of what each turn is doing, and a pane that follows what you filed. It's the fullest surface, and the only one that can talk about an existing ticket or take uploaded material.
  • Slack or Teams. Send @Agent Smith /spec my-project in a channel. The conversation lives in that message's thread (in Teams, in that conversation), and every further message there goes to it; a new thread starts a separate one. /spec without a project works when only one project is configured, and lists the projects otherwise.

Each conversation is a session stored in the relational database, so the transcript survives a Redis flush, a server restart, a weekend. A conversation is scoped to one project and reads that project's repositories.

What grounds the answers

The agent on the other end is the design-partner-master. (A conversation about a ticket that has already run is led by the ticket-steward-master instead; see Work it out.) It answers from your actual code:

  • Each turn starts with the project's principles and context files, read straight from the repositories without a sandbox, and a repository's root DESIGN.md (design tokens as frontmatter, then prose) where there is one.
  • For anything deeper it opens read-only source sandboxes, one per repository. It can read, list, grep and search files, and fetch over HTTP through processes the server builds itself. It gets no shell, and nothing in a source sandbox is writable.
  • Sandboxes are held between turns, so only the first message of a conversation pays for spawning a container and cloning. The hold defaults to 180 seconds, set by sandbox.hold_seconds (or SANDBOX_HOLD_SECONDS) and overridable per project. A held sandbox is given up before a pipeline run is refused capacity. See Sandbox architecture.
  • A turn can fan out into read-only child agents to look at several things at once, bounded by max_sub_agents_per_dialog_turn and max_dialog_sub_agent_loop_iterations. See Multi-agent orchestration.
  • Material you hand it on the dashboard (uploaded sets and images, public URLs, Figma links once a design source is configured) is read, run and rendered with tools of its own. Work it out describes them.

The design partner stays on its project and, in a ticket's conversation, on that ticket. A question about neither gets one sentence pointing back to the project, and no tool call.

Discussion first, proposal after

The first reply to a new piece of work is always a discussion: what it found in the code, the edge cases, the open questions. It won't propose anything until you've answered. Once you have, and the conversation has converged, a turn may end in one of these outcomes:

  • An answer. Sometimes talking it through is the work. Nothing gets filed.
  • A document. You asked for a text to take elsewhere (a prompt, a summary, a brief), and it's written straight away. On the dashboard it shows with a Copy button. Nothing gets filed.
  • A bug ticket. Something's broken; a bug ticket lands in your tracker.
  • A spec. A schema-valid spec, the same YAML shape this project itself is built with.
  • An epic. A cut into several specs, in order, each with its requires: edges. It's still one piece of work: it files one ticket, and one run works the specs in order, a successor never starting before its predecessor verified.
  • A list of tickets. 2 to 20 ticket texts joined by requires, for work whose pieces should each merge on their own. Each ticket is filed separately and starts only once the tickets it requires have landed. Only a conversation with no ticket proposes one. See Ticket dependencies.

A spec or epic proposal goes through review rounds inside the turn that made it: fresh reviewers read each draft against the same repositories and answer build, build with cuts, or refuse, the design partner applies the cuts, and the draft is reviewed again, up to spec_review.rounds rounds. A list of tickets is reviewed as a plan instead. What the review finds is shown next to the proposal when you're asked to approve it, with its evidence. A review that couldn't be taken blocks nothing.

Before that review, the framework checks every fact the proposal cites, without a model call: a cited file that isn't there, or a line past its end, becomes a finding cited evidence does not resolve. Paths are routed the way the agent's file tools route them, with or without the repository prefix. What can't be checked honestly stays unchecked rather than becoming a false finding — a repository the turn never opened, a path that exists in two repositories, a template or reference, a file over 1 MB. Later slices of an epic aren't checked, because they describe a state their predecessors haven't built yet. The proposal panel shows the facts with their evidence and the assumptions, so you see what the proposal rests on before you approve it.

Confirming

Before anything is created you're asked, as Slack blocks, a Teams card, or a card on the dashboard. You can:

  • approve it, which files it;
  • reject it, which files nothing;
  • pick another shape. A bug offers Make it a spec; a spec offers Cut into several specs and Make it a bug ticket; an epic offers Make it one spec. In a conversation about an existing ticket the same shapes are offered, and approving continues that ticket instead of filing a new one (see Work it out). Picking a shape files nothing. It starts a new turn in that shape, and you approve the result;
  • reply with anything else, which is taken as a note, and the proposal is revised with it.

If the buttons can't be shown, a text reply in the thread still works. The question waits fifteen minutes; after that nothing is filed and your next message starts a fresh turn.

What approval files

Approval of a bug, a spec or an epic files exactly one ticket into the project's tracker, on all four trackers (GitHub, GitLab, Azure DevOps, Jira), and reports its key and URL. (A list of tickets files one ticket per entry; what that writes is on Ticket dependencies.) For a spec or an epic, in this order:

  1. The ticket. It carries one label, spec:approved (a tracker can rename it, see labels), and a short note in its body explaining what that label means. The note is removed before any model or pull request reads the ticket. Its work-item type comes from the tracker's work_item_kinds map, keyed by filing role: spec (or phase) for a single spec, work for an epic's ticket, bug for a bug. Without a mapping the tracker's default type is used.
  2. The approved set. The server records the approved specs against the ticket, together with the uploads and images the conversation holds, and writes the specs to the ticket's branch: one file per spec as .agentsmith/specs/planned/{id}-{label}.yaml, a mock a person ratified beside its spec as {id}-mock-v{n}.html, and one manifest for the whole series at .agentsmith/series/{base}.yaml. Every file of a series starts with the series' base id, so one ticket's files sit next to every other ticket's after a merge. If that branch write fails, the filing still stands and says so; the run that claims the ticket publishes the set from the stored record.
  3. The routing tag. When the project resolves tickets by tag, that tag is added so the poller routes the ticket to this project. An empty value, or one the tracker's label syntax can't carry, is reported instead of sent.
  4. The start. If routing would claim the ticket, it's moved into the project's first trigger status, which starts the run. On the dashboard that move needs the runs.control permission. An approval from Slack or Teams never moves a ticket. Either way, a ticket created in a status that already triggers starts on its own. When the ticket isn't started, the filing says why and what would start it.

The ticket reads like one a person would write: the goal first, then Why, What changes, Acceptance criteria, Out of scope and Preconditions, with no steps. Writing a ticket gives the same shape as a template for tickets you write yourself.

A bug is filed the same way without labels and without an approved set: the ticket, the routing tag, the start. It runs the code pipeline from its own text.

A run on a filed spec or epic works the approved set as it stands. It doesn't derive a spec of its own. If there's no set on the branch and no stored record, the ticket is parked, and the park names the path it looked at. A person can still type the spec label (or phase) onto a ticket to route it the same way.

Changing what was approved

Comments and edits on the ticket never re-cut an approved set. There are three deliberate ways to change it:

  • Ask the ticket steward. Once a run has worked the ticket, its conversation on the dashboard is led by the ticket steward, which re-cuts, drops and appends specs that haven't run, checks the result against the code and starts the next attempt when you say so, without a proposal card. See Work it out.
  • Approve again in the conversation. Approving a proposal in the ticket's own conversation continues the ticket by spec id: what ran stays, repeated ids are re-cut, new ids are appended, and the approval preview shows which before you approve. A ticket nobody has worked takes the approved series whole on every tracker, and a ticket a person closed gets a linked follow-up instead (see Work it out). With runs.control, the approval also starts the ticket's next attempt, waits for a run that holds it, or answers its hand-back; while a question waits, it records nothing and asks for the answer first (see When the next attempt starts). Anyone who may write dialogs can continue a ticket's conversation.
  • Ask on the ticket. A comment that opens with the line cut this specification again makes the next run cut the specification again. It counts when it's newer than the last approval and than the run's last "this is how I understood the ticket" comment, and it's for people who can reach the ticket but not the branch. The new cut is recorded as approved by whoever wrote that comment.

A conversation that has filed work is told, every turn, that it cannot change, close or re-cut what it filed, and that describing filed work as changed would be false. The one thing it can do is withdraw: close a ticket it filed that nothing has claimed yet. Once a run holds the ticket, that's refused and you stop the run from the run controls.

Long conversations

A conversation can run for days. Up to 60,000 characters, each turn reads the whole transcript. Past that, the older turns are folded into a digest, written by the summarisation model and stored as a turn of its own, and each turn reads the digest, then the important older turns word for word, then everything after the digest. The important ones are your first message (the requirement as you stated it), every filing and mock ratification, the newest proposal, and your answer to each proposal. A fold leaves about 30,000 characters of recent conversation in full and keeps at least the last two turns, so the next fold comes only after that much new conversation. Every turn stays stored; the digest only changes what a turn reads. The design partner and the ticket steward read through the same window.

The ticket is a conversation, not a string

When a run picks up a ticket, the master gets the whole thing:

  • The comment thread, so an instruction clarified three comments deep actually reaches the agent. GitLab system notes are filtered out; you don't want "changed the milestone" in the prompt.
  • Image attachments: screenshots of the broken UI, architecture sketches. Sent as image parts when the model has vision (supports_vision on the agent config, default true, capped at 10 images), noted as not viewable otherwise.
  • Text documents. A PDF or docx attached to the ticket is converted to markdown in the sandbox and included. One broken document doesn't fail the run; it's skipped with a note.

There's a contract on what the ticket text may tell the agent. In-scope instructions are followed. Destructive or suspicious ones ("delete the test suite", "ignore your review rules") are refused and logged as ignored instructions, so a poisoned ticket is visible instead of silently obeyed.

The clarification gate

The flip side of taking tickets seriously: refusing to invent scope when the ticket is too thin. A title-only ticket, an empty body, a planner that genuinely can't tell what's wanted: the run doesn't guess. It posts its open questions as a comment on the ticket, mentions the assignee (or the reporter, if nobody is assigned) so someone gets notified, and parks the ticket in your configured needs_clarification_status. You answer in the tracker; the run resumes with your answer. No half-baked PR, no burned tokens on invented requirements.

Mid-run questions work the same way: the run checkpoints and waits durably rather than holding compute. How that works is its own page: Expectations & durable dialogue.

Next