Debriefer

The Evolution Of The Form

Headless, accessibility-first React components for building intelligent forms.

A survey, an interview, a job application, a signup: underneath, they are all forms. Each one gathers answers to a set of questions decided in advance. On the web that reduces to one element and one idea: collect a person's answer, typed or spoken, then send it somewhere. The rest of this page is what happens when that idea learns to reason about the answer.

The form was built to collect and submit

The <form> element exists so a page can gather input from a person and hand it to a server. When the form submits, the browser collects the named fields and sends them as a single request. That is the entire contract.

<form action="/feedback" method="post">
  <label for="reason">What made you cancel?</label>
  <textarea id="reason" name="reason"></textarea>
  <button>Submit</button>
</form>

It is a faithful courier. It does not read the answer, and it was never meant to.

Then we added validation to hold a standard

Raw input is messy, so we validate it, usually on the server, before accepting it. Validation enforces a standard for each field.

// on the server
if (!reason || reason.length < 10) {
  return { errors: { reason: "Please add a little more detail." } };
}

Useful, but look at the ceiling. Validation checks the shape of an answer, never its substance. "Twelve characters long" is not "a useful answer." An evasive reply that clears the length check still gets in. A thoughtful reply worth exploring is accepted and filed without a second thought. The follow-up a good interviewer would ask never happens, because the exchange has only two outcomes: pass, or a list of errors.

What if validation could reason?

Keep the mental model of a form and its fields. Replace the pass-or-fail rule with something that can think.

Now each field is appraised by an agent. It can validate, qualify, and assist: accept a strong answer, ask a sharper question when an answer is thin, offer a quick choice when that is faster, and reshape the field at runtime, field by field. Instead of returning a batch of errors after the fact, it responds in the moment.

That is a Cell: a form field that reasons.

import { DebrieferProvider, Form, Textarea, Button } from "@debriefer/blocks-react";

<DebrieferProvider config={{ formId, sessionToken }}>
  <Form>
    <Textarea id="reason" label="What made you cancel?" />
    <Button>Submit</Button>
  </Form>
</DebrieferProvider>

The same field you already know how to write. The difference shows the moment someone answers.

One answer becomes a thread

The respondent types two words. From that single field, the agent builds a thread, qualifying the answer one question at a time.

Field

What made you cancel?

Respondent
Too expensive.
Agent
Compared to what you were paying, or the value you were getting?
Respondent
The value, really. I was only using a fraction of it.
Agent
Which parts did you find yourself not reaching for?
Respondent
The analytics dashboards, mostly.

That field asked one question and came away with a reason, a cause, and a specific feature. Validation could only have told you the answer was long enough.

Underneath, each turn is a single call. The field submits an answer for its node, and the agent returns the next move: ask again, offer a choice, or finish.

const decision = await submitAnswer("reason", "Too expensive");
// { type: "follow_up", prompt: "Compared to what you were paying, or the value you were getting?" }

The thread is that loop, and it stays anchored to its node. A form stops being a list of fields and becomes a set of small, focused conversations, each running field by field.

The agent closes the loop

When the thread has what it needs, the agent does two things a validator never could.

First, it writes a conclusive summary of the answer, the qualified result rather than the raw words:

Summary

Churned on value rather than price. Low feature use, mainly the analytics dashboards.

Second, it can offer that summary back to the respondent to confirm before anything is submitted:

Agent
So the price was fine, but the value was not there because you were using only a fraction of it, mainly the analytics dashboards. Have I got that right?
Respondent
Yes, exactly.

What reaches your backend is both the clean answer and the thread that produced it, ready to store in whatever datastore you already use.

{
  field: "reason",
  answer: "Churned on value rather than price. Low feature use, mainly the analytics dashboards.",
  confirmed: true,
  thread: [
    { role: "respondent", text: "Too expensive." },
    { role: "agent", text: "Compared to what you were paying, or the value you were getting?" },
    { role: "respondent", text: "The value, really. I was only using a fraction of it." },
    // remaining turns
  ],
}

You store the summary as the answer, and keep the thread as the evidence behind it.

Validation became a conversation, and you wrote an ordinary form to get it.

What you write, and what the agent does

You keep everything a form developer expects to own. The agent takes over the part that used to be a blunt rule.

You ownThe agent owns
Which fields exist, and their layoutWhether an answer is sufficient
Styling, through your own componentsWhen to ask a follow-up, and what to ask
The shape of each inputWhen to offer a choice or move on
How the field changes at runtime
The summary it concludes, and the optional confirm step

The one rule

The agent is the sole decision maker. A Cell renders what the agent returns and submits what the respondent enters, nothing more. A Cell never appraises an answer, advances on its own, caps follow-ups, or judges whether an answer is enough: that is always the agent's call, not the client's. See the model.

Keep going

On this page