Build spec answered · Wednesday 12 August 2026
Ken's enrollment checkbox spec of 11 August, built item by item. Box 1 already existed and has been hardened. Boxes 3–6 did not exist and now do — on subscribe.html, appearing the moment the accelerant they belong to is ticked, and enforced at the endpoint behind it. Two things were deliberately not done, and both are Ken's call.
Yes. All four accelerant boxes are live on subscribe.html — the page a customer lands on after enrollment, where the six accelerants are ticked. Each box appears the moment its accelerant is selected, and the Pay button will not go to Stripe until it is ticked. Untick the accelerant and the button comes straight back, so nobody is ever stuck unable to pay for the plan they came for.
The endpoint refuses too. /api/enroll/addons is the only path that turns an accelerant on — at checkout and for an existing client adding one later — and it will not record one without its acknowledgment. Ken's "no accelerant goes on sale before its box exists" is now something the code does rather than something a spec says.
The first version of this page said "no screen sells an accelerant, so the gate went on the API instead." That was wrong. subscribe.html is the accelerant checkout and always has been — six accelerants, tick boxes, posting the selection before Stripe. Dil pointed at it directly.
It was not a harmless mistake. The API gate would have silently broken that page: the selection is saved fire-and-forget with a bare .catch(), so a refusal would have vanished — the customer ticks four accelerants, pays, and the White Glove team builds an account with none of them, with nobody told. That is now fixed twice over: the boxes are on the page so the refusal cannot happen in normal use, and a refusal that does happen is shown to the customer instead of swallowed.
| # | Box | Where it is shown | State |
|---|---|---|---|
| 1 | Agreement acceptance | Enrollment | Was live — hardened today: version + IP stored, button now disabled until ticked |
| 2 | SMS consent | Enrollment | Untouched, as instructed |
| 3 | TCPA — permission to contact | subscribe.html, on ticking the newsletter | Built and enforced |
| 4 | Home Pro — SoP responsibility | subscribe.html, on ticking Home Pro | Built and enforced |
| 5 | Microsites — leased, not sold | subscribe.html, on ticking microsites | Built and enforced |
| 6 | Paid Ads — spend billed separately | subscribe.html, on ticking paid ads | Built and enforced |
It shipped on 11 August and blocked correctly. It was short of the spec in three places.
The page used to argue with you after you pressed Continue. The spec says "the submit button is disabled until it's ticked", and it is the stronger clickwrap: the customer's own account of the moment becomes "I could not continue until I agreed" rather than "I clicked Continue and it complained."
The message beside the button says why it is off — a disabled button with no explanation is the version of this that generates support tickets. The click-time guard and the server refusal both stay. Three layers, not one, because a browser control is a courtesy and anyone can POST straight at the endpoint.
"The version field is the one people forget. When the agreement is amended, you need to prove which text a given client accepted. Without it, a 2028 dispute about a 2026 signup can't be answered."
Now stored as terms-2026-08-11, from a server constant — a browser cannot be allowed to name the version of the contract it is agreeing to.
Taken from x-forwarded-for, not req.ip. Behind nginx, req.ip is 127.0.0.1 for every customer alive — an acceptance stamped with the loopback address proves nothing about anybody. A test now fails if that ever changes back.
That completes Ken's four fields: which agreement · which version · when · from where. Only the first and third were there before today.
Ken's four texts, verbatim. Nothing paraphrases them — a paraphrase of a legal acknowledgment is a different acknowledgment.
The stored text is ours, never the browser's. The server matches on id and version and stores its own string. A client could post any wording it liked, and letting the party in a dispute write the record is not a thing we should build.
A version we have never published is refused. A stale tab means the customer read wording we can no longer identify. Storing that acceptance would claim we know what they read.
Acknowledgments append; they never replace. A client who buys the newsletter in August and microsites in March has two dated acceptances of two different texts. Ken's whole reason for putting these at purchase rather than enrollment is that each one is dated and specific — a list holding only the latest throws that away.
Tick Client & Agent Newsletter and its box appears underneath the accelerant grid, in the words the server will store. Tick Local Microsites as well and a second box joins it. Untick one and its box goes; the other stays ticked, because re-rendering must never quietly un-consent something already agreed to.
The boxes are deliberately not styled like the accelerant cards above them. Those are things you are choosing; these are things you are confirming, and a confirmation that looks like another product to add invites the same absent tick. The TCPA one is not given a scarier colour — a warning-red consent reads as a thing to click past — it is given weight.
Driven in a real browser, end to end: nothing ticked, Pay is live. Converting Website ticked, Pay stays live — Ken ruled it needs no box. Newsletter ticked, Pay goes dark with the reason printed under it. Both boxes ticked, Pay returns. Add a third accelerant and the two existing ticks survive while Pay waits on the new one. The posted body carries each id with the version that was on screen.
The TCPA attestation at the moment of list upload already existed, and was already enforced. It blocks the import without the tick, and stores the wording shown, the file name, the row count, the timestamp and the IP.
Ken's note said to show it at upload "if those are different steps." They are. The box built today is the purchase-time half. Neither replaces the other, and both now exist.
It is the sixth accelerant in the catalogue. The spec does not mention it — not among the four boxes, and not in the "no box needed" note the Converting Website got by name.
No legal wording was written for a product nobody asked about, and no product was blocked that Ken did not say to block. It sells without a box until he rules. The question is recorded in the code where it cannot get lost, and a test fails the day a seventh accelerant is added with neither a box nor a ruling — so the gap cannot repeat quietly.
Ken: does the Authority Builder need a box, and if so, what does it say?
The spec says "don't switch it on until the agreement is published at real URLs and back from the attorney." It was already on when this landed, and turning it off would leave no clickwrap at all — worse than the state Ken was warning about.
His stated red line is the 404: "a checkbox pointing at a 404 is worse than nothing — it proves the client couldn't have read it." There is no 404. The box links /terms.html and /privacy.html; both return 200.
What is true is that the linked document is the site's Terms of Service, not Customer Agreement v5 — which is still a draft with eleven bracketed items, a blank §6.2, and has not been to the attorney. That is exactly what the version field is for. terms-2026-08-11 says permanently, and in writing, that whoever accepted under this version accepted the site terms.
⚠️ When the attorney returns the agreement: ADD a version. Never edit the existing one — editing wording in place rewrites history for everybody who already accepted it.
On the 11 August list, still open
Full suite green, with 20 new tests: no accelerant sells without its box · the stored text is ours and not the caller's · an unknown version is refused · ticking one box does not buy two accelerants · the Converting Website sells with no box because Ken said so · an accelerant nobody has ruled on cannot be sold · the checkout renders the wording from the server rather than a copy of its own · the Pay button waits on the boxes · a refused save is shown rather than swallowed.
Two existing tests were strengthened rather than patched when they broke: one now also asserts the button ships disabled, and the storage test now requires all four of Ken's fields instead of two.
The acknowledgment file was deliberately broken to confirm the new tests fail. A test that cannot fail is worse than no test. The enrollment page was then driven in a real browser: disabled at load with the reason shown, enabled on ticking, disabled again on unticking, no console errors.
It is all on claude/website-updates-ken-tasks-lthtc6. origin/main has not moved since 6 August, so this page, the checkbox work, and the three Josh fixes from the L-run are all sitting in the same queue.