Stop tracking compiled bytecode - #560
Merged
Merged
Conversation
tools/verify-dis-paths.sh checked that each source's declared .dis existed and was no older than the source. Its whole subject is committed bytecode, so with dis/ becoming a build product it has nothing to check -- and it was never a good guard: it recognised 8 of the 25 sources carrying an inline PATH constant, and a scan that died reported success and exited 0. Removed with it: the workflow that ran it on every PR, and hooks/pre-commit, whose only job was to run it before a commit that can no longer contain a .dis file. hooks/post-merge stays, and matters more than before: it is what rebuilds your working tree after a pull now that the tree no longer arrives prebuilt.
dis/ is a build product. It was tracked because the 2006 upstream drop (4643900) tracked it, and because a downloaded release should run without the user building anything. The second reason is real; it just never required tracking. release.yml already stages emu/*/o.emu, never tracked, built earlier in the same job -- and that is the harder case, being per-architecture and needing its shared libraries carried along. Bytecode is architecture-independent. What tracking cost, measured on master rather than supposed: bytecode went stale (dis/acme.dis shipped /fonts/vera/... for five months after the source moved to /fonts/combined/...), modules were compiled to directories the build never installs to, sources were deleted with their binaries left behind -- including a Veltro tool removed by a commit titled "chore(security)" -- developer paths leaked inside released .dis files, and 45 modules shipped that no mkfile compiled. No CI or release job rebuilt appl/ at all, so every release carried ~940 files nothing had built from the tagged source. This could not happen before that was fixed: 25 tracked binaries had no source in the repository, one of them a live runtime dependency of tetris. The preceding commits recovered or accounted for every one. Only *.dis is ignored. 51 hand-written Inferno sh scripts live in these trees (acme/dis/Clean, dis/svc/auth, dis/lc, xenith/dis/Run, ...) and are real sources; they stay tracked. One guard replaces another: tools/dis-manifest.txt the set of modules the build must produce tools/verify-dis-build.sh builds the tree, fails if any are missing Untracking removes staleness by construction, since bytecode is always freshly compiled, and leaves a quieter failure in its place: drop a target from an mkfile TARG and the module stops existing with nothing to say so until something fails to load. The manifest makes that a reviewable one-line diff. 964 binaries become 964 lines of text. A missing module fails the check; an extra one is only reported. release.yml compiles the test-framework self-test into dis/lib/ on the side, and with bytecode no longer committable a stray file cannot be published by accident the way a stale one could. Run by a new CI job on every PR, and at all six release staging sites, which now build the tree instead of copying a committed one.
dis/ no longer arrives with the clone, so "build the runtime" moves
from a thing you could skip to a thing every contributor and agent has
to know. AGENTS.md gets its own section, since that file is the SOP
agents read:
- dis/ is a build product; never commit a .dis
- the rebuild loop, and that hooks/post-merge runs it after a pull
- all four directories are required -- appl/mpeg and appl/veltro are
not in appl/mkfile's DIRS, so `cd appl && mk install` misses them,
which is the single easiest thing to get wrong here
- adding or removing a module means an mkfile TARG entry AND a line
in tools/dis-manifest.txt, in the same commit
- never hand-roll `limbo -o <path>`
CLAUDE.md and QUICKSTART.md carried a claim that was never true: that a
fresh clone "can't boot: no shell, no cat, no ls" without tracked
bytecode. mk and limbo are C programs and emu/*/o.emu was never
tracked, so a clone could run nothing regardless. Both now state what
is actually the case and how to rebuild.
CLAUDE.md's wrong-target section pointed at the deleted verify-dis-paths
guard. Rewritten: untracking dissolves most of that trap, because
bytecode compiled to the wrong place can no longer be committed -- what
survives is your own tree lying to you for an afternoon.
CONTRIBUTING.md gains the rebuild loop and "never commit .dis".
git does not track empty directories, so with dis/ untracked a fresh
clone has no dis/charon/, dis/wm/, dis/lib/ ... and the very first
install fails:
cp: cannot create regular file '.../dis/charon/build.dis':
No such file or directory
Until now the directories existed only because committed bytecode kept
them alive. Every rule that installs into a dis tree now creates it.
mkfiles/mkdis covers most of the tree through the shared
$DISBIN/%.dis rule; the rest are bespoke install rules in acme,
charon, dict, git, lib, sh, svc, wikifs and xenith. appl/veltro
already did this.
The '&&' chains in the rules touched are split into separate commands
while here, per AGENTS.md.
Verified on a worktree containing no .dis at all: mk and limbo build
from C in 29s, the runtime tree builds in 15s, and the emulator boots
into a shell.
Removing verify-dis-paths and untracking dis/ left claims behind in places that matter more than a README. .claude/skills/limbo-dev and limbo-test told agents to run tools/verify-dis-paths.sh when a fix "isn't taking effect", and named it as a CI gate. Both now describe the rebuild loop, name verify-dis-build, and repeat the rule that matters: never hand-roll `limbo -o <path>`. The compliance documents cited the deleted verifier and the tracked dis/ tree as controls -- SP800-53-171 CM and SI-7, CM-5, SLSA, the FIPS-140-3 readiness table, and both Common Criteria documents. Those were accurate when written and are not any more. The mapping is stronger for the change, not weaker: the old control policed committed bytecode for staleness after the fact. The new one removes the possibility -- shipped .dis is built from the tagged source by CI and by every release job, and checked against a tracked manifest so a module cannot silently leave the baseline. Reworded to say that rather than to name a script that no longer exists. tools/compile-limbo.sh's "kept in sync" comment pointed at the deleted scanner; replaced with the rule it was really expressing.
The dis-build job failed on the runner with
comm: file 1 is not in sorted order
comm(1) needs both inputs collated the same way, and sort(1) collates
by locale. The manifest was generated on a machine running
en_US.UTF-8, where 'adeb' sorts before 'Calendar'; the runner used the
C locale, where it does not. Nothing was wrong with the contents.
Pins LC_ALL=C in the script and regenerates the manifest in that order.
The script now also sorts the manifest at comparison time rather than
trusting its committed order, so a line added in the wrong place is a
harmless diff instead of a CI failure.
Verified passing under both LC_ALL=C and LC_ALL=en_US.UTF-8, and still
failing closed when a module is dropped from its mkfile TARG.
This was referenced Aug 28, 2026
pdfinn
added a commit
that referenced
this pull request
Aug 28, 2026
* fix(git): read push credentials in the command, not the Git library appl/cmd/git/push.b called git->readcredentials(), which #338 ("attenuate each tool invocation namespace") removed from the Git module so a library loadable by an agent tool cannot read a credentials file. push.b was left calling it, so appl/cmd/git has not compiled since 2026-07-02. Nothing noticed: dis/ is tracked and no build recompiles the whole tree. Restore the read inside the command rather than the library. The command runs in its own namespace, where visibility of .git/credentials is the capability deciding whether a push can authenticate -- a namespace boundary rather than policy code, and it keeps the read out of a module agent tools can load. * build: restore sources deleted before Tk was reintegrated e3914b1 ("Complete Infernode migration") deleted a set of Limbo sources whose programs needed Tk, at a time when Tk was not ported. It left their compiled .dis in the tracked runtime tree, so the programs kept shipping with no source anyone could rebuild. #350 reintegrated Tk but did not bring these back. Recovered from our own history and verified against what ships: popup, scoretable, cptree, items, wm/date, wm/sh and wm/task all recompile BYTE-IDENTICAL to the bytecode currently in dis/, which proves these are the exact sources those binaries were built from. bounce compiles cleanly and differs only in size from a 2006 build. Two of these were not dead weight: - module/scoretable.m is included by appl/wm/tetris.b, so tetris loads /dis/lib/scoretable.dis at runtime. A 2006 binary nobody could rebuild was a live dependency of a shipped application. - bounce is advertised to agents in lib/veltro/tools/launch.txt. Also restores jitbug, jitbug2 and jitshift (dropped by 9108ed5, a cleanup commit, while their siblings jitbench/jitcrash/jittest kept their sources). All three recompile byte-identically, and the JIT still faults on Linux arm64, so the repro cases are worth keeping. Sources only; the mkfile wiring and rebuild follow separately. * build: compile every module that ships, not just the ones in TARG 45 .dis in the tracked runtime tree had a Limbo source in this repo that no mkfile TARG named, so `mk install` never compiled them. They were in dis/ only because somebody built them by hand once. That is the same failure mode as the wrong-target trap in CLAUDE.md, one step earlier: not compiled to the wrong path, but never compiled at all. Adds the missing targets so the build produces what the tree ships: appl/cmd audiotone bioget gateprobe jitbench jitbench2 jitbug jitbug2 jitcrash jitshift jittest mermaid_test test9p tlsperf appl/cmd/auth aescbc ai2key createuser dsagen rsagen .../proto infauth appl/cmd/dict adict appl/cmd/ip dhcp appl/lib bioauth keyringinst softkbd viewport + popup, scoretable, and a new ftree/ subdir appl/math gr appl/wm bounce date sh task appl/veltro taskboard9p, sources/sms, tools/contacts tests gpu_bench gpu_smoke llmclient_sse_fallback_test tlsclient veltro_cc_alignment_test webfs_test All 45 compile clean against current module interfaces. * chore(dis): remove bytecode that no source in this tree can rebuild Every entry in dis/ should be something `mk install` produces. These were not. Each was checked against the full object history before removal; nothing was dropped merely because the source was hard to find. Sources that DO exist were restored instead (see f8debb7). Source deliberately deleted, binary left behind -- finishing those removals: veltro/tools/safeexec.dis #375 "remove stale safeexec tool" veltro/tools/mail.dis INFR-8, superseded by mail9p tests/veltro_safeexec_test.dis tests/widget_kbdfilter_test.dis tests/widget_scrollbar_test.dis tests/todo9p_test.dis sh/sh-inferno64.dis test-sdl3.dis The first matters: a Veltro tool removed by a commit titled "chore(security)" was still shipping as executable bytecode in the agent's tool directory. It is not in the tools9p registry, but it was in every release tarball. Never had a source here -- the binaries name where they were built: list_dis.dis built from /tmp/list_dis.b elgamal_profile_test.dis built from the repo root test-tempfile.dis tests/ics_test.dis veltro/tools/calendar.dis implements ToolCalendar; appl/veltro/ tools/calendar.b appears nowhere in history. A second unsourced agent tool. map_range_{int3,minimal,str1}.dis variadic.dis GoDis experiment output (45 files). GoDis is an experiment; its compiled output does not belong in the OS runtime tree or in release tarballs. The Go sources stay tracked in tools/godis/testdata/. Stale duplicates in directories the build never installs to: dis/cmd/{9export,bdf2subfont,gateprobe,import,mermaid_test,mount} and dis/cmd/auth/passwd.dis -- appl/cmd installs to dis/, canonical copies are already there. Same disease .gitignore already records for dis/cmd/luci*. dis/lib/{agentlib,nsconstruct}.dis -- module/{agentlib,nsconstruct}.m both declare /dis/veltro/*.dis. dis/authnode.dis -- tests/ installs to dis/tests/ dis/rect.dis -- xenith/bin installs to xenith/dis/ * build(dis): regenerate the tracked runtime tree from source With every shipped module now built by a mkfile, `mk install` over appl, appl/mpeg, appl/veltro and tests regenerates all 939 tracked .dis. This commit is that output. Two reasons a file changed. Stale bytecode -- the committed binary predates its source: dis/acme.dis and dis/acme/acme.dis still referenced /fonts/vera/Vera/unicode.14.font while the source has said /fonts/combined/unicode.sans.14.font since 2026-03-06. The acme in every release since has been looking for fonts by their old names. Also svc/webget/ftp.dis, svc/httpd/parser.dis, lib/git/git.dis and 14 test binaries. Build-path leaks -- limbo records the source path in the .dis, so the builder's absolute path shipped inside the bytecode: /home/user/infernode/... (bufio, json, regex, veltro/cowfs) /.claude/worktrees/persist/... (auditget, newuser) /.claude/worktrees/9p-tutorial/... (countfs) /.claude/worktrees/matrix-live-video/... (video-pane, trfs) Rebuilt from the repo root, these record /appl/... as intended. A scan of the tree now finds no absolute build path in any .dis. Verified: limbo output is deterministic (same source compiled twice is byte-identical) and reproducible across machines -- 724 of the files already in the tree rebuilt byte-for-byte identically on Linux arm64 from bytecode committed on other hosts. That is what makes the CI check in the next commit possible. * ci: require dis/ to be exactly what the source compiles to Rebuilds the runtime tree from the tagged source and fails if the result differs from what is committed, in any direction: bytecode that differs from its source, a tracked .dis nothing builds, or a built .dis nobody tracked. It deliberately does not reason about where a module's .dis belongs. That reasoning is what the wrong-target trap defeats, and mk already holds the answer -- so the check rebuilds and compares instead. Two things make this possible, both measured rather than assumed: limbo output is deterministic, and it records the source path relative to $ROOT, so a build on Linux arm64 reproduces bytecode committed from macOS byte-for-byte. 724 files in the tree already did so before any of this work. Note the tracked .dis must be deleted before rebuilding. mk compares mtimes, and a fresh checkout stamps every file with the checkout time, so mk concludes the tree is up to date and compiles nothing -- the check would pass without doing any work. Verified failing, not just passing: a real source edit without a rebuild, a module dropped from its mkfile TARG, a module compiled to the wrong path, a source that does not compile, and a missing toolchain each exit non-zero. There is no path on which a broken scan reports success. * ci: cover acme/dis and xenith/dis too, and regenerate Jwin Three trees hold tracked bytecode, not one: dis/ is the runtime, acme/dis/ and xenith/dis/ are the editors' own command directories. All three are built by mkfiles under appl/, so all three can drift. Checking only dis/ would have left 57 files unguarded — and one of them had already drifted: acme/dis/Jwin.dis differs from what appl/acme/acme/bin compiles. Regenerated here. The other 56 reproduce byte-identically. * build(release): prove the shipped runtime tree matches the tag No job in release.yml or ci.yml rebuilt appl/ into dis/. The release staged the tracked runtime tree verbatim -- so every artifact shipped ~940 .dis that nothing had compiled from the source in that tag, whatever the last developer to run 'mk install' happened to have. Each of the six staging sites now runs the reproducibility check before copying the tree in, so a release cannot ship bytecode that differs from the source packaged beside it. The toolchain path is derived from uname so the same lines work in the Linux amd64/arm64 and macOS arm64 jobs, all of which have already built mk and limbo by this point. This is belt-and-braces now that the same check gates every PR and push to master -- but a release is exactly where the guarantee has to hold, and it costs about 20 seconds. * docs: correct the dis/ rationale and the stale Tk note CLAUDE.md and QUICKSTART.md both said a fresh clone 'can't boot: no shell, no cat, no ls' without tracked bytecode. That is not why it is tracked and it is not true: mk and limbo are C programs and emu/*/o.emu is gitignored, so a clone can run nothing until the toolchain and emulator are built regardless. Rebuilding the whole runtime tree afterwards takes about 20 seconds. The real reasons are release UX and inheritance from the 2006 upstream drop, which is still in this history as 4643900. Both now also state the invariant CI enforces: the tracked tree must be exactly what the source compiles to, so a .b change lands with its rebuilt .dis. lib/veltro/tools/launch.txt still told agents 'Apps requiring Tk (not available): task, tetris, sh, ftree, deb'. Tk came back in #350 and task, sh and tetris now build from restored sources. * test(cnsa): build authnode where the build installs it Removing dis/authnode.dis in the previous cleanup broke tests/cnsa_nodepair_test.sh, which execs /dis/authnode.dis. Caught by auditing every removal for dangling references rather than by the test running -- it needs two nodes and does not run in the suite. tests/ installs to dis/tests/, so authnode.dis belongs there. Added to tests/mkfile TARG and the script updated to match. Its header comment also told the reader to run limbo -gw -Imodule -o dis/authnode.dis tests/authnode.b which is exactly the hand-rolled -o path CLAUDE.md warns about, and how the file ended up somewhere no mkfile built. Replaced with 'cd tests && mk install'. * Stop tracking compiled bytecode (#560) * chore: remove the guards that have nothing left to guard tools/verify-dis-paths.sh checked that each source's declared .dis existed and was no older than the source. Its whole subject is committed bytecode, so with dis/ becoming a build product it has nothing to check -- and it was never a good guard: it recognised 8 of the 25 sources carrying an inline PATH constant, and a scan that died reported success and exited 0. Removed with it: the workflow that ran it on every PR, and hooks/pre-commit, whose only job was to run it before a commit that can no longer contain a .dis file. hooks/post-merge stays, and matters more than before: it is what rebuilds your working tree after a pull now that the tree no longer arrives prebuilt. * build: stop tracking compiled bytecode dis/ is a build product. It was tracked because the 2006 upstream drop (4643900) tracked it, and because a downloaded release should run without the user building anything. The second reason is real; it just never required tracking. release.yml already stages emu/*/o.emu, never tracked, built earlier in the same job -- and that is the harder case, being per-architecture and needing its shared libraries carried along. Bytecode is architecture-independent. What tracking cost, measured on master rather than supposed: bytecode went stale (dis/acme.dis shipped /fonts/vera/... for five months after the source moved to /fonts/combined/...), modules were compiled to directories the build never installs to, sources were deleted with their binaries left behind -- including a Veltro tool removed by a commit titled "chore(security)" -- developer paths leaked inside released .dis files, and 45 modules shipped that no mkfile compiled. No CI or release job rebuilt appl/ at all, so every release carried ~940 files nothing had built from the tagged source. This could not happen before that was fixed: 25 tracked binaries had no source in the repository, one of them a live runtime dependency of tetris. The preceding commits recovered or accounted for every one. Only *.dis is ignored. 51 hand-written Inferno sh scripts live in these trees (acme/dis/Clean, dis/svc/auth, dis/lc, xenith/dis/Run, ...) and are real sources; they stay tracked. One guard replaces another: tools/dis-manifest.txt the set of modules the build must produce tools/verify-dis-build.sh builds the tree, fails if any are missing Untracking removes staleness by construction, since bytecode is always freshly compiled, and leaves a quieter failure in its place: drop a target from an mkfile TARG and the module stops existing with nothing to say so until something fails to load. The manifest makes that a reviewable one-line diff. 964 binaries become 964 lines of text. A missing module fails the check; an extra one is only reported. release.yml compiles the test-framework self-test into dis/lib/ on the side, and with bytecode no longer committable a stray file cannot be published by accident the way a stale one could. Run by a new CI job on every PR, and at all six release staging sites, which now build the tree instead of copying a committed one. * docs: document the rebuild SOP, and put it in AGENTS.md dis/ no longer arrives with the clone, so "build the runtime" moves from a thing you could skip to a thing every contributor and agent has to know. AGENTS.md gets its own section, since that file is the SOP agents read: - dis/ is a build product; never commit a .dis - the rebuild loop, and that hooks/post-merge runs it after a pull - all four directories are required -- appl/mpeg and appl/veltro are not in appl/mkfile's DIRS, so `cd appl && mk install` misses them, which is the single easiest thing to get wrong here - adding or removing a module means an mkfile TARG entry AND a line in tools/dis-manifest.txt, in the same commit - never hand-roll `limbo -o <path>` CLAUDE.md and QUICKSTART.md carried a claim that was never true: that a fresh clone "can't boot: no shell, no cat, no ls" without tracked bytecode. mk and limbo are C programs and emu/*/o.emu was never tracked, so a clone could run nothing regardless. Both now state what is actually the case and how to rebuild. CLAUDE.md's wrong-target section pointed at the deleted verify-dis-paths guard. Rewritten: untracking dissolves most of that trap, because bytecode compiled to the wrong place can no longer be committed -- what survives is your own tree lying to you for an afternoon. CONTRIBUTING.md gains the rebuild loop and "never commit .dis". * build: create output directories when installing git does not track empty directories, so with dis/ untracked a fresh clone has no dis/charon/, dis/wm/, dis/lib/ ... and the very first install fails: cp: cannot create regular file '.../dis/charon/build.dis': No such file or directory Until now the directories existed only because committed bytecode kept them alive. Every rule that installs into a dis tree now creates it. mkfiles/mkdis covers most of the tree through the shared $DISBIN/%.dis rule; the rest are bespoke install rules in acme, charon, dict, git, lib, sh, svc, wikifs and xenith. appl/veltro already did this. The '&&' chains in the rules touched are split into separate commands while here, per AGENTS.md. Verified on a worktree containing no .dis at all: mk and limbo build from C in 29s, the runtime tree builds in 15s, and the emulator boots into a shell. * docs: update the agent skills and compliance mappings Removing verify-dis-paths and untracking dis/ left claims behind in places that matter more than a README. .claude/skills/limbo-dev and limbo-test told agents to run tools/verify-dis-paths.sh when a fix "isn't taking effect", and named it as a CI gate. Both now describe the rebuild loop, name verify-dis-build, and repeat the rule that matters: never hand-roll `limbo -o <path>`. The compliance documents cited the deleted verifier and the tracked dis/ tree as controls -- SP800-53-171 CM and SI-7, CM-5, SLSA, the FIPS-140-3 readiness table, and both Common Criteria documents. Those were accurate when written and are not any more. The mapping is stronger for the change, not weaker: the old control policed committed bytecode for staleness after the fact. The new one removes the possibility -- shipped .dis is built from the tagged source by CI and by every release job, and checked against a tracked manifest so a module cannot silently leave the baseline. Reworded to say that rather than to name a script that no longer exists. tools/compile-limbo.sh's "kept in sync" comment pointed at the deleted scanner; replaced with the rule it was really expressing. * fix(ci): make the manifest comparison locale-proof The dis-build job failed on the runner with comm: file 1 is not in sorted order comm(1) needs both inputs collated the same way, and sort(1) collates by locale. The manifest was generated on a machine running en_US.UTF-8, where 'adeb' sorts before 'Calendar'; the runner used the C locale, where it does not. Nothing was wrong with the contents. Pins LC_ALL=C in the script and regenerates the manifest in that order. The script now also sorts the manifest at comparison time rather than trusting its committed order, so a line added in the wrong place is a harmless diff instead of a CI failure. Verified passing under both LC_ALL=C and LC_ALL=en_US.UTF-8, and still failing closed when a module is dropped from its mkfile TARG.
This was referenced Aug 31, 2026
pdfinn
added a commit
that referenced
this pull request
Aug 31, 2026
Two conflicts, both from master moving under the branch. dis/tests/tools9p_test.dis: master deleted it in #560, which stopped tracking compiled bytecode. Resolved by taking the deletion — dis/ is a build product now and the file is regenerated by for d in appl appl/mpeg appl/veltro tests; do (cd $d && mk install); done tests/host/tools9p_integration_test.sh: positional, not semantic. This branch adds provision_valid_subset and master added provision_probe_compile_run (INFR-434) at the same point in the file. They are independent tests, so both are kept, in that order. Verified after resolution: the branch's diff against master is exactly its own four files, the runtime tree builds and matches the manifest, and all three host scripts pass bash -n.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #559 — review that first; this diff is against it.
What this changes
dis/stops being tracked. It is a build product, likeemu/*/o.emu.Why tracking was never required
It was tracked because the 2006 upstream drop (
46439007c, still in thishistory) tracked it, and because a downloaded release should run without the
user building anything. The second reason is real — it just never required
tracking.
release.ymlalready stagesemu/*/o.emu, which has never beentracked, built earlier in the same job. That is the harder case: it is
per-architecture and needs
libfido2/libcborcarried alongside. Bytecode isarchitecture-independent.
CLAUDE.mdandQUICKSTART.mdclaimed a fresh clone "can't boot: no shell, nocat, nols" without it.mkandlimboare C programs andemu/*/o.emuis gitignored, so a clone could run nothing regardless. Tracking saved ~20
seconds on a build you could not skip.
What it cost
Everything listed in #559: stale bytecode shipping for five months, modules
compiled to paths the build never installs to, sources deleted with their
binaries left behind, developer paths inside released
.dis, 45 modules thatno mkfile compiled, and releases carrying ~940 files nothing had built from the
tagged source.
This could not happen before #559: 25 tracked binaries had no source in the
repository, one of them a live runtime dependency of
tetris.One guard replaces another
Untracking removes staleness by construction, and leaves a quieter failure in
its place: drop a target from an mkfile
TARGand the module simply stopsexisting, with nothing to say so until something fails to load.
tools/dis-manifest.txt— the set of modules the build must producetools/verify-dis-build.sh— builds the tree, fails if any are missing964 binaries become 964 lines of text, so a module appearing or vanishing is
a reviewable one-line diff. Run by a new CI job on every PR and at all six
release staging sites, which now build the tree instead of copying a committed
one.
A missing module fails; an extra one is only reported (
release.ymlcompilesthe test-framework self-test into
dis/lib/on the side, and with bytecode nolonger committable a stray file cannot be published by accident).
Two things that would have gone wrong silently
acme/dis/Clean,dis/svc/auth,dis/lc,xenith/dis/Run… A blanket ignore ondis/wouldhave deleted them. Only
*.disis ignored.dis/charon/,dis/wm/,dis/lib/… and the first install died withcp: cannot create regular file. Those directories existed only becausecommitted bytecode kept them alive. Ten mkfiles now create their own output
directories.
Documentation
AGENTS.mdgets its own SOP section, since that is the file agents read:dis/is a build product, never commit a
.dis, the rebuild loop, thathooks/post-mergeruns it after a pull, that all four directories arerequired (
appl/mpegandappl/veltroare not inappl/mkfile'sDIRS), andthat adding or removing a module means an mkfile
TARGentry and a manifestline in the same commit.
CLAUDE.md,QUICKSTART.mdandCONTRIBUTING.mdupdated to match.The compliance documents were citing the deleted controls.
SP800-53-171-mapping.md(CM),nist-control-mappings.md(SI-7, CM-5),SLSA.md, the FIPS-140-3 readiness table and both Common Criteria documents allnamed
verify-dis-pathsand the trackeddis/tree. Two agent skills pointed atit too. All updated — and the mapping is stronger, not weaker: the old control
policed committed bytecode for staleness after the fact; the new one removes the
possibility, since shipped bytecode is built from the tagged source and checked
against a tracked baseline.
Verification
.dis: toolchain from C in 29s → runtime in15s → emulator in 41s → boots to a shell.
git statuscompletely clean. That is the propertythe change exists for.
TARG→ 1, source that will notcompile → 2, missing toolchain → 2.
121 passed / 21 failed / 2 skipped (master: 64 / 76). Untracking changed
nothing behaviourally.
mk nukenow leaves zero git changes; recovery is a 14s rebuild.That is the failure Build and test-tree integrity: guard mk nuke, stale bytecode, and dis paths #550 was written to guard against.
Bearing on #550
Four of its six guards have nothing left to guard.
verify-dis-paths.sh, itsworkflow and
hooks/pre-commitare removed here.