Skip to content

fix(statechart): resolve the mise ceiling folder and run the TLC tests on macOS - #314

Merged
mdsmithaustin merged 6 commits into
mainfrom
fix/statechart-darwin-tests
Oct 26, 2026
Merged

mdsmithaustin merged 6 commits into
mainfrom
fix/statechart-darwin-tests

Conversation

@mdsmithaustin

@mdsmithaustin mdsmithaustin commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Why

On a macOS host with mise, 18 of the 150 statechart tests failed, so the statechart tier of tools/gate-check.sh test was red. Three causes produced the 18 failures. Two were in the test file. The third was a real bug in findTlc: under a symlinked TMPDIR, mise read an untrusted mise.toml above the temp folder.

Closes #308

What changed

  • findTlc resolves its empty temp folder before it names it in MISE_CEILING_PATHS, because mise compares the ceiling with its resolved cwd. The install command in the error message uses $(pwd -P) in place of $PWD.
  • The test file resolves TMPDIR once. On darwin, the rename shim loads through DYLD_INSERT_LIBRARIES and replaces mkdir and rename through __interpose entries, because macOS ignores LD_PRELOAD. The real-mise test reaches TMPDIR through a symlink, so on a host with mise it fails without the ceiling fix on any platform.
  • A new test runs findTlc with the fake mise under a locked = true mise.toml above a TMPDIR reached through a symlink. It needs no mise or TLC, so CI runs it and guards the ceiling fix there.
  • PORTING.md lists all seven edits that statechart.test.mjs has beyond upstream, so the next sync from cursor/plugins keeps them. Two of them predate this PR and were missing: the imports that point back into skills/ and the /srv/u fixture path.

