The model was never the bottleneck.
In the beginning, I used AI the way many founders do: I described a feature in a chat window, pasted the code into the repository, fixed what broke, and repeated.
It was if speed looked like progress
I have worked in IT, development, security, infrastructure, and compliance for more than a decade. I knew what quality gates were for. I knew why production checks, separation of duties, testing, and evidence mattered.
AI did not make me forget those things. It made it easy to postpone them.
When a model generates a working screen or endpoint in minutes, caution can feel slow. A page appears. A request returns the expected response. A test is green on one machine. It is tempting to call that delivery.
It is not.
In the early history of this repository, the pattern is visible. On 28 March 2026, about 21,000 lines landed in a single commit. Two days later, a commit called feat: ai features added 16,572 lines across 75 files. A week after that came another 11,155 lines under the wonderfully imperfect message: feat: added several features and optimazations.
On 13 April, the repository had 355 files and not a single test.
I had installed SonarQube early. It ran on every push. But the part that fails a build when the quality gate is red was still commented out.
A scanner that cannot say no is a dashboard. And I was not looking at the dashboard.
The AI was fast. Nothing told either of us when it was wrong.
Every control is a scar
The harness we have now did not come from a tidy best-practice checklist.
Every meaningful control arrived because something broke, drifted, or passed when it should have failed.
An accessibility check found a button with insufficient contrast and a link identified only by colour. That became WCAG 2.2 AA checks in CI.
A production audit found that a read-only viewer could create an API key. That became a central write guard across 181 API handlers, with a test that fails whenever a handler bypasses it.
A missing backup marker revealed that the nightly database backup had not run for weeks. That became an alert, a capability check, and a restore drill.
Tenant isolation lived only in application code. That became a data-layer guard, cross-tenant tests, and Postgres row-level security generated from the schema.
Each control exists because “this should be fine” was not evidence.
The real turning point came during a whole-application audit. Sixty-six AI agents investigated ten areas in parallel. Every finding then went to an independent verifier whose default position was not “confirmed,” but “refuted unless the code proves otherwise.”
Of 54 findings, 51 survived verification.
Two were serious. A seeder could rebuild framework data on container boot and cascade-delete a tenant’s control register, while swallowing the failure as a warning. Another query could read evidence collectors across tenants into the asking tenant’s prompt.
The audit’s conclusion was the sentence I keep returning to:
Nearly every failure was one shape: a control or a document certified something the code did not do.
That is the real risk of AI-written code.
The model can write the implementation, the test, and the documentation explaining that the test passes. When all three come from the same confident source, they can agree with one another while disagreeing with reality.
A harness the AI cannot talk its way past
Today, the agent writes most new code. In one recent month, all 90,687 added lines came through it. Across the repository, just over half of the code was last changed in commits carrying an AI co-author line.
But the agent is no longer the authority on whether its own work is good.
The agent builds. The harness decides.
I think of the harness as eleven controls grouped into five stages.
The AI is free to move quickly inside a harness it cannot talk its way past.
1. Rules come first
Before an agent changes code, it reads the project rules, architectural decisions, and lessons from previous failures.
In Valystrum codebase repository, that means a rules file, a codebook with 197 decisions, around 720 “do not repeat” entries, and a bug log with more than 2,700 lessons.
That is not bureaucracy. It is memory.
Without it, every session starts from a blank slate. The model confidently rediscovers old mistakes, proposes a simpler solution that breaks a deliberate boundary, or invents a route that sounds plausible but does not exist.
2. Decisions are enforced, not remembered
A rule an agent can forget is a suggestion.
The important decisions are backed by automated checks. The codebook is signed, invariant tests run on every change, and sensitive changes, authentication, tenancy, access control, network topology, or content-security policy must include or update an architecture decision record.
The agent cannot quietly move a security boundary without explaining why.
3. Local verification decides whether a change is ready
Every change must pass a verification command before it can be called done.
The fast tier runs type checks, linting, custom security rules, style checks, environment-variable validation, monitoring configuration checks, unit tests, and API drift checks.
There are 819 unit-test files, containing roughly 10,000 test cases.
The full tier goes further. It builds the production image, boots it against a throwaway Postgres database, runs it as the restricted application role with row-level security enforced, proves rollback compatibility, and drives browser harnesses through real routes at mobile, tablet, and desktop widths.
This catches ordinary failures:
- fields invented by the model but absent from the schema;
- permission checks skipped by a new code path;
- tenant lookups based only on an ID;
- tests that remain green while the behaviour they should protect is broken.
These are not uniquely AI problems. AI simply creates them faster.
4. A test must prove it can fail
A green test is not automatically evidence.
After writing important tests, the code is deliberately broken to make sure the test turns red. These are hand-made mutants, not a dedicated mutation-testing platform, but the principle is the same.
If a test passes when the protected behaviour is broken, it is decoration.
One licence check passed 35 out of 35 times even after the titles and names it was supposed to inspect had been made invisible. The test was green. The control was not working.
That lesson is now part of the process: do not only test the implementation. Test the test.
5. Clean machines expose convenient assumptions
A development machine is generous. It has cached dependencies, old databases, running services, and configuration that a fresh environment has not earned.
The first production boot exposed exactly that. A seed script resolved imports through configuration that existed locally but was never copied into the image. The application started without the data it needed.
So the question changed from:
Did it work on my machine?
To:
Does it work on a machine that owes this change nothing?
CI runs on a clean environment with real Postgres and Redis. It includes integration tests, cross-tenant checks, row-level-security paths, browser journeys, and accessibility testing.
SonarQube now has the authority it originally lacked: a red quality gate fails the build.
6. Nothing enters the build unchecked
An agent can add a dependency, GitHub Action, base image, or package as easily as it writes a function.
That means the supply chain needs controls too.
Dependency audits block high and critical advisories unless an exception is explicit, dated, and temporary. Trivy scans application and migration images. Gitleaks scans history for secrets. Third-party GitHub Actions must be pinned to commit SHAs. CI generates a CycloneDX software bill of materials.
The point is not to eliminate all risk. It is to make assumptions visible before they become production dependencies.
7. The data layer says no when application code says yes
The earlier controls try to catch wrong code before it ships. This one assumes some wrong code will still get through.
Tenant data sits behind Postgres row-level security generated from the schema. If a query forgets its tenant filter, the database should still return only the current tenant’s rows.
The application has a tenant guard. The database has row-level security. A cross-tenant test sends 788 probes through 447 handlers on every pull request. The full gate boots the application as the restricted database role.
The critical boundary does not depend on one correct WHERE clause.
8. Review is not ceremonial approval
The agent that writes a change does not get to certify it.
For larger changes, separate reviewer agents inspect authentication, tenancy, entitlements, data, UI, and operations. Sceptic agents then try to refute each finding. If a defect cannot be reproduced from the code, it is not confirmed.
This matters because AI-generated work often looks polished. It arrives with tidy explanations and apparent confidence. That surface quality can make a reviewer less critical exactly when they should be more critical.
The useful questions are straightforward:
What assumption changed? What else does this touch? What happens on failure? What evidence supports this claim? What did we fail to test?
Documentation gets the same scrutiny. A system with beautiful documentation and a different reality is not documented. It is misleading.
9. Attack the system before someone else does
The system is tested from the perspective of an attacker, not only a builder.
During a production audit, an autonomous agent creates test organisations and users, signed in through the real login flow, and exercised the live API as each of them.
An agent told to break a system looks for different things than an agent told to build it.
That does not replace an independent external penetration test. It does, however, make adversarial thinking part of the regular engineering workflow rather than an annual event.
10. Done happens in production
Deployment is not the finish line. It is another state of the system to test.
After every deployment, a production-verification script runs on the host. A non-zero exit means the work is not done, no matter how green the earlier pipeline looked.
That closes the loop.
The agent can write a change. The local gate can approve it. CI can reproduce it. Reviewers can inspect it. But production is where the system has to prove itself.
11. Production keeps watching
Some failures only appear after time, traffic, data, or operational drift enter the picture.
Monitoring rules have their own tests. Backup restoration is exercised. New counters and alerts are treated as evidence-generating controls, not decoration.
A missing backup marker exposed a failure that had been invisible for weeks. A new row-level-security counter revealed a lookup resolving names across tenants.
The system is not finished because it deployed successfully. It is finished only when it keeps behaving as expected.
What still goes wrong
This is not a victory lap. The harness has weaknesses.
Not every gate is enforced by the server. Some controls still depend on scripts, hooks, agent rules, and personal discipline.
The gate has contained defects of its own. The fast tier once passed while Redis tests silently skipped. The full tier once built with a different bundler than production.
Some checks remain softer than they should be. Browser harnesses run locally rather than in CI. One self-scan fails open when a dependent service is unavailable. There is no dynamic application security testing yet, no dedicated mutation-testing tool, and the external penetration test remains planned.
And the AI still invents. It has fabricated folder layouts, overwritten uncommitted work based on false assumptions, and marked the wrong work items as complete.
The difference is not that mistakes stopped happening.
The difference is that we find more of them, understand why they happened, and turn them into a rule, test, or control before the next session begins.
The real lesson
The lesson from AI-written code is not that founders no longer need engineers, or that quality can be delegated to a model.
It is almost the opposite.
AI increases the value of engineering judgement. When implementation becomes cheap, architecture, constraints, verification, and operational discipline become more important—not less.
The best use of AI has not been replacing thought. It has been moving attention away from repetitive production and toward the decisions that carry risk.
I still use AI to move quickly.
I just no longer confuse speed with confidence.
A good harness makes room for both.