D374 - The checkout is a newer FreeBSD than the target

decided - 2026-08-29

Every constant and structure this project reads comes out of one FreeBSD checkout, and that has been fine for nine harvests. stat and dirent are where it stops being fine.

The checkout is FreeBSD 15. The target's user space is a much older generation. These are exactly the two structures that changed shape in between:

struct stat     st_dev 32-bit to 64-bit, st_nlink 16-bit to 64-bit, fields reordered
struct dirent   d_fileno 32-bit to 64-bit, and d_off added

Writing the modern layout for an older guest is not a call that fails. It is a file server reporting the wrong size for every file, and nothing anywhere saying so - the exact failure this project refuses on principle, arrived at by being careful about everything except which release the header came from.

Both layouts are in the same header

sys/sys/stat.h carries struct freebsd11_stat beside struct stat, and sys/sys/dirent.h carries struct freebsd11_dirent beside struct dirent. So neither layout is a guess about what a structure is - both are citable from the one checkout - and the only open question is which one this target uses.

That is a hypothesis, the guest is the only oracle for it, and a hypothesis compiled in is one nobody can test. So it is ORBISTOUN_STAT_LAYOUT, defaulting to the older, because the target's user space predates the change - a reason rather than a measurement, which is why the other is one environment variable away.

The wider point, which applies to every harvest already done

The constants file already says "these are FreeBSD's numbers, not the target's". That caveat was about the target being a fork. This is a second axis nobody had written down: the target is also a fork of a particular release, and a number that is stable across releases and a structure that is not are different kinds of borrowing from the same source.

Error numbers, signal numbers and socket constants have not moved in decades. Structure layouts have. The harvest is as citable as it ever was; what changed is knowing which parts of it carry a second question.

And C octal is not TOML octal

S_IFDIR is 0040000 in the header, and a leading zero is how C says octal. TOML rejects a leading zero outright, so the first file mode harvested made the whole table unparseable - and it surfaced as every constant in every section going missing at once, which reads like a build problem rather than like one number.

The generator emits TOML's 0o form now. Same number, same base, still comparable against the header by eye - which is why it is not simply converted to decimal.