Scope

  • Each of the 18 failures now passes on macOS. No test is skipped. The 4 skips that remain are the existing font and PID-namespace limits.
  • Running the TLC tests in CI (option 3 in statechart: 18 TLC-backed tests fail on macOS and never run in CI #308) is left out. A Linux container with mise ran them green in about 5 minutes, so the change is feasible. But it edits .github/workflows/**, which the gate protects. It also adds about 5 minutes and a 305 MB Java and TLC install to the required skills job, and it brings mise into CI as a new pinned tool for tools/test_repository_hardening.py to admit. That cost belongs in its own PR.
  • Nothing goes upstream to cursor/plugins.

Tradeoffs

The shim keeps upstream's C body and adds #ifdef __APPLE__ blocks before and after it, so the next sync can carry upstream's edits to that body unchanged. On darwin, dlsym(RTLD_NEXT) returns the interposed replacement and recursed, so a macro sends the shim's lookups to libc's own functions.

Blast Radius

The production change touches only how findTlc runs mise. A resolved path names the same folder, so Linux hosts without symlinks behave as before. CI still skips the TLC tests and now runs one more test, the fake-mise findTlc test.

Verification

  • macOS 26 (Node 24.20.0, mise 2026.10.18): node --test tests/skills/statechart/scripts/test/*.test.mjs went from 18 failed to 151 tests with 147 passed, 0 failed, 4 skipped. The real-mise ceiling test fails on its own commit before the fix and passes after it.
  • The fake-mise findTlc test fails on the base tlc-tools.mjs with No lockfile URL found for java@temurin-25 (--locked mode), and it fails the same way when only the realpathSync line is reverted. It passes at head.
  • Linux arm64 in Docker (node:24, no mise, no Java, like CI): the new test fails on the base tlc-tools.mjs and passes at head. The suite gives 151 tests with 58 passed, 0 failed, 93 skipped.
  • Linux arm64 in Docker (node:24, mise 2026.10.19, java@temurin-25, TLC 1.7.4): head gives 151 tests with 147 passed, 0 failed, 4 skipped, with all 5 rename tests running through LD_PRELOAD.
  • tools/gate-check.sh lint and tools/gate-check.sh test pass every check on this macOS host at the head commit, including the statechart tier that statechart: 18 TLC-backed tests fail on macOS and never run in CI #308 reported as FAIL.

✅ Low: The production edit is one realpathSync call plus a $(pwd -P) hint. The test edits are bounded: a global TMPDIR resolve, a symlinked-TMPDIR guard that runs against the fake mise (which, like real mise, walks up from the resolved cwd), and a darwin-only interpose block in the rename shim that Linux compiles out. PORTING.md accounts for all seven test-file edits.

Updates from git push no-mistakes

@mdsmithaustin mdsmithaustin changed the title fix(statechart): isolate mise discovery with symlinked temporary paths fix(statechart): resolve the mise ceiling folder and run the TLC tests on macOS Oct 25, 2026
@mdsmithaustin
mdsmithaustin force-pushed the fix/statechart-darwin-tests branch from 0d3967f to 91f2467 Compare October 25, 2026 22:08
@mdsmithaustin

Copy link
Copy Markdown
Owner Author

Verdict: FAIL
Head: 91f2467
Patch-id: e83913f3fd8ea40abe57ae219042ee0bb73ca477
Docs: needs changes

Root verdict from a full swarm. All three lanes ran at 0d3967f1: a live lane (sonnet@high) and two audit lanes (sonnet@xhigh, stepped down from the opus work model). Neither audit lane relied on the PR body. Since then, the gate rebased the branch onto bf619d69 (#312), which moved the head here. git range-diff ee59a17a..0d3967f1 bf619d69..91f2467c shows the two test commits unchanged. The other two commits differ only in a PORTING.md context line from #312, so the lane results carry over to this head.

Live lane: PASS.

  • macOS: trunk gives 150 tests with 18 fail. Head gives 146 pass, 0 fail, 4 skipped, with the same skip list. 17 of the 18 pass under the same name. The 18th is the renamed real-mise test, which also passes.
  • tools/gate-check.sh test at head passes every line.
  • Linux (node:24, aarch64, mise, TLC): trunk and head both give 146/0/4. The rename tests run there through LD_PRELOAD.
  • Outside the suite, with a symlinked TMPDIR and an untrusted mise.toml, trunk exits 2 with mise's "not trusted" error. Head runs TLC.
  • On macOS, the real CLI from-chart --png runs end to end.

Audit lanes:

  • The darwin __interpose shim does not recurse. The Linux preprocessor output and compiled shim are byte-identical to trunk.
  • Five mutations to statechart.mjs are each caught by the rename tests, and both interpose tuples are live.
  • findTlc resolves the folder it just made and uses that one path for cwd, ceiling and removal. No other site computes a mise ceiling.
  • The real-mise test is red at 216a564c for the stated reason.
  • --help, SKILL.md, README and CI docs stay accurate.

Findings sent back (in scope; real callers reach them):

  1. CI never runs the ceiling fix's guard (tests/skills/statechart/scripts/test/statechart.test.mjs:53, :2008-2015). The global TMPDIR resolve hides the symlinked-TMPDIR bug from every test except the real-mise one. CI has no mise, so that test skips there, and a regression in tlc-tools.mjs:23-26 would pass CI. Users reach the bug through a symlinked TMPDIR under a tree with an untrusted mise.toml, as in agent sandboxes and symlinked workspaces. A CI-runnable test fails on base tlc-tools.mjs and passes at head. It uses the fake mise that outsideRepoWithoutTlc() provides, a locked = true mise.toml above a symlinked TMPDIR, and a direct findTlc() call. Add it red-first.

  2. The PORTING.md statechart entry is not a complete inventory. Its sync rule is "keep ... drop each one that upstream adopts", so a sync keeps only what the entry lists. Two divergences are missing:

    • The test fixture at statechart.test.mjs:6443 uses /srv/u/Count.tla, where upstream has /home/u/Count.tla. A sync that restores upstream's line fails tools/check-personal-paths.py, and with it the lint tier and CI.
    • The test imports (:37-49) and the scripts constant (:56) are rewritten to ../../../../../skills/statechart/scripts/.... The entry cites a "tests-out-of-skills/" rule that PORTING.md never defines.

    Add both, and make the edit count match the list.

Notes (non-blocking):

  • realpathSync at tlc-tools.mjs:25 sits outside the try/finally. No real call can make it throw.
  • A darwin node without the dyld entitlement would fail 4 rename tests loudly instead of skipping them.
  • "a run on a filesystem that cannot rename" passes with the shim off, which is pre-existing on Linux too.

@mdsmithaustin

Copy link
Copy Markdown
Owner Author

On a macOS host with mise, 18 of 150 statechart tests failed, and under a symlinked TMPDIR findTlc let mise read an untrusted mise.toml above its temp folder. findTlc now resolves the folder with realpath before it names the folder in MISE_CEILING_PATHS, and the test file resolves TMPDIR and loads its rename shim on macOS through DYLD_INSERT_LIBRARIES.

Before: a temp folder reached through a symlink gives mise a ceiling that is not mise's cwd, so the search climbs to the mise.toml above, which is crossed out: mise stops on untrusted config. After: the temp folder goes through realpath, the ceiling is mise's cwd, and the search stops.

Not covered. CI still skips 92 of the 151 statechart tests, the TLC ones among them. There, only the fake-mise findTlc test checks the ceiling fix, and the TLC paths rest on the macOS host run and a Linux container run.

Details for reviewers

Risk. Low. The one production change is the resolved ceiling folder and the $(pwd -P) install hint in tlc-tools.mjs. Everything else is in the test file and PORTING.md.

Base bf619d69 on main, head 0fdd6f17. 3 files, +70 -12.

Start here. findTlc resolves its folder.

Then.

Proof. lint run on the head, where the fake-mise findTlc test passes. The PR body lists the macOS and Linux test runs.

These pointers are a start, not the review. Read the whole diff, because a defect can sit in a file this recap ranks low.

@mdsmithaustin

Copy link
Copy Markdown
Owner Author

Verdict: PASS+NOTES
Head: 0fdd6f1
Patch-id: c754359e5b4c28ae3defa40ef995997c8bb7bb15
Docs: pass

Root verdict for round 2. One trail reviewer (sonnet@xhigh, stepped down from the opus work model) checked it, because the round is a minor fix: +31 -1 across two files, the new test and PORTING.md, with tlc-tools.mjs unchanged. It replaces the round-1 FAIL at 91f2467c.

  • Gates at head, run alone: tools/gate-check.sh lint and test pass every line. The statechart tier gives 151 tests: 147 pass, 0 fail, 4 skipped. The skips are the known font and PID-namespace limits.
  • Finding 1, CI-runnable guard: fixed. The new test is at statechart.test.mjs:2007.
    • It passes at head on macOS, and in a node:24 container with no mise or Java, where it is not skipped.
    • It fails with mise ERROR No lockfile URL found for java@temurin-25 (--locked mode), the ceiling climb, in two cases: with tlc-tools.mjs from ee59a17a, and with only the realpathSync line reverted.
    • CI run 40243339056 shows it as ok 50 with no SKIP.
  • Finding 2, PORTING.md inventory: fixed. Diffed against upstream e73df47, every test-file hunk maps to one of the seven listed edits, and the .mmd fixtures are byte-identical. With upstream's /home/u path restored, check-personal-paths.py exits 1. The cited rule exists at PORTING.md:106.

Notes, non-blocking:

  • PORTING.md:50 says "made printed folders absolute". "Resolved" would be more exact.
  • The new test leaves its temp folder behind, as its sibling tests do.
  • The round-1 notes on realpathSync and on the dyld entitlement still stand.

…acOS

On a macOS host with TLC, 17 of the 18 failing statechart tests failed
for two reasons in the test file, not in the scripts.

The test folders came from an unresolved TMPDIR under /var, while a
run's cwd resolves to /private/var. The scripts then printed the kept
work folder as an absolute path where the tests expect one relative to
the cwd, and TLC printed module paths under /private/var. The test file
now resolves TMPDIR once, before any folder is made.

macOS ignores LD_PRELOAD, so the rename shim never loaded and four
tests saw renames succeed. A fifth passed without exercising anything.
On darwin the shim now loads through DYLD_INSERT_LIBRARIES, which
Node's signature allows. It replaces mkdir and rename through
__interpose entries, and it calls libc's functions directly, because
dlsym(RTLD_NEXT) returns the replacement there and recursed.

Refs #308
…ling test

findTlc runs mise in an empty temp folder and names that folder in
MISE_CEILING_PATHS. mise compares the ceiling with its resolved cwd, so
a temp folder named through a symlink does not stop the search, and
mise reads the untrusted mise.toml above it. The test now reaches
TMPDIR through a symlink, so it fails on any platform until the
ceiling is a resolved path. Before the test file resolved TMPDIR, the
/var link on macOS hit the same failure.

Refs #308
findTlc named its empty temp folder in MISE_CEILING_PATHS as
os.tmpdir() spelled it. mise compares the ceiling with its resolved
cwd, so when TMPDIR runs through a symlink, as /var does on macOS, mise
climbed past the folder and failed on an untrusted mise.toml above it.
findTlc now resolves the folder first.

The install command that the error prints had the same fault, because
the shell's $PWD keeps the symlink. It now uses $(pwd -P).

PORTING.md records these and the test-file edits as port edits, so the
next upstream sync keeps them.

Closes #308
The entry left out the installCommand helper and the spread call sites
of failingRenames, so a sync that took upstream's test file whole would
break tests against the ported script.
…runs it

The real-mise ceiling test skips in CI, which has no mise, so a regression
in the resolved MISE_CEILING_PATHS would pass there. This test runs findTlc
with the fake mise under a mise.toml that sets locked = true, above a TMPDIR
reached through a symlink. With the unresolved ceiling, mise reads that file
and install fails in --locked mode.
A sync keeps only the edits the entry lists. It missed two older ones: the
imports and scripts constant that point back into skills/ since #297 moved
the file to tests/, and the /srv/u fixture path that check-personal-paths.py
needs since #304. It also cited a test-location rule by a name PORTING.md
never defines, so it now names the entry that states the rule. The count
covers the new fake-mise findTlc test.
@mdsmithaustin
mdsmithaustin force-pushed the fix/statechart-darwin-tests branch from 0fdd6f1 to 86280d0 Compare October 25, 2026 23:53
@mdsmithaustin
mdsmithaustin merged commit 87edb11 into main Oct 26, 2026
7 checks passed
@mdsmithaustin
mdsmithaustin deleted the fix/statechart-darwin-tests branch October 26, 2026 00:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

statechart: 18 TLC-backed tests fail on macOS and never run in CI

1 participant