When I published Building the Dreamcoded Community App at 9am, the community had registration, an activity feed, a responsive app shell and a more useful Markdown editor. It felt like a natural stopping point for the morning.

It did not stay that way for long. Sixteen more commits followed during the rest of 30 September, and the work spread beyond the community itself. I added a second layer of spam moderation, rebuilt the confirmation flow for sensitive account changes, chased mobile Safari rendering gaps, organised a division of specialist coding agents and used their review to connect the journal, project pages and discussions more deliberately.

The result was less a new headline feature than a day of joining systems together. The community became safer to operate, the site became easier to navigate on awkward screens and the projects gained a clearer history of the work behind them.

Adding AI-assisted moderation without handing over the decision

The community already had deterministic protection against obvious abuse: rate limits, posting cooldowns, duplicate detection, a hidden form trap and deliberately strict rules against links. Those checks are predictable and cheap, but they cannot judge the difference between somebody describing their own project and somebody using a discussion as a sales pitch.

I added a second moderation layer using TypeSafe’s Jev model. For each ordinary member topic, reply or edit, the application sends the post and a small amount of discussion context for a structured spam assessment. It does not send the member’s email address, IP address or database ID.

I made the feature configurable in three modes. off makes no request, shadow records the assessment without changing visibility, and quarantine holds only submissions that cross both configured thresholds. The current production values require at least a 90% spam probability and 80% confidence before a post enters the review queue.

The integration is deliberately not the final authority. Jev can recommend quarantine, but only an administrator can approve or deny the post. Every result is stored with the model version, probability, confidence and review outcome, which makes later threshold changes something I can base on evidence rather than instinct.

External moderation also has to fail safely. A timeout, missing credential, malformed response or service failure leaves the existing deterministic protections in charge and allows the post through. Rate-limit and overload responses receive one short retry before the system fails open. I would rather review an occasional missed post than make the whole community unavailable because another service is having a bad afternoon.

I first ran the integration in shadow mode, then enabled quarantine once the queue and tests were in place. The moderation screen now separates Jev reviews from member reports and shows the assessment behind each held post.

The last part was closing the loop with the author. Approval publishes the post and sends a branded email with a direct link to the discussion. A denial requires a community-rules reason and an explanation, which are included in the corresponding email. The notification can fail without rolling back the moderator’s decision, so mail delivery does not leave the database in an uncertain state.

Making account changes secure without making them confusing

Members can now change their public username from account settings. The same validation and case-insensitive uniqueness rules used during registration apply to the change. Existing discussions continue to belong to the account, while the old profile URL stops resolving rather than pretending to represent somebody else.

That exposed a weak point in the original account-security experience. Username changes, email changes and deletion requests require a recent sign-in, but the first version could only reject a stale session and tell the member to sign in again. It did not remember what they were trying to do, so they had to find the setting and enter everything twice.

I replaced that dead end with a dedicated reauthentication flow. When a sensitive action needs fresh confirmation, the application stores the intended action and pending value against a single-use login token, sends a purpose-specific email to the current address and remembers only a signed reference in an HttpOnly cookie. Following the email creates a fresh session and returns the member to the exact account section with the pending value restored.

Nothing changes merely because the email link was opened. The member still reviews and submits the account form, and expired, reused or mismatched tokens fail closed. The smoother path therefore keeps the important boundary: control of the current inbox is required before a sensitive change can proceed.

Improving the small states people see every day

A cluster of smaller interface changes followed the account work. Forms gained contextual placeholders instead of leaving every field to explain itself through its label. Avatars now use the same component in discussion cards, activity entries and the signed-in account navigation. Removed posts receive a clear staff-only state rather than looking like ordinary content with an ambiguous action beside them.

I also corrected the Activity feed so members no longer receive unread counts for their own topics and replies. The per-user watermark still advances across the full visible timeline, but the query excludes the current author when calculating and rendering new activity. Posting something yourself should not immediately create work for you to clear.

The public site received its own pass at the same time. Blog and category archives now share clearer category navigation and article search controls. Long articles can generate a contents panel, bylines wrap more cleanly and empty community profiles explain what is missing instead of showing a bare blank section. I added the community to the main footer and tightened the checks that ensure fingerprinted CSS and JavaScript references agree with the generated asset names.

Even the small About-page heading was brought back into the same visual language as the rest of the site. None of those changes is large by itself, but together they reduce the feeling that different routes were designed at different times.

The mobile Safari fix that changed twice

