Overview

The first version of the assistant planned before it acted.

For a request with several steps, it generated a plan, rendered every action in a review card, waited for approval, and then executed the steps in order. That architecture made hidden actions visible and gave cross-step references an explicit home.

It also made ordinary conversation feel like workflow software.

The next version kept the safety boundaries but changed where planning lived. Instead of exposing a plan object for every compound request, a conversation agent now chooses and chains typed tools inside a bounded loop.

The improvement was not “more autonomy.” It was fewer competing abstractions.

Before: Three Execution Paths

The original system had separate routes for:

  1. Atomic actions.
  2. Multi-step plans with explicit approval.
  3. Open-ended conversational tool use.

Over time, these paths drifted. The same design-analysis action could produce different response shapes depending on whether it came from free text, a suggestion button, or an approved plan. Fixes had to be repeated across branches.

The plan card also imposed the same ceremony on very different risks. Creating several dependent work items deserved review. Reading a sprint and then listing its items usually did not.

After: Two Paths, One Executor

The current architecture has two entry paths:

free-text message
  -> conversation agent
  -> choose tool or answer
  -> execute tool
  -> observe result
  -> repeat within hard limit

shortcut / suggestion / missing-field reply
  -> deterministic resolver
  -> execute tool

both
  -> shared result contract
  -> shared card predicate
  -> shared renderer

The conversation agent receives full history and may chain tools, but it cannot invent new capabilities. Each action still passes through typed schemas, permission checks, and the same executor used by deterministic UI flows.

This convergence fixed more than duplicated code. It made behavior explainable: once a tool has returned a result, presentation no longer depends on how the request entered the system.

Dispatch and Synthesis Are Different Jobs

Choosing a tool is classification. Explaining a result is synthesis.

Treating both as the same LLM task wasted latency and reasoning budget. The revised system uses a lower reasoning tier for dispatch and a stronger one only when a user-visible response needs composition.

Many read operations skip synthesis entirely. Structured results pass through deterministic formatters, producing stable links, chips, and tables without paying for another model call.

The agent reasons where ambiguity exists and gets out of the way where it does not.

Design Planning as a Selectable Contract

Design generation also moved away from a single opaque prompt.

The assistant first analyzes a work item and returns two selectable lists:

  • considerations the design should address
  • sections the document should contain

The user can remove irrelevant concerns or add missing sections before generation. The resulting document always ends with explicit key decisions.

For a batch of related work, those decisions become context for the next item. A later design does not reopen the cache, locking, or API choices already settled by an earlier one.

This is lightweight architectural memory: not a generic chat transcript, but a compact list of decisions with downstream consequences.

Independent Story Criticism

Generated work items are reviewed by a separate critic across dimensions such as completeness, clarity, acceptance criteria, scope, persona alignment, and decomposition.

The author does not grade itself.

When revision is needed, the user sees dimension-level suggestions and chooses which ones to apply. Approved dimensions are rewritten surgically; rejected suggestions leave the original content intact. Repeated failure escalates with the review history rather than silently cycling.

Sibling work is included in the review context, so a UI story is not penalized for behavior deliberately owned by an API story.

The critic is a gate with human control, not an automatic rewrite loop.

UI-First Prototyping

UI work takes a different path before design generation.

The assistant selects the closest screen from a regularly refreshed snapshot catalog of the real application. It combines that template with route-specific sample data and asks the LLM to apply only the requested change.

The prototype then runs through a browser smoke test. Labels, filters, dialogs, and primary interactions must work—not merely render.

This lets the design discussion start from the current product instead of an LLM’s memory of what enterprise software tends to look like.

Reliability Lessons

Several smaller changes mattered as much as the architectural rewrite:

  • Cap message count and total content before prompts are assembled.
  • Sanitize coding-agent CLI output before it reaches user-visible synthesis.
  • Cache expensive analysis, but refetch mutable work-item data before generation.
  • Make critic availability an explicit state; “unknown” is not the same as “disabled.”
  • Preserve confirmation flags across scope and planning transformations.
  • Bound in-memory design and conversation state with eviction.

Agent systems rarely fail at the center of the happy path. They fail in the seams between state representations.

What Worked

  • Replacing explicit plans for routine compound work with bounded tool chaining.
  • Keeping deterministic shortcuts for unambiguous UI flows.
  • Making all paths converge on one executor and response contract.
  • Persisting decisions instead of replaying entire conversations.
  • Separating author, critic, and human approval roles.

What Was Hard

  • Removing the legacy planner without changing visible behavior.
  • Preserving context across tool calls without passing oversized raw results back to the model.
  • Distinguishing critic-disabled, critic-unavailable, and critic-pending states.
  • Preventing cached analysis from turning into stale generation input.

Takeaway

An agent does not become simpler when it does more reasoning.

It becomes simpler when every path shares the same tools, contracts, and boundaries—and reasoning is reserved for the parts that are genuinely ambiguous.