W36: Gates That Measure, Not Suggest
The week several systems stopped running on belief and started running on measurement — a health check that reports the deployed commit, a session counter without its inflated 31%, and a price gate that reads the live page instead of asking a model. Meanwhile the blog editor still costs twenty manual saves per article.
- vt-blog — 120 commits, six PRs to production; the chat no longer invents dates and prices but reads them off the live destination page (deterministic gate, zero LLM calls)
- T-077 fully closed — 56 synthetic sessions out of 179 deleted; the count had been inflated by 31%, and the filter was rebuilt from blacklist to whitelist
- Every live service now returns its deployed commit from /health — 'is the fix out?' is finally a measurement, not a guess
- vt-executive-dashboard — Railway health check wired up; until now every deploy meant ~2 minutes of 502s, because the old container was pulled before the new one was ready
- vt-internal-services — the rejection email became its own mode rather than a sub-function of the improver; input can be a note or a finished letter
- Python production dependencies pinned with hashes across three projects (57 packages, --require-hashes) — a supply-chain layer closed
- CLO finished eight legal work packages — Denmark, Sweden, Estonia, Lithuania, Germany, Austria, the Netherlands, Belgium; one argument (F-39) retracted because an invoice refuted it
- biznesam.ai — article on the TrapDoor attack in both languages; the AI briefing moved to structured output that guarantees valid JSON
- A gate that doesn't measure is a suggestion. The duplicate-import check existed, but it didn't measure the thing that broke the build, so it didn't stop it
- If a number has been wrong for weeks, look at the channel, not the formula — the T-077 root cause was what got counted at all, not how it was counted
- A service that doesn't know its own commit can't answer the one question deployment raises; /health with a commit is the cheapest proof you can build
- A deterministic check against the live page is both cheaper and more honest than a model's memory — prices and dates should be read, never generated
- The same manual work repeating in the same place two weeks running isn't content work; it's a tool defect waiting for a ticket
- The MDX save gate let a duplicate import through — six production build failures before anyone noticed (T-080)
- The session count was inflated by 31% for weeks; the root cause turned out to be the channel probes arrived through, not the counting logic
- LinkedIn rejected the social image because the page served WebP — a JPG sibling was needed, and no gate asked for one
- I reported the vt-crm database as '1.1 GB' of data; it was the volume size, and deletion had to be halted mid-way (M-076)
- The fourth layer of the Latvian quality check was silently skipped — 37 of 38 ads went out with grammar errors
- Editing one blog article took twenty manual saves this week; last week the same defect cost 28 — and the ticket for it still isn't written
Ecosystem overview
The thread running through this week was proof. In several places, systems that had been running on assumption — “it’s probably deployed”, “that’s probably the session count”, “that’s probably the price” — were rebuilt to answer with a measurement instead. It isn’t loud work and it doesn’t look like new features, but it is exactly the layer that has produced the most wrong reports over the past months.
vt-blog
The largest volume — 120 commits, six PRs to production. The most important is the chat’s price and date gate: until now the chat could state a departure date or a price it had “remembered”; now it reads the offers from the live destination page and compares them deterministically, without a single model call. Alongside it, a daily monitor compares what the chat says against what actually stands on the VT page — if they start to diverge, we see it before a customer does.
The second significant piece was closing T-077. The session count was inflated by 31% — of 179 sessions, 56 turned out to be our own probes. The filter had been built as a blacklist trying to recognise synthetic sessions by their traits; it could never keep up. We rebuilt it as a whitelist that counts only what matches a known session format. All counters now filter identically, and the old synthetic sessions have been deleted with a copy kept outside git.
The social image story ran in two steps: first, seven pages had no default image at all; then it turned out LinkedIn rejects WebP. The page kept WebP, and a JPG sibling was placed next to it.
And alongside all of that — more than ninety manual editing commits across six articles. One article took twenty saves. Last week I called this a tool defect; a week later it is still a defect, and the ticket for it still isn’t written.
Infrastructure and deployment
In three projects — vt-internal-services, hotel-reviewer-agent and the rescuer service — /health now returns the deployed commit. It sounds minor, but until now the question “is the fix live?” had to be answered on faith. Now it can be compared against git history and answered precisely.
vt-executive-dashboard surfaced a quiet problem: Railway removed the old container before the new one was ready, so every deploy meant roughly two minutes of 502s. We wired up the health check, and fixed a second issue alongside it — changes to railway.json itself were being skipped.
On the supply-chain side, three production projects now have their Python dependencies pinned to exact versions with hash verification — 57 packages. And in the TrapDoor-class attack assessment, two defensive layers came out clean while three new gaps appeared; the git credential store was removed.
vt-internal-services
Six commits carrying one good lesson about product thinking. The rejection email had been built as a sub-mode of the letter improver — you paste a draft, it improves it. It turned out that a rejection is a different action: there is no draft to improve, there is a decision to state. We rebuilt it as its own mode, where the input can be either a short note or a finished letter, and the decision itself is always stated rather than danced around.
Legal layer (CLO)
The CLO’s first full working week — eight work packages of comparative research on guarantee amounts: Denmark, Sweden, Estonia, Lithuania, Germany, Austria, the Netherlands, Belgium. One previously stated argument (F-39) was retracted because an invoice simply refuted it. In parallel, the AI Act oversight layer was put in order: a zoned register, named editorial responsibility, and separated roles between building the solutions and controlling what users actually see.
biznesam.ai
Four commits. Published the TrapDoor attack article in both languages with its own image, and moved the AI briefing generator to structured output — it now guarantees valid JSON instead of trying to parse it out of free text.
Insights
The main lesson repeated three times in different places: a gate that doesn’t measure is a suggestion. The MDX check existed, but it didn’t measure duplicate imports — and six production build failures happened exactly where “the check is already there”. The same with social images: no gate asked for a JPG sibling, so there wasn’t one. And the same with the session count: a filter that tries to recognise the bad ones will always lag behind a filter that admits only the known good ones.
The second lesson is about where to look for a root cause when a number has been wrong for weeks. With T-077 we re-measured the formula several times, and the formula was right — the problem was the channel through which probes entered the count at all. As long as you measure the same spot, the answer doesn’t change.
Mistakes and lessons
Two things were uncomfortable this week. First, I reported the vt-crm database size as “1.1 GB” of data when it was the volume size; deletion was halted mid-way and the backup verified 28 of 28. Misreading a unit nearly became data loss.
Second, the fourth layer of the Latvian quality check was silently skipped, and 37 of 38 ads went live with grammar errors. “Silently skipped” is the same class as “a gate that doesn’t measure”: a step whose execution nobody verifies will eventually not be executed.
And third, not a mistake but a sign: the blog editor defect repeated for a second week running. Twenty manual saves for one article. I noticed it, described it, and did not write the ticket. Next week will be the third time.