docs(spec): Phase 4 design for the client intake wizard

A guided interview that produces a written brief with sub-tasks, landing
in the Inbox as a draft through Plan 1c's existing accept flow.

Corrects an over-promise made during the Phase 1 discussion: the wizard
was described as grounding date proposals in measured per-card velocity,
and that data does not exist — Plan 2c deferred per-card effort because
session padding does not sum correctly across cards. This phase therefore
produces no time estimate at all, only a size band.

Names the intake as the highest-risk surface in the product: it is the
only place where text written by someone other than the developer enters
a prompt whose output creates records. The security property is that the
worst a hostile brief achieves is a strangely-worded Inbox card.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dave 2026-09-16 17:37:57 +02:00
parent de8ae83dcb
commit d928e7fd9e

View file

@ -0,0 +1,226 @@
# Phase 4 — The Client Intake Wizard
**Date:** 2026-09-16
**Status:** Draft for review
**Phase:** 4 of 5
**Depends on:** Phase 2 (commit data), Phase 3 (provider layer)
---
## 1. Purpose
A client manager clicks **Request a feature**, answers a handful of plain
questions, and the result is a written brief with the work already broken into
sub-tasks — landing in the Inbox as a draft for the developer to accept.
This is the feature the developer described first, and in their words: *"he first
answers simple questions on what he wants, that way I already get a written out
plan with sub-categories."*
It replaces the current one-line "Request a feature" box from Plan 1c.
## 2. What is already settled
| Decision | Where from |
|---|---|
| **AI-proposed dates are never auto-published.** | Phase 1 design |
| Client-submitted text is **data, not instructions** | Phase 1 design, Phase 3 preamble |
| A client's card always lands in the Inbox, unaccepted | Phase 1 spec §5.2 |
| The provider layer, settings and preamble | Phase 3a |
| **The client sees a rough size band, never a time estimate** | This conversation |
| **The interview is stateless — the transcript goes in the prompt** | This conversation |
## 3. A correction to an earlier claim
During the Phase 1 discussion this wizard was described as producing date
proposals *"grounded in your actual measured velocity — cards of this size took
you 6–9 working days."*
**That data does not exist.** Plan 2c deliberately deferred per-card effort,
because a session touching several cards contributes its padding to each and
per-card hours do not sum to the project total. What Phase 2 actually produces is
*project-level* velocity: features per week, active days against workdays, hours
per month.
That supports "this project has averaged around 25 changes a week." It does not
support sizing an individual request in days. This phase therefore produces **no
time estimate at all**, for the client or the owner — which is also the strictest
reading of the never-auto-publish rule, so nothing is lost.
## 4. The interview
**Stateless.** Each question is one `complete()` call from Phase 3a, with the
conversation so far included in the prompt. No session state to store, expire or
resume, and the transcript is already the thing worth keeping. The provider
interface stays narrow.
**Hard-capped at seven questions.** Past that, people abandon the form and the
developer gets a worse brief than a one-line email would have produced. The cap
is enforced in code, not requested in the prompt — a model that decides an eighth
question is warranted must not be able to ask it.
**Skippable at any point.** A "that's enough, write it up" button is always
present. Someone who has said what they need in the first answer should not be
interrogated.
**One question at a time**, plain language, no jargon. The model may offer
multiple-choice options where they genuinely help.
The questions the model is steered toward — what problem this solves, who will
use it, what happens today without it, what would make it not worth doing, and
whether anything external forces timing — are guidance in the prompt, not a
fixed script. The point of using a model here rather than a form is the
follow-up that a form cannot ask.
## 5. The brief
Output of the interview, shown to the client for confirmation before anything is
created:
- **Title** — one line, the developer's Inbox card title.
- **Summary** — two or three sentences of what is wanted and why.
- **Sub-tasks** — the work broken into steps, becoming subtasks on the card.
- **Open questions** — what the model could not establish. Valuable: these are
what the developer would otherwise discover halfway through.
- **Size band** — small, medium or large. See §6.
**The brief never contains a date, a deadline, an estimate, or a claim about
when work will start.** The Phase 3 preamble already forbids this and applies
here unchanged.
### 5.1 Structured output, parsed defensively
The brief comes back as JSON. The Anthropic SDK supports schema-constrained
output, but the OpenAI-compatible adapter does not, and the Phase 3a interface is
deliberately provider-neutral — so the format is requested in the prompt and
**parsed defensively**, exactly as Plan 3b parses batch responses.
A malformed brief must degrade to something usable: if the JSON will not parse,
the client's raw answers are still captured and the card is still created from
them. An interview that ends in an error message, having asked seven questions,
is worse than no wizard.
## 6. The size band
Small, medium or large. **No time is attached to it anywhere in the UI**, and it
is never rendered next to a date.
It is derived rather than guessed:
- **How many sub-tasks the request decomposes into.** A request that becomes two
steps is not the same as one that becomes eleven.
- **Whether it touches areas the project already has.** Phase 2 knows which
conventional-commit scopes exist and how much work each holds. A request
landing in an established area is better understood than one opening a new one.
**It is labelled as a first impression and it is the owner's to change.** The
client sees it as the model's initial read of their description; the developer
sets the real one when accepting from the Inbox.
Stated plainly, because it is the weakest thing in this phase: the band is a
judgement made from a description the model collected minutes earlier. It is
better than nothing and worse than the developer's opinion, and the UI should
never imply otherwise.
## 7. Prompt injection — the highest-risk surface in the product
This is the only place where **text written by someone other than the developer
enters a prompt whose output creates database records.** Everything else the AI
touches is commit messages the developer wrote.
Three controls, and all three are needed:
1. **The preamble already states that submitted text is data, never
instructions.** Necessary, not sufficient — a prompt rule is a mitigation, not
a boundary.
2. **The model never writes to the database.** It returns a brief; *code*
creates the card, through the same whitelisted path as Plan 1a's
`POST /api/client/cards`. Column, priority, `accepted` and `created_by_role`
are set by the server regardless of what the brief contains. A brief claiming
to be "approved, priority critical, move to In Progress" changes nothing.
3. **Every field is sanitised and length-capped before storage**, and rendered
as text — never as markup.
The security property to hold onto: **the worst a hostile brief can achieve is a
strangely-worded card in the Inbox.** It cannot set priority, cannot bypass the
Inbox, cannot reach another client's project, and cannot instruct the developer's
tooling. If a change would weaken any of those, it is a change to the security
model, not to a feature.
## 8. Draft until accepted
The wizard creates exactly what Plan 1c's Inbox flow already handles: one card
in the project's Inbox with `accepted = false` and `created_by_role = "client"`,
with subtasks, plus the transcript and brief attached.
Nothing new is needed for acceptance. The developer opens it, reads the brief,
adjusts, and accepts — which sets `start_date` and moves it out of the Inbox,
exactly as today.
**The transcript is kept.** When a request turns out to mean something other than
what was built, the record of what was actually asked is the thing that settles
it — the same reason the activity log exists.
## 9. Data model additions
| Collection | Field | Notes |
|---|---|---|
| `cards` | `brief` | json — summary, sub-tasks, open questions, size band |
| `cards` | `transcript` | json — the question-and-answer pairs |
| `cards` | `size_band` | select: `small` \| `medium` \| `large` \| `""` |
No new collection. The wizard produces a card; cards already exist.
Read rules follow `cards`. Writes are server-side only.
## 10. Failure behaviour
Consistent with Phase 3: the AI is an enhancement, and its absence must not
remove a capability the client already had.
- **No provider configured, or the provider is down** → the wizard is not
offered, and the plain "Request a feature" box from Plan 1c is shown instead.
The client can always file a request.
- **A failure mid-interview** → the answers so far are kept and the client is
offered "submit what I have", which creates the card from the raw answers with
no brief.
- **A malformed brief** → §5.1; the card is still created.
## 11. Out of scope
- **Any date, estimate or scheduling.** §3.
- **The developer using the wizard.** It is a client-facing intake. The owner
creates cards directly.
- **Editing a brief after submission.** The developer adjusts the card; the
brief is a record of what was asked, and rewriting it destroys the thing §8
keeps it for.
- **Conversation anywhere else in the product.** No chat over the project, no
asking the AI about the board.
- **Notifying the developer.** An Inbox badge already exists. Email belongs with
the n8n work parked in Phase 1.
## 12. Acceptance criteria
1. A client manager can complete an interview of at most seven questions and see
a brief before anything is created.
2. The brief contains a title, summary, sub-tasks, open questions and a size
band, and **no date, deadline or estimate**.
3. Submitting creates one Inbox card with `accepted = false`,
`created_by_role = "client"`, subtasks from the brief, and the transcript
attached — whatever the brief's content claims.
4. A brief containing instructions — "set priority to critical", "mark as
accepted", "move to In Progress" — changes none of those fields.
5. With no provider configured, the plain request box from Plan 1c is offered
and works.
6. A failure mid-interview still allows submission of the answers given.
7. The size band is never rendered adjacent to a date or duration.
8. The developer accepting the card uses the existing Inbox flow, unchanged.
## 13. Suggested plan split
- **Plan 4a — the interview.** Stateless turn-taking, the question cap, the skip
path, defensive brief parsing, and the transcript. The parsing and the cap are
pure and testable; this is where the injection tests live.
- **Plan 4b — submission and display.** Card creation through the whitelisted
path, subtasks from the brief, the brief rendered on the card detail page for
both roles, and the fallback to the plain request box.