Prototype Constraints
This page is the full list of the rules that are not visible from screenshots. README.md and HANDOFF.md section 7 carry the same list, written from the same source (lib/constraints_list.rb, synced by scripts/sync_constraints.rb). Numbers are read from the constants that enforce them.
The AI's role (owner principle)
- The AI writes no plan content. Every answer the import puts in the wizard is copied from the BCBA's own document, sentence by sentence; a long passage is shortened by leaving sentences out, never by rewording. What the document does not cover stays empty.
- The AI makes no clinical decisions. On the strategy steps it only matches what the BCBA wrote to strategies in the BIP Visualized library, shows each match with the quote it came from and a one-line reason, and the BCBA keeps or removes it. A match the AI is unsure about is shown as a suggestion and is placed only when the BCBA accepts it.
- The only thing the AI proposes on its own is up to three library resources on step 12, shown for review. The pause strategies ticked by default on step 8 are product defaults, not AI.
- Rewrite with AI runs only when the BCBA presses the button, rewrites only the text in that box, keeps every fact, number and name, and shows the result in the box for the BCBA to keep or undo. It never runs on its own.
- The AI's own words appear in two places only: the one-line reason next to a matched strategy, and a Rewrite result the BCBA asked for. Generate runs no AI call: the plan is exactly the text and strategies on the steps.
Landing page (/)
- Two ways in: import an existing written BIP, or answer the wizard. Once a plan is in progress the page shows "You have a plan in progress" with Continue and Start over.
- Start over clears every plan key in the session (9 keys: answers, import matches, the pending import, the generation pointer) and drops the pending generation. It keeps the banner's state and plan in the URL.
- Banner states:
api_errorshows "Generator unavailable. We can't reach the AI service right now."permissionshows the free-plan gate; so doesemptywhen the plan is free. There is no separate empty state on this page. - The free-plan footer with Upgrade to Pro shows only when the plan toggle is free.
Import: files and paste
- Accepted files: .txt, .docx, .pdf (extension matched case-insensitively). Maximum 10 MB (
FileExtractor::MAX_BYTES). - The file's first bytes must match its extension: a .pdf starts with %PDF-, a .docx is a zip, a .txt is plain text. A renamed file is refused with "This file doesn't look like a .ext file. Save it again in that format, or paste the text instead."
- Text read from a file or pasted: at most 400,000 characters (about 150 pages). The paste box has the same
maxlength. Over the limit: "This document is too long to read (more than 400,000 characters, about 150 pages). Upload the part that holds the behavior plan." - Windows line endings count as one character. A whitespace-only paste does not enable the button. The paste counter counts characters the way the server does (an emoji is 1).
- One source per submit: the tab open at submit is the source. A file is dropped unread when the tester submits from the Paste tab. Dropping several files keeps the first and says so.
- Empty submit: "Paste your BIP text or upload a .txt, .docx, or .pdf file to continue."
- A new import over a plan in progress needs the "Replace my current plan" tick. Without it: "You already have a plan in progress. Tick \"Replace my current plan\" in the box above to import over it, or continue in the wizard." Nothing is read and nothing is lost.
- Word files (.docx):
- Form-field answers, both kinds of checkbox (legacy form fields and content controls) and tables are read. Checkbox states reach the AI as ☑ and ☐ markers.
- A file whose content unpacks to more than 50 MB is refused as too large, even when the file itself is under 10 MB.
- Unreadable file: "Couldn't read this Word file. Save it again as .docx and retry, or paste the text instead."
- PDF files:
- Page text plus AcroForm checkboxes (as ☑ and ☐). Pages that cannot be read are skipped and named once in a notice on the first wizard step. When no page can be read: "None of this PDF's pages could be read. Please paste the text instead."
- A PDF counts as scanned when its text is under 100 characters, or when it has 3 or more pages and more than half of them hold under 200 characters. A PDF with checkbox fields is never treated as scanned.
- Scanned PDFs go to Amazon Textract OCR only when AWS keys are set in the environment (the deploy file sets none). Without keys: "This PDF appears to be scanned or image-based. Please copy and paste the text instead."
- OCR handles at most 25 pages. Two failed pages in a row stop it, more than half failed is an error, and OCR text under 100 characters falls back to the paste message.
- Unreadable PDF: "Couldn't read this PDF. Please paste the text instead."
- Text files (.txt): read as UTF-8, with UTF-16 (BOM) and Windows-1252 fallbacks; invalid bytes become "?".
- Uploaded files are never written to disk. The tempfile is closed and deleted before the request returns; only the extracted text stays in memory long enough to be sent to the AI. Log lines carry sizes, kinds and error classes only: no filename, no text, no exception message.
Import: reading the document with AI
- Model settings for every call:
gpt-5.4-mini(gpt-5.4for the last goal-shortening attempt of Rewrite), 60-second timeout, temperature 0.1, 8,192 output tokens. WithoutOPENAI_API_KEYthe server refuses to boot in production and warns locally. A rate limit (HTTP 429), a 5xx or a dropped connection is retried up to three times (3, 8 and 15 seconds, honoring Retry-After up to 20 seconds) and a timeout once before the call counts as failed. - Long documents: over 30,000 characters the document is split into sections of about 2,500 characters and each is rated 0 to 3 for being part of one learner's behavior plan. Kept text is trimmed to 48,000 characters, background sections first, learner details second. No plan found: "We read N sections of this document and found no behavior plan in it. Upload the plan itself, or paste the pages that describe the behavior."
- Behavior detection reads the first 48,000 characters, runs three times and keeps the majority answer. It offers only behaviors that have their own definition in the document (otherwise just the first one), at most 6, and numbers duplicate names "(2)". Several behaviors send the tester to a chooser page before extraction; the first is pre-selected, an unknown pick returns to the import page, and Back keeps the pending import.
- The same document gets the same behavior list and the same kept sections until the server restarts (both are cached per document digest; the text itself is not cached).
- Extraction into the wizard reads the first 48,000 characters and gets a second, larger attempt (4,096 then 8,192 output tokens) when the answer is cut off. Still too long: "This document is too long to read, even after a second try with more room. Paste the pages that describe the behavior instead." No structure: "We couldn't read enough structure from this document. Please try again, or paste the text instead." AI down: "The AI service could not read this document right now. Please try again in a moment."
- An extracted answer that is only a placeholder (null, none, N/A, unknown, not specified, no information, not in the document) is dropped, so the field stays empty and the rail shows Needs info. Hard returns inside an answer are collapsed into sentences, except in the two goal fields.
- An import may prefill a field up to twice its limit, never more than 3,000 characters, cut at a word boundary. Goal fields take up to 800 characters, the describe boxes up to 2,000. An over-limit field turns amber and Generate BIP waits.
- The teaching box (step 6) is filled only from text that says how the replacement skill is taught (skill acquisition procedures); text under a proactive or antecedent heading never lands there, and priming, timers, first/then, behavior momentum, choices and praise are never teaching. Most plans say only what is taught; then the box stays empty and the rail shows Needs info (clinical reviewer, 2026-09-16).
- After extraction each strategy step is matched on its own text: step 5 per group, step 6 the teaching method, steps 7, 9, 10 and 12 their strips. Every match is shown on its step with a From BIP badge, the quote it came from and a one-line reason. The wizard opens on step 1. Nothing is placed invisibly.
- Learner names are kept as the document writes them; nothing is redacted in the prototype. The import page says so and asks testers to remove PHI first. The loading screen still shows a "Redacting your BIP" step on purpose: that step exists in the real product and runs there (owner decision, 2026-09-10).
Wizard: steps, navigation and sessions
- 12 steps. Optional: 11 (About the learner), 12 (Resources). Every step is reachable from the left rail, and a rail jump saves what was typed first.
- Continue never blocks (owner decision, 2026-09-10): every step saves and moves on, even empty. Required answers only drive the rail's "Needs info" flag once a plan is in progress: Learner's initials (step 1), Target behavior and definition (step 2), Baseline data (step 2), Common situations (step 3), How to reinforce during teaching (step 7), How to reinforce in real-life situations (step 7), Proactive strategies (step 9), Response strategies (step 10). An optional step always counts as filled; a checkbox step counts as filled with one box ticked or one strategy placed.
- A half-finished plan may be generated (owner decision, 2026-09-09) and finished in the Builder.
- Answers live in a server session in memory for 8 hours. A deploy or restart wipes every plan in progress. On the shared Render demo the free plan stops the process after about 15 idle minutes, which wipes plans the same way.
- One request at a time per browser: a Find in progress makes a Continue click wait for it. Rewrite, Generate and the progress poll are not held back (
SessionSerializer, at most 2,000 locks). - The banner's state and plan survive every redirect and rail jump; the generating page keeps its
from=marker across a banner toggle.
Wizard: text limits and Rewrite with AI
- Character limits, read from
data/wizard_questions.json, counted in code points on both sides:- Step 1 (Cover): Learner's initials: 50 characters; Plan subtitle: 80 characters.
- Step 2 (Target behavior): Target behavior and definition: 500 characters; Baseline data: 500 characters; Replacement behavior goal(s): up to 4 goals of 200 characters; Behavior reduction goal(s): up to 4 goals of 200 characters.
- Step 3 (Situations): Common situations: 600 characters.
- Step 11 (About the learner): My preferred communication style is: 90 characters; Some of my favorite activities are: 90 characters; I like when: 90 characters; I can do these activities by myself: 90 characters; I may need help with: 90 characters; Items and supports I should always have: 90 characters; When I need help, you can support by: 90 characters; I don't like when: 90 characters; How I show that I'm frustrated or need help: 90 characters; When I am upset, the following helps me feel calm: 90 characters.
- No
maxlengthon wizard fields, so an import never loses text silently. The counter and an amber note flag over-limit text; Generate BIP refuses it with "<label> is too long (<n> of <max> characters)." or "<label> has too many goals (<n> of <max>)." and names the step. - Goals: up to 4 replacement and 4 reduction goals of 200 characters each (the Builder's two goal lists), one goal per line in the goal editor.
- Describe boxes that feed Find (steps 5 to 10 and 12) have no character limit (owner decision). Find reads only their first 6,000 characters; the resources Find reads the first 4,000 of the combined plan text (at import, of the document). A toast says so when text was left out.
- Rewrite with AI appears only on textarea fields that land in the BIP verbatim (steps 2, 3 and 11 and the goal lists); the 11 match-only boxes and the two step-1 inputs never show it. It refuses text over 4,000 characters ("Rewrite handles up to 4,000 in one go"), gives goal fields a budget of goals x characters, and never cuts its result: if it is still too long the amber counter stays and the tester shortens by hand (owner decision).
Wizard: Find, strategy strips and pools
- Find is add-only. Each press merges into the step's stored matches by id: strategies from an earlier Find or the import stay, only new ones are added, and a strategy the tester removed on this visit never comes back. Find on an empty box calls no AI and changes nothing.
- Each Find returns at most 8 matches. On step 12 the AI suggests at most 3 resources in total, the best three, even when more would fit; the tester adds any others by hand (owner decision, 2026-09-15). Only matches the AI marks confident are placed; the rest appear as review suggestions and are placed only when accepted. Every AI-placed strategy carries a quote from the text (cut at 200 characters) and a reason (cut at 300), shown in the why modal. A hand-added strategy has no quote.
- The Find button reads "Find matching strategies" until something is placed, then "Find more matching strategies". A filled button means the text changed since the last Find.
- What the AI sees for each strategy it may pick from: the title, the whole description and every tag, on one line. Tags carry the technical names (DRA, FCT, planned ignoring, token economy) the friendly titles hide. Behavior-word tags shared by most strategies are never evidence on their own (owner decision, 2026-09-10). The production library must keep its tags filled in.
- Each Find searches only its section's pool; the Add picker offers the whole library (96 strategies, 262 resources):
- Step 5: per group. Functional communication 3, Emotional regulation 6, Executive functioning 8; Other: everything outside those groups.
- Step 6: the 17 strategies tagged "Teaching strategies"; at most one per Find.
- Step 7 (both boxes): 10 Reinforcement strategies. A "high five" in the text always matches Behavior-Specific Praise and nothing else (owner decision).
- Step 8: only the three pause strategies, one per Find or nothing; without Escape ticked, only Model Emotional Regulation.
- Step 9: 37 Prevent strategies.
- Step 10: 13 behavior-management strategies. A strategy may sit here and on step 8 when the tester places it in both (owner decision, 2026-09-09).
- Step 12: resources meant for BCBAs only (108 of 262, CEU trainings and the like) are never suggested; the picker still lists them. The AI suggests at most 3 resources in total.
- Strategy limits per plan section (
BipSchema::SECTION_CAPS, the Builder's limits):teach_what_and_how.what_to_teach(step 5, Replacement skill): 6 strategies.teach_what_and_how.how_to_teach(step 6, Teaching strategies): 3 strategies.teach_reward.how_to_reward(step 7, rewards during teaching): 3 strategies.teach_reward.how_to_respond_to_withdrawal_of_assent(step 8, Respond to withdrawal): 3 strategies.prevent.how_to_prevent(step 9, Proactive strategies): 9 strategies.reward_and_respond.how_to_reward(step 7, rewards in real life): 6 strategies.reward_and_respond.how_to_respond(step 10, How to support): 3 strategies.resources_and_courses(step 12, Resources): 12 resources.
- Over the limit: on steps 5, 6 and 8 extra strategies show gray ("Doesn't fit") and slide in when a placed one is removed; steps 6 and 8 hold up to 12 on the step. On steps 7, 9 and 10 an add past the limit is refused ("This answer holds N strategies at most. Remove one first.") and extra Find or import picks stay unplaced. Step 12 accepts any number on the step; generate keeps the first 12 and the Builder seed cuts there too, without a notice.
Wizard: step by step
- Step 1 (Cover): learner name or initials, verbatim, and as the subtitle the document's own title or subtitle line, copied as written or left empty; the AI never composes a subtitle (fixed 2026-09-23). The import keeps the full name as written.
- Step 2 (Target behavior): target behavior, baseline data and the two goal lists, verbatim. A goal is copied only from a sentence the document states as a goal, objective, target or criterion; the AI never builds one out of a behavior name (fixed 2026-09-23).
- Step 3 (Situations): triggers and situations, verbatim, into
prevent.when_to_prevent. - Step 4 (Function): four checkboxes; all four keys always land in the plan as true or false.
- Step 5 (Replacement skill): four describe boxes, one per group. Functional communication places Teaching to Request, Emotional regulation places Self-Regulation, Executive functioning offers 8 sub-skills. Each group's Add picker holds up to 4 extra strategies. A group counts as ticked while a strategy sits in it; typed text does nothing until Find is pressed or a strategy is added. Placement order is Functional communication, Emotional regulation, Executive functioning, Other, and the first 6 land.
- Step 6 (Teaching strategies): nine checkboxes with fixed strategies: Visual supports place Visual Supports; Modeling, Behavior Skills Training (BST), Video examples place Modeling; Modeling AAC place WOW; Role-play place Role-Play; Prompting place Prompting; Social Stories / Power Card place Power Card; Behavior contract place Behavior Contract. Video examples placing Modeling is a stand-in (TODO: confirm with stakeholder). The describe box's Find adds at most one teaching strategy; the box itself is never matched at generate.
- Step 7 (Rewards): two look-alike boxes. Rewards during teaching hold 3 strategies; rewards in real life hold 6.
- Step 8 (Respond to withdrawal): starts with Model Emotional Regulation ticked, plus the two Kind Extinction strategies only when Escape is ticked on step 4 (clinical reviewer, 2026-09-16). A step never opened generates those defaults; a step saved with none generates none; an import never populates Kind Extinction without Escape and always adds the defaults that apply on top of what the document said. Other strategies reach this step only through the Add picker. The plan holds 3.
- Step 9 (Proactive strategies): one describe box and a strip of up to 9.
- Step 10 (How to support): one describe box and a strip of up to 3.
- Step 11 (About the learner): 10 optional fields of 90 characters, verbatim. The Builder's own editor allows 100 for these fields, so a plan edited there can hold more than the generator accepts.
- Step 12 (Resources): one describe box; the Find matches against the whole plan text, not only the box. The AI suggests at most 3 resources, the best three; the tester adds more by hand, up to 12 in the plan.
Generate
- Generate BIP assembles the plan from the answers with no whole-document AI call. Typed text lands verbatim; on every step the strategies shown are the strategies placed. Nothing is matched from text at generate (owner rule, 2026-09-10); Find on the step is the only matcher. Every placed strategy carries the library title and description; the generator never writes custom ones.
- The only gate: over-limit text on any step blocks Generate BIP, the rail shows "Too long: shorten", and the message names the step.
- The generating page waits 90 seconds for the first answer, then checks every 2 seconds for up to 4 minutes before showing the error screen. Its copy says "This usually takes 15-30 seconds in production." Reload or Back never generates the same plan twice.
- Errors: "The plan could not be generated. The AI service did not answer in time..." with a retry; an invalid AI answer marks the generation failed ("AI returned invalid JSON.") and lands on the same screen. Typed answers stay in the session.
- The server keeps at most 50 generated plans for 8 hours and drops the oldest; a generation stuck for 10 minutes may be started again.
- Unknown strategy or resource ids are skipped at generate (blank title) and the Builder shows "Strategy #id"; no error is raised. Array order is kept as received.
Handoff to the Builder
- The handoff page renders only a finished plan. After a restart or an unfinished run it returns to the start page instead of showing a fixture.
- The plan is written to
sessionStorageunderbip-seed, then the page redirects to/builder. The Builder reads the seed on load throughdata/seed-loader.js, maps it onto itsappState, and clears it so a reload does not re-apply. - When site storage is blocked (private mode, blocked site data) the page shows "Couldn't open your BIP. Your browser is blocking site storage, which the Builder needs to receive the plan..." with a link to try again. "The generated plan couldn't be loaded into the Builder" is the other failure.
- Same origin: Sinatra serves
index.html,script.js,style.css,data/*.js,views/*.jsandapp/javascript/*.jsatlocalhost:9292. The Vite dev server is not used. - The Builder itself has no resource cap and allows 4 goals per list; the generator's caps above apply on the way in. Title (60), description (225) and quote (300) limits apply to edits made in the Builder.
- The Builder's PNG page export does not work when served by Sinatra (its library needs the old build tool); left as is by owner decision.
Plans, access and debug routes
- The plan toggle in the banner is free or pro. The permission gate replaces the whole page body; no control is disabled. Preview it with
?state=permissionon any Generator screen, or with?plan=free&state=emptyon the landing page. - "Free users can generate 1 BIP per time frame" is a preview only: nothing in the prototype counts generations.
- There are no role links: nothing in the prototype changes by role.
/generator/debug/last-generation,seed-demoandseed-bipanswer only in development from localhost; anywhere else they return 404.
Open questions (TODO: confirm with stakeholder)
- Free plan: which time frame (day, week, month), and how credits refresh. Pro: unlimited or a higher cap.
- Production handling of an invalid AI answer: retry silently, or show the failed screen the prototype shows.
- Should the wizard remember answers between visits (the prototype keeps them for 8 hours in memory)?
- How production hands a new plan to the Builder (the prototype uses browser storage).
- Does production keep Amazon Textract for scanned PDFs, and where does OCR run for HIPAA and PHIPA data?
- Step 6: Video examples places Modeling as a stand-in until a video strategy exists.
- Step 2: baseline data is marked required in the wizard while the schema allows it empty.
State coverage per screen
The guide asks every screen for six states: default, loading, empty, form_errors, api_error, permission. Toggle them in the banner or append ?state=<name> to the URL. Cells: ✓ implemented, n/a not applicable, missing not built.
| Screen | default | loading | empty | form_errors | api_error | permission |
|---|---|---|---|---|---|---|
/ (landing) |
✓ | ✓ | shows the plan gate when free | n/a | ✓ | ✓ |
/generator/import |
✓ | ✓ | missing | ✓ | ✓ | ✓ |
/generator/import/behaviors |
✓ | on submit only | missing | missing | missing | missing |
/generator/wizard/1..12 |
✓ | ✓ | missing | step 12 only, as a toast | ✓ | ✓ |
/generator/generating |
✓ | is the loading state | missing | missing | ✓ | ✓ |
/generator/handoff |
✓ | missing | missing | missing | ✓ | ✓ |
/constraints |
✓ | n/a | n/a | n/a | n/a | n/a |
/builder mini-BIP pages 1 and 2 |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
/builder other pages |
✓ | missing | missing | missing | missing | missing |
The Builder has no prototype banner; its two mini-BIP pages read ?state= from the URL on every render. The wizard's form errors arrive as a toast, not an inline list (owner decision).