JONAS STAMM
— FIELD NOTES
technical-deep-diveAugust 20, 2026· 7 min read

Wide tables, umlauts, and who is allowed to write into a folder

If you want to know whether an AI product is really running inside a construction company, don't ask about the model. Ask what broke last week. For us the answe...

JS
Jonas Stamm
Founder, BauGPT

If you want to know whether an AI product is really running inside a construction company, don't ask about the model. Ask what broke last week.

For us the answer was: a table too wide for the page, umlauts in filenames, and folder rules that have to match a real org chart. None of that is exciting. All of it decides whether a Bauleiter opens BauGPT again on Monday.

Here is what that looked like in the last stretch of work on the BauGPT backend.

A table that doesn't fit on the page

Construction output is table-heavy. Positions, Nachträge, Mengen, deadlines, responsibilities. So when the assistant generates a Word document, some tables are simply wider than a portrait page.

The old behaviour was to rotate the whole file so the widest table would fit. That fixes one table and ruins everything else: the cover page, the text sections, the signature block. Now a table that doesn't fit gets its own rotated section, and everything around it stays exactly as written.

In the same batch: line breaks render properly in OnlyOffice again, the assistant no longer draws ASCII art inside documents, and repeated table header rows are gone.

That last one sounds trivial until you think about where these files go. They get printed. They get attached to an email to the Bauherr. They land in a Bauakte, sometimes with a signature underneath. A header row that appears twice is not a rendering detail at that point, it is the thing someone has to explain in a meeting.

We also shipped Word template autofill: a user brings their own .docx file with placeholders in it, and the model fills those in. This is the version of "AI writes your document" that enterprises actually want. Not our layout, not our fonts. Their Vorlage, their letterhead, their paragraph order that some Prokurist approved years ago.

Umlauts are not an edge case in German construction

Two small fixes from the same week: we now tell the multibyte string helpers explicitly which encoding they are dealing with, and long filenames get cut at a character boundary instead of at a byte count.

Why bother? Because German construction vocabulary is one long stress test for byte-level string handling. Baustellenüberwachung. Nachtragsprüfung. Straßenbauarbeiten. Slice at a fixed number of bytes and you land in the middle of a character, and the user gets a file with a broken symbol where the ü should be.

Nobody files a ticket for that. They just quietly trust the tool a bit less. In an industry where the software reputation baseline is already low, you do not have much of that trust to spend 🙂

Permissions that survive an actual org chart

The second half of the work was access. This is where most "AI for the Bau" demos fall over, because a demo has one user and a real Baufirma has departments, Niederlassungen, external Planer, and a Geschäftsführung that wants some folders read-only.

What we built:

That fourth one is worth sitting with for a second. If your backend takes the tenant ID from whatever the client sends, you do not have multi-tenancy, you have a suggestion. In construction the tenants are often companies bidding against each other on the same Ausschreibung. It has to be settled server side, every request, no exceptions.

Project-level context got the same treatment. Context that existed but never actually reached the chat now lands there properly: the project brief, a memory tier, pinned items. The knowledge was in the system, it just wasn't showing up where the user was typing.

Onboarding is where enterprise deals get won or lost

Rolling out to a construction enterprise usually means importing a whole company at once out of their directory, not a signup form. Which creates a category of bug that never appears in your own testing.

Users who arrive through a bulk import now get a personal folder and an onboarding record like everyone else. Same starting state as someone who signed up by hand.

Single sign-on got an honest failure mode too. It used to crash when someone came in through the personal path and an account for them already existed. Now it hands back an error you can act on. The difference matters because the person hitting that error is an IT admin during rollout week, and they will either forward a useful message to us or forward a stack trace to their boss.

For the same audience, we spelled out in the Active Directory setup steps which Graph permissions are Application-level and which are Delegated. That distinction is the single most common place a Microsoft-side setup stalls. Writing it down in the docs is not engineering work, but it removes a call from the onboarding path.

The assistant should be honest about what it can do

One prompt change: the assistant was getting its own capability list wrong, so we fixed that.

An assistant that overstates itself is worse than one that says "I can't do that yet", especially with a first-time enterprise user who is already sceptical. One wrong claim about what it can read or generate, and they conclude the whole thing is a toy.

Two performance changes on the prompt side: every user now starts from the same cached prefix, and the unchanging text sits in front of the per-user memory blocks. Same idea both times. The parts that never change belong at the front so they can be cached, and the parts that vary per person come after. Put a memory block near the top and you invalidate the cache for everyone.

The connection also stays open now while the model is thinking for longer. On a hard question about a Vertragsklausel, it may be a while before the first token appears, and a UI that looks frozen reads as broken.

The boring safety nets

Document layout is now guarded in CI. That eval suite kept breaking the guard step, so it got its own database. Layout is treated like any other regression risk, because in this product it is one.

Migrations got made idempotent so re-running them is safe, and AI generation spans now carry the company ID, which means we can look at behaviour per customer instead of one undifferentiated blob.

Then there is the identity plumbing on the CRM side, which took four separate fixes. We call identify first and alias afterwards, so merges stop getting dropped. Identify calls that vanish quietly get spotted and repaired. A canary whose result was going straight to /dev/null, and therefore verified nothing, actually reports now. And if a backfill gets started without an end date, you get a warning.

My favourite of the batch: accounts that came in through a directory sync never get replayed into the drip campaign. If a construction company syncs their employees in, those people should not receive a cheerful "welcome, try BauGPT" email sequence. They did not sign up. Their IT department added them. Getting onboarding email into an enterprise inbox is a good way to make the Geschäftsführung regret the rollout.

The point

Every one of these is small. Together they are the actual product.

The model is the easy part now. Anyone can wire one up in an afternoon. What separates an AI tool a Bau company keeps from one they cancel after the pilot is whether the Word document opens correctly, whether the ü survives the filename, whether the folder the Geschäftsführung marked read-only is genuinely read-only, and whether the admin who imported the users gets a clear error instead of a crash.

Ship the boring half. That's where the trust comes from 😎

Keep reading

All writing →
technical-deep-dive · Aug 31, 2026
01

The hard part of a tender radar is not the matching

Most of the work in shipping BauGPT's Ausschreibungs-Radar had nothing to do with matching. Deciding whether a public tender fits a Bau company's profile was th...

7 MIN READ
technical-deep-dive · Aug 24, 2026
02

The hard part of a tender radar is not the matching

Most of the work in shipping BauGPT's tender radar had nothing to do with matching. Matching worked early and was never the risky part. Everything after that wa...

7 MIN READ
technical-deep-dive · Aug 20, 2026
03

What Bau Enterprises Actually Made Us Build

Most of what we shipped on BauGPT in the last weeks had nothing to do with the model. It was folders, permissions, single sign-on, and Word files that open corr...

7 MIN READ
— THE NEWSLETTER

One note a week.
No fluff, just what works.

AI engineering, growth hacks, and messy lessons from shipping BauGPT. Unsubscribe anytime. I'll even miss you.

FIELD NOTES · NEXT ISSUE DROPS MONDAY
↳ No spam. One note weekly.