<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Phase 7 &mdash; Wiki UX &mdash; Arc Plan</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 760px;
    margin: 1.5em auto; padding: 0 1em; line-height: 1.5; color: #1a1a1a; }
h1 { font-size: 1.4em; }
h2 { font-size: 1.1em; margin-top: 1.4em; }
ol { margin: 0.3em 0; } li { margin: 0.25em 0; }
ol.outline { list-style-type: upper-roman; }
ol.outline ol { list-style-type: upper-alpha; }
ol.outline ol ol { list-style-type: decimal; }
ol.outline ol ol ol { list-style-type: lower-alpha; }
.check { color: #1a7f37; font-weight: bold; }
.next { color: #b35900; font-weight: bold; }
.review { color: #0969da; font-weight: bold; }
.nope { color: #82071e; font-weight: bold; }
.q { color: #8250df; font-weight: bold; }
code { background: #f2f2f2; padding: 0 0.25em; border-radius: 3px; }
em { color: #555; }
</style>
</head>
<body>
<h1>Phase 7 &mdash; Wiki UX</h1>
<p><em>Arc started 2026-07-22, after Phase 6. Sections are Roman numerals
and steps within them are lettered; each step's title says what it does,
followed by a sentence on what was proposed and, once worked, a few on what
was done and anything learned. Marker legend:
<span class="next">&#9654;</span> being worked on,
<span class="review">&#9873;</span> implemented and waiting to be reviewed,
<span class="check">&#10003;</span> done,
<span class="check">&#10003;</span><span class="nope">&#10007;</span>
closed by deciding not to do it, <span class="q">?</span> open decision,
unmarked = planned.</em></p>

<h2>What this arc is for</h2>
<p>The wiki is where a group keeps its shared pages, so its editing and
resource surfaces are what a group touches most. This arc finishes the
page-resource folder UI (I), gives the editor a live preview and then a
markup toolbar (II), makes the category mechanism a real content tool (III),
completes the email witness flow for secret ballots (IV), and documents the
folder UI once it is finished (V).</p>

<ol class="outline">
<li><span class="check">&#10003;</span> <strong>Finish the page-resource folder
    UI.</strong> The folder is what a group touches most, so it is finished
    first.
    <ol>
<li><span class="check">&#10003;</span> <strong>The page-resource folder reflows
        as a card grid.</strong> A fixed table with four hard column widths gave
        the folder a 502px floor. The grid is sized by a container query on the
        folder itself.</li>
<li><span class="check">&#10003;</span> <strong>Rows in the folder can be picked
        and acted on together.</strong> Rows are picked in the browser. Nothing
        is sent until an action is asked for.</li>
    </ol></li>
<li><strong>Give the editor a preview and then an assist.</strong> The preview
    comes first, since the toolbar shows its result.
    <ol>
<li><span class="check">&#10003;</span> <strong>A wiki page shows a live preview
        beside its source.</strong> It renders in the browser through the
        existing parser port, so typing costs no request.
        <ol>
<li><span class="check">&#10003;</span> <strong>The two panes share one
            window.</strong> It is of settled height and split by a draggable
            bar. Under 52rem the preview sits above the source.</li>
<li><span class="check">&#10003;</span> <strong>The preview opens from a book
            icon and starts closed.</strong> A page that is never previewed pays
            nothing for the feature.</li>
<li><span class="check">&#10003;</span> <strong>Picking words out in either pane
            marks them in the other.</strong> They are brought into view. The
            pane being worked in never moves.</li>
<li><span class="check">&#10003;</span> <strong>Charts, tables, math and search
            boxes all draw in the preview.</strong> Each is rendered from what
            the markup carries, so nothing is fetched.</li>
<li><span class="check">&#10003;</span> <strong>A resource shown as an example
            is not drawn.</strong> Text inside nowiki is left as itself, in both
            parsers.</li>
        </ol></li>
<li><span class="check">&#10003;</span> <strong>Buttons insert wiki markup at
        the cursor.</strong> The textarea stays the source of truth and the
        preview shows the result.
        <ol>
<li><span class="check">&#10003;</span> <strong>The preview follows what a
            button puts in.</strong> A browser raises nothing when a script
            writes into a field, so the preview went on showing what stood
            there before the markup was added.</li>
<li><span class="check">&#10003;</span> <strong>What a button puts in can be
            taken back with the undo key.</strong> The whole field had been
            rewritten, which throws away what the browser remembers of the
            writing; the words now go in the way typing puts them in.</li>
<li><span class="check">&#10003;</span> <strong>A picture stored with a page
            now shows in the preview.</strong> The preview asks the site for
            such a picture by group and page, and it was asking with both of
            those set to zero, so the site had nothing to look up and the
            picture came out broken. The page being edited knows its group
            and its own id, so both are written onto the editing field as
            data attributes and the preview reads them from there.</li>
        </ol></li>
    </ol></li>
<li><span class="next">&#9654;</span> <strong>Make the category mechanism a
    real content tool, so a group can run as a newsroom.</strong> A wiki page
    becomes an article, other pages front those articles, and a category is
    what ties the two together. Today a journalist types the icon, the
    open-graph properties and each category tag by hand, in two places, with
    nothing checking the spelling. The five steps below remove that typing and
    make a category something the site knows about rather than a string in a
    body. To-do item 20.
    <ol>
<li><span class="review">&#9873;</span> <strong>A front page collects articles
        into a shape a writer chooses.</strong> Such a page is laid out by
        picking a shape and naming what fills each of its places, rather than
        by typing tags. What is saved is ordinary wiki markup, so anyone can
        read or edit the page without the form.
    <ol>
<li><span class="review">&#9873;</span> <strong>Four shapes are offered, each
            drawn as a thumbnail.</strong> A writer picks by the look of it: a
            lead story with headlines beneath, a row of cards, a grid of four,
            and stacked sections. The shapes and their places appear as soon as
            the kind is chosen rather than after a save.</li>
<li><span class="review">&#9873;</span> <strong>Each place says what it
            holds.</strong> A lead holds an article and the headlines beneath
            hold a category, so neither asks; anywhere else offers the choice,
            then a page to name or a category to pick from the group's
            list.</li>
<li><span class="review">&#9873;</span> <strong>The settings show what belongs
            to the kind of page being made.</strong> Every kind showed every
            control, so an alias page offered a border and a front page offered
            a resource path. One table says which parts each kind uses, the
            form follows it as the kind is chosen, and the settings offer
            saving alone.</li>
<li><span class="review">&#9873;</span> <strong>One of a set of buttons stands
            for the whole set when a form is sent.</strong> Every button was
            being sent, so the last of them stood for the choice whichever a
            person had picked.</li>
<li><span class="review">&#9873;</span> <strong>The shapes are given the
            styling that makes them look like themselves.</strong> Cards lay
            along a line and wrap, the grid is two across and one on a narrow
            window, sections stack with a rule between, and headlines under a
            lead are a plain run of names. The elements a list is made of are
            each told how to lay themselves out, since a browser lays an
            unknown element along the line like a word.</li>
<li><span class="review">&#9873;</span> <strong>A list of what is filed under
            a name draws at all.</strong> The parser left the words asking for
            a list as words, while the site looked for a mark those words
            never became, so no list had ever drawn. The parser leaves that
            mark now.</li>
    </ol></li>
<li><span class="check">&#10003;</span> <strong>A group knows its own
        categories and what its pages are for.</strong> Everything after this
        leans on the group's list existing, since a chooser can only offer
        what the group knows.
    <ol>
<li><span class="check">&#10003;</span> <strong>A group's settings hold the
            names its pages may be filed under.</strong> Each name stands on
            its own row with a mark that drops it. A field below adds
            another, the way a domain is added on the servers page. A name
            already standing, or one holding a comma, is passed over. What
            is sent is still one line with commas between, kept in a hidden
            field and rebuilt as rows come and go. A front page's category
            pickers offer these alongside any name the group's pages are
            already filed under.</li>
<li><span class="check">&#10003;</span> <strong>A group says what kind of
            page a new page starts as.</strong> A newsroom sets it to news
            article, so every page a journalist creates begins as one. A page
            that already exists keeps the kind it was saved with. The
            setting is labeled for what it holds rather than for what it
            does. Its name and the categories' name each carried a colon
            the markup supplies, so both read with two.</li>
<li><span class="check">&#10003;</span> <strong>A wiki page can be a news
            article.</strong> A new page kind carrying what an article needs
            and nothing else. The two new group settings live in columns of
            their own, added by a version step so a database already in use
            moves forward. Naming the columns in the seeding inserts keeps
            them working when another column is added.</li>
    </ol></li>
<li><strong>A journalist writes a news article without leaving the
        editing screen.</strong> Writing one today means filling in the
        settings panel for the title, the author, the thumbnail and the
        description, going back to the body to type the category tags, and
        then typing the open graph properties by hand. All of that moves
        onto the editing screen, and saving fills in the rest.
    <ol>
<li><span class="review">&#9873;</span> <strong>The title, author,
            thumbnail and abstract sit above the writing area.</strong>
            They are the first things a journalist needs and were three
            clicks away in the settings panel. The four are moved out of
            that panel onto the editing screen rather than drawn a second
            time, so no field is asked for twice. The description is
            labeled Abstract and the icon Thumbnail while a news article
            is being written.</li>
<li><span class="review">&#9873;</span> <strong>Categories are chosen
            from a list beneath the abstract.</strong> They had to be typed
            as tags in the body, where a misspelling filed the page
            nowhere. The group's own names are offered, and a name chosen
            stands as a row with a mark to drop it. What a page is already
            filed under is read from its body, so opening one shows what is
            there.</li>
<li><span class="review">&#9873;</span> <strong>Saving writes the tags,
            the head and the open graph properties.</strong> A journalist
            should not have to write the
            same category in three places, nor know what an open graph
            property is. Saving writes the category tags into the body, so
            searching by category still finds the page; puts the title,
            author, thumbnail and abstract into the page's head; and writes
            og:title, og:description, og:image, og:url and a property for
            each category. The tag also files the page under that
            category, which is a link from the page to itself carrying
            the category's name.</li>
<li><span class="review">&#9873;</span> <strong>A reader can see which
            sections an article is in and follow them.</strong> Nothing on
            a finished article said what it was filed under, so a reader
            wanting more of the same had nowhere to go. The names stand
            under the article's last words, each a link to the list of
            what else is filed there.</li>
    </ol></li>

<li><span class="review">&#9873;</span> <strong>A category list becomes a
        real listing.</strong> A page drawing a category could ask only for
        a name and a class. It can now say how many, in what order and
        which page of them, the list it draws is ordinary markup, and a
        category has a page of its own to lead to.
    <ol>
<li><span class="review">&#9873;</span> <strong>A list takes named
            settings.</strong> How many to show, in what order, and how to
            style them, given by name rather than by position, and any of
            them may be left out. A list written as a bare set of classes
            still reads, so pages already written go on working.</li>
<li><span class="review">&#9873;</span> <strong>A long list offers the
            rest.</strong> A front page showing one section of a busy group
            gave a reader the first few and no way past them. Where more
            are filed than were asked for, a line beneath the list says how
            many there are and leads to all of them.</li>
<li><span class="review">&#9873;</span> <strong>A category has a page of
            its own.</strong> Asking for a category gives a page naming it
            and listing what is filed under it, built as it is asked for
            rather than kept, so a category has one from the moment
            anything is filed under it. Every link to a category leads
            there: the tags beneath an article and the way on beneath a
            listing.</li>
<li><span class="review">&#9873;</span> <strong>The list a category
            becomes is ordinary markup.</strong> It had been tags of our
            own invention, which carry a name but no meaning, so nothing
            reading the page for someone could tell it was a list of
            links. It is now a list of links.</li>
    </ol></li>
<li><strong>A category can reach another group, and the forms are written
        up.</strong> A front page in one group collects articles filed in
        another, written the way the site already writes a page in another
        group. Planned.
    <ol>
<li><strong>A category may name another group.</strong> Planned.</li>
<li><strong>The forms that reach another group are checked in both
            routes.</strong> A link written that way works on the site and in
            the preview alike. A file named that way works in neither today,
            so that is new work rather than a regression to guard.
            Planned.</li>
<li><strong>The category forms are written up on the syntax page.</strong>
            Neither the tag nor the list appears there today. Delivered as wiki
            help source in a text file, since that page is generated.
            Planned.</li>
    </ol></li>
    </ol></li>

<li><strong>Let named witnesses take part in a secret ballot by email.</strong>
    The asynchronous flow lets them answer in their own time rather than all at
    once. To-do item 21. Planned.</li>
<li><strong>Document the wiki resource-folder interface.</strong> It is written
    as wiki help source and delivered as a text file. Best done after the folder
    itself. To-do item 37. Planned.</li>
<li><strong>Other work accomplished, oldest first.</strong> Each step below was
    reached while working on something else.
    <ol>
<li><span class="check">&#10003;</span> <strong>Git clone, push and pull work
        over HTTP against a Yioop repository.</strong> The smart protocol now
        works over both HTTP/1.1 and HTTP/2.
        <ol>
<li><span class="check">&#10003;</span> <strong>Packfiles are built on a
            fiber.</strong> The server answers other requests while one is
            built.</li>
<li><span class="check">&#10003;</span> <strong>Negotiation settles.</strong>
            The server names a commit both sides share, so a client with a long
            history stops asking.</li>
<li><span class="check">&#10003;</span> <strong>A reply states its length and
            streams.</strong> A deferred reply sends only its buffered body,
            which claimed a length it did not send.</li>
<li><span class="check">&#10003;</span> <strong>Only what the client lacks is
            sent.</strong> Objects go as deltas against what it holds. A new
            GitDelta writes and reads that format on bytes.</li>
<li><span class="check">&#10003;</span> <strong>A deep clone of the live
            repository completes.</strong> It stopped partway before, with a
            differing amount delivered each time.</li>
        </ol></li>
<li><span class="check">&#10003;</span> <strong>A running Yioop keeps its memory
        bounded.</strong> Both servers were made to say where their memory went,
        and then to use less of it.
        <ol>
<li><span class="check">&#10003;</span> <strong>The web server reports its own
            memory.</strong> The request that finally fails is no longer the one
            blamed.</li>
<li><span class="check">&#10003;</span> <strong>The mail server reports its own
            memory.</strong> It had gone from 120MB to its 1GB limit in ten
            seconds with nothing said.</li>
<li><span class="check">&#10003;</span> <strong>A mail server fatal names what
            it was doing.</strong> It gives the account, the folder and the full
            command, where it named only the bare verb.</li>
<li><span class="check">&#10003;</span> <strong>Mail folder indexes are held as
            plain values.</strong> The same folder costs 6MB where it cost 48MB.
            The cache is bounded by records rather than folder count.</li>
<li><span class="check">&#10003;</span> <strong>A web session lives in a file
            rather than in memory.</strong> What the server keeps is the time
            and use-count. A session past the request-size limit is
            refused.</li>
        </ol></li>
<li><span class="check">&#10003;</span> <strong>Every page fits its own
        window.</strong> A minimum width on the body container meant pages
        overflowed between about 601 and 1060 pixels. An activity that is mostly
        working area now takes the window's width.</li>
<li><span class="check">&#10003;</span> <strong>A missing wiki page no longer
        leaves nulls in the media path.</strong> One request raised a warning, a
        deprecation and a second warning, all from the same defect.</li>
<li><span class="check">&#10003;</span> <strong>A visitor can report an issue
        without an account.</strong> The tracker is public to read, so someone
        who hits a bug no longer has to register or stay silent.
        <ol>
<li><span class="check">&#10003;</span> <strong>A report is held for an
            editor.</strong> There is a queue with bulk discard. A reporter may
            be barred for a week, a month or for good.</li>
<li><span class="check">&#10003;</span> <strong>Each repository chooses its own
            setting.</strong> It is none, moderated or unmoderated, defaulting
            to moderated.</li>
<li><span class="check">&#10003;</span> <strong>Issue lists are read by
            scrolling.</strong> Reaching the end fetches the next page as rows.
            That needed a stable sort order and an allowed offset field.</li>
        </ol></li>
<li><span class="check">&#10003;</span> <strong>Yioop no longer needs three
        outside programs.</strong> qrencode, image magick and calibre were each
        required for one small thing.
        <ol>
<li><span class="check">&#10003;</span> <strong>Yioop draws its own code
            symbols.</strong> The same arithmetic is written in PHP and in the
            browser. A test compares the two square by square.</li>
<li><span class="check">&#10003;</span> <strong>Each kind of file makes its own
            thumbnail.</strong> A document, a book and a text file each do their
            own, in the processor that owns them.</li>
<li><span class="check">&#10003;</span> <strong>Audio messages are transcribed
            in the browser.</strong> The browser listens while a message is
            recorded. The server keeps nothing beside the recording.
            <ol>
<li><span class="check">&#10003;</span> <strong>A recording can become its
                words before sending.</strong> A button beside play and
                delete replaces the recording with what was heard. The text
                can be edited and is sent as an ordinary message.</li>
<li><span class="check">&#10003;</span> <strong>A recording sent as audio
                carries its words inside the message.</strong> They are its
                resource description. The pipe and doubled parenthesis are
                taken out so the words cannot end the markup early.</li>
<li><span class="check">&#10003;</span> <strong>The reader's browser draws
                a toggle that shows the words.</strong> It reads them off the
                page itself, so the click costs no request. Fresh pushed
                messages get their toggles the same way.</li>
<li><span class="check">&#10003;</span> <strong>The send guard resets when
                a message is sent.</strong> It was set once and never let
                go, so a second message on the same page load was silently
                held.</li>
            </ol></li>
        </ol></li>
<li><span class="check">&#10003;</span> <strong>A document thumbnail shows its
        front page.</strong> It reads that page alone, rather than the whole
        document.
        <ol>
<li><span class="check">&#10003;</span> <strong>The page tree is followed to
            find the first page.</strong> That includes reaching through the
            packed bundles a document may keep its objects in.</li>
<li><span class="check">&#10003;</span> <strong>A picture written in the four
            colors ink uses is read.</strong> It is taken apart here, since
            PHP's own reader returns it as all zeros.</li>
<li><span class="check">&#10003;</span> <strong>The page's words are drawn where
            the page has them.</strong> They take the size and color it set,
            with nothing behind them. Their place is the frame in force when the
            text began.</li>
<li><span class="check">&#10003;</span> <strong>Shapes the page fills are drawn
            too.</strong> That is how a nameplate set as an outline appears
            rather than being lost.</li>
<li><span class="check">&#10003;</span> <strong>A document with a font in it
            gives up its words.</strong> The going back to reading words was
            compared rather than set. Everything after the first font was
            skipped.</li>
        </ol></li>
<li><span class="check">&#10003;</span> <strong>A message with a recording
        sends once and plays on both sides.</strong> Sending a recording
        raised an error and left broken copies, as many as ten. Two halves,
        both from this afternoon's commits, measured over HTTP/2 against the
        secure server with a real microphone.
        <ol>
<li><span class="check">&#10003;</span> <strong>The lost helper is
            restored.</strong> The GroupModel rewrite dropped
            linkResourceFolders while newMessage still called it. The Error
            escaped the route, so the stream was never answered, and the
            browser resent the same body each time.</li>
<li><span class="check">&#10003;</span> <strong>The route accepts the bare
            token the renderer writes.</strong> The renderer had been changed
            to put the token value alone in a recording's address. The route
            still demanded name=value or a dash, so every read was turned
            away to the error page.</li>
        </ol></li>
<li><span class="check">&#10003;</span> <strong>Opening a conversation no
            longer answers itself with the whole conversation.</strong> Every
            page load sent a call-ended signal, and the reply to it carried
            the whole conversation back, some six hundred kilobytes a time.
            Nothing read that reply. Three changes together took the cost to
            eleven bytes.
            <ol>
<li><span class="check">&#10003;</span> <strong>Ending is only said when a
                call was in progress.</strong> Leaving a page said a call
                had ended whether or not one had begun.</li>
<li><span class="check">&#10003;</span> <strong>The event body is read from
                where the atto server puts it.</strong> It had been read
                from the stream the server leaves empty, so every signal
                looked like nothing at all.</li>
<li><span class="check">&#10003;</span> <strong>A call signal is answered
                with a small acknowledgment.</strong> The sender never reads
                the reply, so a page is the wrong thing to send back.</li>
<li><span class="check">&#10003;</span> <strong>A sent message carries no
                unnamed parts.</strong> Buttons with no name went up with
                every message and were dropped on arrival.</li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>A conversation's live channel
            rides HTTP/2 and sends only new messages.</strong> The channel
            had to fall back to an older connection, and each push carried
            the whole conversation. It now rides the connection the page
            already has, and a push is about seven hundred bytes.
            <ol>
<li><span class="check">&#10003;</span> <strong>The server offers WebSockets
                over HTTP/2.</strong> It advertises the extended connect
                setting and accepts the connect that follows, so a socket
                shares the page's own connection.</li>
<li><span class="check">&#10003;</span> <strong>An outbound frame is split to
                the maximum data-frame size.</strong> A frame larger than
                that broke the connection's framing.</li>
<li><span class="check">&#10003;</span> <strong>A push sends the conversation
                list alone.</strong> It had rendered the whole page, so
                every push carried the chrome around the messages.</li>
<li><span class="check">&#10003;</span> <strong>Control frames answered on a
                socket keep the connection's framing.</strong> A close echo
                written raw corrupted the stream carrying it.</li>
<li><span class="check">&#10003;</span> <strong>A carrier stream is let go
                when its socket closes.</strong> It had been held until the
                stream itself ended, which need never happen.</li>
<li><span class="check">&#10003;</span> <strong>A connection carrying a
                socket still pumps what is waiting.</strong> It took a path
                meant for sockets alone, and a large reply hung.</li>
<li><span class="check">&#10003;</span> <strong>A conversation lets go of its
                channels when the browser leaves the page.</strong> Safari
                otherwise reported the socket closed by suspension.</li>
<li><span class="check">&#10003;</span> <strong>A socket is handed what
                opened it, and touches nothing else of the request.</strong>
                The query and cookies had been put in the arrays an ordinary
                request uses, where a finished response writes the session
                named among them.</li>
<li><span class="check">&#10003;</span> <strong>How a socket's frames are
                written is held in place by tests.</strong> Seven cases cover
                the splitting, the wrapping and the drain path.</li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>A conversation and its
            contacts can be let go of, in whole or in part.</strong> Only the
            person who wrote a message could remove it, and there was no way
            to clear a conversation or drop a contact. Each person now
            manages their own half, and the other keeps theirs.
            <ol>
<li><span class="check">&#10003;</span> <strong>Any message in one's own half
                can be taken off it, whoever wrote it.</strong> A message is
                kept twice, once in each person's group, and only the writer
                could remove one.</li>
<li><span class="check">&#10003;</span> <strong>A whole conversation can be
                let go.</strong> Only that person's copies go; the two remain
                contacts with an empty conversation between them.</li>
<li><span class="check">&#10003;</span> <strong>A contact can be removed from
                one's own list.</strong> Their messages go from this side,
                along with the row the conversation hung from.</li>
<li><span class="check">&#10003;</span> <strong>A contact's row carries three
                marks that open what can be done.</strong> The marks stand at
                the end of the row and open a list of the two.
                <ol>
<li><span class="check">&#10003;</span> <strong>The marks are drawn rather
                    than written as a letter.</strong> The letter for them is
                    missing from some of the faces a browser has.</li>
<li><span class="check">&#10003;</span> <strong>The row lays its parts out
                    along a line.</strong> It had been a plain block, so the
                    marks dropped below the row.</li>
<li><span class="check">&#10003;</span> <strong>The words in the list stand
                    in the ordinary flow.</strong> The rule placing a
                    contact's name by hand had squashed them.</li>
                </ol></li>
<li><span class="check">&#10003;</span> <strong>The message box fills the
                width of its panel.</strong> A stray closing tag had left the
                conversation drawn in three panels.</li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>A recording is made in one
            kind every browser can play.</strong> Each browser writes its own
            kind and none writes the same one, so a recording made in one
            played nowhere else. The site now makes every recording over into
            one kind, written one way.
            <ol>
<li><span class="check">&#10003;</span> <strong>The kind asked for is the one
                the most browsers play.</strong> mp4 and mp3 are asked for
                under every name they go by, by building a recorder with each
                rather than asking whether it is supported.</li>
<li><span class="check">&#10003;</span> <strong>A recording that already
                arrives in the wanted kind is made over as well.</strong> Two
                browsers writing the same kind do not write it alike.</li>
<li><span class="check">&#10003;</span> <strong>Recording is not offered
                where the site cannot make one over.</strong> Without FFMPEG
                a recording made in one browser could not be played in
                another.</li>
<li><span class="check">&#10003;</span> <strong>A recording carries the mark
                for its words in the lower corner.</strong> It had sat at the
                top left, over the play controls.</li>
<li><span class="check">&#10003;</span> <strong>A recording with no words
                carries no description, and one is still a recording.</strong>
                It had been given a description naming its own file, and a
                resource with none was left in the message as plain text.</li>
<li><span class="check">&#10003;</span> <strong>A browser that will not listen
                is not offered the button that asks it to.</strong> Only a
                plain refusal counts as one, since a browser that heard
                nothing says so with other words.</li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>Signing out works
            again.</strong> A browser was told the sign-out address had moved
            for good, so it went straight to where it had been sent once and
            never signed anyone out again.
            <ol>
<li><span class="check">&#10003;</span> <strong>Being sent on is no longer
                announced as a move for good.</strong> A reply carrying only
                a Location header was answered as moved permanently.</li>
<li><span class="check">&#10003;</span> <strong>Signing out empties the
                request rather than doing away with it.</strong> This server
                keeps its process between requests.</li>
<li><span class="check">&#10003;</span> <strong>A failure while a request is
                handled can be let out rather than caught.</strong> Caught,
                only that connection is let go and the request answers with
                nothing, which gives little away.</li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>A wiki page being read names
            itself on its body.</strong> A domain's own stylesheet can reach
            reading alone, and reach one group or one page within it, without
            reaching the rest of the site.
            <ol>
<li><span class="check">&#10003;</span> <strong>The three classes are
                read-wiki, the group's name and the page's name.</strong>
                Spaces and underscores become hyphens and anything a class
                cannot carry is dropped.</li>
<li><span class="check">&#10003;</span> <strong>A wiki page served as a
                static page carries the same classes.</strong> The group
                named is the one the domain routes to, so two domains give
                two different group classes.</li>
<li><span class="check">&#10003;</span> <strong>The class for acting as
                someone else is gone, along with the rules that waited on
                it.</strong> The name the body looked at was never set
                anywhere.</li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>Searching a mail folder lets
            go of each message once it has been read.</strong> The reader
            that walks a search's terms names itself, and a thing that names
            itself is not let go when the count of what points at it drops.
            It held each message body, so a search over a large folder ran
            the mail server out of memory.</li>
<li><span class="check">&#10003;</span> <strong>Tests are kept one class to a
            file and write under the test files.</strong> Cases had been
            spread across narrow files of their own, and one wrote its
            scratch work into the work directory.</li>
<li><span class="check">&#10003;</span> <strong>A page keeps itself while it
            is being written.</strong> A writer no longer has to remember to
            save. The page saves itself once they stop typing, and the
            controls for saving by hand step aside while it does.
            <ol>
<li><span class="check">&#10003;</span> <strong>A control beside the editor's
                other buttons turns the keeping on and off.</strong> It is
                two arrows going round, drawn rather than written. Green says
                the page is keeping itself, red with a line across says it is
                not, and the word Saving stands beside it while a save goes.
                </li>
<li><span class="check">&#10003;</span> <strong>Five quiet seconds after a
                change the page is saved.</strong> Nothing further is saved
                until another change is made. Such a save gives its own
                reason, so the history reads properly.</li>
<li><span class="check">&#10003;</span> <strong>The controls for saving by
                hand step aside while the page keeps itself.</strong> The
                reason for an edit, the save button and the way to see the
                page are left out. A page's settings are a form to fill in
                and send, so they keep their save button and are never
                offered the keeping.</li>
<li><span class="check">&#10003;</span> <strong>Each save takes the fresh
                mark the site sends back.</strong> A save whose mark is older
                than the page's last change is refused. Without this the
                first save of a sitting landed and every one after was turned
                away.</li>
<li><span class="check">&#10003;</span> <strong>A file dropped into the
                writing area is sent at once.</strong> Its name went into the
                page while the file waited for a press of save that never
                came. The writer stays where they were while it goes, and the
                count of how much has gone up is cleared when it lands.</li>
<li><span class="check">&#10003;</span> <strong>A page written for the first
                time learns its own name.</strong> Such a page has no id
                until it is saved, and a page that saves itself never
                reloads. The id is taken from what the site sends back, so
                the preview can ask for the files kept with it.</li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>The preview reads a page the
            way the site will.</strong> A writer sees what a reader will see
            rather than an approximation. Each piece was built by rendering
            samples through the site and matching them.
            <ol>
<li><span class="check">&#10003;</span> <strong>A page written in markdown
                has a preview of its own.</strong> There was a reader for
                wiki markup and none for markdown. The new one covers words,
                headings of both kinds, lists, quotations, rules, code,
                tables, links, notes and the marks that work within a
                line.</li>
<li><span class="check">&#10003;</span> <strong>Both readers take the
                templates that name a class, an id or a style.</strong> Only
                the plain names were read, so a page using the block forms
                lost its wrapping. A template laid out over several lines is
                read too, on the site as well as in the browser.</li>
<li><span class="check">&#10003;</span> <strong>A block written with quotes
                reads the same after a save.</strong> Text is cleaned on its
                way in, and cleaning turns a quote into the name standing for
                it. Both marks now open a value, so a block survives the
                round trip.</li>
<li><span class="check">&#10003;</span> <strong>Both previews draw the fields
                of a form.</strong> A page showed the words asking for a
                field rather than the field. One drawer serves both readers
                and covers every kind the site knows.</li>
<li><span class="check">&#10003;</span> <strong>A form looks as it will to a
                reader.</strong> The site leaves standing-in marks and fills
                them when it draws a page, so the preview showed them as
                words. A fresh form now comes up empty, and the date rides
                along without being shown.</li>
<li><span class="check">&#10003;</span> <strong>A list carries its choices
                and a field keeps the words after it.</strong> A list takes
                its choices from a block named beside it, which the preview
                never copied. A field followed by anything on the same line
                was left unread.</li>
<li><span class="check">&#10003;</span> <strong>The preview is dressed the
                way a reader will see the page.</strong> It carries the same
                three classes a page being read carries. A domain's own
                stylesheet reaches what the writer is looking at.</li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>Files kept with a page draw
            in the preview.</strong> A picture, a document or a recording
            named on a page shows as itself. The address it is asked for at
            is built the way the site builds it.
            <ol>
<li><span class="check">&#10003;</span> <strong>A file is asked for the way
                the site serves it.</strong> Where the site serves pretty
                addresses it serves a file as a path rather than a query.
                The address is rooted at the site, since a path read against
                the page points under that page.</li>
<li><span class="check">&#10003;</span> <strong>A file is asked for under the
                mark the site would use.</strong> A file belonging to the
                public group, or to any group anyone may join, is asked for
                under a row of zeros. The page works this out as the site
                does and tells the preview.</li>
<li><span class="check">&#10003;</span> <strong>Every kind of picture the
                site serves is drawn.</strong> The list of picture endings
                was written before webp and the kinds that followed it. A
                picture asked for without a link is drawn on its own, and a
                document is shown in a frame.</li>
<li><span class="check">&#10003;</span> <strong>A file asked for as a thumb
                draws the small picture kept beside it.</strong> The reader
                drew the file itself, which for a document meant a bare link.
                The site draws it the same way now, and neither points at a
                picture that was never made.</li>
<li><span class="check">&#10003;</span> <strong>A file named inside a table
                cell stays whole.</strong> Such a naming carries a bar of its
                own, and a bar ends a cell. The reader follows the depth of a
                naming as it already followed a link's.</li>
<li><span class="check">&#10003;</span> <strong>A file added to a page is
                described with a space before its name.</strong> The word and
                the name ran together. </li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>A page carries its locale
            wherever it goes.</strong> A writer works on a page in one
            language while reading the site in another. Every way of looking
            at the page stays in the language being worked on.
            <ol>
<li><span class="check">&#10003;</span> <strong>A writer chooses which locale
                a page is written in.</strong> The locale above the editor
                was a word; it is a choice now. Choosing another goes to that
                locale's page, so what is saved after goes there.</li>
<li><span class="check">&#10003;</span> <strong>The locale goes with every
                way of looking at a page.</strong> Reading a page's history,
                its source or its editor fell back to the reader's own
                language. It travels on the row of activities and on the
                links the page draws.</li>
<li><span class="check">&#10003;</span> <strong>Reverting goes back to the
                history, in the locale it was read in.</strong> A revert
                wrote the old words into the reader's locale rather than the
                version's. The way out afterwards led to the group's talk
                rather than the history.</li>
<li><span class="check">&#10003;</span> <strong>A locale is named by its
                language wherever it is shown.</strong> The history and the
                message about a group having no pages gave the tag. They now
                read as the edit page has always read.</li>
<li><span class="check">&#10003;</span> <strong>A page's history carries a
                way back to it.</strong> Reading the history left no way out
                but the browser's own back. A close mark stands beside the
                button that asks for a difference.</li>
            </ol></li>
<li><span class="check">&#10003;</span> <strong>The scripts are named for
            what they hold.</strong> help.js was mostly a reader of wiki
            markup. The reading now lives in wiki_parser.js, the preview
            machinery in editor.js, and markdown_parser.js reads markdown.
            Everything that includes or names them moved with them.</li>
<li><span class="check">&#10003;</span> <strong>A message naming a character
            set the machine has never heard of no longer stops the mail
            server.</strong> A subject written in windows-1250 was handed
            straight to the converter, which raises an error rather than
            reporting a failure. The conversion is attempted and any error
            caught, another way is tried, and text that cannot be converted
            is kept as it came.</li>
<li><span class="check">&#10003;</span> <strong>A pull of the live repository
        completes.</strong> Cloning worked while pulling did not, in a
        different way each round. A pull now finishes against the repository
        the site itself is served from. Each fault was a way of doing too
        much at once. A whole pack in memory, a whole history walked, a
        whole chain of differences held. The parts below are those, oldest
        first.
        <ol>
<li><span class="check">&#10003;</span> <strong>Serving a pull no longer holds
            the whole site.</strong> One pull ran for two minutes with
            nothing else answered. Building a pack walks history and works
            out differences, a long stretch with no waiting in it. The
            service runs inside a fiber and hands the loop back between
            objects. A task carries its own running-time limit, since the
            page-rendering one cut the pull short.</li>
<li><span class="check">&#10003;</span> <strong>A pack is built into a file
            rather than in memory.</strong> Holding every object exhausted
            two gigabytes on a repository of any size. The reply is built
            into a scratch file that spills to disk. The server drains it a
            piece at a time. A failure part way through is now an error
            the other side can see.</li>
<li><span class="check">&#10003;</span> <strong>A repository holding a large
            file can be cloned.</strong> An object was refused past
            sixty-four megabytes, so a project holding a larger one could
            not be cloned. The ceiling is five hundred and twelve megabytes
            now. A very large object is sent whole, since comparing two such
            objects holds both.</li>
<li><span class="check">&#10003;</span> <strong>A long chain of differences is
            read one step at a time.</strong> A chain ran to a thousand
            steps, each holding its difference while the version below was
            read. The version below is read first and the difference
            unpacked after. The chain is followed no further than git itself
            writes.</li>
<li><span class="check">&#10003;</span> <strong>A client's lines are read only
            as far as they go.</strong> Reading carried on past the last
            line into what followed, which in a push is the pack. Those
            bytes went to a hexadecimal reading that took whichever
            characters were valid. Anything that is not a length now ends
            the reading.</li>
<li><span class="check">&#10003;</span> <strong>A pull stops where the two
            sides' histories meet.</strong> Only the commit a client named
            was marked as reached, not the ones before it. So a commit off
            the path being walked stopped nothing. The whole repository was
            gathered for a pull of a few objects. The client's own history
            is walked first, commits alone, costing a fraction of a
            millisecond each. On a branch that left the trunk forty commits
            back, the walk now gathers what git names.</li>
<li><span class="check">&#10003;</span> <strong>A request and a failed reply
            both say what they were.</strong> A build that failed left no
            headers, no body and nothing in any log. Several rounds of
            chasing it had no evidence to work from. A switch records each
            request's length, first bytes and encoding, and what was built
            in answer. A build that throws is recorded with the object it
            failed on and answered with the reason.</li>
        </ol></li>
<li><span class="check">&#10003;</span> <strong>The search bar and the results
        share the reading column's measure.</strong> The bar stood twice as
        tall as it should on a wide window. It and the count of results ran
        to the window's edge. Both now end where the reading column ends.
        That measure is named once and shared. Right edges line up rather
        than drifting apart as a window widens.
        <ol>
<li><span class="check">&#10003;</span> <strong>The search bar beside the logo
            is one line again.</strong> Its field had a width of its own,
            leaving too little room beside it. The two buttons were sent
            onto a second line. The field takes whatever room is left now,
            which keeps the bar one line at any width. The logo and the bar
            sit side by side as a row. Before, one was lifted by a fixed
            amount to appear beside the other.</li>
<li><span class="check">&#10003;</span> <strong>The panel beside the results
            moves into the column when it cannot fit.</strong> Kept beside
            the results it lay over the advertisement, the count and the
            results themselves. Below the width where column and panel
            cannot both fit, the panel is carried into the column. The one
            panel is moved rather than a second drawn, so the page says each
            thing once.</li>
        </ol></li>
<li><span class="review">&#9873;</span> <strong>A group sets a theme,
        header and footer for all its pages.</strong> Each page had to be
        set by hand, so a group of any size drifted apart. A group now
        holds all three for a page that sets none of its own, and can say
        whether a page may set its own at all; where it may not, the
        controls are not offered. Its settings screen is grouped into
        Organization, Membership, Appearance, Feeds and Wiki, each with its
        own help.</li>
<li><span class="review">&#9873;</span> <strong>A writer can keep several
        lines together where only one line was allowed.</strong> The
        meaning beside a term in a definition list ended at its line, so a
        list or a second paragraph could not go inside one. A same-unit
        mark now holds as many lines as are wanted. Markdown gained
        definition lists at the same time, having had none.</li>
<li><span class="review">&#9873;</span> <strong>A writer can put in any
        markup without remembering how it is spelt.</strong> The row of
        marks above the writing area covers what is reached for most, and
        everything else had to be typed from memory or looked up on the
        syntax page. A mark beside the search one opens a list of the rest,
        and choosing a line puts that markup in where the writer was, with
        an example in each part it takes. The list scrolls, so it does not
        push the writing area off the screen.</li>
        </ol></li>
</ol>
</body>
</html>
X