Front door builder chat

Conversational authoring for organization-specific Agent and Workflow definitions, entered through the normal Front Door and backed by the existing DefinitionService.

1 min read Updated Sep 13, 2026

Front door builder chat

Users should be able to say:

"Create an agent that qualifies UK fintech companies for SEO outreach."

or:

"Make a custom workflow based on Signals Search, but add a human review before outreach."

The experience starts in the normal AgencyCore chat. Architecturally, however, the Front Door does not become a definition editor. It delegates authoring intent to one platform-authored Definition Builder Agent.

TEXT
User
  │
  ▼
Channel Gateway
  │
  ▼
Front Door
  │ authoring intent
  ▼
Definition Builder Agent
  │
  ├── search templates / capabilities
  ├── create or patch draft
  ├── validate draft
  └── publish after explicit user intent + policy
          │
          ▼
     DefinitionService
          │
          ▼
 Agent / Workflow definition
          │
       publish
          ▼
 normal Front Door -> RunManager execution path

From the user's point of view this is one chat experience. From the platform's point of view the boundaries remain unchanged.

What the feature owns

The Builder Chat owns the conversational authoring experience:

  • understand what the admin wants to build or customize;
  • decide whether the target is an Agent or a Workflow;
  • find relevant platform templates, Agents, Skills and Tools;
  • ask only for missing high-impact requirements;
  • create and iteratively update one durable definition draft;
  • explain validation failures in product language;
  • summarize the effective design before publish;
  • publish only after explicit user intent and normal policy checks.

It does not own runtime execution, policy, tool implementation, trigger scheduling, definition storage or validation rules.

Why this is not more Front Door logic

The platform Front Door stays responsible for intent and capability selection. It must not absorb definition authoring because that would give it another open-ended responsibility and turn it into a second runtime.

The Definition Builder is therefore a normal platform-authored Agent Definition that the Front Door can select like any other capability.

TEXT
Front Door
  answer / clarify / delegate / control_run
                         │
                         └── delegate(definition_builder, authoring brief)

No new Front Door outcome is required.

Definition Builder Agent

The Builder Agent has one responsibility: turn conversational requirements into a valid definition draft.

Its tool set is intentionally narrow.

ToolPurpose
definition.search_templatesFind platform Agent/Workflow templates that are good starting points.
definition.search_capabilitiesFind published Agents, Skills and Tools that may be referenced.
definition.get_draftLoad one draft by ID when continuing authoring.
definition.create_draftCreate one organization-owned draft.
definition.forkCopy one platform template into this organization as a draft.
definition.update_draftApply a bounded patch to draft_config.
definition.validate_draftRun the shared deterministic DefinitionValidator.
definition.publishPublish through DefinitionService after explicit user intent and policy.

These are normal internal Tools over the existing Definition APIs/services. The Agent never writes definition rows directly.

A template is read-only, so definition.fork is what opens one. A template belongs to no organization, so the ordinary definition read answers not_found for one and no other tool here can see what a template holds. Without the fork, definition.search_templates answers a list of ids with no next move and "Template customization" below describes something the tool set cannot do. The fork calls DefinitionService.fork, which deep copies the reachable set and rewrites every id, so the new draft belongs wholly to the organization.

The names are domain.verb. Every tool of the deploy follows that rule and internal_tool refuses a name that does not.

Four of them are an admin right, and the other four are not. definition.create_draft, definition.fork, definition.update_draft and definition.publish belong to _DEFINITION_WRITE_TOOLS in governance/policy/principals.py, which only an owner and an admin hold. A member's Run of the Builder therefore declares the four reads and none of the four writes, so the model never proposes a write it cannot make. DefinitionService still refuses a non-admin write on its own: absent from the tool subset is the first gate, and the authority check is the second.

A refused write must never come back as a success. DefinitionService answers a stale expected_updated_at by returning None rather than raising, because its caller always reloads. A tool that maps that None onto an ordinary result tells the model the patch landed. The model then summarises a design it did not write, and offers to publish it, and the config that publishes is the other admin's. That is worse than the silent overwrite this page already forbids, because nothing anywhere disagrees.

So every write tool returns the refusal as a value the model must read:

ToolRefusals it must surface
definition.update_draftstale_draft
definition.publishstale_draft, validation_failed, too_many_referrers, definition_in_use

The model never carries expected_updated_at. Asking a language model to hold an opaque timestamp across turns and echo it back correctly is asking it to be an optimistic concurrency client, and it will eventually send a stale one or invent one. definition.update_draft instead reads the draft, applies the patch and writes conditionally on the updated_at it just read. The window shrinks to one round trip and the compare-and-swap still catches the other admin, so the guarantee is unchanged and the model holds no state. On stale_draft the agent reloads and applies the patch again.

