The build journey

How {brand:name} was planned and built, one chapter per build: why it was done, the plan it followed, what was weighed and decided, what went wrong, and how it was proven.

Kept with every slice · last chapter: The library, and the door that stalled

{brand:short_name} is the one internal app that does, once, the jobs every {brand:company} product would otherwise rebuild. People never see its name, which is why its screens and e-mails say {brand:company}: they see a {brand:company} account, a text, a voucher, a tutel.ink link or a chat with Tutela.

The story so far is told below, a day at a time: the latest day is open, and every other opens with a click. The changelog (CHANGELOG.md) lists what changed; this page says why. TuteCore's own journey, up to the release Atrium started from, stays in TuteCore.

At a glance

ChapterWhatCommitsTests
1 · 6 NovFrom a release, not a clone166bbec a3dc12e618
2 · 6 NovThe checklist, read against the codesee the changelog618
3 · 6 NovWhat's already built, and the build ordersee the changelog618
4 · 6 NovOne account, organisations apartsee the changelog618
5 · 6–7 NovThe foundation, built5c6bb8e to 137a685767
6 · 7 NovRunning, and where it goes nextsee the changelog770
7 · 7 NovNuntius’s send path0fe72e3 to 339a283856
8 · 7 NovClavis, decidedsee the changelog856
9 · 7 NovSign-in across domains, proveda throwaway branch856
10 · 7 NovWhat people will see of Clavissee the changelog856
11 · 7 NovAn account that needs nothing elsesee the changelog863
12 · 7 NovOne door, whatever you sign in withsee the changelog888
13 · 7 NovEvery product, one sign-insee the changelog900
14 · 7 NovFrom “Start free” to a running productsee the changelog913
15 · 7 NovA school’s own address, on one listsee the changelog924
16 · 7 NovAn account’s own placesee the changelog940
17 · 7 NovNo code to wait forsee the changelog947
18 · 7 NovWhat products ask of accountssee the changelog954
19 · 7 NovOne door, looked at hardsee the changelog955
20 · 7 NovBilling, decided and drawn twicesee the changelog955
21 · 7 NovWhat the products sell, as data85ea5df968
22 · 8 NovPaid once, however the news arrivesdf5f806 to 5345d30981
23 · 8 NovThe year as a ribbon, and nobody dropped while Atrium sleepsa57c68b1,005
24 · 8 NovA receipt until the GRA says otherwise39144a61,017
25 · 8 NovA switch that won’t move until it’s lawful8daecbe1,025
26 · 8 NovRead by someone who didn’t write it0ea1bc01,033
27 · 8 NovMoney that can only go back oncecf158d3 1a7697c1,047
28 · 8 NovA paper for the money that went backfaf856e1,053
29 · 8 NovSixteen hours to answer the bank4cd7c091,072
30 · 8 NovThe keys handed over, and a certificate readc4316e11,079
31 · 8 NovLocks before the doors openc0daab6 to 1befb321,113
32 · 8 NovWho pays for a text3878ab0 to 5e4376d1,133
33 · 9 NovSix hundred parents, never half40a971f1,145
34 · 9 NovThe name on the phonee1056dd1,154
35 · 9 NovEvery product its own address8dba2491,163
36 · 9 NovNever written to again07cc3511,171
37 · 9 NovTwo readers who hadn’t written itc692e461,183
38 · 9 NovSeven characters a parent can trust7cc06791,192
39 · 9 NovOne code, spent onced754358 to 8e64ec91,214
40 · 9 NovThe limit that waited for the queuef5b3ffe1,227
41 · 9 NovThe library, and the door that stalled229fa301,281

To follow along on this Mac