The hardest visual problem was not a dramatic layout failure. It was a pair of narrow gaps around the community’s fixed mobile navigation in iOS Safari.

Safari’s changing browser chrome can expose the root page behind a sticky header or bottom dock, especially when the visual viewport and layout viewport disagree. My first pass adjusted safe-area spacing and tested the header and tab bar after scrolling. A second pass used a WebKit-specific document scroller and extended the lower dock into Safari’s composited strip. That stopped content leaking above and below the interface while leaving other mobile browsers on the normal document scroller.

The early workaround also locked pinch zoom because zooming out could make the whole app appear tiny inside the viewport. It solved one visual problem by creating an accessibility problem: people could no longer enlarge the interface when they needed to.

I changed the final implementation to keep the lower scale bounded without blocking enlargement. The community viewport now uses a minimum scale of one, and the gesture-prevention JavaScript is gone. Pinching in can enlarge the page, while pinching out cannot shrink the complete layout below its intended scale.

That work also led to a simpler community header. I removed an unnecessary orange bar, matched the compact branding more closely to the main site and kept the smallest-screen fallback for widths where even the Community label becomes too crowded.

Building an engineering division, then using it

Later in the day I added 64 specialist agent definitions to the repository. They cover focused roles such as frontend and backend engineering, accessibility, privacy, identity, databases, reliability, developer tooling and code review.

The point was not to pretend that every task needs a committee. It was to make it easier to ask a precise question from several useful perspectives without turning one general-purpose review into a vague list of opinions.

I used that setup to examine the main site and community from three angles: site experience, community product and community architecture. Some suggestions—site-wide community search, following members or topics, notifications, links and uploads—were deliberately left for later. With no real discussion history yet, those features would add policy and maintenance work before there is evidence that anybody needs them.

The recommendations that solved current structural problems were more useful, so I implemented those first.

Connecting projects, articles and discussions

Project pages had good descriptions but behaved like isolated case studies. They did not show where a project currently stood or gather the articles written while building it.

They now work as project hubs. A project can declare its current milestone and next concrete step, while any article whose project field matches the project slug appears automatically in its Build journal. The North Is Not Up page now gathers its six development articles into one history instead of making a reader reconstruct that sequence from the general blog archive.

I also added an optional bridge from an article or project to one canonical community topic. The workflow is intentionally manual: create the real discussion first, then place its final topic slug in communityTopic front matter. That avoids automatically creating empty forum threads for every article. No existing post uses the field yet because there are no genuine community topics to link to, but the invitation will appear as soon as one is configured.

Inside the community, the main feed now supports Active, Newest and Unanswered views. The selected view survives pagination and works within category pages as well as the full feed. This is enough discovery for the community’s current size without introducing a separate search system prematurely.

The topic form also offers optional structure for the two categories where context matters most. “What Are You Building?” can insert sections for the project, what is working and the feedback the author wants. “Help & Problems” can add the goal, expected result, actual result and previous attempts. The button appends to an existing draft rather than replacing it, and the ordinary textarea still works without JavaScript.

Recording the real deployment and publication state

The operational documentation had drifted behind production. It still described registration and posting as paused, shadow moderation as the current mode and a direct deployment process.

I reconciled it with the actual configuration: the community is live, registration and posting are enabled, Jev uses quarantine mode and Cloudflare Pages builds and deploys automatically when main is pushed through the existing GitHub connection. I left a separate CI workflow out because it would duplicate a deployment path that already works.

I also restored a detail that matters now that two articles can share a calendar date. Every blog publication date now includes an explicit time and UK offset. Existing posts use 9am, while the authoring guide tells future posts to use their actual, distinct times. The visible page still shows the friendly calendar date, but RSS, structured metadata and newest-first sorting keep the full timestamp.

Where the day ended

The final checks cover much more than the visible changes. The community suite now contains 91 automated tests, including the moderation thresholds, review decisions, reauthentication intent, activity exclusions and feed ordering. The real local registration flow still exercises Turnstile, email verification, sign-in and topic creation, followed by 44 responsive accessibility page checks, keyboard navigation and no-JavaScript browsing.

There are still deliberate omissions. Ordinary members cannot publish links or upload files. Search, follows and notifications can wait until real conversations make their value clearer. The article-to-discussion bridge is ready but has no canonical topics yet.

That feels like the right ending for the day. The morning work made the community feel real. The later work made it safer, corrected the awkward edges and connected it to the projects and writing that give it a reason to exist.