What shipped, and what we got wrong.
Written from the commit history rather than remembered afterwards. Shipped capability, rebate data updates, and the defects — including the ones we put there ourselves. If a wrong number reached a customer's document, it is on this page with its date.
Two repositories, one product.
Jouli was built inside Mission Green, the installer that is now client #1, and it ran that company's entire quoting operation before it had a name of its own. So the engine, the cockpit and the rebate data have their history in the Mission Green repository, and only the last few days sit in Jouli's. Both are below, on one timeline. Dates are commit dates.
The counts are real and they are dated. The figures in the strip above were read off the live database on 6 October 2026. They are small because there is one client. Nothing on this page is a roadmap item: if it is listed, it is in production. Work that exists in the code but is switched off is not listed at all.
October.
Jouli becomes the product and Mission Green becomes a customer of it. The access model was written down before the second company exists, on purpose: the platform operator can switch a tenant's features on, and still cannot see that tenant's cost base, margin or floor. Two separate memberships, never one session.
Groundwork for a second company on the platform. The signed-in session now carries the organisation, read off the roster row rather than decided by a branch in the code, and a default-deny guard refuses any database call that cannot name one. Not finished, and worth saying so: all 88 statements that touch tenant data are now enumerated by a test, and 8 of them are scoped. The other 80 are the next job.
A pricing change belonging to somebody else's unfinished work had been swept into one of our own commits. Taken back out before it could ship.
Schema changes are migrations now, rather than someone typing into a console, and a deploy can see a schema break instead of discovering it at request time.
The cockpit went live on jouli.com.au with four dead buttons — job sheet, single-line diagram, customer preview, open-from-scoreboard — and six fonts returning 404. Only the page had been proxied, not the things the page loads, and a missing webfont never throws: it renders in a fallback and merely looks a little off. Fixed, and a check now fails the configuration if a dependency is left behind.
The team signs in at jouli.com.au and the magic link returns to the domain it was asked from. The customer's document deliberately did not move with it: it stays on the installer's own domain, because it carries the installer's ABN, licence and bank details, and it is their contract with their customer, not ours.
The owner types the price the customer should pay and the engine works the discount backwards to it, against a margin floor it cannot cross. The internal note explaining why the price moved stays internal — it is never rendered on the customer's document.
The admin panel moves inside the cockpit instead of living at its own address, and one dashboard reads differently for each of the five roles.
A battery brand had been in the catalogue and in the pricing engine since 30 September but in neither the comparison list nor the picker. It could be priced and could not be quoted or compared, and resuming a saved draft that used it blanked the control. The comparison list now carries every brand at every size it is actually built in.
Catalogue: a third battery rung at 27.99 kWh, which comes out $627 under the alternative route to the same capacity on the same job.
September.
The month the engine was built. Most of what the product does today was written here — and so was most of what had to be corrected.
One battery brand is single-phase only. The product now says so at the point the choice is made, instead of letting a three-phase home be quoted hardware that cannot serve it.
The quoting form becomes eight collapsible links and a sticky summary rail instead of three long cards, so a rep can work it top to bottom on a phone in a driveway.
The wrong-building guard had never once run. It required an operator pin, and every scan in production is an address scan, so its condition was false every time. Five of twelve scans had snapped between 19 and 121 metres from the address, two of them returning as approximate matches, and the cockpit drew up to 100 panel positions on a neighbour's roof and looked confident doing it. The guard now runs on the path people actually use, the view never re-centres on a substitute building, and the warning banner fires.
Manufacturer datasheets sit on the hardware line they specify, downloadable from the proposal. The commit said "every hardware line". That was wrong. It was 28 of 34 products that day; today it is 31 of 37 carrying a datasheet and 14 of 37 carrying a written warranty term. Six products were refused rather than guessed, because inventing a warranty term for a customer's document is worse than leaving the field empty.
A deploy was one command away from rolling production back eight commits, including three live price changes: one state's panel-removal rate would have reverted and two extras would have lost their prices outright. Every check was green, because not one of them looked at production. Two guards added — the deploy no longer rewrites the engine from whatever happens to be sitting in the tree, and it refuses to promote a version that is an ancestor of the one already serving traffic.
We reported that a mapping API the roof scan depends on had been deprecated. It had not. Our own probe sliced the response before parsing it, so it threw on every healthy reply and invented the fault. Claim withdrawn, probe fixed.
A team scoreboard every role can open. Counts only — quoted, sent, accepted — and no money on it, which is precisely why it can be shown to everyone.
When the customer picks one of the two compared options from the proposal itself, the proposal re-quotes to that option and sends. Two options is the hard maximum, by design.
A cancelled job kept counting as a live job — on the board, in the charts and in the certificate ledger.
No draft is created until a consultant is allocated to the job, so a document cannot reach a customer with nobody's name on it.
Old panel removal was being ticked by the operator, priced on screen, and then never sent to the engine. The customer's price was short by the whole line.
Every email sent about a job is recorded against that job, and reps get their own proposals list rather than everybody's. 161 pieces of correspondence logged as at 6 October.
A typed quantity silently dropped the line instead of charging for it. Separately: an interstate quote showed no travel cost at all, and re-opening a job to refine it lost the kilometres the travel had been priced on.
One deposit invoice for the whole job, raised in Xero the moment a proposal is signed — not one invoice per milestone. A job signed before the accounting connection existed still reconciles, by job reference. 9 raised to date, no errors.
A Finance view that reads the payment ladder and the certificates still to be claimed, and a fifth role — accountant — whose entire job is to read it.
The roof photograph is version-keyed, so re-opening a job to refine it can no longer show the customer last week's aerial underneath this week's panel layout.
Disabling someone did not disable them. A shared access code authenticated without ever consulting the roster, and came back carrying a privilege level nobody had designed: it could send, void, reassign and refine, it saw every job rather than its own, and it could release a discount above the cap. A person marked inactive in the admin panel still held a working key. Closed — access is now whatever the roster says, and nothing else.
Measured, not reasoned: 46 requests against a limit of 40 produced a counter value of 11 and never a refusal, because the counter was edge-cached and the increments collapsed into one. It undercounted exactly the traffic shape it existed to stop. An atomic per-minute limit now sits beside it on the four paths where a burst costs money or opens a door, including the customer's signature.
The single-line diagram had no earth drawn on it. And the job sheet had been packing the van one inverter short.
The daily purge is declared in a form the scheduler accepts and actually fires. It had been written two days earlier and rejected silently, so it had never run once. Together they do what the privacy page promises: unsigned proposals are deleted on a schedule, signed ones are kept.
Sign-in by magic link, an admin panel behind one door, and five roles over a ten-person roster. A rep reads and quotes; every change of hands goes back through one desk.
We built the new sign-in gate and moved nobody onto it, so for one morning every door rejected the entire team. It was the second time that same mistake was made in this product, which is why an access change now ships with its roster migration in the same commit.
Single-line diagrams, drawn as vector from the design the quote already held, and a job sheet that states whether the network will accept the connection as configured.
The proposal cover showed the customer a photograph of their bare roof and labelled it their layout. Also: a kit and its component parts were billing the same items twice, and internal to-do notes were printing in the customer's warranty table.
Said plainly, because the site had been conflating them: the list of states the engine auto-prices is not the list of states the installer services. The second list is longer.
A live overcharge of $660 on one state's jobs, found while checking a third state's numbers against the first two.
Nothing had ever backed up the ledger. Every proposal, lead and signed contract sat in one database whose only recovery was a 30-day rollback, which is not a copy. A dated export now runs weekly and before every migration, written outside the directory the website serves — a backup inside it would have been a public download of every customer's address. First run: 9 tables, 246 rows, 384 KB. A deep health check reports whether the things pricing depends on actually answer, rather than saying "ok" while the database is down, and every deployed version is tagged with the commit it came from, so a rollback is no longer a guess between a column of identifiers.
The job sheet becomes a stock checklist an installer can work from: part numbers, string design, and an equipment table you can tick off at the van.
A sentence on the proposal told a customer about their "existing 0 kW array". And sign-in broke in production, by us, in exactly the way it had broken the day before.
The worst one. The address reader took the first four-digit number in the string, under a comment asserting that four digits cannot be a street name. Rural Australian addresses have four-digit street numbers all the time. "3648 Some Road, Angle Vale South Australia 5117" resolved to Victoria. No error and no warning — and the wrong state means the wrong certificate zone, the wrong network fee and the wrong price, on a document the customer signs. It now reads the postcode that follows the state, which is where a formatted Australian address always puts it, and the live job that had been priced against the wrong state was re-priced.
The suites that guard the proposal system run on every push, and the deployed engine is proved to be the tested engine by comparing build hashes instead of assuming.
A double-storey home was charged the access surcharge on a solar job and nothing at all on a battery-only job, which silently dropped $440.
Open tracking that is one-sided on purpose: opened and read mean the customer, or they mean nothing. An operator previewing a document never counts as a customer view, and neither does a mail scanner fetching the pixel. 52 of 65 proposals first-viewed, 36 read.
One reference per job, built to survive being typed into a banking app, with the bank details printed on the proposal beside a payment-fraud warning. Signatures are kept whole and can be looked at afterwards.
Duplicating a job carried one customer's discount onto the next one, and an approval survived a change to the reason it had been granted for.
Two-brand comparison on a single proposal, priced against the same roof and the same bill so that the two numbers are genuinely comparable.
The proposal states what is warranted and what is modelled, side by side with the saving, rather than presenting both in the same voice.
The roof scan warns when the address match is weak, or when the roof snapped a long way from the pin, instead of carrying on quietly. Marking the right roof when the pin lands on the wrong house had been added the day before.
Concessions get named reasons, the rep's own half funds first, and there is a hard floor at cost plus a fixed amount that no discount crosses. Margin interpolates by kilowatt, so there are no price cliffs between the sizes printed on a rate card.
The pricing model moves out of the page and into one engine. Everything above this line is downstream of that: two reps pricing the same house get the same figure, and so does the re-price when the customer accepts three weeks later.
Pricing audit: a state income test that was not being applied, certificates counted on a rounded rather than an exact system size, and a deeming period that had already ended.
Signature and acceptance in the browser, and the consumer-rights disclosures the installer's code of conduct requires placed on the front of the document. Those obligations belong to the installer — Jouli is software and holds no accreditation or approval of its own.
First proposal built end to end: engine, catalogue, operator console, and a page the customer can open.
August.
Before the engine existed, the work was the rebate data the engine now prices against — and the habit of correcting that data in public.
Sweep verified 31 August across every jurisdiction; two items added, including two scheme administrators entering liquidation and a state concession for embedded networks.
Five new items, a correction to a state renters' rent-cap rule — correction 23 in the public ledger — and a virtual power plant figure resolved against its source.
The rebate dataset is published as a citable reference and licensed CC BY 4.0, with its corrections ledger public. Anyone can check our numbers, including against us. All eight jurisdictions were re-verified the same day.
An accreditation claim naming the wrong body was retired everywhere it appeared — 58 occurrences across 12 pages. The accrediting body is SAA; accreditation is held by a person rather than a company, so there is no company to search for; and the solar class does not cover batteries, which is a separate class. A dedicated page now states all three and points readers at the official register instead of at us.
A state's 50% eligibility qualifier had gone missing again, from five statements. It had already been swept onto seven pages the first correction missed, eleven days earlier. The same correction, third pass — which is the argument for the guard added on 7 August.
A manufacturer recall disclosed on the pages that sell the product, rather than left to the spec sheet.
A "~30% battery rebate" claim qualified across twelve guides, with the tier explainer linked from each, because the unqualified figure was only ever true at the top tier.
A disputed state VPP figure resolved against the primary source — and our own item on that state's scheme corrected in the same pass.
The rebate checker reads the dataset directly, and the date it shows is bound to the date the data itself records as verified, gated on the two agreeing — so a stale snapshot cannot present itself as current.
The retraction guard runs on every change, not only on data builds, so a withdrawn figure cannot creep back through an unrelated edit.
The correction that taught us how to correct things. An "up to $1,500" figure for one state's VPP scheme had been retracted twice in July. Both times the fix touched only the single entry it named. A full check on 6 August found the figure still live in 44 places — ten pages, a script and a chat widget. All 44 now state what the state actually publishes, which is no fixed amount. The build re-greps the whole site for retracted figures after every data build and fails if one reappears, and that guard was verified against a seeded regression rather than assumed to work.
The news feed was publishing a policy's effective date as its publication date, so items appeared older or newer than they were.
Thirteen verified items, with every rebate figure in the dataset re-checked against its primary source.
Judge it on the corrections.
Any product can list what it shipped. The useful test is whether it will tell you what it got wrong, on which day, and what that cost.