Folder
/Users/reji/dev/Tutelora/tuteatrium, branch develop, pushed to tutelora/atrium through the github-tu alias. A push deploys nothing
Run it
Always running: the launchd agents com.tutelora.atrium-8100, -queue, -queue-bulk, -schedule. People at http://id.localhost:8100, the console at http://api.localhost:8100, short links at http://ink.localhost:8100
Mockups, docs
The mockups entry on 8101, the docs entry on 8102: http://127.0.0.1:8102/reference/atrium-system-map.html
Sign in
The console's Service is service@atrium.test, at http://api.localhost:8100/console. The demo's people, at http://id.localhost:8100: ama@northwind.test (Owner, of Northwind, which holds TuteLoop) and kofi@northwind.test (Admin in both). Since 8 November every local account, the Service’s included, uses DEMO_PASSWORD from .env, which git never sees. An owner sets up two-factor at the first sign-in; Ama’s is set up, so she is asked for an authenticator code. Local only: no other database has these. A plain localhost:8100 or api.localhost:8100/ says Not Found by design, since only the three hosts answer, and only at their pages
Demo data
php artisan migrate:fresh --seed (local only), which prints the accounts to sign in with. It's TuteCore's demo: two workspaces and their people, nothing of Atrium's own
Tests
npm run build first, then php artisan test --parallel on PostgreSQL (atrium_test; the app's database is atriumdb). Without the build, every page test fails with "Not a valid Inertia response"
Checks
php artisan brand:check · php artisan core:status · php artisan app:check
Docs
The system map beside this page; reji/docs/reference/*.md; plans in reji/plans, starting with the master plan; task lists and lessons in reji/tasks
6 November 2026From the release to a plan for discussion4 chapters
Chapter 46 November 2026see the changelogdecided

One account, organisations apart

The premise. The plans held an organisation's products inside one Accounts and organisations module, at accounts.tutelora.app. The Architect stopped the work to clarify: an architecture like Atlassian's, with a central account at id.tutelora.com, each product keeping its domain, organisations registered with no product information, and a dedicated subscription subsystem.

What was found. Atlassian's own pages bore that out, with detail: it runs organisations apart from id, signing up for an app creates an organisation too, and what's sold, what's running and who may use it are separate concerns. In Atrium almost nothing had to change, because tenant already had no product columns. The cost is in the products, above all Campus.

Decided by the Architect. Everything a person does is at id.tutelora.com, with no admin host; api on tutelora.com too. Accounts can exist with no organisation. Organisations register alone or from a product. Parents and pupils hold the same account and reach id quietly, by invitation or through the school's web address. Admins add people at id or inside the product. Atrium owns an organisation's details. Self-service billing is in from the start. The new subsystems are named Album and Tabula.
Decided by the Architect. No changes to the TuteCore repository: these plans concern Atrium alone. Foundation changes are built here as recorded exceptions, overriding the handbook's "TuteCore first" for this work.
Found. Reading Desk's billing corrected an earlier claim. Its Paystack code is Tutelora charging its own customers, which is Tabula's job, not Aerarium's. So the spec was right that Loop is the likely second product for fees and dues. Billing can't land before Clavis either, because someone must be signed in at id to pay.

The plan. reji/plans/2026-11-06-IA-identity-architecture.md. The spec is unedited, because its diagrams show the old design too; reji/docs/spec-amendments.md records what overrides it.

Where it stoodEnd of chapter 4 · 6 November 2026Built 1 · Decided 10
Where it runs
This Macnot yetcPanel stagingnot yetA VPSnot yet
Modules
Nuntius · send pathin the planClavis · accountsdecidedAlbum · pagesdecidedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsdecidedOrganisation recorddecidedWhat’s helddecidedServers and keysdecidedUsagedecidedWebhooksdecidedConsoledecided
Starting point
TuteCore 2.1.1built
Chapter 36 November 2026see the changelogfor discussion

What's already built, and the build order

The premise. The spec sets an order for the modules and gives its reasons. Asked to justify that order, the honest test was whether its reasons hold against what the products have actually built, so the apps were read before a word of the justification was written.

What was found. Most of what the spec describes already runs in Campus: phone sign-in by SMS code, phone-only accounts, about 4,600 lines of SMS with wallets and sender IDs, a chain of mail providers, vouchers, three payment gateways. So most of Atrium is extraction from a live product, not new work.

Found. Clavis can't land before Nuntius: Campus's live sign-in sends its codes by SMS, and Atrium can't send anything. Campus's voucher redemption checks the code outside its transaction with no lock, so two people can spend one code. Desk has Paystack built and switched off, so the second payments product is likely Desk, not Loop. Campus, Desk and Loop have no routes/api.php, which Tutela needs. No pilot date is recorded anywhere, and Campus is already in production, so no Atrium module blocks a pilot.
Proposed, not decided. A small part of Nuntius before Clavis; Tutelink before Tessera; Tutela, Aerarium and Lector wait for named triggers (BO-1 to BO-3). The rest of the spec's order stands.
Decided by the Architect. Every module is discussed and its mockups planned ahead of its build. A finished step opens the next discussion, not the next build. It's policy P1 of the master plan and in PROJECT.md.

How it was checked. An agent surveyed the apps; the claims that decide the order were then read by hand in each app's code, with file and line in the plan. The system map's data was checked by script: every flow node and edge points at something real.

The plan. reji/plans/2026-11-06-M00-atrium-master-plan.md.

Where it stoodEnd of chapter 3 · 6 November 2026Built 1 · Discussing 7
Where it runs
This Macnot yetcPanel stagingnot yetA VPSnot yet
Modules
Nuntius · send pathin the planClavis · accountsin the planAlbum · pagesin the planTabula · billingin the planNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsdiscussingOrganisation recorddiscussingWhat’s helddiscussingServers and keysdiscussingUsagediscussingWebhooksdiscussingConsolediscussing
Starting point
TuteCore 2.1.1built
Chapter 26 November 2026see the changelogfor discussion

The checklist, read against the code

The premise. Step 0 of the spec's build plan evaluates TuteCore against what a shared platform needs. TuteCore was built for apps people use, and Atrium's main users are other apps.

What was found. Of the twelve items, none ships as it stands: seven are part-way and five missing. An API token must belong to a person and a membership, so a product's server can't hold one. Every route answers on every host, so tutel.ink would serve the login page. An account reaches a membership only while the two hold the same e-mail.

Where the spec and the handbook disagree. "The request names the tenant" against rule 1.2; per-app credentials against 12.6; /v1 against /api/v1; a shared service against "no microservices". Each needs a ruling rather than a quiet choice, so they became decisions AF-1 to AF-8.

The plan. reji/plans/2026-11-06-AF-atrium-foundation.md, drafted and not approved. Later that day the Architect set the rule that the TuteCore repository isn't changed, so everything here is built in Atrium.

Where it stoodEnd of chapter 2 · 6 November 2026Built 1 · Discussing 7
Where it runs
This Macnot yetcPanel stagingnot yetA VPSnot yet
Modules
Nuntius · send pathin the planClavis · accountsin the planAlbum · pagesin the planTabula · billingin the planNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsdiscussingOrganisation recorddiscussingWhat’s helddiscussingServers and keysdiscussingUsagediscussingWebhooksdiscussingConsolediscussing
Starting point
TuteCore 2.1.1built
Chapter 16 November 2026166bbec · a3dc12edone

From a release, not a clone

The premise. Atrium starts from TuteCore. The first step was a clone of tutelora/core, but TuteCore's own runbook says a new app starts from a tagged release: its history stays its own, and with the clone's origin still pointing at Core, a plain push from this folder would have gone into the starter pack.

Decided. The clone was replaced with an archive of v2.1.1, with no remote. The short name is Atrium, not Tuteatrium, because the spec says "shortname atrium": internal names drop the Tute- prefix, so a repository shows at a glance what is plumbing. The folder keeps its name. The port block is 8100–8109.
Found. The first full run of the suite had 175 failures, every one "Not a valid Inertia response". The runbook's npm run build had been skipped, so pages had no Vite manifest. The run also reported exit code 0, which was only tail's, so the figure in the output was what counted.
Proven. After the build: 618 passed, 3 skipped, none failed, with the suite's own exit code captured. core:status reads 945 files as released, 0 differ. brand:check passes. app:check fails only on what this Mac doesn't run: the scheduler, a file vault key and the malware scanner.
Where it stoodEnd of chapter 1 · 6 November 2026Built 1
Where it runs
This Macnot yetcPanel stagingnot yetA VPSnot yet
Modules
Nuntius · send pathin the planClavis · accountsin the planAlbum · pagesin the planTabula · billingin the planNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsnot yetOrganisation recordnot yetWhat’s heldnot yetServers and keysnot yetUsagenot yetWebhooksnot yetConsolenot yet
Starting point
TuteCore 2.1.1built
7 November 2026The foundation built, running, and pushed; Nuntius’s send path built; Clavis decided, proved, drawn, built and assessed; Tabula decided, drawn twice, and its first slice built17 chapters
Chapter 217 November 2026see the changelogdone

What the products sell, as data

The premise. Before anyone can pay, Atrium has to know what each product sells, for how much, and what each offering allows; and, as the Architect asked, nobody pays for a product until its billing is switched on, unless the company agrees otherwise with one organisation.

Built. An offering’s monthly and yearly price, and its limits and features in the product’s own words, are rows. Every product is complimentary as shipped: products are told it holds all of it. The Service can agree a price with one organisation until a date, and charge one organisation from today, with seven days to choose a plan, or keep one complimentary for good, always saying who agreed it. Free and complimentary terms roll on each night. Products read GET /v1/entitlement, hear entitlement.changed, and report their counts. The console has Billing.
Found and fixed. Joining looked for an offering coded “free”; one offering a product is now marked where organisations start. The billing role’s code is a word the role guard rightly refuses anywhere else, so the product’s column is billing_state and the permissions’ group subscription. Prefixed public IDs need 30 characters, which the shared column helper doesn’t give. The products’ colours went into a migration first; the brand guard sent them to the brand layer, config/brand.php. The three ways an organisation can be billed are code, not Input Fields: each changes what Atrium does.
Proven. Complimentary gives all of it and charging gives the offering’s share; one organisation charged while the rest stay complimentary, its owners and billing people told and its admins not; an agreed price giving way to the list price after its date; a Free term two years old catching up and a charged paid one left for its renewal; the products’ calls refusing a key without Tabula. The full suite passes: 968 tests.

The plan. TB2: paying through Paystack’s hosted page, with the billing role’s pages at id.

Where it stoodEnd of chapter 21 · 7 November 2026Running 1 · Built 10 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuilding · 1 of 6Nuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 207 November 2026see the changelogdone

Billing, decided and drawn twice

The premise. With Clavis built, the next module was Tabula: how the company charges its own customers. Its discussion opened with what it is, what stays in the product, and how things carry on if Atrium is away.

Decided. TB-8 to TB-15 as recommended: Free renews itself; monthly or yearly at checkout; limits and features as data the product enforces; seven grace days before Free; Desk’s rule for moving up and down; billing switched on per product; an invoice numbered at payment and cleared through E-VAT behind; a school paying net of withholding keeps its plan. Then the Architect’s own TB-16: every product complimentary until it charges, and one organisation charged from today, or kept complimentary for good, from the console.
Found and fixed. The first mockups were called “standard boring and bland”: the kit’s panels and three equal cards with billing words in them, for the third time. They were redrawn around one picture per page: the year as a ribbon, a staircase of plans, a ticket that gets stamped, the money as split bars and the GRA’s seal, a dated launch. The lesson now says to name each page’s picture before drawing it.
Approved. The five mockups, after the redraw, with “complimentary” for what isn’t charged and the console’s page for billing one organisation.

The plan. Plan TB §6.1: six slices, TB1 to TB6.

Where it stoodEnd of chapter 20 · 7 November 2026Running 1 · Built 10 · Mockups approved 2
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingmockups approvedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 197 November 2026see the changelogdone

One door, looked at hard

The premise. Plan IA asked for a security assessment before id is first deployed, because one breach of it would reach every product. With every slice of Clavis built, it was time.

Assessed. Sign-in and the products' sign-in score 8; the account pages 6; web hardening 4. Overall 6.6: not ready to deploy until plan IA's own rules for id are met: a Content Security Policy, uploads served from another host, a fresh check before changing an organisation's addresses, products or people, and legal advice on children's data before pupils' accounts move.
Found and fixed. Microsoft's e-mail claim can be set by any organisation's own directory admin, so trusting it would have let a stranger's tenant sign in as someone by their e-mail; Microsoft now signs in only through a link the account made itself. An organisation resetting a pupil's account left no trace; it's in the audit log now. The production session cookie was already host-only and __Host- prefixed.
Proven. Microsoft's e-mail alone refused, Google's verified e-mail still linking, no account ever made; the dependency audits clean; the full suite passes.

The plan. reji/docs/SECURITY_ASSESSMENT_ID.md: its Priority 1 is the next work on id, before it's deployed.

Where it stoodEnd of chapter 19 · 7 November 2026Running 1 · Built 10 · Mockups approved 1 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 187 November 2026see the changelogdone

What products ask of accounts

The premise. Products keep their own roles (CL-16), so they need to add people, know them, and know when they leave, without Atrium learning what each person does inside them.

Built as decided. A product adds someone naming the admin who acted (CL-9); reads the account's own shared details (CL-17); rechecks every 15 minutes in case a webhook was missed (CL-8); hears when someone changes their own details; and its own Service account signs in at the product (CL-14).
Found. The recheck needed to know which sessions had ended, which sign-out only sent as webhooks, so ended sessions are now kept for a day. A product reading an account it doesn't share an organisation with would learn of someone it has no business with, so it gets a 404. The Service could reach the products' sign-in and be refused only after the tenant gate, so the gate now lets the refusal speak.
Proven. A person added by phone and invited, with the acting admin in the audit; a pupil's account set up; an account's shared details given to its own organisation and refused to another; a change told to the organisation's product; the recheck answering for a session signed out, and for signing out everywhere; the scope required; the Service refused. The full suite passes.

The plan. reji/plans/2026-11-07-CL-clavis-accounts.md §6.1; next, CL9, the docs and the security assessment of id.

Where it stoodEnd of chapter 18 · 7 November 2026Running 1 · Built 9 · Mockups approved 2 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuilding · 8 of 9Album · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 177 November 2026see the changelogdone

No code to wait for

The premise. The Architect chose Google, Microsoft and passkeys for the first version (CL-10). They had to fit the rules already set: one account per person, and nothing that makes an account by the back door.

Built as decided. Passkeys through the passkeys package Fortify already brought, behind Atrium's own routes and limiters. Google and Microsoft through Socialite, as Campus does: a verified e-mail the account already holds links it, and nothing ever creates one.
Found. The passkeys package wants a plain throttle and a password to add a passkey; Atrium's limiters and a fresh sign-in stand in, so an account with no password can add one. A passkey proves possession and the person at once, so it counts as more than a code when a product asks for more. Google and Microsoft wait for their keys, which are the Architect's, and aren't offered until then.
Proven. Passkey options for a guest, registration only soon after signing in, never the Service or a locked account, and more than a code once used; a verified Google address signing in and linking itself; an unverified or unknown address refused with no account made; linking from the account's page, refused when linked elsewhere, and unlinking. The full suite passes.

The plan. reji/plans/2026-11-07-CL-clavis-accounts.md §6.1; next, CL8, the products' side of the API.

Where it stoodEnd of chapter 17 · 7 November 2026Running 1 · Built 9 · Mockups approved 2 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuilding · 7 of 9Album · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 167 November 2026see the changelogdone

An account's own place

The premise. With sign-in, products and organisations in place, a person needs somewhere of their own at id: to get to their products, manage how they sign in, see where they're signed in, and make an account a school set up for them their own.

Built as drawn. id's own menu; a launchpad home; how you sign in; devices and signing out everywhere; an organisation's products and adding one; claiming a school-made account, folded into the holder's own when their number already has one (CL-4).
Found. Account mails failed outright for an account with no e-mail, which CL1 had decided is mailed nothing; they now pick no recipient for it. An account with no organisation was sent to a "closed" page; it now lands on its own Home. Folding an account in tripped the audit, which sealed the name of the account being folded away, so the holder signs in as the account they keep first. The People page in id's own design and the billing-only view remain: the starter pack's People page serves, and billing is plan TB's.
Proven. The launchpad with and without organisations; a number added by code, kept private, removed but never the last; a number on another account sent nothing; devices signed out one by one or everywhere with the products told; a product added with a matching short name and its address waiting; a school-made account claimed, or folded into the holder's own with its username and memberships kept and the products told. The full suite passes.

The plan. reji/plans/2026-11-07-CL-clavis-accounts.md §6.1; next, CL7, Google, Microsoft and passkeys.

Where it stoodEnd of chapter 16 · 7 November 2026Running 1 · Built 9 · Mockups approved 2 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuilding · 6 of 9Album · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 157 November 2026see the changelogdone

A school's own address, on one list

The premise. A school that uses portal.jaeda.edu.gh for Campus mustn't be able to point the same name at Loop, and an address nobody has proved must open nothing.

Built as decided. One list in Atrium (CL-18): each address opens one product, is verified by its CNAME to that product's own address for the school, and becomes a return address for that product's sign-in. Products serve the addresses themselves, so a short outage here leaves them working.
Found. Telling every product the organisation holds about an address would tell TuteBooks that the school runs TuteCampus, so only the product it opens hears. Saying which product another organisation's address opens would leak the same, so it says only "taken". The key from an address to its instance now holds the organisation on both sides, as the house schema asks.
Proven. An address routes nothing until its record is found, then joins its product's return addresses and that product alone hears; a record taken away stops it; a second product can't claim it, nor another organisation; bare domains and ours are refused; removing one stops it at once. The full suite passes.

The plan. reji/plans/2026-11-07-CL-clavis-accounts.md §6.1; next, CL6, the account and organisation pages at id.

Where it stoodEnd of chapter 15 · 7 November 2026Running 1 · Built 9 · Mockups approved 2 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuilding · 5 of 9Album · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 147 November 2026see the changelogdone

From "Start free" to a running product

The premise. Someone new arrives from a product's "Start free" with nothing: no account, no organisation. They should leave with both, and the product running, without being asked anything the product already tells us.

Decided by the Architect. Campus's region and district come from a directorate register Atrium doesn't have, so the Architect chose Atrium's own lists: Ghana's 16 regions and 261 districts, as Input Fields every product shares.
Found. A list of kinds in a product's column broke the house rule on lists, so it became rows (product_kind). Joining with a number that already has an account now signs in to that account, so one number never gets a second. The cost brake had waited for this slice, since joining is the first thing that texts a number nobody has proved. The organisation pages in id's own navigation are a reshaping of the starter pack's People and Settings, and they move to CL6 with the account pages, where that navigation is built.
Proven. From a product to a running instance: a new number gets a code and an account, an existing one signs in to its own; the school's form refuses a district outside its region, a held or taken short name, and a kind the product doesn't serve; the start makes the organisation, its owner, the Free plan and the address, and the product hears; the brake holds a new number and never a known one; a text invitation names the organisation and lets the person in by a code. The full suite passes.

The plan. reji/plans/2026-11-07-CL-clavis-accounts.md §6.1; next, CL5, addresses.

Where it stoodEnd of chapter 14 · 7 November 2026Running 1 · Built 9 · Mockups approved 2 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuilding · 4 of 9Album · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 137 November 2026see the changelogdone

Every product, one sign-in

The premise. The proof showed Passport with a thin layer of Atrium's own could sign one person into products on different domains. This chapter made it Atrium's, to the house rules.

Built as decided. One client per product server; return addresses worked out from the product's live organisations at each request; PKCE required of everyone; a product asking for the password gets that alone, and the token says what was proved; signing out at any product ends the session at id. Products hear by signed webhook when a session they hold ends, or a membership in their organisation does.
Found. Passport turns on the client-credentials grant on its own; it's off, since product servers already have keys. Its tables broke the house rule (plural, times without zones) and its models let them be renamed, so they were rather than recorded as an exception. A sign-out told to every product would tell one product who uses another, so only products the account signed into hear. The proof's code used a PSR-7 library Atrium doesn't have. Merging accounts isn't built until CL6, so its webhook moved there.
Proven. The proof's nine checks in Atrium's own suite, now through CL2's real sign-in: a phone-only person signed in by code reaches two products on two domains with one sign-in; an address the product doesn't own is refused, and an ended organisation's at once; PKCE is required; a product's request for the password is named and honoured; tokens verify against the published key; sign-out returns only to the product's own address and tells only the products signed into. The full suite passes.

The plan. reji/plans/2026-11-07-CL-clavis-accounts.md §6.1; next, CL4, joining and organisations at id.

Where it stoodEnd of chapter 13 · 7 November 2026Running 1 · Built 9 · Mockups approved 2 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuilding · 3 of 9Album · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 127 November 2026see the changelogdone

One door, whatever you sign in with

The premise. With accounts free of e-mail, the door has to take whatever a person holds: a phone, an e-mail or a username, in one field, and never say whether an account is behind it.

Built as decided. One field read by its shape, then a code by text through Nuntius, a password, or a code by e-mail. Campus's code rules carried over as constants. Turnstile with a puzzle behind it for a second code from one address (CL-6). An enrolled authenticator always asked, and a password asked of an account quiet for months (CL-5), each a setting where an either/or remained: the bot check and the quiet days.
Found. Campus's pause never tripped: it counted wrong codes in a cache that can't increment a missing key, so the count now lives in a table. Its range rule counted a person's own resends, so a fourth code within ten minutes paused someone waiting for a text; it now counts the range's other numbers. The mockup gave a known and an unknown number different words, which would have told a stranger which numbers have accounts, so both now read the same, and a username no longer names its school before the password. The cost brake has nothing to hold until something texts an unproven number, so it moved to joining (CL4).
Proven. Every state of the sign-in mockup by test: the first step for each kind, codes right, wrong, spent, expired and resent, an unknown number and address sent nothing, every wait, limit and pause, the bot check by puzzle and by Turnstile, the authenticator after a code, and the password of a quiet account. The full suite passes.

The plan. reji/plans/2026-11-07-CL-clavis-accounts.md §6.1; next, CL3, Atrium as the products' OIDC provider.

Where it stoodEnd of chapter 12 · 7 November 2026Running 1 · Built 9 · Mockups approved 2 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuilding · 2 of 9Album · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 117 November 2026see the changelogdone

An account that needs nothing else

The premise. Everything in Clavis stands on one change: an account that needs no e-mail, no password and no organisation. It changes the core model, so the house rule applied: every surface listed, the list agreed, then one pass.

Agreed by the Architect. Identifiers as rows, with user.email kept by the database as the primary verified e-mail so the sixty places that read it read on; a workspace lets an account in through any verified e-mail or phone it holds, or a membership the account claimed; the person's phone moves to the account; nothing mailed to an account without an e-mail; account IDs start acc_.
Found. The old rule let an account in only by its e-mail, which kept out every phone-only person, and its reason still stands: a claim is per membership, so it never reaches another workspace. A refused verification inside a request would have aborted the whole database transaction, so it now runs in its own savepoint. The person's phone could only ever be one they confirmed, so it went entirely rather than staying as a second copy.
Proven. An end-to-end test: a phone-only account with no organisation joins through its verified phone, is let in, leaves, and its number stays its own; a second account can't verify it; e-mail and username accounts work the same way. The full suite passes.

The plan. reji/plans/2026-11-07-CL1-accounts-audit.md; next, CL2, signing in by phone, e-mail or username.

Where it stoodEnd of chapter 11 · 7 November 2026Running 1 · Built 9 · Mockups approved 2 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuilding · 1 of 9Album · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 107 November 2026see the changelogapproved

What people will see of Clavis

The premise. With the decisions made and sign-in proved, Clavis was drawn: every way in at id, a person's own account, joining, running an organisation, and what a product shows, including Atrium being unreachable.

Changed by the Architect at review. People choose a product in the Tutelogue, tutelora.com's catalogue, before they reach id, so id never asks what kind of organisation they are; with no product chosen, it shows the products and lets them sell themselves. Addresses follow Campus's protocol: a short name from the initials, or matched to one the organisation already uses, and its own domain never assumed. The school's form copies Campus's, with region and district required. The app's word is "Verified".
Found. Three pages were called boring, and were: a catalogue of equal cards, a list of links for a home, and plain kind choices. Each was redrawn from what the person is deciding, with only what Atrium truly knows on it: an owner's products with their figures, a launchpad that leads with where Kofi left off, and products that each say what they're for.

How it was checked. Every state was opened in light and dark and at phone width; none scrolls sideways, and no word a person reads says "Atrium".

The plan. reji/plans/2026-11-07-CL-clavis-accounts.md §5 and §6.1: nine slices, starting with accounts that need no organisation.

Where it stoodEnd of chapter 10 · 7 November 2026Running 1 · Built 9 · Mockups approved 2 · Decided 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsmockups approvedAlbum · pagesmockups approvedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 97 November 2026proof/oidc-throwaway, never mergedgo

Sign-in across domains, proved

The premise. Clavis rests on one thing nobody had run on this stack: Atrium acting as an OpenID Connect provider for products on their own domains. Plan IA said to prove it before drawing anything, because a failure would change Clavis's shape.

What was weighed. Two ways, on one throwaway branch with its own database, judged by the same nine checks. A: Passport with jeremy379/laravel-openid-connect, about 1,360 lines. B: Passport with a thin layer of Atrium's own, about 220 lines on Passport's hooks.

Found. A's tokens were refused by a standard verifier until a key ID was set by hand. It can't say what a sign-in proved, which CL-5 depends on, and Passport's access token names the account by its row number. Atrium's User fails for a phone-only account, as CL-1 expected. Adding Passport also moved Laravel up a minor version, so that step gets its own commit and full suite.
Proven. In the browser, Esi, with no e-mail, signed in to Campus by code, then reached a school's own domain and Loop with no second sign-in. Loop was refused an address it doesn't own. Kofi's staff page asked for his password alone, and the token said so. Signing out ended the session at id. Option B passed all five tests and all nine checks; A failed two. Not proven: back-channel sign-out, Turnstile, Google, Microsoft, passkeys, real SMS, HTTPS.

The plan. reji/plans/2026-11-07-CL-P-oidc-proof.md. Next: Clavis's mockups.

Where it stoodEnd of chapter 9 · 7 November 2026Running 1 · Built 9 · Decided 4
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsdecided · OIDC provedAlbum · pagesdecidedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 87 November 2026see the changelogdecided

Clavis, decided

The premise. With the send path built, Clavis was next: one key for every product. Plan IA had settled its shape, so the discussion was about what IA left open.

Set by the Architect. Every module's discussion now opens with what it is in simple terms, what Atrium handles and what stays in the product, and how things carry on if Atrium fails for a short period (master plan P11). Working through the failsafe found two decisions the first list missed: each product's Service sign-in stays local, and a product's own "remember me" works while id can't be reached.

What was weighed. The Architect questioned three of the recommendations. On roles: Atrium should know nothing of a product's roles. That overturned two recommendations which had quietly needed it, how strong a sign-in must be and who may add people, and both now sit with the product. Atrium keeps only that someone is in an organisation, and three roles for running the organisation itself. On contact numbers: a person's own proven numbers are identity, while what a school types about someone is not, since a clerk's typo would otherwise attach a stranger's phone. On custom domains: one list in Atrium, with the product still serving the traffic, so an outage can't take a school's website down.

Found. Two faults in Campus's live sign-in, both read in the code and not run: the pause after ten wrong codes probably never starts on the database cache, and a code sign-in skips an enrolled authenticator. On the law, Act 843 is in force and the Data Protection Bill, 2025 is pending; one firm reads the Bill as keeping children's data in Ghana, which could bear on hosting.
Decided by the Architect. CL-1 to CL-18, as recommended except four alternatives: Turnstile allowed on the sign-in pages; Google, Microsoft and passkeys in the first version; Campus's password hashes and authenticator secrets carried across; and pupils moving with everyone else.

The plan. reji/plans/2026-11-07-CL-clavis-accounts.md. Next: a throwaway proof of OIDC on this stack, then the mockups.

Where it stoodEnd of chapter 8 · 7 November 2026Running 1 · Built 9 · Decided 4
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsdecidedAlbum · pagesdecidedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 77 November 2026see the changelogbuilt, not yet called

Nuntius’s send path

The premise. Clavis can’t land without SMS, because Campus’s phone sign-in sends its codes that way. So the first module after the foundation is the smallest part of the company post office: one text, its receipt and its count.

Decided by the Architect. Sailup sends and Arkesel stands by. Sailup’s webhooks say what became of every text. Whether a text’s links are shortened is a setting, off until Tutelink exists.

What was weighed. A webhook settles a text once Atrium knows the provider’s ID for it, and a timed-out send has no ID. So unknown stayed, narrowed: a report that matches no ID is matched by number within an hour. It’s never retried and never called failed, since either could double-text or misreport. Campus’s own code refunds a timeout, which is the fault left out.

Found. A resend can’t hold a foreign key to its first: the first may be purged first, and a frozen row can’t be nulled. A route taking both GET and POST appeared in the API document under one method only, which the guard caught. Two test runs sharing one database produced dozens of false failures, so full runs now go alone.

How it was proven. A test for each driver answer, each state, a retry counting once, a code in no log, another server hearing nothing, and an outside client never going out under the company’s name. Every page was looked at in light, dark and at phone width, and switching provider ran through the real password step. Not proven: no real provider has been called, and nothing calls Atrium yet.

Where it stoodEnd of chapter 7 · 7 November 2026Running 1 · Built 9 · Decided 4
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsdecidedAlbum · pagesdecidedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 67 November 2026see the changelogdone

Running, and where it goes next

The premise. With the foundation built, it needed a home: a repository, something keeping it up here, and a place to stage it.

Decided by the Architect. Atrium is tested on this Mac, then staged on cPanel, before it has a VPS. That reverses part of the first plan, which assumed Redis and long-running workers from the start.

What was weighed. cPanel has no Redis and no supervisor, and Campus already solved that: the scheduler starts a worker each minute. Reusing it took one setting. What staging can't show, separate workers and real load, waits for the VPS. The one thing that could stop it is PostgreSQL: Atrium needs 15 or later with pg_trgm, which has to be confirmed on the host.

How it was proven. The agents were bootstrapped, the server was killed and came back on its own, and real mail went through the queue worker.

Where it stoodEnd of chapter 6 · 7 November 2026Running 1 · Built 8 · Decided 4 · Discussing 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathdiscussingClavis · accountsdecidedAlbum · pagesdecidedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 56–7 November 20265c6bb8e to 137a685done

The foundation, built

The premise. The Architect approved the mockups, which opened the gate. The plan's order held: the inherited foundation strengthened first (C1 to C4), then Atrium's own (A1 to A7).

What was built. Worker pools; idempotency keys reserved before a request runs; signed webhooks on the mail engine's catalogue; request IDs through logs, jobs and outbound calls. Then the name, three hosts, the client's record and what it holds, server keys and the first /v1 call, metering and server reports, and the console from the approved mockups, carrying their stylesheet across so the pages match.

Found. The full test runs kept finding what filtered ones missed: a running import could be handed to a second worker (reproduced, then fixed); rate limits counted by address rather than by server because of the middleware's order; a webhook could tell one product about another's clients; issuing a third key crashed. Each is fixed and has its test.
Changed while building. The sign-in answers on two hosts, since the Service signs in to its console there. A key surviving rotation moved from C2 to A4, which brought server. The daily usage rollup waits until Nuntius needs it.

How it was proven. 767 tests, 3 skipped, with one running from a product's signed call to the console's figures, and every page looked at in light, dark and at phone width. Not proven: nothing has run on a server, and no product calls Atrium yet.

Where it stoodEnd of chapter 5 · 7 November 2026Built 8 · Decided 3
Where it runs
This Macnot yetcPanel stagingnot yetA VPSnot yet
Modules
Nuntius · send pathin the planClavis · accountsdecidedAlbum · pagesdecidedTabula · billingdecidedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
8 November 2026The console’s sign-in mended and Billing checked in the app; Tabula built: paying, renewals and lapses, invoices, switching billing on, its money paths reviewed, money back, credit notes, chargebacks and withholding’s review; the build order handed over; and what must be true before the first deployment: id’s Priority 1, and Atrium’s own risks assessed and fixed; then Nuntius’ rest decided and drawn, and its money built: what a text costs, and buying credit11 chapters
Chapter 328 November 20263878ab0 · b3730ac · 5e4376dbuilding

Who pays for a text

The premise. Nuntius could send one text; the spec wants a post office: a school’s free texts, then credit it buys, a fee reminder to 600 parents paced to the networks, its own sender name, products’ e-mail from their own domains, and an address that bounces known to every product.

What was found. Campus, Desk and Loop were read first, and their faults checked in the code: in Campus a school could never buy credit (the top-up has no caller), a bulk send was one job sending one number at a time with no pacing, and its SES endpoint would take forged complaints from anyone’s topic; Desk records bounces but never stops a send while its pages say it does.

Decided. Money per organisation in Atrium: each product’s plan includes some texts a month, spent first, then one credit balance shared by the organisation’s products, bought at id. A text costs one credit a segment. Out of credit, a text is refused, never kept to go out days later. WhatsApp and inbound replies wait for Desk’s launch; Loop and Campus move in their own plans.
Built. The ledger under a lock, a text given back once if it never left, low and out told once; Texts at id as two tanks draining; packs priced by the Service and bought through Paystack like a plan, invoiced with their taxes. Charging stays off until the Architect sets the price, so nothing a pilot sends is refused.
Found in the app. A reason list read as a list when it’s a map refused every reason; a finished text is frozen, so the give-back had to be a line of its own; a slip said “SMS credit” twice.
Proven. Segments counted as the networks do; the plan’s texts, then credit, then refused; the last credit not spent twice; a failed text given back once; low told once a crossing; a pack credited once however the news arrives; money back on credit only keeping the texts, a chargeback taking its share back. A pack bought through the development checkout in the app.

The plan. Bulk sends paced to the networks, sender names, products’ e-mail and the shared block list (NU3 to NU6).

Where it stoodEnd of chapter 32 · 8 November 2026Running 1 · Built 12 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbeing builtTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 318 November 2026see the changelogdone

Locks before the doors open

The premise. Nothing new for anyone to use: what must be true before Atrium is first deployed. id’s own assessment had said it wasn’t ready (no Content Security Policy, uploads served beside the session, no fresh proof before an organisation’s addresses, products or people change), and nobody had yet assessed Atrium as a platform.

Decided. A nonce-based policy on every page, report-only for the first week; files sandboxed now and a files host ready for its DNS; a sign-in within 15 minutes or the step-up before what matters, and, for an account with no password, a code to one of its own instead of a dead end. Then a reviewer who hadn’t seen the build, to assess the platform.
Built. The policy, walked page by page in the browser until nothing was refused; the sandbox and the signed hand-off to a files host; the code step-up in the dialog and on its own page. From the review: a server’s keys call only what the Service grants, one key can be revoked or a server switched off, a product’s company-paid code text takes only digits and its own host, and a product learns nothing of an account it links by a phone number.
Found in the app. SweetAlert2 wrote a style of its own, which the policy refused; the e-mail log failed for any workspace with two e-mails; a server that had never reported couldn’t be switched off from its drawer; a module switched off since was sent back and refused, silently. All put right.
Proven. Tests for each finding and each rule: the nonce on every inline script and style, Turnstile on the bot check’s pages alone, the files host’s signature, every guarded change asking, a code’s five tries, the same answer for a known and an unknown number, one key revoked while the other works, a switched-off server’s tokens gone. id rescored 6.6 to 7.6; the platform 6.2 to 7.9. The full suite passing.

The plan. The first deployment waits on the Architect’s cPanel account and DNS. Next in the build: Nuntius’ rest.

Where it stoodEnd of chapter 31 · 8 November 2026Running 1 · Built 12 · Building 0 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 308 November 2026see the changelogdone

The keys handed over, and a certificate read

The premise. The Architect: “Take over go autonomous. decide the build order and only pause if you need some input from me.” The order and each module’s decisions became Claude’s, still written into the plans and drawn as mockups so they can be read afterwards; the pauses are kept for facts only the Architect holds and for anything outward or irreversible.

Decided. The order keeps the Architect’s own (Nuntius’ rest, Tutelink, Tessera) and puts two risk-closing pieces first: withholding’s review step, the lowest score in the money review, and what must be true before Atrium’s first deployment.
Built. Withholding certificates in the console, each read beside what it should show and accepted or rejected with a reason. Rejected, or none by its day, the withheld amount is owed: the school hears once, and at id sends a corrected certificate or pays it on Paystack’s page; until then it can’t pay net again. The plan is never taken away.
Found in the app. Two words shared one key, so the console’s “Settled lately” showed the invoice banner’s sentence; the money bar had no style in the console; the paper still said “certificate to come” after the money was paid. All put right.
Proven. Six tests: accepted and settled; rejected, told once, withholding refused, a corrected certificate taken; overdue and told once; a balance paid and settled once with no period or invoice; a late payment kept and refunded; the Service alone, behind the password. A balance paid through the development checkout in the app. The full suite passing. Withholding now 7 of 10; the money paths 8.0.

The plan. Next, what must be true before the first deployment: id’s Priority 1, and an assessment of Atrium’s own risks.

Where it stoodEnd of chapter 30 · 8 November 2026Running 1 · Built 12 · Building 0 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 298 November 2026see the changelogdone

Sixteen hours to answer the bank

The premise. TB-22: a chargeback leaves the plan running until the bank decides; the Service defends or accepts; lost, the period ends, a credit note follows, the card is dropped and the organisation hears the outcome, never the dispute.

Built. Chargebacks found by Paystack’s webhook or the check, the Service told at once. Money back opens with a red clock for each, counting Paystack’s hours; its drawer gathers the evidence (the payment, the invoice that goes as the receipt, the card’s history, whether the plan still runs) and the answer. Lost, whether accepted, decided by the bank or left to run out, the money is a refund of its own with its credit note, the card is dropped, and the organisation is told what became of its plan.
What was weighed. Paystack’s own documentation, read before building, changed the mockup’s two days to 16 hours, after which Paystack accepts by itself; and defending needs a receipt uploaded, which the invoice’s PDF already is. In the app the first lost chargeback was on a payment whose plan had been moved up from, so the organisation rightly kept its current plan; the e-mail that said it was on Free was wrong, and now says what became of the plan.
Reviewed again. A reader who hadn’t written it went through every money-back path. Three serious faults: a failed refund recorded as paid by hand, or asked again, could give back more than was paid; a chargeback lost after a full refund was credited again; and after Paystack’s “already exists” the first row of its list was taken as ours. Three lesser: undo could bring back a period a refund had ended; an undone renewal left the morning run failing every day for that organisation; a dispute’s outcome was guessed from the amount. All fixed; 7.7 of 10.
Proven. Eight tests for chargebacks and nine for the review’s fixes, one for each finding. Checked in the app, light, dark and on a phone. The full suite passing.

The plan. Money back is built. Before live money: Paystack’s dispute resolutions confirmed with test keys, and withholding’s review step (Priority 2). The next module waits for its discussion.

Where it stoodEnd of chapter 29 · 8 November 2026Running 1 · Built 12 · Building 0 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 288 November 2026see the changelogdone

A paper for the money that went back

The premise. A refund that leaves its tax invoice standing tells the GRA the money is still with the company. TB-20 asks for a credit note once the money has gone, never before, so a refund that fails never leaves a paper to undo.

Built. Credit notes, numbered from their own gapless count (CN-2026-0001), against the invoice they adjust, the invoice’s price and each tax reversed in proportion to the money returned, cleared through E-VAT by the same job and the same hourly run as invoices. At id the invoice now carries the refund’s track, a stamp, and its credit notes beneath; each opens on a page of its own, prints, counts in the year, and fills a row of the spreadsheet below nothing. The refund e-mail names it.
What was weighed. A table of its own, or a kind of invoice. A credit note is cleared, numbered, kept as issued, printed and sent exactly as an invoice is; a second table would have copied every one of those paths. So it’s an invoice of its own kind, with its own count and its own kept lines. And a school that withheld: what it’s credited is the refund plus its share of what it withheld, on its own line, so what went back is the refund to the pesewa; the accountant’s view of that is still the Architect’s to get.
Proven. Six tests: a credit note from its own count, reversing the invoice line by line and cleared by E-VAT; a school that withheld credited its share; nothing for a payment that bought nothing, and a credit note only once a payout by hand is recorded; the invoice, the credit note’s page, the year, the spreadsheet and the PDF at id; its lines kept whatever the rates do after; E-VAT refusing and then clearing it. Checked in the app at id and in the console, light, dark and on a phone. The full suite passing.

The plan. TB9: chargebacks, and the money paths reviewed again.

Where it stoodEnd of chapter 28 · 8 November 2026Running 1 · Built 11 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backmockups approvedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 278 November 2026see the changelogdone

Money that can only go back once

The premise. The review’s first priority was the paths that take money back. The Architect chose to build them before the next module, and decided TB-17 to TB-22 as recommended: the Service alone gives money back, choosing each time what happens to the plan; the amount is suggested and only lowered; a credit note follows only once the money has gone; a wallet Paystack can’t reach is paid by hand and recorded; a chargeback leaves the plan running until the bank decides.

Built. Money back in the console. Each refund on its way home as a track, from asked to sent back; a refund made in Paystack’s own dashboard found and asked about; a payment set aside on a stale quote returned in one action. Giving money back opens a drawer with the payment’s period drawn as the days used and the days coming back, the three effects as cards, the amount suggested (the unused days, or all of it within 14 days of paying), what goes back line by line, and a reason from Input Fields. Ending a period moves the organisation to Free, with nothing deleted; undoing a move up puts the old plan back as it was. The organisation hears only once the money has gone.
What was weighed. When the effect on the plan should land. Waiting for Paystack would leave a school on a plan it had asked to leave for days; applying it at once risks a refund that then fails. The effect lands when the Service asks, under a lock on the payment; a refund that fails keeps its money in the Paystack balance, and the Service sends it by hand or asks again, so the plan never waits on the bank. And the refund reference: Atrium’s own, which Paystack takes once, so a timeout followed by a second ask finds the refund already made instead of making another.
Found on the way. In the app, the reason picker opened under the drawer and the next click closed it, and the password dialog opened behind it too: the console’s drawers sat above everything. They now sit over the shell and under whatever opens over them. And the line-by-line shares were worked against what was paid, which is less than the invoice when a school withheld tax, so they added up to more than went back; they’re worked against the invoice’s total now.
Proven. Eleven tests: the unused days suggested and anything more refused; all of it within the refund days; never more than was paid, whoever asks; an undone move up putting the old plan back; no answer from Paystack asked again under the same reference and counted once; a wallet Paystack can’t reach recorded as paid by hand, and Paystack’s later word not undoing it; a failure asked again under a new reference; a dashboard refund found by the check when no webhook came; a set-aside payment returned whole; the Service alone, behind the password; Paystack’s 409 read as the refund already made. A real refund given in the app, light, dark and on a phone. The full suite passing.

The plan. TB8, credit notes: their own count, issued once the money has gone, cleared through E-VAT. Then TB9, chargebacks, and the review again.

Where it stoodEnd of chapter 27 · 8 November 2026Running 1 · Built 11 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backmockups approvedNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 268 November 2026see the changelogdone

Read by someone who didn’t write it

The premise. Code that moves money should be read, before it moves any, by a reader with no stake in how it was built, and every claim that reader makes should be checked against the code before it’s believed.

Built. A review of every money path, from Paystack’s webhook to the invoice’s seal, scored as the earlier assessments are: 7.4 of 10, ready for staging on test keys and not yet for live money. What holds the score down is written as priorities: the paths that take money back, and withholding that trusts what a school declares.
Found and fixed. The serious one: a card renewal whose answer came back unclear was marked failed and charged again two days later, and a success arriving meanwhile was dropped. Four lesser: the grace days could be had free by paying another plan, a stale quote could be applied over a newer period, the scheduled check could overwrite a payment the webhook had just settled, and the development stand-in would have taken nothing yet called it paid in production. Smaller still: a zero price nobody could pay, credit lost quietly, an unsigned step, a missing password check, a limit shared by two doors.
Proven. A test for each fix: the unclear renewal waiting and settling once, the late success settled, the stale payment kept and refunded rather than applied, the grace days paid for, the check yielding to the webhook, the stand-in refusing in production. Both dependency audits clean. The full suite passing.

The plan. Tabula is built. Next, the Architect’s: the facts only they hold (prices, the VAT registration, E-VAT access, live Paystack, the terms), and the priorities the review left.

Where it stoodEnd of chapter 26 · 8 November 2026Running 1 · Built 11 · Building 0 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltNuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 258 November 2026see the changelogdone

A switch that won’t move until it’s lawful

The premise. Everything built so far waits behind one switch per product. Turning it on must be impossible until the law and the terms allow it, must give every organisation fair notice, must reward those who came first, and must charge nobody a day early.

Built. The console’s Billing opens with the money Atrium holds. A complimentary product gets Switch billing on: the launch drawn as today, the notice, the day and the founding price, beside the e-mail its organisations will get. Five checks stand in the way, each saying what’s missing: prices, the VAT registration, E-VAT, Paystack and the terms. Once on, every organisation hears once; on the day, the products hear and charging starts; those already here pay the founding price, and a plan given complimentary runs to its term’s end.
Found and fixed. The count of organisations on their own terms read “0 on their own terms keep them”; it now says plainly when there are none or one.
Proven. By test: blocked by a missing VAT number, by too short a notice and by unconfirmed terms; told once, and never to one complimentary for good; complimentary until the day, then the products told and Free after the grace days; the founding price for those here and not for a late arrival; called off before the day and refused after it; the month’s money. In the app as the Service: the money at the top, and the launch for TuteCampus, blocked, in light, dark and on a phone. The full suite passing.

The plan. TB6: the docs, and a security review of the webhook, the gateway and the invoices.

Where it stoodEnd of chapter 25 · 8 November 2026Running 1 · Built 10 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuilding · 5 of 6Nuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 248 November 2026see the changelogdone

A receipt until the GRA says otherwise

The premise. Ghana’s law wants every tax invoice issued through the GRA’s own system, with its mark and QR on the paper, and lets a school pay net of what it withholds. A school must still get its paper the moment it pays, whether or not the GRA answers, and a correct deduction must never read as an underpayment.

Built. Every payment gets its paper at once, numbered from a gapless count a year, with the parties as they stood and the quote as its lines. It’s a receipt until E-VAT clears it, then a tax invoice with the GRA’s seal and QR; a driver sends it, the real one refusing until the GRA’s access and specification come, so nothing about the GRA’s API is guessed. Withholding tax and withholding VAT are dated rates: the plan starts on what was paid, the certificate has 30 days and a drop on the paper, and the Service hears when it comes or doesn’t. Invoices at id: the year, each paper, its PDF, a spreadsheet, and a link sent to an accountant.
Found and fixed. A payment’s entitlement couldn’t be found when the scheduler or the webhook settled it outside any organisation, so a renewal’s paper would have named no product. Pay printed its credit line’s plural raw, and its button still showed the total before withholding. The list broke for an organisation with no invoices yet, and the spreadsheet’s name broke in another language. The seal sat on the paid amount.
Proven. Every state of the invoice mockup by test: numbered and cleared, E-VAT refusing six times with the Service told once, a rate changed after issue leaving the paper as it was, paying net and sending the certificate, a certificate lapsing, the PDF, the spreadsheet, the signed link. Then in the app as a demo owner: paying for Plus net of withholding tax through the checkout, the receipt waiting for the GRA, the PDF looked at page by page; light, dark and a phone. The full suite passing.

The plan. TB5: switching billing on for a product, with its notice and founding price.

Where it stoodEnd of chapter 24 · 8 November 2026Running 1 · Built 10 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuilding · 4 of 6Nuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 238 November 2026see the changelogdone

The year as a ribbon, and nobody dropped while Atrium sleeps

The premise. A paid plan has to renew without anyone remembering to: a card charged on the day, mobile money reminded in time, a slow payment given its grace, and only then Free, with nothing deleted. And none of it may count against a school while Atrium itself was down.

Built. One morning pass, safe to run twice, that decides each step from what’s recorded: a period paid ahead starts and the plan follows it; reminders go 14, 7 and 1 days before, once each; a saved card is charged and tried again two days later; an unpaid period keeps its plan for the grace days, then Free. The grace days are counted from the day Atrium tells the organisation, which is how an outage never counts. And Billing at id became the page the mockup drew: what a year costs, what’s next with a countdown ring, and each product’s year as a ribbon that narrows to the weeks that matter.
Found and fixed. A renewal couldn’t be priced on the day the period ended, before the grace days began, so the card was never charged. The page’s “ending soon” state lost to the plain next payment, because a merge kept the old order. Pay still said “Move up” for a renewal.
Proven. On a frozen clock, every state of the subscription mockup and every path of the scheduler: renewed by card, reminded and paid ahead, declined twice then Free, paid in the grace days, an outage of two weeks, Free chosen, a smaller plan following on its day, charged from today with nothing chosen. Then in the app as a demo owner, the demo moved through each state by the scheduler itself: ending in seven days, overdue, Free, and back to Standard through the checkout; light, dark and a phone. The full suite: 1,005 passing, after the guards caught a uniqueness missing its organisation, a timeline placed by left rather than start, and avatar colours that weren’t the theme’s.

The plan. TB4: tax invoices and receipts, cleared through the GRA’s E-VAT.

Where it stoodEnd of chapter 23 · 8 November 2026Running 1 · Built 10 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuilding · 3 of 6Nuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 228 November 2026see the changelogdone

Paid once, however the news arrives

The premise. An organisation’s owner, or the person who pays its bills, should be able to choose a plan and pay for it without Atrium ever touching a card, and a payment must count exactly once, though Paystack can tell Atrium three ways and none of them is certain to arrive.

Built. Billing at id: each product’s plans rising as a staircase with the organisation pinned on it, Pay with the order as a ticket and each tax on its own line, and the outcome back from Paystack, stamped paid, declined or waiting. Paystack’s own page takes the money. The return, the signed webhook and a check every ten minutes each try to settle a payment, and a lock on its row lets only the first. Moving up starts at once with the unused days counted off before tax; a smaller plan waits for the next period. In development a stand-in for Paystack pays on Atrium’s own page.
Found and fixed. The console’s sign-in had refused its own second step since Clavis split the sign-in in two: the Architect couldn’t sign in. Checked in the app, the console’s Billing showed its staircase flat, wrote “GHS” for the cedi, previewed an unpriced offering as GH₵ 0.00 and counted one organisation’s neighbours on no product. The schema guard sent the paid period’s copied figures back to the payment, where they’re said once. An offering with no price at all had counted as free.
Proven. Every state of the pay mockup by test; the return, the webhook, the check and a second settle all at one payment, and one period; a forged signature refused and a signed but unpaid report trusting nothing; 293 unused days credited before tax. Then by hand at id, as a demo owner: declined, tried again, paid. The full suite: 981 passing; a search test failed once on random data and passes alone and beside the billing tests.

The plan. TB3: renewals and lapses, with the organisation’s Billing page and the year as a ribbon.

Where it stoodEnd of chapter 22 · 8 November 2026Running 1 · Built 10 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuilding · 2 of 6Nuntius · the restin the planTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
9 November 2026The pilot declared free; Nuntius’ bulk sends built: a fee reminder to a school’s parents reserved whole, paced to the networks, resumed without double-sending, and stopped with the rest given back; then sender names, asked at id and registered with each provider; products’ e-mail, each from its own address along a chain of mailers; who isn’t written to, with sharp screenshots kept from now on; Nuntius reviewed by two fresh readers, two serious faults fixed; Tutelink, short links, decided, drawn and built; Tessera, vouchers: Campus’s read, decided, drawn, built, and reviewed by two fresh readers; and Legatus, the client library every product will install, built, proved live in a browser and reviewed, with every change the products need recorded for after deployment9 chapters
Chapter 419 November 2026229fa30done

The library, and the door that stalled

The premise. Everything Atrium does is for products that can’t yet reach it: none signs anyone in through id, hears a webhook or calls /v1. The Architect asked for the client library, and set a rule for the session: Atrium only, and every change another product needs written down for after Atrium is deployed, one product at a time.

What was weighed. Its own repository or a folder in Atrium’s: a folder, proved where its other half lives, its own repository when the first product installs it. A framework package or a bare client: the products are all Laravel 13, so a package that wires itself saves each one the work. How a product ends a session Atrium ended, when Atrium never sees the person’s browser: a row per session, keyed by id’s sid, checked on every page. Whether the recheck fails open or closed: open, so Atrium being away signs nobody out; a definite answer still ends it.

Decided. Legatus (atrium/legatus): sign-in with PKCE and an ID token checked against a cached key, the membership asked once; step-up and sign-out through id; sessions ended by Atrium’s signed webhooks and a 15-minute recheck; one client for every call, retrying only what can’t act twice; the school’s plan kept for when Atrium is away; the server’s report every five minutes; a fake for products’ own tests. The register of what each product must change, reji/tasks/other-apps.md, with Campus’s known faults moved into it.
Built. The package, its config and two tables, three commands and two middleware, and its tests inside Atrium’s suite, answered by Atrium’s own routes: every endpoint, sign-in with Atrium’s own provider, sessions ended by webhooks Atrium itself sent, and the fake’s answers compared with Atrium’s real ones. Then live: a throwaway product on port 8103 signed a demo account in through local Atrium in the browser, reported to the console, was signed out within seconds when the membership was turned off, and was refused afterwards.
Found. The browser found what no test could: a password given at id, on its way back to the product, went through an XHR that can’t follow a redirect to another site, so sign-in stalled on the form with the person signed in at id. Atrium now turns such a redirect into a full page visit. The fake’s shape test found five answers wrong before anyone used them. The two reviewers (7 and 7, nothing serious) found a suspended school’s people staying signed in, a host’s maintenance page breaking every signed-in page, the sign-out address carrying a name and phone into a shared computer’s history, a “still running” answer mistaken for a refusal, and the plan ordered by two clocks; Atrium gained a sign-out hint naming nobody, max_age for step-up, an as_of on the plan, and sign-out everywhere on a password reset.
Proven. Every endpoint through the package against Atrium; a sign-in end to end with PKCE, state and nonce; refusals for a forged code, another host, a non-member and an unknown person; the recheck ending a turned-off membership and keeping a session while Atrium was away; step-up, and sign-out at id; each review finding by its own test. Live, in the browser: sign-in, sign-out, step-up past its window, a membership turned off, a refused return. The full suite green.

The plan. Atrium deployed (A8 waits on the Architect’s cPanel account and DNS); then each product joins through Legatus, from its own folder, by the register. Tutela, Aerarium and Lector wait for their triggers.

Where it stoodEnd of chapter 41 · 9 November 2026Running 1 · Built 16 · Building 0 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbuiltTutelinkbuiltTesserabuiltLegatus · client librarybuiltTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 409 November 2026f5b3ffedone

The limit that waited for the queue

The premise. A voucher is money in a parent’s hand, so before any school sells one, someone who didn’t write Tessera should try to break it: the plan’s last slice, as for Nuntius.

What was weighed. One review or two: two, in parallel, one of redeeming and one of batches with the console, each given the intent and the files but not the author’s view of them. Each finding was checked in the code before it was believed. And the hourly ceiling on misses, whether to stop everyone once it trips or only those who missed: only those who missed, so a guesser can’t shut out parents who type a right code.

Decided. Fix everything that holds, each with a test named for its finding. Accept one, with its reason: a prefix stays unique across one school’s products, so a serial on a card always names one code for its support.
Built. A batch counted against the month’s limit the moment it’s asked for. The console’s search posted, so a code typed from a card never sits in an address. The ceiling counted for one school on one product. Attempts taken one at a time from a source. The database’s clock deciding a campaign’s last instant. The cards’ words fitted once per file instead of once per card. One keep date on the batch. A batch’s void shown only on the codes it voided. And vouchers:check, picking up a batch whose worker died.
Found. Both reviewers, independently, found the same serious fault: a batch reserves its numbers at once but its rows come later from the queue, and the limit counted rows, so two large batches asked together both passed. The tests had missed it because the queue runs at once in tests. The second serious fault was the console sending a code in the address, the very fault the plan had listed in Campus as one not to repeat. Thirteen lesser.
Proven. Each fix by its own test, “redeem 1” to “redeem 8” and “batch 1” to “batch 6”, with the minors. The full suite green. The reviewers’ 7 and 6.5 as found; every finding fixed but the one accepted.

The plan. Tessera is built. What follows waits on triggers: the client library when a product connects its server, then Tutela, Aerarium and Lector, each when it’s needed. Waiting on the Architect: Campus’s payment fault, tutel.ink’s DNS, and the SES set-up.

Where it stoodEnd of chapter 40 · 9 November 2026Running 1 · Built 15 · Building 0 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbuiltTutelinkbuiltTesserabuiltTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 399 November 20268e64ec9building

One code, spent once

The premise. A school sells admission vouchers, online to a parent who has just paid and on cards a bank sells over the counter; one day GWCL may sell its own. The code is the money: whoever types it gets what it grants, once. Campus already had vouchers, and reading them first found what not to copy. A code was checked outside its transaction and marked used without a condition, so two people could spend one. Guessing was unlimited, behind a public prefix and answers that confirmed a code existed. Lower-case codes always failed. Codes sat in plain text in the table, the e-mailed link and the logs. In passing, a worse fault in Campus’s payments: anyone signed in can mark any pending payment paid.

What was weighed. How long a code: twelve characters from 31 with no look-alikes, about 59 bits, so the lock-out isn’t the only thing between a script and a parent’s money. What the check character should be: Luhn, at first. Whether issuing goes behind the shared idempotency store: no, because it keeps the whole answer, code and all, in plain text, so the same reference gives the same code back from an encrypted copy instead. How to print 50,000 codes: dompdf builds the whole document in memory, so the cards are a PDF written by hand, streamed a page at a time.

Decided. A product opens a campaign for a school; its codes are bearer (printed cards) or bound to the product’s own reference for their holder. Codes are kept only as a keyed hash, unique across every code ever issued, with a serial printed beside them so support never needs the code itself. Redeeming is one conditional update. Five misses from one person in 15 minutes and they wait 15 minutes. A printed batch’s codes leave Atrium a week after its first download. A code or a whole batch can be voided at once; every change is a line nobody can edit.
Built. The campaign, issuing, checking and redeeming API; batches made in the background, 500 at a time, carrying on where a dead worker stopped; the cards, ten to an A4 sheet with cut lines, and a CSV for a print house; Vouchers in the console, each campaign a bar from issued to redeemed, a code found from a parent’s card by its serial, its life as a timeline, voiding with a reason and closing early, the product told of each.
Found and fixed. Luhn mod N needs an even alphabet: with 31 characters, two values double to the same number, and the code test caught a slip passing on a random run. The check became a weighted sum mod 31, which, 31 being prime, catches any one slip and any swap of two. Also: a campaign copying its server’s product, which the normal-form guard refused; the ended campaign’s mockup still counting “left to issue” and offering to print; the console’s campaign page failing locally until the new Input Fields list was synced, a step the deployment notes already name.
Proven. Two redemptions at one moment, one winning; a bound code refused for anyone but its holder; a person shut out after five misses, another person not, a mistype never counted; another school’s or product’s code answering exactly as one that never was; a 10,000-code batch made, printed as 1,000 pages and a CSV, and a printed code redeemed; codes emptied a week after download and still redeeming by their hash; a dead batch job carrying on without a number made twice; the console’s voiding telling the product, and printing asking for a fresh proof. Pages captured sharp in light, dark and on a phone.

The plan. A fresh reader’s review of Tessera’s code paths (TE4). Campus’s payment fault is for the Architect.

Where it stoodEnd of chapter 39 · 9 November 2026Running 1 · Built 14 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbuiltTutelinkbuiltTesserabeing builtTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 389 November 20267cc0679done

Seven characters a parent can trust

The premise. A fee reminder’s link is longer than the text it rides in, and a 160-character text costs money for every part it runs to. The spec’s answer is tutel.ink: short links, trusted because they only ever lead to a school’s own pages. No app had built it: Campus, Desk and Loop all send full addresses.

What was weighed. Where a link may go: “any Tutelora domain” against the calling product’s own addresses and the school’s verified ones, the narrower, so one product can’t point a link at another’s pages. A 301 against a 302: the 302, so turning a link off works even for someone who tapped it before. Counting every fetch against counting taps: WhatsApp and iMessage fetch a link to draw its preview, and that isn’t a parent.

Decided. System links only, https only, from a product’s server or from Nuntius where the school has short links on. Codes of seven characters with no look-alikes, never reused. Every link ends, a year unless the product says sooner, and can be turned off at once. A tap is a count, and nothing about who tapped is kept. Where a link went is encrypted, and forgotten 180 days after it ends.
Built. The API to make, read and turn off a link; the redirect answered before any session, so the short-link host sets no cookie; the page for a link that doesn’t work, naming who sent it and never where it went; Nuntius shortening a text’s links, one link for a whole bulk send; Links in the console, with a month of taps and a switch.
Found and fixed. The comments named the production host, which a guard rightly refuses in code; the tests found the host check is off unless a test turns it on, as production has it on; a settings cache that forgets one school only when told which; and on the console a turned-off link showed when it would have ended rather than when it was turned off.
Proven. A link made to the product’s own address and refused anywhere else, an outside client refused; a tap redirected uncached with no cookie and counted, a preview and a HEAD not; expired, turned off and unknown each saying so, never where the link went; a server reading only its own; an old destination forgotten with its code kept; a text’s long link shortened, an outside one left, one link for a bulk send; the console’s turning off telling the product. Captured sharp in light, dark and on a phone.

The plan. tutel.ink’s DNS waits on the Architect. Next in the build order: Tessera, vouchers.

Where it stoodEnd of chapter 38 · 9 November 2026Running 1 · Built 14 · Building 0 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbuiltTutelinkbuiltTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 379 November 2026c692e46done

Two readers who hadn’t written it

The premise. Nuntius now spends money, the organisations’ on texts and the company’s on codes, and sends in volume. Before anyone is charged, someone who didn’t write it should try to break it: the plan’s last slice.

What was weighed. One review or two: two, in parallel, one of the money and one of the sending, each given the plan’s intent and the files but not the author’s opinion of them, so neither read was anchored. Every finding was then checked against the code before it was believed.

Decided. Fix everything found that holds, each with a test named for its finding; accept with a reason only what can’t be fixed honestly. Charging is the Service’s, never the payer’s: one client differs only by the Service’s exception.
Built. A text claimed before the provider hears of it, so a run that comes again never sends it twice; one count a second per provider for every sender, a “too many” taken as a wait; the purge able to take a charged text; give-backs and a chargeback’s take-back in one transaction or swept; a bulk send’s cursor that only moves forward and a wait that is the send’s own; a product’s e-mail time-boxed, ended when its tries run out, its recipients written under a lock; codes capped an hour a server; texts ten parts at most; old SNS messages refused.
Found. Two serious: a school could switch its own charging off, and a single text could go twice after a worker died. Seventeen lesser. One of the fixes was itself caught by its test: the chargeback’s dispute was flipped to lost before the take-back ran, so a failure would still have left it lost with nothing done; the flip moved into the same transaction.
Proven. Each fix by its own test, “money 1” to “send 10”; the full suite green. Accepted: an e-mail handed to a second mailer after a timeout may arrive twice, since SMTP can’t tell when the first one took it. The reviewers’ 6.5 and 7 became 8.1.

The plan. Nuntius’ rest is built; WhatsApp and inbound replies wait for Desk. Next in the build order: Tutelink, the short links.

Where it stoodEnd of chapter 37 · 9 November 2026Running 1 · Built 13 · Building 0 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbuiltTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 369 November 202607cc351building

Never written to again

The premise. An address that bounces, a parent who marks a school’s e-mail as spam, or one who just wants the reminders to stop: each must be honoured by every product, or one product’s carelessness spoils every product’s sending. Campus took bounce reports from anyone’s SNS topic, so anyone could silence any address; Desk recorded bounces and sent anyway.

What was weighed. Keying the list to an account or to the address: the address, since most parents have none. Unsubscribing on the link’s first open, as the mockup drew it, or asking first: asking, because mail scanners open every link and would unsubscribe people who never chose to; a mail client’s one-click still acts at once. And whether an erasure takes the list with it: no, a suppression list is how someone is never written to again.

Decided. A hard bounce, a complaint or three soft bounces in a week stop every product; an unsubscribe stops one school’s announcements, never its receipts, codes or sign-in links. SES is believed only from Atrium’s own topic, with Amazon’s signature checked. The list is asked for each recipient at the moment of sending. Numbers are blocked by the Service’s hand until a real provider’s reports are known.
Built. The list and its lines kept for good; SES through SNS with the topic allow-list, the signature against Amazon’s certificate, each event once, the e-mail learning it bounced; the one-click unsubscribe header and link on every announcement, the page at id that needs no sign-in, asks, and undoes; Undeliverable on the console, with search, blocking by hand and lifting with a reason. And, from the Architect, a new house rule: sharp screenshots, kept with the project, taken with a small tool that drives Chrome headlessly at 2× and 3×.
Found and fixed. A repeated event hit the unique index, which in PostgreSQL spoils the whole transaction, so it now inserts or ignores. The unsubscribe page opened on the sign-in page’s pitch, which a parent with no account doesn’t need; it has a quiet layout of its own. Its button sat in a class named act, which turned out to be the console’s blinking status light, so the button would have blinked; found through the sharp screenshots, whose own tool was fixed to finish animations rather than cancel them.
Proven. A forged topic, a forged signature and a certificate from anywhere else refused; a hard bounce stopping a receipt from another school; three soft bounces blocking; an unsubscribe stopping that school’s announcements only, the page asking, the one-click acting, undo restoring; no unsubscribe on a receipt; a number blocked by hand that no text reaches; lifting with a reason from Input Fields. Every page captured sharp in light, dark and on a phone.

The plan. A fresh reader’s review of the money and send paths (NU7), then Tutelink.

Where it stoodEnd of chapter 36 · 9 November 2026Running 1 · Built 12 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbeing builtTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 359 November 20268dba249building

Every product its own address

The premise. Products send e-mail of their own: fee reminders, receipts, reports. Sent each from its own servers, a bounce or a provider’s outage stops one product while another works; sent from one shared address, one product’s complaints harm them all. Campus had a chain of mailers with quotas; nothing else did.

What was weighed. Editing mailers and their keys on the console against keeping them in the environment, as every mailer’s credentials already are: the environment, with the chain and each mailer’s month shown on the console. SES through its SDK against its SMTP interface: SMTP, so nothing new is installed. Templates for products against their own words: their own words, as texts already move, until a product’s wording needs to live here.

Decided. Each product sends from no-reply@notify. its own domain, named for the school, under its own SES configuration set. Each recipient gets an e-mail of their own. Each e-mail goes along the chain, the first mailer with quota left that accepts it taking it; with every one spent it goes anyway. One row an e-mail with every recipient on it, never a row each. Counted, never charged.
Built. The API and its log, the chain with monthly quotas and reset days, retries for those not reached, the finished webhook, the purge and the erasure door for the new log, a privacy part, and Products’ e-mail on the console: the chain in order with each mailer’s month, and each e-mail as it was sent with every recipient’s outcome.
Found and fixed. Quota was counted from the saved row, saved only at the end, so one e-mail could overshoot a spent mailer by up to fifty; each recipient is now saved the moment a mailer takes it, which also means a crash never resends. The guard against mail outside the engine flagged the second door, now named with its reason; the new log needed the same erasure door as the e-mail log; on a phone the chain’s chip crushed the mailer’s line.
Proven. A send failing over to the second mailer; a spent month moving to the next, and every one spent sending anyway; three waits for a mailer that’s down, then not sent, the server told once; the product’s own address and configuration set on each e-mail, one each; no domain, no send; the row frozen; a person’s e-mails emptied on erasure. Checked on the console in light, dark and on a phone.

The plan. The shared block list: hard bounces and complaints from SES through SNS with its topic allow-listed, and unsubscribing with one click (NU6); then a fresh reader’s review (NU7).

Where it stoodEnd of chapter 35 · 9 November 2026Running 1 · Built 12 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbeing builtTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 349 November 2026e1056ddbuilding

The name on the phone

The premise. A parent trusts a text from “JAEDA SCH” more than one from a company they’ve never heard of. Each SMS provider registers a name by hand, takes days, and sometimes refuses; Campus recorded the names but no school could ask for one, and nobody could see where a request stood.

What was weighed. One request with one state hides that one provider registered the name and another refused it; a row per provider tells the truth. Switching to a new name at once would put texts under a name no provider has approved; keeping the old one until the new one is registered with that provider never leaves a gap.

Decided. Whoever manages the organisation’s settings asks at id, or its product asks through the API. The Service records each provider’s answer in the console; a refusal carries the provider’s own reason, which the organisation reads. The old name sends until the new one is registered, then retires. No upload of the registration letter until a provider needs it in Atrium.
Built. A name asked for with every provider; the Service told by e-mail; Sender names on the console, waiting names first, with the moves the server allows and the company’s own name per provider; at id a phone showing the name texts go under today, each provider’s state, and asking for another; the organisation and its products told when a provider decides.
Found and fixed. Changing the column’s default from registered to to-register would have quietly unregistered the names older tests made, so each now says what it means; the console page carried its own copy of the allowed moves, now sent from the server; a provider’s reason ending in a full stop read “.”.” in the note; and the company name’s Save was the small button an inline form doesn’t use.
Proven. Asking once with every provider, again changing nothing; only settings managers asking; each provider walked through submitted, registered and refused, a refusal needing its reason; texts under the old name until the new one is registered, then the new; the console recording answers and the company’s name, audited. Checked at id and in the console in light, dark and on a phone.

The plan. Products’ e-mail through Nuntius with a provider chain (NU5), then the shared block list and unsubscribing (NU6), and a fresh reader’s review (NU7).

Where it stoodEnd of chapter 34 · 9 November 2026Running 1 · Built 12 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbeing builtTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built
Chapter 339 November 202640a971fbuilding

Six hundred parents, never half

The premise. A school sends a fee reminder to every parent at once. Campus did that as one job sending one number at a time, with no pacing, no progress and no stop, and a timeout half-way could lose the record of texts already sent. The Architect also settled the money for now: no prices yet, the pilot is free, everything in it complimentary.

What was weighed. Pacing by counting a second’s worth and then waiting let two half-seconds bunch together, and a slow answer was made up with a burst; spacing each text a fixed gap after the last never does either. A send in one job could die mid-way and resend; a cursor written before each text means a run that dies resumes after it, and the text in flight is marked unsure rather than sent twice.

Decided. A product sends one template to up to 10,000 people in one call. Every recipient is checked before anything is kept; the whole cost is reserved before the first text, or the send is refused whole and kept as refused. Each provider has a pace, set on its page. A product reads its counts and who didn’t get it, cancels what hasn’t gone, and hears once when the last has gone, not ten thousand receipts. A code can never go in a bulk send. Charging stays off for the pilot.
Built. The batch API and its table, the paced runs on the bulk queue, one run at a time per provider, the cursor, the waits for a provider out of reach and the rest given back after them, the sweep for a send gone quiet, cancel with the cost given back at once, and Bulk sends on the console: a river that fills, its pace and time left, and a stop.
Found and fixed. The template’s new fill() overrode Eloquent’s own and broke every save; a cancelled text is stored as failed, so the list drew a cancelled send all red; the dialog offered “Cancel” beside “Cancel them”; counts like 1224 weren’t grouped because the words’ library writes over :count; and “36 didn’t arrive” sat beside “none failed”, since an undelivered text keeps its charge.
Proven. 1,000 recipients at 100 a second, no second holding more than 100; a send refused whole for credit or the month’s limit; a cancel giving back exactly what hadn’t gone, the product told once; a run that died resuming with the text in flight marked unsure and nobody texted twice; a provider out of reach waited for three times, then the rest given back. Checked in the console in light, dark and on a phone, a demo send stopped from the phone.

The plan. Sender names asked for at id (NU4), products’ e-mail through Nuntius (NU5), the shared block list and unsubscribing (NU6), then a fresh reader’s review (NU7).

Where it stoodEnd of chapter 33 · 9 November 2026Running 1 · Built 12 · Building 1 · Mockups approved 1
Where it runs
This MacrunningcPanel stagingdecidedA VPSin the plan
Modules
Nuntius · send pathbuiltClavis · accountsbuiltAlbum · pagesmockups approvedTabula · billingbuiltTabula · money backbuiltNuntius · the restbeing builtTutelinkin the planTesserain the planTutelain the planAerariumin the planLectorin the plan
Foundation
Three hostsbuiltOrganisation recordbuiltWhat’s heldbuiltServers and keysbuiltUsagebuiltWebhooksbuiltConsolebuilt
Starting point
TuteCore 2.1.1built