How Solstice is moving from hand-wired creative workflows to agents that choose their own steps, and the structure we are keeping.
Arindam Sharma
·
8 min read
Why we’re letting AI agents choose the workflow
Move across the blue bands to set the curtain flowing, with a soft shimmer following each band. Click or tap for a springy ripple. With the artwork focused, use left and right arrow keys to explore, Home or End for the first or last band, Enter or Space to bounce, and Escape to return to rest. Motion follows your system preference and can also be turned off.
Before we called it graph engineering
We kept adding branches because people kept asking for things our branches did not cover.
At Solstice, a request to make an email can involve choosing evidence, writing copy, interpreting a reference, placing images, preserving required language, compiling a document, and checking the result in a browser. We built explicit workflows for that work. Then we built more workflows for the variations.
Our older implementation is a record of those decisions: separate routes for different kinds of email, banners, social posts, slides, video, and webpages. Inside those routes, LangGraph workflows divide the work into named steps and connect them through shared state.
We were doing the work before we had much reason to call it “graph engineering.” We had a product to ship and a sequence of decisions that needed to happen reliably.
Now we are moving more of the creative work into agents that can choose their next action from the task and the result of the previous one. The migration is still underway. The new generation path currently covers email, banners, and social assets; the older system handles a broader set of formats.
We still write plenty of ordinary control flow. We are changing which decisions we make in advance.
The graphs earned their place
A workflow graph is a map of a process. A node performs a step; an edge determines what can happen next. A conditional edge chooses among routes based on the current state. The state carries the information collected and produced along the way.
One of our older email graphs groups claims, filters them, adds surrounding copy, checks the email for cohesion, and applies visual edits. It can route a visual consistency check back to the editor. It can wait for user feedback and send that feedback into another edit. These were already systems that used models, tools, and loops.
Another email workflow makes the production sequence more explicit: plan the content, assign evidence to sections, write the substantive copy, add microcopy, allocate the final content, prepare references, and design the header. The default rendering branch creates MJML, compiles it into email HTML, runs screenshot-based visual QA, and continues through cohesion and compliance checks.
MJML is an authoring format for responsive email. We use a compiler because email clients have their own layout constraints; a browser-friendly page alone is not a sufficient email artifact.
Those separate steps gave us places to inspect intermediate output and improve a particular part of the process. If the references were wrong, we had a references step. If the rendered email looked wrong, we had a visual pass. The division made the system easier to reason about while the shape of the request stayed familiar.
Conceptual comparisonFig. 01
Who chooses the sequence?
An explicit workflow
Plan content
Assign evidence
Write + design
Review + revise
Visual QA
Compile + render
Feedback can loop back
The application names the stages and routes. Older Solstice workflows already contained models, tools, and feedback loops.
A model-directed loop
Read context
Edit the artifact
AgentChoose the next action
Retrieve evidence
Render + inspect
The application prepares capabilities and boundaries. Within them, the agent chooses the order based on what the work needs.
Conceptual comparison. The older architecture was already agentic; the migration changes how much of the creative sequence is selected at runtime.
Then the request stopped fitting the route
Consider an illustrative request: “Use this layout, keep the approved copy, replace the hero image, and make the mobile version less cramped.”
That is a reasonable instruction from a person. It crosses several categories in a workflow. Understanding the layout may require inspecting a reference. Keeping the copy limits what the writing step may change. A new image can change the balance of the whole page. A mobile fix may require revisiting spacing after the desktop layout already looks finished.
We can encode every one of those actions in a graph. We can also add conditional routes and loops. The cost arrives when we have to decide, in advance, how they interact with every other supported request.
An extra route creates questions about the state it receives, the work it repeats, the checks it skips, and the edits it invalidates. If the layout changes after a reference pass, which checks need to run again? If only the image changes, how much of the content process should restart? The flowchart is only the visible part of that maintenance.
Our old orchestration code shows the accumulation: content-specific branches, reference-specific paths, alternative renderers, and multiple generations of pipelines living together. It shows how much product behavior had become routing logic.
When users changed their requirements, we too often had another routing problem to solve before we could handle their request well.
Let the agent choose the next useful action
In the newer architecture, the model gets a task, a working environment, and a defined set of tools. It can inspect what is available, make a change, observe the result, and decide what to do next. Tool results become input to the next decision.
For a visual task, an illustrative sequence could be: inspect the reference, read the current document, change the layout, render it, notice that a heading wraps badly, and revise that heading’s container. Another request may need a small copy edit and one render. We want the work to follow the problem actually present in the document.
This is the distinction between a predefined workflow and model-directed tool use described in Anthropic’s discussion of effective agents. Our older system already mixed both patterns. We are giving the model more responsibility for the sequence inside the creative task.
The runtime still owns the mechanics of a turn, including event delivery, interruption, and the handling of incomplete work. The backend owns the operation lifecycle and persistence. A model does not need to rediscover how to save a draft safely every time it adjusts a heading.
We also keep generation and editing distinct. Creating a draft from a brief and changing an existing document have different starting conditions and product expectations. They can share infrastructure without pretending to be the same operation.
Illustrative loopFig. 02
A result becomes the next decision’s input
01InspectRead the artifact and relevant context
02ActChoose an available tool and make a change
03ObserveUse the result to decide what happens next
Example: a heading wraps badly after an image change. Inspect the rendered result → adjust the container → render again. A different request may require a different sequence.
An illustrative creative loop. The runtime still manages persistence, interruption, and the operation lifecycle.
Compose capabilities instead of another end-to-end pipeline
We organize the new agent application around capabilities. A capability contributes the pieces needed for one kind of behavior: relevant workspace files, tools, instructions, and, where appropriate, verifiers.
An agent composes a set of those capabilities for the operation. The email generation agent, for example, combines email authoring with image inspection, claims context, domain rules, and a brand guide. Approved design examples are included when they are available. The editing agent can add reference-layout behavior when the request carries a reference, or stricter fidelity behavior when the task calls for preserving it closely.
These are concrete choices made from the request package at initialization. “General purpose” describes the agent’s ability to work through a problem using those capabilities. It does not mean every session receives every tool.
Before running, we validate the composition. It must contain one content capability. Required capabilities must be present. Two capabilities cannot silently register the same tool name or claim ownership of the same workspace output. Invalid combinations fail during setup.
This moves a useful amount of complexity into small, inspectable contracts. Adding reference fidelity should mean defining its context, tools, and checks, then composing them where needed. It should not require teaching every layer of the runtime another content-specific exception.
We also keep the application separate from the adapter that connects it to a particular agent runtime. The adapter translates the tool and session contracts. The application continues to own the behavior we are building for Solstice.
Freedom inside a prepared workspace
“Let the agent do whatever it wants” is a useful expression of frustration with an over-specified process. It is a poor description of an operating system.
We want freedom over the approach to a task. We still decide which content types the system supports, which capabilities are available, which files belong to the operation, and how results enter the product. The new generation dispatcher rejects unsupported content types. The application tool roster is sealed for the session; a mode can narrow that roster.
Preparing the context is part of this work. The backend sends a persisted package for the operation. The agent’s workspace turns the relevant parts into files it can inspect, with ownership assigned to the component that produces each file.
The ordinary email generation agent even narrows the package before deriving its context. It keeps the original creative request and the resolved audience, while removing the detailed blueprint and raw reference attachments from that view. Reference-led requests have their own compositions.
That choice gives the email agent room to design from the brief and brand material without treating an upstream implementation plan as the user’s creative instruction. It also makes the distinction inspectable in code.
A bigger context window does not decide which information should govern a task. We still have to make that decision. Giving an agent every intermediate plan would make it harder to tell which instructions express user intent and which describe a previous implementation’s assumptions.
Division of responsibilityFig. 03
Autonomy has a defined scope
01The application prepares
Supported task
Generation or editing; supported format
Available capabilities
Context, tools, instructions, checks
Operation lifecycle
State, persistence, interruption
02The agent decides
What to inspect
Choose relevant material for this task
What to change
Select a useful next action
What to revisit
Respond to tool results and findings
General-purpose reasoning operates inside the capabilities prepared for the task.
A finished turn still needs a definition of done
When the model chooses the sequence, we need to inspect what it produced. The model saying it has finished tells us that its turn ended. We also need evidence about the artifact.
Our capability system can attach verifiers to the resulting work. The evaluator runs applicable measurement checks before judgment checks. That ordering lets concrete findings reach the evaluation process before more interpretive review. Verifiers can request bounded follow-up work, including a fresh review after apparently clean measurements.
The limits are deliberate. An open-ended repair loop can keep spending time on a document without reaching an acceptable result. The runtime has step budgets and interruption handling. A verifier failure is recorded as a failure; an exception in a check is not treated as evidence that the asset passed.
Completion policy still differs across paths. Some compositions can require verified generation and reject an incomplete result. The ordinary email generation composition currently allows a draft to survive with an explicit warning when the step budget or render verification leaves work unresolved.
These checks answer different questions. A compiler can tell us whether an artifact can be built. A render check can expose layout problems. Neither proves that the design feels right for the audience. Human review and preference evaluation remain necessary for the choices that technical validity cannot settle.
Keep the structure that pays for itself
We are keeping explicit orchestration where the order is known and mistakes are expensive. Acquiring an execution environment, identifying the active operation, delivering events, saving a version, and cleaning up after a turn are ordinary software responsibilities. We want those paths to be predictable.
We are giving agents more discretion where the necessary work depends on what they discover: interpreting a reference, finding relevant evidence, revising a composition, and deciding which part of a draft needs another pass.
The old graphs also contain behavior worth preserving. Screenshot feedback, evidence handling, and user-directed revision did not become obsolete because we changed the surrounding execution model. We have to carry their purpose forward and verify that the new approach still handles the cases they existed to solve.
The migration introduces costs of its own. Tool choice can be wrong. A model can repeat work, miss a constraint, or spend too long polishing a minor detail. A smaller flowchart does not prove a better product. We need to compare completed artifacts, user corrections, elapsed time, and execution cost across the requests we actually support.
For the next capability we move, the test is concrete: can the agent complete the supported task with the right evidence, preserve the required content, produce a usable artifact, and surface unfinished work? If a small fixed sequence does that more reliably, we will keep the sequence.