On July 30, three days before the EU AI Act's Article 50 disclosure duty took effect, we checked whether our own products showed the AI-generated label we'd written in June. They didn't. The label was frozen, published, and imported by nothing. We wired it into four apps in one day, and let review catch what the rush nearly missed.
In June we built the obvious piece of Article 50 infrastructure: one shared package exporting the exact strings every AI surface should show — "AI-generated," a draft caveat, a model note, a blog footer — so the wording reads identically everywhere instead of drifting app by app. It shipped. Then, while checking readiness for a separate change that would switch on live AI credentials across the company, we went looking for where it was actually rendered. A grep across every product turned up nothing outside the package itself. The label had been written, frozen, and never connected to a single pixel.
This wasn't a live violation — every AI surface we run fails closed until credentials are switched on, so there was no undisclosed AI output in front of a real user that week. But the obligation doesn't attach when you get around to it. It attaches the moment the surface goes live, and we were about to make that switch.
We'd been carrying an open question internally: does the grace period on machine-readable marking apply to us or not, two internal notes disagreed. It turned out both were right about different situations, and the distinction was a single date. The regulation's grace period on marking runs to December 2, but only for systems already placed on the market before August 2. Flip the AI switch before that date and you're carrying roughly four months of runway to finish the marking work properly. Flip it after, and there's no grace at all — marking has to work at the moment you go live.
That reframed a debate we'd been treating as something to resolve before acting into a countdown. The visible-disclosure half of Article 50 — showing a person that what they're looking at is AI-generated — has no grace period under either reading, so it had to ship regardless. We had a handful of days to wire it into every surface that mattered.
Four pull requests carried the fix: an explainer app, an invoicing dashboard, the pipeline that posts our own blog updates to social channels, and a customer-support classifier. A fifth surface, our main product's AI features, turned out to already be covered — it renders the label through a shared UI component our first grep hadn't traced back to the source. Not everything was missing; we'd just measured it wrong the first time.
Business-legal review on the other four caught what speed alone would have missed. One dashboard button labelled AI suggestions correctly, but only when a specific field came back set — and that field was optional in the underlying response type. If the code path ever returned AI text without it, which is exactly the kind of one-line regression that survives a busy week, the disclosure would vanish precisely when the AI output it was supposed to label went live. We closed the gap by making the field non-optional and flipping the default so an unrecognized value shows the label rather than hiding it.
A disclosure that fails silently open on exactly the code path where the AI output is real isn't a smaller version of coverage — it's the one failure mode indistinguishable from not having built it at all.
The social-posting pipeline needed a narrower fix: a hard character limit on one channel meant the disclosure marker and the link back to the full post were both competing for the same tight budget as the post text. Review made sure any trimming shortens the copy, never the two things that carry the actual legal weight.
The riskiest-looking surface on our list going in was an invoicing dashboard's automated payment-reminder emails, sent to a company's clients rather than to our own user — someone who never opted into an AI interaction in the first place. Checking it against the actual code found that surface doesn't exist: every outbound reminder is composed from static templates, and the AI feature on that dashboard only suggests a tone adjustment for a human to review before sending, nothing it writes ever reaches an outbound email on its own. We wrote that down as a decision rather than leaving it as an assumption, with a binding rule attached — any future feature that lets AI text reach an outside recipient directly ships its disclosure in the same change that builds it, not afterward.
The customer-support classifier got the same treatment in the other direction. It reads incoming messages and sorts them into internal categories; nothing synthetic is ever shown to the person who wrote in. We recorded that as genuinely out of scope, with explicit conditions that would reopen the question — the classifier's output ever being shown as text to a person, or that output ever choosing which reply gets sent without a human releasing it. Writing the boundary down, with the conditions that would move it, is worth more than a label bolted onto a surface that doesn't need one.
This post was drafted by an AI system from Dekimu's public engineering record and published with automated checks, without per-post human editing.
← Back to blog