Skip to content

Windows: restore a CI leg, and close the O_NOFOLLOW confinement gap it exposes #175

Description

@ethanj

Summary

CI runs Linux only. That is what let #163 ship — a separator bug that made every nested profile directory fail containment on win32, so no profile could load, and nothing in CI could see it. #172 fixed the bug and recorded the decision in .github/workflows/ci.yml, but the underlying gap is unchanged: win32 behaviour is held by a test seam, not by a runner.

Restoring a windows-latest leg is not a one-line workflow change, which is why it is its own issue.

What we already know

A windows-latest leg was added and taken back out inside #172. It confirmed the separator fix holds on win32, then surfaced 64 unrelated failures across 44 files. The dominant cause is not test infrastructure:

O_NOFOLLOW is POSIX-only. On Windows the flag is ignored, so every "fails closed on a symlinked leaf" guard silently degrades to a plain open. That pattern is used in 18 source files across the confinement, store, and trust layers:

src/artifacts/store.ts                              src/utils/confined-read.ts
src/commands/artifact.ts                            src/utils/embeddings-store.ts
src/commands/workflow.ts                            src/utils/jsonl-store.ts
src/events/store-record.ts                          src/utils/lock.ts
src/profile/load.ts                                 src/utils/pending-embeddings.ts
src/profile/paths.ts                                src/utils/state.ts
src/profile/templates/publish/distribution-paths.ts src/workflows/local-config.ts
src/profile/templates/publish/tap-key-file.ts       src/workflows/store.ts
src/relations/store-record.ts                       src/trust/journal.ts

So this is a correctness-and-safety gap on Windows, not only a red CI leg. The tests that fail there are largely the ones asserting those guards fail closed — they are reporting something true.

Two other things worth not rediscovering:

  • continue-on-error does not give you a non-blocking leg. The workflow run goes green, but the job's check-run still reports failure and blocks the merge.
  • npx is not resolvable from child_process on Windows without shell: true, since the Node 22 hardening for CVE-2024-27980. fix(profile): compare declared paths with the POSIX separator on win32 #172 already fixed the one instance of this in the vitest global setup, but it is the kind of thing that recurs in tooling scripts.

Why it matters

Every symlink-confinement guard in the list above is load-bearing for path safety. On Windows they currently provide weaker protection than their tests claim, and nothing reports that. Anyone running llmwiki on Windows today gets the separator fix from #172 but not the confinement guarantees the same code advertises.

Suggested shape

Sequencing matters more than the total size here — a leg that lands red trains people to ignore it.

  1. Decide the Windows story for no-follow opens: a supported equivalent (FILE_FLAG_OPEN_REPARSE_POINT semantics via an lstat-then-open-with-verification path, reusing the existing hardened reader rather than hand-rolling per call site), or an explicit documented non-goal with the guards failing closed on win32 instead of degrading open.
  2. Fix the remaining non-confinement failures in the 44 files.
  3. Land the windows-latest leg blocking, once it is actually green.

Acceptance

  • windows-latest runs in CI as a blocking leg
  • no confinement guard silently weakens on win32 — either it holds, or it refuses
  • the .github/workflows/ci.yml note explaining the Linux-only choice is removed, because it no longer applies

Context: #163, #172.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions