vibeapp.win

What broke

Windows treated Docs and docs as the same folder; the Linux container did not

Adding the fridge app as a source exposed two path bugs. Mount names only accept ASCII, so a folder name with 'ø' was rejected. The rules that hide a Portal docs folder were also case-sensitive, so they could miss the folder in a repo that spells it 'Docs/portal'. The fix was an ASCII junction, covers that use the folder's real spelling, and case-insensitive excludes.

The short version

What went wrongThe hiding covers and exclude rules only knew docs/portal, so in a Linux container a repo spelling it Docs/portal could stay mounted and readable. Non-ASCII mount names were also rejected.

How we found itAdding the fridge app as a source exposed both problems: Henning got stuck on the ø, and the repo spelled the folder Docs/portal, which Windows treated as the same as docs/portal.

The rule nowCheck hiding rules against the real on-disk spelling: write covers with the real spelling, match excludes case-insensitively, and use ASCII mount names with a junction when needed.

This post documents something Claude got wrong. The claim rests on a commit or a file, not on memory. All of Claude's mistakes

The newsroom engine behind this site reads other app repositories so it can write about them. Each repo is a source. It is mounted read-only into the engine’s container, and some folders inside it are hidden before the engine sees anything. On 4 October 2026 we added the fridge app as a source. That surfaced two problems, and the second one touched the hiding.

How a source is mounted

The hidden folders are listed in a sync script that runs on the Windows host. It checks each repo for a fixed set of folders, one of them the Portal docs folder docs/portal. If a folder exists, the script writes a tiny empty read-only tmpfs over that path in the container’s compose file. The engine then sees an empty folder where the real one would be.

On top of that, the engine has exclude rules. These are simple patterns such as dir/ prefixes, *.ext suffixes and exact names. Any path that matches is skipped when the engine reads a repo.

So there are two layers: the cover hides the folder at mount time, and the exclude rule filters it at read time.

Problem one: the folder name was not ASCII

Henning tried to add the fridge app from the admin page and got stuck. The commit says the reason plainly: “mount names are ASCII only.” Both the compose file and the source register only accept letters A to Z, digits, dot, underscore and hyphen. The fridge app’s folder name contains the Norwegian letter ø, so it could not be added from the admin page.

The repo did not need renaming. Portal already had an ASCII junction next to the folder, made earlier for the same reason, and it points to the same place on disk. We changed the sync script to:

  • collect every ASCII-named junction or symlink in the repos folder, along with the real path it points to
  • offer a non-ASCII folder as a candidate only if such an alias exists, and mount it under the alias
  • otherwise leave the folder out, with a note saying an ASCII junction is needed

The admin page now shows the real folder name with “(via …)” and the junction name. When the source is registered, its id is built from the real name with the letters swapped out: æ becomes “ae”, ø becomes “o”, å becomes “a”, and anything else outside the allowed characters is removed. The duplicate check now compares ids as well as mount names, so the same repo cannot be added twice under two names.

The source register records the fridge app as approved on 4 October, with a note that Henning tried it from the admin page and stopped on the ø.

Problem two: Docs is not docs

The second problem was easier to miss. The fridge repo spells the Portal docs folder Docs/portal, with a capital D. The cover and the exclude rules knew it only as docs/portal.

On Windows the difference does not matter. The sync script runs on the host and asks whether docs/portal exists. Windows answers yes, because its filesystem ignores case. The script then writes a cover for docs/portal into the compose file.

Inside the Linux container, docs/portal and Docs/portal are two different paths. The cover lands on docs/portal, which does not exist there, and the real Docs/portal stays mounted and readable. The commit puts it this way: the cover and the exclude rules “only knew docs/portal, which the Linux container treats as a different path.”

The exclude rules had the same gap. They compared paths case-sensitively, so a docs/portal/ rule would not match a file under Docs/portal/.

That meant both layers could miss a folder that was supposed to be hidden. We found it while adding the source. The fix went into the same commit that added the fridge app, so the source went live with the fix already in place. The material does not show the engine ever reading that folder. What it shows is that the protection depended on how one folder happened to be capitalised.

This was a fault in code Claude wrote. The cover list, the existence check and the exclude matcher all treated a single spelling as the only one.

The fix

We made three changes in one commit:

  1. Covers use the real spelling. A new helper walks the path one part at a time. It reads each directory and finds the entry whose name matches ignoring case, then returns the path as it is actually spelled on disk, for example Docs/portal. The cover is written for that path. If any part is missing, no cover is written.
  2. Exclude matching ignores case. Both the path and the rule are lowercased before they are compared. The code comment gives the reason: repos made on Windows spell it Docs/portal as often as docs/portal.
  3. Non-ASCII folders go through an ASCII junction, as described above, with a readable id and a duplicate check on both id and mount.

The second problem comes from a split. The sync script runs on Windows, but the container that reads the files runs Linux. Windows ignores case; the container does not. Anything the script checks on the host can give a different answer inside the container.

The rule

When a rule decides what is hidden, check it against how the path is actually spelled on disk, not how we expect it to be spelled. Hiding rules match case-insensitively, covers are written using the real spelling, and mount names are plain ASCII, with a junction when the folder name is not.