Skip to content
Skip to main content
Novel Systems home
Decision log
D-102August 7, 2026

The published telephone number is real, and a check enforces that it stays real

Affects: lib/constants.ts, scripts/check-phone.mjs, package.json, lib/engineering-practice.ts, docs/legal/FLAGGED-CLAIMS.md, docs/directory-listings.md, config/site.ts, components/layout/site-footer.tsx, app/faq/page.tsx, components/ui/live-chat.tsx

Decision. COMPANY.phoneDisplay is (437) 237-6827 and COMPANY.phoneHref is tel:+14372376827, a real line confirmed by the owner. A new build check, check:phone, refuses to let that pair regress. VERIFY_CHECK_COUNT moved 23 → 24.

What it was, and why that was worse than a placeholder normally is. It read (416) 555-0142. 555-01XX is the range the North American Numbering Plan reserves so that fiction cannot dial a stranger, so as a *placeholder* it was the responsible choice. The problem was where the value went. Every consumer reads from the same constant, and among them are app/layout.tsx, which puts it in the telephone field of the Organization JSON-LD and in OpenGraph phoneNumbers; lib/email/templates.ts, which prints it in the footer of every transactional email; and components/cpq/proposal-modal.tsx, which puts it on every generated proposal. Structured data is not a caveat-bearing surface — a crawler that reads telephone treats it as an assertion, and Google may print it in a knowledge panel beside the company name. docs/legal/FLAGGED-CLAIMS.md carried this as I1 at High severity, and noted the specific asymmetry that made it worse than its sibling: the *escalation* number is disclosed as a placeholder on /support; this one was not disclosed anywhere.

Why a check and not just a fix. Three ways this breaks, and each has been seen in some form in this codebase already. The number is written twice in two notations a few characters apart, so a half-applied edit leaves the page showing the new number while the tap-to-call link dials the old one — invisible on screen, wrong in the hand. The reserved range can come back in, because a plausible-looking number is exactly what somebody reaches for when a real one isn't available yet. And a real number can be copy-pasted into a component, where changing lib/constants.ts will never reach it. check:phone fails on all three.

Reserved ranges stay legal to hard-code. The check exempts anything in the 555 exchange rather than banning all literal numbers. Form placeholders on /signup and the demo-request form, the technician phone numbers in lib/fms-data.ts, and the formatting example in lib/contact-engine.ts are all 555 numbers and all should be. That is what the range is for. Rewriting them to the real company number would be a regression: a placeholder in a field asking for the *visitor's* number should not suggest they enter ours.

What the check cannot do. It cannot tell whether anyone answers. No script can. The only defence there is provenance, so lib/constants.ts records that the number came from the owner and on what date.

Amended the same day: the number was real and nobody could see it. The fix above changed lib/constants.ts and stopped there, and for the rest of 7 August 2026 the site displayed no telephone number on any page. The footer is the only contact block that appears sitewide, and it does not read COMPANY. It reads siteConfig.contact.phone, and it renders the phone row only when that string is non-empty — an empty string is how config/site.ts says "not published yet", which is the same convention that correctly suppresses the street address and the tax id. That convention was right while the number was fictional. It became a defect the moment the real line landed in the other file, and it was invisible precisely because check:phone was passing: all three of its rules read the constant, and the constant was correct. The two fields have been collapsed — siteConfig.contact.phone is now COMPANY.phoneDisplay and a new siteConfig.contact.phoneHref is COMPANY.phoneHref — so the number is stated once and referenced twice rather than written twice. The footer links through phoneHref rather than stripping punctuation out of the display string, because that strip produces ten digits with no country code, which dials from inside the NANP and fails from outside it. Two surfaces that offered an email address and no way to call also now carry the line: the procurement note at the foot of /faq, and the "Talk to a human" reply in the chat widget.

A fourth rule, because the first three could not have caught it. check:phone now also asserts that siteConfig.contact.phone is non-empty and references COMPANY.phoneDisplay rather than restating the digits, and that phoneHref is COMPANY.phoneHref. The general shape of the miss is worth naming: a guard that validates a value proves the value is right, not that anything renders it. This one had been written to stop a fictional number reaching the public, and it did not occur to it that a real number might reach nobody. The rule was tested by setting the field back to "" and confirming the check fails with that message, then restoring it — a guard nobody has watched fail is a guard nobody has tested.

What would make this wrong. If the line is ported, cancelled or rerouted, the check passes and the site is confidently wrong — the guard proves the number is well-formed and unfictional, not that it rings. If a real second line is ever provisioned for escalation, escalationPhoneDisplay comes off its 555 value and the placeholder disclosure at app/support/page.tsx has to come out in the same commit, or the site will disclaim a number that is real.