Hot reload was killing ten-minute jobs
Some of the work this platform does takes ten to fifteen minutes, a model building a page across many steps. Hot reload killed every one of those jobs...
|
Jun 7, 2026An agentic pipeline generates large objects. A page is one. It is structured, many blocks deep, each block a piece of typed data. And it is not built by one call. An agent builds the object by calling sub-agents. Those sub-agents call sub-agents of their own. A finished page is the output of a tree of nested agentic work.
When the tree finishes, an auditor reads the object. The auditor is another agent, and it runs two passes. The first validates the data against the schema, which catches a malformed property, a value drifted out of range, or a block that came back in a form the rest of the object cannot use. The second looks at the rendered page and pulls out anything that passed validation and still looks like shit. A block can be structurally perfect and wrong on the page. Most of the time the object is fine. Sometimes one part is not, and the question is what you do about that one bad part.
The obvious answer is to generate the object again. You run the step that made it once more and hope the second draft is clean. It usually is. It is also the most expensive fix there is. A large object costs a lot of tokens and a lot of seconds to produce, and you are paying all of that to correct a single property a much smaller effort could have repaired. A regenerated object is a new draft carrying its own risks. The property that was wrong might come back right. Three that were right might come back wrong. You spent tokens to throw away work that was already correct.
Sub-agents are how this pipeline does almost everything. A step that matters has children, and often children of their own, so the tree runs wide and deep. Every sub-agent is there for a reason, and every one is a call that can fail. The odds that an entire tree comes back clean are the odds of every call in it succeeding at once, and those odds fall off fast as the tree grows. You can hold each call to a declared schema, and the schema is rarely the trouble. A call can still die on a transient error, or simply not return. A full regeneration re-rolls every one of those calls. On a deep tree it can fail, and fail again on the retry, because every retry starts the entire tree over. Each failed attempt has cost you a full one anyway.
The better answer is to fix only what is broken. The auditor named which part is bad and how, which is a location and a defect. With both of those, you can leave the rest of the object alone. You regenerate the one property, aim the fix at exactly where the defect is, and insert it in place. Everything that was already correct is untouched.
A block comes back with one field out of range, a list that should hold four items holding six, or a required value left empty. The auditor names that block and that field. The fix regenerates that block alone, against the same inputs, checks it, and drops it back into the slot the broken one held. The page around it never moves.
The same holds when a call died instead of returning a bad value. One node of the tree failed on a transient error. You re-run that node and leave the tree alone. The children that came back fine are never re-rolled, so they never get the chance to fail the way the first one did.
| Regenerate the object | Surgical fix | |
|---|---|---|
| Calls re-run | Every call in the tree | The one that failed |
| Work already correct | Re-rolled, can come back wrong | Untouched |
| Failure surface | Every retry restarts the whole tree | One node |
| Model required | The heavy model that built it | A lighter one |
BlockAdvancedStyles
// Advanced styles that apply to ALL blocks (maps to sx props)export type BlockAdvancedStyles = { // Layout - responsive marginTop?: ResponsiveValue<string>; marginRight?: ResponsiveValue<string>; marginBottom?: ResponsiveValue<string>; marginLeft?: ResponsiveValue<string>; paddingTop?: ResponsiveValue<string>; paddingRight?: ResponsiveValue<string>; paddingBottom?: ResponsiveValue<string>; paddingLeft?: ResponsiveValue<string>; width?: ResponsiveValue<string>; maxWidth?: ResponsiveValue<string>; zIndex?: number; // Background backgroundType?: BackgroundType; backgroundColor?: string; backgroundImage?: string; // Resolved URL (for rendering) backgroundPosition?: string; backgroundSize?: string; backgroundRepeat?: string; backgroundAttachment?: string; // Gradient gradientType?: "linear" | "radial"; gradientAngle?: number; gradientColorStart?: string; gradientColorEnd?: string; gradientLocationStart?: number; gradientLocationEnd?: number; advancedGradientLayers?: Array<{ enabled: boolean; color: string; alpha: number; angle: number; stop: number; }>; // Border borderType?: BorderType; borderWidthTop?: string; borderWidthRight?: string; borderWidthBottom?: string; borderWidthLeft?: string; borderColor?: string; borderRadiusTopLeft?: ResponsiveValue<string>; borderRadiusTopRight?: ResponsiveValue<string>; borderRadiusBottomRight?: ResponsiveValue<string>; borderRadiusBottomLeft?: ResponsiveValue<string>; boxShadow?: string; // Positioning alignSelf?: "auto" | "flex-start" | "center" | "flex-end" | "stretch"; position?: "static" | "relative" | "absolute" | "fixed" | "sticky"; offsetTop?: string; offsetRight?: string; offsetBottom?: string; offsetLeft?: string; // Responsive visibility hideOnDesktop?: boolean; hideOnTablet?: boolean; hideOnMobile?: boolean; // Motion Effects entranceAnimation?: EntranceAnimation; animationDuration?: number; animationDelay?: number; // Attributes cssId?: string; cssClasses?: string;};None of this works on a blob of text. The block editor was chosen on a theory, that structured data holds up better under machine editing than prose does. Every block on this page carries the styles object above, fifty fields of it, and each field sits at a path that can be written on its own. A single property is something you can name and reach. An audit that finds the padding wrong on mobile rewrites styles.paddingTop.mobile, and nothing else in the object changes. Styles are only one of the two targetable surfaces. A block's own settings carry the rest, so the addressable surface of a single block runs well past these fifty fields. I have not tested the same repair against markdown, so I cannot say how badly it degrades, only that the theory came first and the pipeline was built on it.
Addressability buys more than the token count. A fix aimed at one property is a small enough job for a smaller model. The heavy model builds the page and a lighter, faster one repairs a field in it. The audit is specific, and it comes from another model reading the finished object rather than a rule checking it. The contract holds the object together while you edit it. Replacing one part in place cannot break what is around it. And because the job is durable, the fix is one more step in a run that survives. Each of those was built for its own reason. The surgical fix only became possible once all four were in place.
A surgical fix is always cheaper to run. It spends a fraction of the tokens and a fraction of the time, because it touches a fraction of the object. And it spends that little on every draft, for as long as the system runs. The platform drafts and audits constantly, so a fix that fires on even a small share of that work still fires a great many times.
It is also more expensive to build. Regenerating the whole object is easy to write, because the location does not matter. You just run the step again. Targeting one property and inserting a repair in the right place is harder. You pay that build cost once, in engineering, and a smaller run cost for the life of the system.
When a surgical fix cannot be made cleanly, the pipeline falls back to regenerating the section. The small fix is always tried first, because the only thing it costs when it fails is the attempt.
Remote, Pacific time, full-time or contract. Get in touch.