Draft writes are product authoring effects, not execution. Publishing is the safety boundary and may be gated by Policy.

Durable authoring state

There is no authoring-session table in V1.

The conversation carries the draft ID. An authoring conversation outlives its Run: the Front Door starts a fresh Run for every turn, so the second turn's Builder Agent has no memory of what the first one created. "The current draft" is not a thing the platform can resolve, because an organization may hold many.

So the draft ID lives on the conversation, in the one place that already spans turns.

TEXT
definition.create_draft returns the ID
  -> the Run result carries it as a ResourceRef
  -> the conversation records it as an entity in scope
  -> the next turn's delegated Run carries the scope as input refs
  -> the Builder's ContextBrief renders the draft
  -> definition.get_draft(id) loads it

Four pieces already existed, and six links had to be built. This page said "nothing new is built" until ENG-2409 measured the chain. agent.conversation_entities, ResourceRef, RunResult.refs and the row-pointer declaration were all there. Nothing that joins them was:

LinkWhat it wasWhat it is
The scope storeConversationScope refused every PLATFORM_REF_KINDS member, and agent.definition is oneUNSCOPED_REF_KINDS is the set the scope refuses. It holds the knowledge chunk and the memory, and not the definition
The delegated Run_run_input wrote the model input and the message text, so ContextRequest.entities was empty for every delegated RunThe turn writes the scope it already read into the run input as refs
The readerDefinitionSummaryContextSource read no entities and listed state="active" rows onlyIt resolves a scoped agent.definition ref by id, draft included, and places it first
The registryThat source was registered for the Front Door turn alone, so an agent run could not resolve it and the publish check refused an Agent that declared itbuild_context_sources takes the definition reader and registers the source, so any Agent may declare it
The AgentAn Agent that declares no context source builds an empty brief, and ContextPolicy() is exactly thatThe Builder declares conversation and definition_summary
The lineContextPacker writes no [kind id] bracket for this source, so the rendered line was a name with no id, and definition.get_draft takes an idThe draft item opens its body with id: <uuid>

The last two were found by running it, not by reading it. Every unit test passed while the Builder answered "I do not have a valid draft id" with the draft named on the line above it. A chain of six links fails silently at any one of them, so the live turn is the only check that covers them all.

The Front Door prompt block still drops agent.definition: a definition ref is also an executor UUID, and FD23 holds the prompt free of one. That refusal and the scope refusal are two sets now, because they answer two questions.

An admin who wants a different draft names it, and the agent searches.

The definition row is the durable state:

TEXT
definition
  id
  organization_id
  kind: agent | workflow
  origin: platform | custom
  source_definition_id
  state: draft | active | disabled
  draft_config
  published_config

The conversation may remember the active definition_id as lightweight thread/session metadata so follow-ups such as "add CRM read access" resolve naturally.

If that conversational pointer is lost, the draft is still safe and can be reopened by ID/name from the Builder or direct Agent Builder UI.

Agent authoring flow

TEXT
"I need an agent that qualifies inbound companies."
        │
        ▼
identify responsibility + expected output
        │
        ▼
search available Tools / Skills / templates
        │
        ▼
ask only for required missing constraints
        │
        ▼
create Agent draft
        │
        ▼
validate
   ┌────┴─────┐
 invalid     valid
   │           │
explain      summarize
+ patch      effective design
               │
         explicit publish
               │
               ▼
      DefinitionService.publish()

A custom Agent draft may configure:

TEXT
responsibility / instructions
model
context_policy
tool_ids[]
skill_ids[]
budget_defaults

The Builder does not invent permissions. The Agent may reference only Tools/Scopes available to the organization and compatible with the authoring principal.

Workflow authoring flow

The Builder composes the existing V1 Workflow Definition language:

TEXT
sequence      parallel                     containers
agent         tool        subworkflow      work
branch        wait        approval         control

It may reference existing published Agents, Tools and subworkflows. Validation remains deterministic and enforces node IDs, references, types, cycles, nesting depth, wait node shape and declared scopes.

The approval node is how an author says "stop here for a person", which is the request in the example below. It is one of the three sources that write an agent.approvals row. See human review.

TEXT
"Create a workflow that researches a prospect,
gets human review, then starts outreach."
        │
        ▼
Builder finds published capabilities
        │
        ▼
sequence
  -> agent/signals research
  -> approval node
  -> subworkflow/email sequence
        │
        ▼
validate -> summarize -> publish

Schedules and event conditions do not belong inside the Workflow Definition. If the user asks "run this every Monday" or "run when a CRM signal changes", that is Trigger configuration and remains a separate platform concept.

