Go-live
AI voice phone calls to participants, adaptive inject logic that reacts to performance, hosting migration, and production go-live.
My first paid production engagement: an AI layer built onto a real company's legacy crisis-simulation platform. A scenario goes in as plain English, a full playable exercise comes out as structured JSON.
Live · Stage 1 shipped · Stage 2 built & verified · modernisation quotedWhy this one is different. Every other node on my map is something I own: my idea, my repo, my call. SimX is the first time a real company (Socom) paid me a fixed price to ship production software into a platform their business already runs on. The headline isn't "I built a thing," it's "I ran a professional software engagement end to end": scoped work I didn't define, planned it with a non-technical client, quoted and invoiced it properly, worked inside a legacy codebase without breaking it, and delivered against a contract. And it has grown past the build: the client now leans on me as an advisor on the product roadmap and the platform's infrastructure, not just its features.
SimX is a crisis-communication training platform: a self-contained fake internet (a fake Facebook, a fake Twitter, a newsroom, email) that companies use to run crisis exercises for their staff. It's an old but serious stack, ASP.NET MVC on .NET Framework with AngularJS and NHibernate, and historically every exercise was built by hand: a facilitator typing in every social post, email and news article one at a time.
I was hired to add an AI layer on top of it. In Stage 1, a Socom team member describes a scenario in plain English, Claude generates the entire simulation as structured JSON (characters, injects, a full escalation timeline, even the facilitator's whiteboard run sheet), and it loads straight into the platform ready to run. I also built bulk JSON and CSV import so a new client's whole staff list becomes participants in one drop.
This is what makes SimX a career story, not just a code story.
The work arrived as discovery + vision docs and an inbound ZIP of an unfamiliar codebase. Before writing anything I read the platform inside out, mapped the database, and worked out exactly where an AI layer could hook in without a rebuild. Integrating with existing code always takes longer than greenfield, and I quoted a buffer for exactly that.
David understands the product vision, not the implementation. Everything got explained in plain language, and the value was always framed the way he feels it: less time in the control room.
A proper written proposal: itemised hours, a standard-agency-rate comparison to show the saving, a fixed-price commitment (overruns are on me, not the client), 50% on commencement / 50% on delivery, ABN and GST status, IP retained until final payment clears. Fixed at $2,100 AUD, 25 hours, for Stage 1.
A 2016-era stack: ASP.NET MVC5 + Web API on .NET Framework 4.6.1, AngularJS 1.5, NHibernate over SQL Server, a Quartz Windows Service, SignalR, and a C#6 compiler pin. No dev packs, retired tooling, hardcoded config, a broken transaction pattern platform-wide. I had to be careful, faithful to the existing patterns, and honest about what was fragile.
Every deliverable verified end to end in a real browser, not just "it compiles." Wrote a testing checklist and a full handover doc so the work is reviewable and continuable.
Stage 1 is delivered, demoed and invoiced. I walked David through the whole pipeline over Teams: create an organisation, bulk-import a staff list from a CSV, describe a crisis in a sentence, watch Claude generate the full simulation in about a minute, then press one button and watch it play itself out live across the fake feeds, inbox and newsroom. His reply was "looked great", and he immediately asked to show it to two people: his wife, who runs the finances, and Fulton Hogan, a large construction firm he wants as a foundation client to fund the next build. A client demoing your work to his own clients, unprompted, is the outcome you want.
David left the demo with a short list of tweaks. I built and shipped every one the same day, then emailed him they were done before he had to chase: a set-password option on participant import (type {}1! and each person's username builds into their own password), a clear "generation takes a minute or two" notice, and the facilitator whiteboard trimmed to fit two slides because he presents from it. Shipping a client's feedback inside a day, unasked, is the cheapest way to build trust, and it is exactly what a bigger agency is too slow to do.
Stage 1 was four contracted deliverables. Along the way I built several things that were not in scope because they genuinely improved the product, roughly seven hours I gave free (an agency would have billed closer to nineteen).
The platform was built for a facilitator to fire every inject by hand. For an AI-generated pack that is dozens of clicks, so pressing Play now schedules the entire timeline automatically while still leaving the facilitator manual control. The honest part: I first built it on the server, and it tripped a subtle bug deep in the ageing database layer where scheduling many injects in one transaction corrupted a related record. Rather than fight the legacy code, I moved the logic to the browser using the same proven path the manual button already used. Knowing when to ship the safe approach instead of the elegant one is the actual skill.
A one-click reset so a finished exercise can be re-run from scratch. The original platform never needed it because a real facilitator runs each exercise once; it only matters once the same pack is replayed for training and demos, which is exactly the world the AI layer creates.
Recipient auto-fill, a whiteboard timeline summary, and a fix so AI-generated emails always schedule correctly. Minor on their own, but they are the difference between a demo that flows and one that stalls.
This is the update that changed the shape of the engagement. It stopped being "write this code" and became "help me decide where this platform goes."
Stage 1 proved the engine; Stage 2 is the productisation. A client organisation logs in, describes the exercise it needs, generates it, runs it themselves, gets AI characters (journalists, regulators, members of the public) replying to participants live, and receives an automatic evaluation report at the end, written from the platform's own record of everything that happened. My favourite decision was not code: the client wanted people to upload meeting recordings to transcribe for the report, and I pointed out the platform already logs every inject, post, email and response, so the AI can just read that. Simpler for him, cheaper to run, a better product.
My first proposal draft had three subscription tiers, a lever price list and a per-exercise table, and it read as money-hungry. I collapsed it to one clean all-in-one monthly plan bundling AI usage, account management and hosting into a single figure, with a friendly "ask me and we will adjust" note instead of a wall of rates. I also credited the client's own work: he had already built a custom GPT defining his report methodology, which makes the report feature hours of integration rather than days of design, and I said so in the quote.
He mentioned the platform costs roughly $8,000 to $10,000 a year to host on AWS and asked how to reduce it. Answering properly meant acting like an architect: I audited the codebase's platform dependencies (an old Windows-only .NET target, 14 projects, ~36,000 lines, the whole web, real-time and database stack tied to Microsoft), explained the bill is driven by Windows Server and SQL Server licensing baked into the instance rather than AWS itself, and honestly ruled out his hope that an Office 365 licence would unlock a discount. Then I quoted the real fix: rebuild on modern cross-platform .NET, Linux and PostgreSQL to remove the licence costs and cut the bill an estimated 60 to 70 percent, around 110 to 160 hours, with real payback maths and a free review of his actual AWS bill first so the number is real. I was careful about his one hard requirement, fast global access: the OS does not change network distance, but a lean Linux setup is cheap enough to run in several regions at once, which is what actually removes the delay. I also stood up a spend-safe, on-demand Azure demo host so he can show Fulton Hogan a live link without any risk of overspend.
Being trusted to advise on a platform's hosting economics and modernisation roadmap, not just its features, is the strongest single signal in this whole engagement.
Stage 2 is where the legacy platform bit back. These are the faults I am glad I found, and the habit that found them.
The lesson I am carrying into every project after this: a feature that works on the server and cannot be reached by a user is not delivered. The evaluation report, the Word round trip and the SimX agent were all unreachable on any exercise that had actually been run, because one line of routing sent published exercises straight to the control room. The Word export had no button on the customer's side at all and could only be triggered with a REST client. Both Word downloads were plain links, which carry no Authorization header, so they opened a blank tab with a raw JSON auth error. And the customer could not stop their own exercise, so it kept polling and kept billing until somebody at Socom remembered. Every one of those was found by using the product, not by reading it. I found it out four separate times.
Restarting an exercise crashed silently, but only once somebody had filled in the closing survey. Three tables point a non-nullable foreign key at the runtime survey and none of them cascades, so clearing the exercise tried to orphan-delete a survey that still had answers attached. SQL Server refused, and a catch at the bottom of the method swallowed it. What makes it a good story is where it failed: after every inject delete had flushed, but before the line that re-arms them. So the exercise sat holding eleven injects marked "already sent" with nothing left to send. Press Play and a participant stares at an empty inbox for the whole window while the timeline insists everything went out. My client just told me his exercise "did nothing". Every symptom pointed at the dispatcher. The dispatcher was fine.
"Live character responses" was signed off and did not work. It turned out to be four separate bugs stacked: only emails resolved to a character, so a team running everything through social media got silence; when it did resolve, the reply was always composed as an email addressed to whoever sent the trigger, so a social trigger produced an email addressed to nobody; the character was told "you have had no answer" while holding the participant's reply, because the code reading the message body only understood emails; and an empty body was discarded as silence. Fixing any one would have proved nothing. A feature that fails in four places at once looks exactly like a feature that fails in none.
The usage screen is what Socom prices from, so approximately right is worse than useless. Every model was costed at the Sonnet rate, including every Haiku character response, overstating the cheapest and most frequent thing in the product about threefold. The voice rate was a placeholder 49 percent high, so I replaced it with real arithmetic off the actual account: the model now predicts 5,226 credits for a 13 minute call against the 5,220 actually consumed. Historical rows are recomputed on read rather than migrated, so the stored figure stays as what was reported at the time. I also stopped the API returning the provider's raw cost at all rather than just hiding the column, because a provider cost sitting beside a charged one states the management margin exactly, and hiding it in CSS leaves it one dev-tools tab away.
My client kept reporting that features I had just shipped did not exist, and twice I told him it was his browser cache. I was partly wrong and it cost most of an evening. The app is around 140 JavaScript files served with no version in the URL, so browsers apply heuristic freshness and quietly run last week's controllers against this week's templates. The tell, once I read it properly, was a dropdown rendering blank: that meant the object driving it did not exist, which meant an old controller, which meant the chart code I had been debugging for an hour was innocent. Fixed in two layers: every script now carries a build stamp from the assembly write time, and the page carrying those stamps is itself revalidated, because an old document asks for old scripts by name and gets them, perfectly correctly, from cache. That second layer is the one I would have missed a year ago.
Socom sells into government and large enterprise, which means sooner or later somebody asks what happens to their staff's data. In August the client brought in a data security specialist to tell me what those buyers actually want. Preparing for that conversation turned into the most valuable fortnight of the engagement, and almost none of it was programming.
Getting ready for the review, I went looking for what I was likely to be asked. Account lockout was configured in the platform: five attempts, five minutes, sitting in the identity options exactly where you would expect. Nothing in the entire solution ever called it. The sign-in path used FindAsync(name, password), which validates a password and touches none of the lockout machinery, and no code anywhere called AccessFailedAsync or IsLockedOutAsync. There was no limit on password guessing, on a platform with government customers, and there had not been for years. The fix is small. Finding it is the point, because configuration that is present and never invoked reads as a control to everybody who looks at the settings, including the person writing the security answers. I verified it against the database rather than the responses, since a lockout that reports success was precisely what I had just finished not having.
You cannot write a privacy position from memory, so I searched the codebase for every outbound call and worked out what each one carries. Three services: Anthropic, ElevenLabs and S3. Voice turned out to be the interesting one. The audio never passes through the platform at all. The server asks ElevenLabs for a signed URL and the participant's browser opens the connection itself, so the product never holds a recording and stores only a transcript, which is a better answer than I expected to be able to give. It also carries a disclosure nobody had written down, which is that the participant's IP address is visible to the provider. I caught myself on the same exercise: an early draft said the platform sends no email at all, and there is a dormant SES project sitting in the solution that nothing references. Nothing calls it and none of it is configured, but "there is no email capability" and "the deployed platform sends none" are different sentences, and only one of them survives being checked.
Australian Privacy Principle 13.4 lets someone whose correction request is refused require that a statement of their disagreement be attached to the record. Reading that against this product stopped me, because SimX generates written assessments of how named employees performed under pressure. That is exactly the kind of record a person disputes, and there is nowhere in the platform to attach anything to one, so a line of privacy law became a build item rather than a paragraph. The same reading produced the sharper point sitting behind it: generating an evaluation of a named person does not merely use data already held, it creates new personal information about them, an opinion about their competence. Once the assessment is collection rather than processing, several other questions change their answers.
The output was three things: a data handling review, a privacy policy draft for a lawyer, and a data security and compliance statement written as a schedule to the customer agreement rather than as a brochure, because a document a buyer reads does not reduce anybody's exposure and a document they sign does. Writing to that standard changed how I described my own work. I wanted to say customer data is separated so one organisation cannot reach another's exercises. It is true of the customer portal, which resolves the organisation from the database on every single request, and not quite true elsewhere, because two read endpoints check that you are signed in without checking that you belong. On a marketing page that is a rounding error. In something a client signs it is a false statement, so it went in as a flagged caveat instead. Retention went the same way: the platform cannot currently delete an exercise that has been run, so I would not let a retention schedule into a signed agreement until it can.
The useful parts were not the ones I had revised for. Do not chase IRAP or ISO 27001 yet, which saves a great deal of money and reframes customer-hosted deployment as the real answer for government work rather than a fallback. Single sign-on is wanted and is worth pricing as its own stage. And when protecting personal data on the way to an AI provider, do not mask it into unreadable characters, substitute plausible values, because the realism is the product. He also sent through how Salesforce structures the same problem, which was worth mapping the platform against properly. The instruction-injection defence and the human-approval queue already in SimX turned out to be the same two patterns they describe, arrived at independently, and the genuine gaps were reversible masking, scoring model output before a participant sees it, and recording what each AI interaction actually did.
The part I did not expect is how little of this is a technical skill. Deciding that a caveat belongs in a client document, that a compliance gap is a build item, that a piece of unpaid analysis is worth doing now because customers are being signed this month and worth charging for next time: none of that is engineering, and all of it moved the project further than any commit I made in the same fortnight. It also gave me the clearest lesson of the engagement about my own position, which is that being the person who can answer the security questions is worth a good deal more than being the person who writes the features, and that I had been giving the first away for free with the second.
The code is AI-led, same as my other builds. But SimX carries two whole layers that are almost entirely me and that AI can't touch: the professional engagement, and the legacy-platform judgment. The balance tilts further to me as the job matures: the demo, the client relationship, the product and pricing calls, and the whole infrastructure strategy are mine, with AI writing implementation to my brief.
Stage 2 sharpened what that actually leaves me. Most of the code was written by an AI to my specification, so what decides whether the thing ships is knowing what to build, reading a legacy codebase well enough to know where a change will land, and above all not believing a report of success without evidence. The single most valuable habit of the whole project was refusing to accept "done" without seeing it. Nearly every real fault was found by clicking through the product as a user, and several were missed by measurements that looked convincing. The worst was a chart I confirmed was working by reading the widget's own options object, while the bars had never painted on screen at all. The options were correct. Nothing had been drawn.
| Area | Split | Who did what |
|---|---|---|
| C# / Angular code | ~70% AI | AI wrote most of the import controllers, validators, the generator client, and the Angular UIs to my specs. |
| Architecture & schema | ~55% me | I set the JSON contract to mirror the real DB tables, made the import pipeline reuse the platform's own factory, and made it transactional. |
| Legacy debugging | ~80% me | The transaction/session mismatch, the real column limits behind wrong docs, the C#6 pin, getting the Quartz service running against LocalDB. |
| Claude API / prompts | ~50/50 | Structured-outputs schema and the generation prompt iterated together; I drove the domain rules and the reliability fixes. |
| Client engagement | ~100% me | Scoping, planning, the proposal, quoting, invoicing, terms, and the whole relationship. None of this is AI. |
| Testing & QA | ~70% me | End-to-end verification, failure-case testing, the checklist and handover. |
AI voice phone calls to participants, adaptive inject logic that reacts to performance, hosting migration, and production go-live.
Audited the platform's Windows-only dependencies and quoted a rebuild on cross-platform .NET, Linux and PostgreSQL to cut the client's ~$8k to $10k a year hosting bill by an estimated 60 to 70 percent, with a free review of his real AWS setup first. Plus a spend-safe, on-demand Azure demo host so he can show a prospect a live link.
Eleven contracted deliverables, two more added mid-project. An organisation buys credits, describes the crisis it wants to rehearse, and an AI agent writes the whole exercise; they refine it by conversation or edit any inject by hand. It runs live across the fake internet with AI characters (the journalist, the regulator, the angry resident) who genuinely answer participants back, and an in-platform phone that rings the team at a scheduled moment and talks to them in an Australian accent, transcribed into the record. Afterwards an agent reads everything they did and scores it against the client's own fifteen-capability crisis framework. Plus a Word round trip so a subject-matter expert with no login can mark an exercise up in Word and have the AI apply their tracked changes, and per-organisation cost monitoring, because that is what the business prices from.
A scenario in plain English becomes a full, playable, self-dispatching exercise: JSON + CSV import, the AI-generation dashboard, and git + offsite backup for a codebase that had neither. Fixed 25-hour, $2,100 contract, 50/50 terms. Demoed over Teams; the client loved it, is showing it to a large construction firm to fund the next build, and every piece of his feedback shipped the same day. I also gifted several out-of-scope features (one-click auto-run, a full exercise reset) because they made the product better.