How it's made
One person, one repository, four products.
The IDE you design in, the runtime underneath it, the player that is the executable of every application you build, the server and the client that pair with it — and this website, the manual and the example projects. All of it out of one folder, written with an AI and decided by a person. This page is the inside story: how it fits together, how that arrangement is kept honest, and two afternoons where the numbers moved.
Everything is in one place
There is no separate team for the server, none for the website, none for the docs. The repository holds one Xcode project and one Swift package, and everything else is a small tool beside them — the manual is generated, the example projects are generated, the website's pictures are generated, even the application icon is drawn by a program.
That is a constraint, not a boast. One person cannot maintain two of anything. So there is one list component and the data browser draws it too; one printing engine and the quick print from a list goes through it; one shell behind find, sort, import and export. Every place where a second version of something could have appeared, it did not — because there would have been nobody to keep the two in step.
| Part | What it is |
|---|---|
| Runtime | SQLite, schema, migration, the JavaScript bridge, forms, reports, transfer, the editors |
| IDE | the designer, the debugger, the build pipeline |
| Player | a small host — the binary of every application you build |
| Server + Client | the same payload, one holding the data |
| Website | 16 pages, checked by a script |
| Manual | generated into the site and into the IDE |
| Examples | generated, never hand-edited |
There is no compiler in the build
This is the decision the whole shape rests on. A finished application is our precompiled player — one binary, built and signed by us once — with your project beside it as an encrypted payload. Building copies, encrypts and signs.
A test build of a real project finishes in about two seconds. The two minutes a release build takes are almost entirely Apple's notarization, not ours.
The runtime is linked by the IDE and by the player. A bug fixed there is fixed in the designer, in Run, in every application rebuilt afterwards, and in the server — at the same moment.
Anything a running application needs lives in the runtime, never in the IDE. That is why the find window, the import window and the report editor exist inside the applications you ship, not just in the tool.
Written with an AI, decided by a person
Most of this code was written by an AI, working from decisions a person made and under review by that person. That is a plain fact about how Neuridion is built, and it is written here rather than left to be guessed at.
It is also the honest answer to the obvious question — how does one person build an IDE, a database runtime, a client/server layer, a website and a manual? This is how. The interesting half is not that an AI writes code; it is what has to be true for that to be safe.
The project's own notes say how many tests there are, how many warnings the build has, how many German strings are allowed to remain in an English product. A script compares each number against reality and fails when they differ. A number nobody verifies drifts — three of them did, on one day, before this existed.
After a fix, the fix is deliberately broken again to watch the new test go red, then put back. Twice a test written after a bug would have passed with the bug still in place. Once — this week — a test looked right and proved nothing, because the two names it compared happened to sort correctly anyway.
Decisions are written down with their date and their reason. If something turns out to be impossible, the work stops and the person is told — instead of a slightly different thing being built and never mentioned.
When a diagnosis is a guess, the answer is a spike: the smallest possible program that settles it. Both stories below started that way, and one of them overturned what everybody assumed the problem was.
A story
The attachment nobody was looking at — and how a database became two files
Neuridion stores a scanned invoice in the record itself: no media folder beside the data, nothing to lose when somebody moves a directory. The question is what that costs when a list shows two hundred rows and nobody wants to see a single scan.
The answer, for a long time, was everything. Reading records compiled to
SELECT table.*, so every row arrived with its documents attached, whether or not
anybody looked. The laziness everyone believed in was one floor too high: it sat at the
bridge — a script was carefully handed an object without the bytes — while the bytes
had already been read from disk, decrypted, and put in memory to build that object.
A spike settled it: three hundred rows, a five-megabyte scan on each, a database of 1.5 gigabytes, read once the old way and once with file columns left out of the query.
| Reading 300 records | Before | After | Faster by |
|---|---|---|---|
| Plain database | 0.376 s | 0.001 s | 376× |
| Encrypted (SQLCipher) | 1.725 s | 0.002 s | 862× |
Why the database is now two files
Making the read lazy raised the next question: where do those bytes live? Leaving
them in the row means every page SQLite touches drags megabytes past the disk head. So the
documents moved into a sibling database — Customers.files.sqlite beside
Customers.sqlite — attached to the same connection with the same key.
Attached to the same connection, a write that touches a record and its document is one atomic transaction. Two files, one promise.
The sibling uses the same key. There is no second thing to configure and no way to end up with an encrypted database and plaintext scans.
The record's own column keeps the envelope — name, kind, dates, size — so a list draws “Contract.pdf · 2.4 MB” without touching the second file at all.
A project without documents has exactly one database, as it always did.
Another story
We tortured the server until it stopped answering
A MacBook Air as the server, a MacBook Pro flooding it over Wi-Fi with real paired clients — lists, counts, sums, grouped aggregates and writes, in a tight loop with no think-time at all. Far harsher than any office; the point was to find the edge.
| Concurrent clients | Requests / second | Typical answer |
|---|---|---|
| 1 | 57 | 12 ms |
| 10 | 370 | 18 ms |
| 25 | 539 | 34 ms |
| 50 | 665 | 62 ms |
| 100 | 712 | 114 ms |
| 200 | 600 | 262 ms |
| 400 | 271 | 1.1 s |
Then it stopped accepting anybody
After a hard flood the server went deaf. New clients hung at Connecting…, the status window barely refreshed, and only a restart helped. The obvious suspect was the single serial lane that keeps every write and every table event in one honest order — saturated with backlog, surely.
It was not. The status window told on the real culprit: 189 connections still listed, minutes after the last client had gone, trickling about two requests a second between them. They were dead — clients killed, Macs asleep, a FIN lost over Wi-Fi — and nothing ever noticed, because the listener ran on plain TCP with keepalive switched off. The receive path only ever sees a clean goodbye; the sweep deliberately left quiet-but-established connections alone. So the sockets sat there holding file descriptors until there were none left to accept with.
Ten seconds of quiet, then three probes five seconds apart. A living idle client answers them in its own TCP stack and stays; a dead one is reclaimed. The wedge disappeared with it.
52 connections turned away at 240 clients looked like the same story. It was accept starvation: handshake cryptography and database serving shared the one lane, so every new client queued behind everybody else's queries.
Receiving, handshake, sealing and sending now happen on each connection's own queue. Only the answer itself — the SQLite writer and the order of table events — stays on the central lane, and accepting has a queue of its own.
The encrypted channel counts a nonce per sealed frame, so answers and pushes of one connection have to be sealed in a single serialised chain. The per-connection queue is exactly that chain — a hard constraint, written down where it can be read.
What the whole exercise bought: zero failed requests and zero refused connections through 400 clients hammering with no pause; the edge, where requests finally begin to time out, sits around eight hundred such clients. And the behaviour that matters more than any number — it slows down, it does not fall over, and it heals itself without a restart.
And at the end
The tests, and the commands that keep the story honest
Not as a rule somebody remembers — as the way the work is done. Sound arrived with ten tests, the update check with thirteen, computed fields with six suites. The tests are what makes the next change safe, which is the only thing that matters to somebody already using the version before it.
After a bug is fixed, the fix is deliberately broken again to watch the new test go red. Twice a test written after a fix would have passed with the bug still in place — which is a test that proves nothing and reassures everybody.
The designer canvas and the running window are separate renderers. A test counts the settings each one reads: anything the window honours and the canvas ignores fails the build. Four rounds of reading the two files side by side had missed what counting found in seconds.
One command runs all ~85 scripts of the shipped projects through the real engine: does it parse, does every table and field it names exist, can the debugger rewrite it. The first time it ran it found fifty-five complaints — every one of them a bug in the checker, under correct code.
Some faults exist only while the program is running — a keystroke that never reaches its handler, a sheet that swallows every click. Those are found by a test that opens the application and uses it, not by reading the source.
They regenerate with one command, so a picture cannot quietly go on showing a version that no longer exists — which is exactly what had happened to the set before them. A second command checks that every picture a page names is actually there: it found a chapter of the manual pointing at a file nobody copied, in the shipped product, with nothing failing over it.
The lesson that cost the most time
A test that measures the machine is worse than no test. Four of them wobbled under load — and three had one cause, which was not the clock: the thing they waited for is delivered on the main queue, which an application services constantly and a test process services only by luck. They passed on an idle Mac and failed while something else was compiling. The cure was an injectable delivery queue and waiting for a state instead of a duration; the proof is the suite run three times with four processes deliberately burning CPU.