Template customization

Platform templates are read-only. Customization forks them.

TEXT
platform template
      │ customize
      ▼
organization draft
  origin = custom
  source_definition_id = template.id
      │
      ▼
edit -> validate -> publish

This allows product capabilities such as Signals Search to become starting points for organization-specific workflows without mutating the platform definition.

Later changes to the platform template do not silently change the organization's fork.

Publish and test

V1 keeps the boundary simple:

Drafts are not executable.

The flow is:

TEXT
draft
  -> deterministic validation
  -> user sees summary
  -> explicit publish intent
  -> Policy / DefinitionService.publish()
  -> current published_config
  -> normal Front Door / RunManager Run

There is no separate draft runtime or preview execution engine.

After publish, "test it with Acme" is simply a normal request to the Front Door. The newly published capability is found through CapabilityIndex and runs through the standard RunManager path with a frozen Run snapshot.

Example: custom Agent

TEXT
User:
Create an agent that reviews a company and tells me whether it fits
our UK fintech SEO ICP.

Builder:
- finds CRM read + intelligence search Tools
- proposes an Agent responsibility and output shape
- asks whether it may write qualification back to CRM

User:
Read only for now.

Builder:
- creates/patches draft with read-only Tools
- validates successfully
- summarizes model, context, Tools and budget
- asks whether to publish

User:
Publish it.

definition.publish
  -> Policy
  -> DefinitionService.publish()
  -> capability becomes discoverable by Front Door

Example: customize a product workflow

TEXT
User:
Make our Signals Search workflow require a person to review the
prospect before Email Sequence can start.

Front Door
  -> Definition Builder Agent

Builder
  -> finds Signals Search template
  -> forks organization draft
  -> adds the supported wait/approval boundary
  -> validates references and types
  -> summarizes the changed workflow
  -> publishes after explicit confirmation

The new Workflow uses the same shared Tools, Policy, Context, Runs, spans and Idempotency Service as the platform workflow. Customization creates data, not infrastructure.

Roles and policy

V1 keeps the existing rule:

Admins author. Authorized users run.

Rules:

  • Only organization admins may create, patch or publish custom definitions.
  • The Builder Agent runs with the user's Principal; it cannot widen the user's authority.
  • A custom Agent cannot grant itself Tool scopes the organization/principal cannot authorize.
  • definition.publish passes through normal Policy and deterministic validation.
  • The Builder cannot bypass an approval by editing the definition around it; runtime action policy is evaluated live at execution.
  • Tenant isolation remains database/RLS enforced through the underlying service path.

Failure handling

FailureProduct behavior
Requirement is genuinely ambiguousAsk one focused question.
No suitable Tool/capability existsExplain the missing capability; do not fabricate one.
Draft validation failsExplain the deterministic validation errors and propose a patch.
Concurrent admin editReload after optimistic concurrency failure; never overwrite silently, and never report a refused write as done.
Referenced capability is disabledValidation blocks publish until replaced.
Publish policy deniesKeep the draft and explain the policy decision.
Conversation loses active draft pointerSearch/reopen the durable draft; no work is lost.

V1 rules

  • Front Door routes authoring; it does not author definitions itself.
  • Definition Builder is one normal platform Agent, not a new runtime.
  • One durable definition draft is the source of truth; no authoring-session database.
  • All draft lifecycle operations go through DefinitionService.
  • Deterministic DefinitionValidator is authoritative; the model cannot waive an error.
  • Drafts never execute in V1.
  • Publishing is explicit and policy-controlled.
  • Workflow schedules/events stay in Triggers, not in workflow config.
  • Organization customization forks platform templates; it never mutates them.
  • After publish, the capability uses the exact same Front Door -> RunManager execution path as every other Agent/Workflow.

Minimum scenarios

  • Non-admin cannot create or patch a custom definition through chat.
  • Front Door routes authoring intent to Definition Builder without adding a new Front Door outcome.
  • Builder cannot reference a Tool/Skill outside the visible/allowed capability set.
  • Invalid Agent/Workflow draft cannot publish even if the model says it is valid.
  • Concurrent draft update produces optimistic-concurrency failure rather than silent overwrite.
  • A stale draft write reaches the model as stale_draft, and the agent reloads instead of reporting success.
  • A publish refused for referrers or for a definition in use is rendered to the person, never swallowed.
  • No tool call carries expected_updated_at from the model.
  • Platform template customization creates a separate organization-owned definition with source_definition_id.
  • Draft cannot be started as a Run.
  • Published custom capability is discoverable through CapabilityIndex and starts through RunManager.
  • Trigger/schedule request is not serialized into Workflow config.