Repository navigation
One Runtime
One Runtime
Foreman v0.3.0
Status: released. The four exit predicates were measured on the commit recorded
in the table below; this commit adds documentation only and carries its own
green gates-linux.
v0.3.0 is one sentence: the Node migration is finished — one runtime, one
language.
Product
Foreman's own Python is gone. git ls-files '*.py' returns seven files: four
vendored scrapling templates, one vendored scrapling test, one vendored
superpowers token-usage utility, and one archived schema checker under
openspec/changes/archive/. None of them is Foreman's.
Three subsystems moved to Node.js 24 and TypeScript in this release.
The session store. skills/foreman/scripts/fm-session.py was 1090 lines
across four tables — facts, measurements, obligations, sessions. It is now
packages/orchestration/src/fm-session-main.ts, bundled behind a thin adapter.
tests/session.bats drives the CLI through FM_SESSION_CMD and passes 29/29
against the bundle. Point that variable at a command that does not exist and all
29 fail, which is how we know the suite is exercising the bundle and not
something else.
The Tier 2 evaluator. tests/tier2_compare.py computed percentiles and
bootstrap confidence intervals. The port reproduces Python's output byte for
byte, which required porting MT19937 including its SHA-512 string seeding, and
Python 3.12's compensated (Neumaier) float summation — naive addition deviates
at the sixteenth decimal, which is enough to move a confidence bound.
Three docs/research/ utilities are retired rather than ported.
The six verified defects
§W2 of the design doc listed six defects found by review agents that ran the
code. All six are fixed in this release.
| Defect | Landed by |
|---|---|
Unguarded recursion in canonical-json.ts threw a raw RangeError |
a744e9f |
| POSIX backslash path-confusion, three sites | 4c12622 |
argvWithoutDetach corrupted re-exec argv |
a744e9f |
decodeUtf8Fatal stripped a BOM, so digest and text disagreed |
a744e9f |
| Unthrottled spawn fallback discarded the measured vendor caps | a744e9f |
Six set*RaceHook functions reached the published surface |
d74600e |
The path-confusion one is worth its own paragraph, because it is the defect this
release nearly shipped. On POSIX a backslash is a legal filename character, so a
normalizer that strips a trailing separator turned /tmp/x\ into /tmp/x — a
caller could present a path that compares equal to one directory and denotes
another, inside credential-authority comparison. It existed in three copies. Two
of them were reported fixed earlier in the release run and were not; measurement
on the release candidate found all three still present.
The fix deletes the strip rather than gating it on Windows. resolve() already
removes a trailing separator for the platform's own path flavour and preserves
roots, so the strip was dead code for legitimate input, and its only live
effects were bugs: the POSIX corruption above, and on Windows turning the root
C:\ into C:, which isAbsolute rejects as drive-relative. The originally
planned platform gate would have preserved the second one.
Exit predicates
Four predicates on one unchanged pushed commit. The table in ROADMAP.md
carries the measured verdicts and the commit they were taken against.
Predicate 2 was rewritten during the release. It previously also required a
green gates-windows that ran the Bats suite. That suite was then measured on
Windows for the first time at pass=444 fail=270 skip=26 and does not fit the
60-minute job cap, so Windows is excluded by decision rather than by
omission — and the exclusion is enforced by a test: re-adding a gating
trigger to the Windows workflow fails hosted-node24-ci.test.ts. Both
polarities were demonstrated before that change landed.
What this release does not do
Windows is not a gating platform and the Bats suite has never passed there. The
absent flock alone accounts for 134 of the 270 failures. Recorded as BW-004.
Sprints 8 through 13 and 16, Sprint 6's project registry, and the knowledge and
graph plane move to v0.4.0. Their change packages are unmodified; only their
release assignment moved.
brokenwindows.md carries the open register. It grew during this release rather
than shrinking, which is the intended behaviour of a log for breakage that is
known, undecided and unassigned.
Notes on the work
Two Tier 2 ports were rejected before the third succeeded. The first passed all
27 tests in its oracle and was still not equivalent: whole-valued floats lost
their decimals and a bootstrap confidence interval landed 16% from Python's on
identical input. The second shipped a string sentinel into output. Neither was
caught, because that suite asserts exit codes and shapes and no statistical
value.
The fix was not a better implementer. It was tests/tier2-golden.bats, which
freezes Python's exact output and diffs against it. The third port was
mechanical once the oracle could see what mattered.
The same shape appeared twice more. In the session port, three project tests
and two white-box Python tests retired with their subject, and their properties
— worktree store sharing, single-snapshot sidecar reads, and
row-check-after-write-lock — were replaced rather than dropped, because a cold
review refused to let concurrency invariants retire uncovered. And in the
path-confusion fix, a cross-vendor audit found that the new tests used a bare
return on Windows, which node:test reports as a pass with zero assertions,
and that the black-box test asserted only that a scan had not refused rather
than which directory it had walked. Both were reworked before the merge; the
stronger test plants a secret in the sibling directory the old normalizer
collapsed onto, so a clean result can only mean the scan bound to the tree it
was handed.
The recurring lesson of this release is that the oracle is usually the defect.
Five separate "it passed" reports in the release run were false, and every one
of them was caught by a check that could discriminate — never by a more careful
reading of the report.