:greeting + :asks), extracted and diffed against this proposal. beat-6-current.json vs beat-6-proposed.jsonprofile.json) has not been generated. All source-package generation is gated on the IDENTITY law, which has cleared one committee round with no confirmation round yet.SEAM-onboarding-beat-6-profile.md under the old convention. The drafted IDENTITY law makes this beat's canonical beatId profile and rejects onboarding-beat-6-profile as position-bearing. Three other documents still say SEAM-profile.md. Reconciled when IDENTITY is approved, before the seam signs.submit_profile tool contract is stale: it still declares nickname and referral_source, both removed from onboarding capture by decision 26. This blocks the per-ask write below from shipping as designed.Two structural reasons, not an arbitrary pick. It is the only beat with more than one step (a greeting, an age question, a gender question), so the step model gets exercised rather than assumed. And it runs two of the three output pipelines side by side in one container: the greeting is spoken live, the two asks play recorded clips. A design that only worked for one pipeline would fail here immediately instead of hiding until a later beat. It also carries zero variants, on purpose, so the step problem and the variant problem get solved one at a time. Once this beat runs end to end, the other beats are duplication of a proven pattern, not invention.
| Tag | What it means for OUTPUT |
|---|---|
| MP3 · Verbatim | Plays a fixed recorded audio clip. No brain call for output. |
| Cartesia · Verbatim | Synthesizes a fixed script line live via TTS. No brain call for output. Used only where a live slot (the user's name) makes a pre-recorded clip impossible. |
| Cartesia · Generative | Not used on this beat. All three lines here are fixed script, not generated. |
| Vapi · Generative | Not used on this beat. |
| Silent | Not used on this beat. No step here is voice-off. |
The no-brain property is OUTPUT ONLY. Every step on this beat, recorded or live, still calls the brain to LISTEN to and interpret whatever the user says back, by voice or by tap.
This is the second structural reason the beat was built first: it is the one place both non-generative output paths run next to each other.
{name} slot filled from flow-statesubmit_profile, then advance_step) once required fields are captured| Tool | Args | When |
|---|---|---|
| submit_profile | { age: integer, gender: "Male" | "Female" | "Other" } | Once the field for the current step is captured. Fires per ask, not once at the end (see the write path below). |
| advance_step | {} | Immediately after a successful submit_profile. |
Step 1, greeting, overrides this to explicit mode: "none". No tools at all, nothing is captured or saved on the greeting. The schema keeps "none" distinguishable from "inherit" on purpose: collapsing the two would let the greeting silently inherit the asks' tools, which is exactly the bug decision 31a exists to prevent.
The user's name is already known from sign-in (registration, decision 26) and is never re-asked anywhere on this beat.
profilesKeyed on anon_id. Source: the live seam draft, MR !6, open, unsigned.
Gender is nullable ONLY between the two writes inside one beat pass. It is required to COMPLETE the beat. Completeness is enforced AT THE BEAT (mandatory, no skip, no exit, decision 33), not by a NOT NULL column, which is why the column itself stays nullable.
profiles row is CREATED. age is set, gender is left NULL.gender is set. The beat is now done and advance_step fires.gender NULL (the answered age is also recoverable from turn memory). Resume lands directly at ask-gender. Age is never re-asked.No second write location exists anywhere for this data. profiles is the one write target: a separate holding place for the partial (R4 option 2) was considered and rejected, because it recreates the two-tables-nobody-checked failure this seam exists to prevent.
From the coach memory spec, section 13.2 (QA6 green, durable-set contract approved). Applies to every turn on every beat, not only this one.
Why labels, not keys: if beat identity ever changes meaning, a label gets edited instead of a foreign key silently breaking across a million rows.
Gender is stored here and consumed downstream: at the pick-category beat, Female and Other select the women's variant, Male the default. That routing reads the value; it is not a second exit from this beat.
| Edge | Behavior |
|---|---|
| tool failure | one silent retry, then surface it, never fail silently, never advance |
| invalid age | not stored, one light redirect, re-ask plainly |
| declines gender | never accepted as a skip; a declined answer resolves silently to Other and advances |
| Decision | Binds | How |
|---|---|---|
| 16 | yes | gender mandatory, no skip |
| 25 | yes | two of the three output pipelines run in this one container, verbatim and recorded |
| 26 | yes | captures narrowed to age and gender; name and referral source removed |
| 31a | yes | toolPolicy REPLACE, never accumulate; the greeting is mode "none" |
| 31b | yes | this beat produced the global-vs-local promotion test |
| 32 | yes | Female and Other both route to the women's variant |
| 33 | yes | no exit, GR-05 overruled |
| 35 | yes | rebuilt file by file, each approved before it counts |
| GR-02 | yes | the greeting is the sole live-generation exception |
| GR-27 | yes | answering both age and gender at once (by voice) fulfills ask-gender without playing its clip |
greeting · stepId s-profile-greetingIllustrative mockup: one coach bubble, no input control, the orb active while speaking.
Resume is keyed on stepId, never on stepKey or order. Renaming this step's label or reordering the beat does not change stepId, so a user mid-flow is unaffected.
Line: "Awesome {name}, two quick things so I can tailor this to you."
Ruled by Yair as the SOURCE wording. The engine's own alternate line ("Good to meet you, {name}...") and an older Sheet snapshot are both SUPERSEDED by this ruling.
Reveals bubble:opener on entry. Karaoke, per word, on the bubble. No gate: this is the one line on this step.
n/a No interactive component. A single coach bubble that auto-advances.
Trap flagged in the source: coach_greeting.mp3 exists (13.28s) and reads like this line, but it belongs to a different beat (beat 3). Wiring it here would look like a repair and would be wrong.
The coach already knows the user's name from sign-in. Greet them warmly, then set up the two quick things about to be collected (age and gender). This step is only the greeting, spoken live in the user's name. The asks come next.
toolPolicy mode: none Explicit none, not absent. Nothing is captured or saved on the greeting. This is the first instance of toolPolicy mode "none" in the system, and the schema must keep it distinguishable from "inherit" or the greeting silently inherits the asks' tools.
writes nothing The name is read forward from registration, not written here.
Auto-advances to ask-age when the greeting's live line completes. No gate.
Name missing from state (unexpected): fall back to a name-free warm greeting rather than voicing an empty slot; still auto-advance.
Audio fails to play: show the line as text and still auto-advance; never strand the user on a silent screen.
With a hydrated name, the coach says the locked sentence byte for byte after substituting only {name}.
The greeting never plays a clip and never calls a tool.
Advances exactly once, only after the live line's completion event.
n/a Single-turn: the step auto-advances and the user has no turn here. Barge-in (R9) still applies as behavior only, no fixed line: if interrupted, the coach stops speaking and responds to what was actually said.
| Direction | Key | From / to |
|---|---|---|
| in | profile.name | flow-state, written by registration |
| out | none | |
Binds: GR-02 (sole live exception), decision 26 (name from registration, never re-asked), decision 31a (toolPolicy mode none).
ask-age · stepId s-profile-ask-ageIllustrative mockup: the wheel opens with its scroll origin at 35 (R11), never a displayed or pre-filled value.
Transcript: "How old are you?" recorded, approved, DONE
Asset audio/approved/voice/ob/onboard_01_form_1.mp3, 1.12s. No variations, this line is closed.
Reveals component:age only. Progressive, cumulative reveal (R2): the leftover from the bundled legacy step, where both controls appeared here, was deliberately removed.
Collect two things: age and gender. Ask gender plainly and never let the user skip or decline it. Accept voice or taps. If they give one, ask for the other. Both are required before moving on, gender included. Ask for nothing else.
toolPolicy mode replace. Called once age is captured; called again with gender if the user gave both in one turn (GR-27).
Writes profiles.age. Creates the row if it does not yet exist, with gender left NULL. See the write path panel above for the full timeline.
From greeting (auto-advance). Advances to ask-gender once age is captured, unless gender was also given in the same turn (GR-27), in which case both are captured and the step exits directly toward completion.
Invalid age: not stored, one light redirect, re-ask plainly. Tool failure: one silent retry, then surface it, never advance without success.
A valid answer calls submit_profile with an integer age, never raw wording or a string.
An age-only answer advances to ask-gender and does not complete the profile row.
During ask-age, only the age control is on screen.
| Scenario | Fires when | Behavior | Status |
|---|---|---|---|
| age_not_a_number | e.g. "blue" | not stored, one light redirect, re-ask plainly | pending authoring |
| age_out_of_range | e.g. "200", "5" | outside 13 to 120, not stored, re-ask | pending authoring |
| age_ambiguous | e.g. "twenties", "about thirty" | the coach heard something close and should say so | pending authoring |
Barge-in (R9): behavior only, no fixed line. The coach stops speaking and responds to what was actually said.
| Direction | Key | To |
|---|---|---|
| out | profile.age | profiles.age, via submit_profile |
| out (opportunistic) | profile.gender | profiles.gender, only if volunteered in the same turn (GR-27) |
Binds: decision 26 (canonical wire type), GR-27 (opportunistic capture), decision 33 (no exit, so a forgiving wire type is required rather than a trap).
ask-gender · stepId s-profile-ask-genderIllustrative mockup: the age value shown (29) is an example answer for the mockup, not a captured fact. Nothing is preselected on gender.
Transcript: "What's your gender?" recorded, approved, DONE
Asset audio/approved/voice/ob/onboard_01_form_2.mp3, 1.2s. No variations, this line is closed.
Reveals component:age (filled, still editable) plus component:gender. Cumulative: nothing disappears once shown.
Collect two things: age and gender. Ask gender plainly and never let the user skip or decline it. Both are required before moving on, gender included.
Same beat tools as ask-age. submit_profile completes the row; advance_step follows immediately.
Writes profiles.gender, completing the row created at ask-age. This is the point the beat becomes "done".
From ask-age. On success, submit_profile then advance_step exits the beat to onboarding-beat-10-experience-fork, for every user. Gender is read downstream (pick-category) to route Female and Other to the women's variant, Male to default; that read is not a branch of this beat.
Declines gender: never accepted as a skip; resolves silently to Other and advances (R14a). Tool failure: one silent retry, then surface it, never advance without success.
A declined or non-binary answer ("I'd rather not say", "non-binary") resolves to Other, is stored, and advances. There is no decline that blocks advancement.
A gender answer outside the three options is stored as Other with no follow-up.
During ask-gender, both the age and gender controls are on screen, age still editable.
No step-scoped scenarios of its own (canonicalization absorbs the decline case, R14a). Shares the beat-scoped scenario below. Barge-in (R9): behavior only, no fixed line.
| Scenario | Fires when | Behavior | Status |
|---|---|---|---|
| resume_midbeat | user returns with age captured, gender missing | acknowledge what is known, ask only for what is missing, never re-ask a captured value | pending authoring |
| Direction | Key | To |
|---|---|---|
| out | profile.gender | profiles.gender, via submit_profile, completing the row |
Binds: decision 16 (mandatory, no skip), decision 32 (downstream routing read), GR-27 (fulfillment without a clip).