While a person is working
An application in a container changes under the person using it more often than an ordinary web page does, and at moments they did not choose. There are two doors, and applications written from these instructions have been found with both of them open:
- Before the application is ready. The page appears before its script has finished starting — in a shared document the script waits for the host's write rules. Anything a person types into a form in that moment is lost: the browser either submits the form itself and replaces the page, or ignores the press and the application's own start-up then resets the form. Which of the two happens varies between browsers and between one opening and the next.
- While they are in the middle of something. Another copy's rows arrive — from a link somebody opened, or in the background from the mailbox — and the application redraws. A redraw that rebuilds an open editor from the stored wording throws away what was being typed.
They are one failure: the application changing under a person while they are working in it, and losing their work without a word. An author who has guarded one door should guard the other, so they are explained together here. Each stays a constraint of its own, because they happen at different moments and are fixed in different places — the first in how the page starts, the second in how it redraws.
NO-INPUT-LOST-WHILE-OPENING
Nothing a person does while the app is opening is lost
Until the application's script has finished starting — the database open, the handlers attached, the first draw done — show nothing a person can type into or submit. Put the interactive part of the page in an element marked both hidden and inert, <main id="app" hidden inert>, beside a short line such as "Opening…". inert is what keeps it out of reach: hidden alone is undone by any style rule that sets display on that element, while an inert element takes no focus, typing or clicks whatever the CSS says. Run the whole start-up — openDatabase(), the first draw — inside try/catch. On success, hide the line and remove both attributes. On failure, replace the line with what went wrong and what the person can do next: never leave "Opening…" showing over a start-up that has already ended. Do not wait for an event to learn that start-up failed — the frame's dai:error message is posted outward to the host, is never delivered to the application, and nothing acts on it today; the application's own catch is the only place that knows. A page built only from the kit's elements is already safe: they are not <form> elements, so a button inside them submits nothing before the kit has started. A <form> of your own is not.
Why. The script's start-up waits on await window.dai.openDatabase(), which in a shared document waits for the host's write rules. A form on screen before then takes a person's typing while the application cannot yet handle it. Pressing Enter or its button then either makes the browser submit the form itself and replace the page, or does nothing at all — and the script's own start-up resets the form a moment later. Which of the two happens varies between browsers and between one opening and the next; either way what they typed is gone, with nothing to say why. Both blind runs copied examples that showed their forms early. The same failure arriving mid-edit, when another copy's rows land, is SHARED-REDRAW-ON-MERGE.
Applies to every shape · not checked by anything · NO-INPUT-LOST-WHILE-OPENING in Constraints
SHARED-REDRAW-ON-MERGE
Redraw when the other copy's rows arrive
Listen for the dai:merged event on window and redraw everything drawn from shared tables when it fires: window.addEventListener("dai:merged", (event) => { redraw(); }). event.detail carries applied, duplicate, rejected, newReplicas, conflicts and via — "carrier" when a file or link was opened, "mailbox" when rows arrived in the background. If the page uses the kit's reading elements, call window.daiKit.refresh() in the listener. A redraw must never discard what the person is in the middle of — text typed into a field, an editor that is open, a selection: rows arrive whenever the other copy's changes do, including mid-sentence. Keep work in progress outside what the redraw rebuilds — in a form written once in the HTML rather than recreated on every draw, or in a local drafts table the redraw reads back — or leave the element being edited untouched until it is saved or cancelled. The same failure arriving at start-up rather than mid-edit is NO-INPUT-LOST-WHILE-OPENING.
Why. Nothing else tells the application that another copy's rows landed. Without it the application draws once and redraws only after its own writes, so a two-person document looks broken in exactly the case it exists for. And a redraw that rebuilds an open editor from the stored wording throws away what was being typed, silently — found by running a blind candidate over the mailbox, where a background merge landed while a term was being edited.
Applies to passable, session · checked by the lint (shared-no-merge-listener) · SHARED-REDRAW-ON-MERGE in Constraints
Where to see them done
The receipts and tic-tac-toe examples show both. Each keeps its page hidden and inert behind an "Opening…" line until its start-up has finished, and replaces that line with what went wrong if start-up fails. Receipts edits through a form written once in the HTML, which no redraw rebuilds. tests/examples-shared.spec.ts holds them to it on three browsers: a half-typed edit that survives the other copy's receipts arriving; nothing typeable while either example opens, even when a style rule undoes hidden; a failed start-up that says so; and, beside them, the same example with its gate removed, where the same moment loses what was typed.
How they were found
Both were found by running applications rather than reading them, and by the same method: give a fresh model only the model file, ask for something, and drive what it writes. docs/evaluation.md describes it.
The start-up window showed first as flaky tests: the receipts example's own tests typed into a form that was on screen and not yet working, and passed or failed depending on timing. It was confirmed as a teaching defect, not an example defect, when both blind candidates copied the examples' pattern of showing their forms before the script had started.
The mid-edit loss was found by the second blind run, a two-person agreement, driven over the mailbox on three browsers: on one of them a background merge landed while a term was being edited, and the edit was gone.