Skip to content

fix: Windows — shell detection, CRLF catalogs, and slink that actually links - #10

Merged
MY-RV merged 3 commits into
mainfrom
fix/windows-preview-feedback
Sep 21, 2026
Merged

MY-RV merged 3 commits into
mainfrom
fix/windows-preview-feedback

Conversation

@MY-RV

@MY-RV MY-RV commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Everything here was found by running v0.3.0-preview.1 on a real Windows host,
which is what the preview was for.

Every PowerShell user was told they were in cmd

Detection read the parent process, and something is usually in between: scoop
installs godo as shims\godo.exe, which starts the real godo.exe as a child,
so the parent of the process asking the question was godo. Not a shell, so
the answer fell through to %ComSpec%.

It now walks up the process tree to the nearest shell — which also covers npm,
bun and make wrappers, and an editor's terminal. The walk is a pure function
over a process table, so the shim case is tested off Windows.

godo -e runners also says how it chose, because a wrong answer is only
diagnosable if you can tell a stale GODO_SHELL from a fallback.

A catalog written on Windows lost its decorators

With CRLF, the YAML parser files the comment above a key as the previous
key's foot comment, so # @dialect matcher decorated nothing — unless a blank
line happened to sit above it, which is why it read as "no space between the
command and the comment breaks it".

Line endings are normalized before parsing, which YAML already calls the same
line break. It also keeps a \r out of a block scalar, where it was reaching
the shell as part of the command.

A decorator YAML genuinely puts out of reach (trailing on the line, or after
the last key) is now an error naming where it belongs, instead of vanishing.

fs.slink did nothing on Windows

It called os.Symlink, and a symlink there is a privilege rather than a file
operation. It now makes the link Windows offers — a junction for a
directory, a hard link for a file — neither of which needs a privilege.

The reparse buffer is built in portable code and tested field by field.
DeviceIoControl itself is unverified; it only runs on Windows, and that
is why the release this cuts is preview.2 rather than 0.3.0.

./godo ci green, go vet green for windows/amd64.

Reported from a real Windows host running v0.3.0-preview.1, which is what the
preview was for. The roadmap said Windows detection was written from the
documented behavior of those shells and unverified; it was also wrong.

Every PowerShell user was told they were in cmd. Detection read the parent
process, and something is usually in between: scoop installs godo as
shims\godo.exe, which starts the real godo.exe as a child, so the parent of the
process asking the question was *godo*. Not a shell, so it fell through to
%ComSpec%. It now walks up to the nearest shell, which also covers npm, bun and
make wrappers and an editor's terminal. The walk is a pure function over a
process table, so it is tested off Windows too.

Also from the same session:

- A decorator at the end of a line was dropped in silence. YAML files it as a
  line comment on the value, and nothing reads it, so the catalog ran with a
  dialect its author believed they had written and failed somewhere else. Same
  for a decorator after the last key. Both are errors now, naming where the
  decorator belongs.
- fs.slink on Windows returned "A required privilege is not held by the
  client" — true, and useless unless you already know Developer Mode grants it.
- godo -e runners says how the shell was chosen. A wrong answer is only
  diagnosable if you can tell a stale GODO_SHELL from a fallback.
Two corrections to the previous commit, both mine.

A catalog written on Windows has CRLF, and with it the YAML parser files the
comment above a key as the *previous* key's foot comment — so the decorator
decorated nothing, unless a blank line happened to sit above it. That is the
"no space between the command and the comment" report, and the misplaced-
decorator error added in the last commit would have fired on a file where the
decorator was placed correctly. Line endings are normalized before parsing,
which YAML already calls the same line break; it also keeps a \r out of a block
scalar, where it was reaching the shell as part of the command.

slink is symlinks on Unix, junctions and links on Windows. It was calling
os.Symlink everywhere, and the last commit answered the resulting failure with
advice about Developer Mode — which is not the op working, it is the op
explaining why it does not. It now creates a junction for a directory and a
hard link for a file, neither of which needs a privilege. The reparse buffer is
built in portable code so the layout is tested off Windows; the syscall around
it is not.
Everything in it came from running preview.1 on a real Windows host. The
junction syscall behind slink is the reason this is preview.2 and not 0.3.0:
its buffer is tested, the call itself has been run by nobody.
@MY-RV
MY-RV merged commit 9c126dc into main Sep 21, 2026
4 checks passed
@MY-RV
MY-RV deleted the fix/windows-preview-feedback branch September 21, 2026 19:28
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.

1 participant