agent-compose compose cannot run on native Windows (operating_context validation) #887

Closed
opened 2026-08-20 05:29:16 +00:00 by coilyco-ops · 2 comments
Member

Found while setting up acompose on kai-tower-3026 (native Windows). Related to #356.

Problem

agent-compose compose (v2.32.0) cannot converge on native Windows, a catch-22:

  1. compose requires operating_context: with it absent it exits agent-compose.yaml must declare operating_context repositories.
  2. But the same binary's validator rejects every operating_context value on Windows: repository "coilyco-flight-deck/agentic-os" must be a clean owner/repository path. Even foo/bar fails, and the backslash form owner\repo is rejected too.

The check appears to run filepath.Clean() (OS-specific) on each entry. On Windows that rewrites owner/repo -> owner\repo, so Clean(v) != v and the value is declared "not clean". Net: a config that validates (no operating_context) cannot compose, and a config that could compose cannot validate. The composer is unusable on native Windows.

Also: config discovery path differs by OS

The Windows binary reads ~/.agent-compose/agent-compose.yaml (C:\Users\<user>\.agent-compose\...), not the ~/.config/agent-compose/agent-compose.yaml that the Ansible agent-compose role writes on mac/linux. (The role is already gated off the windows group, so this only bites once #356 wires Windows in.)

Fix direction

The operating_context cleanliness check should use path.Clean (forward-slash, OS-independent) rather than filepath.Clean: these are logical owner/repo identifiers, not filesystem paths.

Workaround in place (kai-tower-3026)

Harness load point is hand-maintained per group_vars/windows.yml: ~/.claude/CLAUDE.md @-includes agentic-os-kai/AGENTS.md directly. A staged, inert ~/.agent-compose/agent-compose.yaml (sources only, no operating_context) is left ready for when the composer is Windows-operable.

Repro on kai-tower-3026:

$ agent-compose compose --verbose
roster  C:\Users\<user>\.agent-compose\sources\personality (34 files)
agent-compose: agent-compose.yaml must declare operating_context repositories

$ agent-compose config validate <cfg-with-operating_context>
operating_context: repository "coilyco-flight-deck/agentic-os" must be a clean owner/repository path
Found while setting up acompose on kai-tower-3026 (native Windows). Related to #356. ## Problem `agent-compose compose` (v2.32.0) cannot converge on native Windows, a catch-22: 1. `compose` **requires** `operating_context`: with it absent it exits `agent-compose.yaml must declare operating_context repositories`. 2. But the same binary's validator **rejects every** `operating_context` value on Windows: `repository "coilyco-flight-deck/agentic-os" must be a clean owner/repository path`. Even `foo/bar` fails, and the backslash form `owner\repo` is rejected too. The check appears to run `filepath.Clean()` (OS-specific) on each entry. On Windows that rewrites `owner/repo` -> `owner\repo`, so `Clean(v) != v` and the value is declared "not clean". Net: a config that validates (no `operating_context`) cannot compose, and a config that could compose cannot validate. The composer is unusable on native Windows. ## Also: config discovery path differs by OS The Windows binary reads `~/.agent-compose/agent-compose.yaml` (`C:\Users\<user>\.agent-compose\...`), **not** the `~/.config/agent-compose/agent-compose.yaml` that the Ansible `agent-compose` role writes on mac/linux. (The role is already gated off the `windows` group, so this only bites once #356 wires Windows in.) ## Fix direction The `operating_context` cleanliness check should use `path.Clean` (forward-slash, OS-independent) rather than `filepath.Clean`: these are logical `owner/repo` identifiers, not filesystem paths. ## Workaround in place (kai-tower-3026) Harness load point is hand-maintained per `group_vars/windows.yml`: `~/.claude/CLAUDE.md` @-includes `agentic-os-kai/AGENTS.md` directly. A staged, inert `~/.agent-compose/agent-compose.yaml` (sources only, no `operating_context`) is left ready for when the composer is Windows-operable. Repro on kai-tower-3026: ``` $ agent-compose compose --verbose roster C:\Users\<user>\.agent-compose\sources\personality (34 files) agent-compose: agent-compose.yaml must declare operating_context repositories $ agent-compose config validate <cfg-with-operating_context> operating_context: repository "coilyco-flight-deck/agentic-os" must be a clean owner/repository path ```
Author
Member

Diagnosis confirmed and fixed upstream: agent-compose#313.

Root cause

internal/cascade/cascade.go, validateConfiguredRepository. Exactly as this issue predicted:

if value == "" || strings.Contains(value, `\`) || filepath.IsAbs(value) || filepath.Clean(value) != value {

Probed on kai-tower-3026:

filepath.Clean("coilyco-flight-deck/agentic-os") = "coilyco-flight-deck\\agentic-os"  clean=false
path.Clean("coilyco-flight-deck/agentic-os")     = "coilyco-flight-deck/agentic-os"   clean=true

filepath.IsAbs was the same OS-specific trap one operand over.

Fix

path.Clean for the cleanliness test, a leading-slash check in place of filepath.IsAbs, and the split drops filepath.ToSlash because the backslash rejection above it already guarantees forward slashes. Unix behaviour is unchanged, since path.Clean and filepath.Clean agree there.

Verification on kai-tower-3026

Shipped v2.32.0 against the fixed build, same config:

$ agent-compose config validate probe.yaml          # v2.32.0
operating_context: repository "coilyco-flight-deck/agentic-os" must be a clean owner/repository path
$ ./acompose-fixed config validate probe.yaml       # PR #313
exit=0

A full compose run against an isolated HOME, which v2.32.0 cannot start at all:

cascade outputs=2 load-points=2 repository-plan=1 changed=5
skills  managed=336 load-points=2 verified=0 linked=336 removed=0 preserved=0
exit=0

go test ./... on Windows drops from 33 failures to 12: 22 tests fixed, 0 newly broken, across cascade, compose, converge, and nativelaunch. Most of them write operating_context into their fixture config and were failing at LoadConfig.

On the config-discovery half of this issue

Not a bug, and no OS split. internal/home/home.go makes ~/.agent-compose the canonical state directory on every platform, and ~/.config/agent-compose is the pre-consolidation legacy path it migrates once, leaving a compatibility symlink. So the Ansible agent-compose role is writing to the legacy location on mac and linux and getting away with it via that migration. Worth pointing the role at ~/.agent-compose directly before #356 wires Windows in, rather than treating it as a Windows-only path difference.

Follow-up, not in scope here

12 Windows test failures remain, all pre-existing on main and unrelated to operating_context: path separators baked into assertions, HOME vs USERPROFILE in TestResolveSkillLoadPointsDefaultsWireClaudeAndCodex, a stale palette snapshot, embedded-roster mismatches, and a skillmount verified/preserved split that TestConvergeComposesRosterIntoCascade now reaches for the first time. These want their own issue on agent-compose; none of them block compose from converging.

Once #313 releases, the staged ~/.agent-compose/agent-compose.yaml on kai-tower-3026 can drop its inert banner and take back the operating_context block its comment already spells out.

Diagnosis confirmed and fixed upstream: **[agent-compose#313](https://forgejo.coilysiren.me/coilyco-flight-deck/agent-compose/pulls/313)**. ## Root cause `internal/cascade/cascade.go`, `validateConfiguredRepository`. Exactly as this issue predicted: ```go if value == "" || strings.Contains(value, `\`) || filepath.IsAbs(value) || filepath.Clean(value) != value { ``` Probed on kai-tower-3026: ``` filepath.Clean("coilyco-flight-deck/agentic-os") = "coilyco-flight-deck\\agentic-os" clean=false path.Clean("coilyco-flight-deck/agentic-os") = "coilyco-flight-deck/agentic-os" clean=true ``` `filepath.IsAbs` was the same OS-specific trap one operand over. ## Fix `path.Clean` for the cleanliness test, a leading-slash check in place of `filepath.IsAbs`, and the split drops `filepath.ToSlash` because the backslash rejection above it already guarantees forward slashes. Unix behaviour is unchanged, since `path.Clean` and `filepath.Clean` agree there. ## Verification on kai-tower-3026 Shipped v2.32.0 against the fixed build, same config: ``` $ agent-compose config validate probe.yaml # v2.32.0 operating_context: repository "coilyco-flight-deck/agentic-os" must be a clean owner/repository path $ ./acompose-fixed config validate probe.yaml # PR #313 exit=0 ``` A full `compose` run against an isolated `HOME`, which v2.32.0 cannot start at all: ``` cascade outputs=2 load-points=2 repository-plan=1 changed=5 skills managed=336 load-points=2 verified=0 linked=336 removed=0 preserved=0 exit=0 ``` `go test ./...` on Windows drops from 33 failures to 12: **22 tests fixed, 0 newly broken**, across `cascade`, `compose`, `converge`, and `nativelaunch`. Most of them write `operating_context` into their fixture config and were failing at `LoadConfig`. ## On the config-discovery half of this issue Not a bug, and no OS split. `internal/home/home.go` makes `~/.agent-compose` the canonical state directory on every platform, and `~/.config/agent-compose` is the pre-consolidation legacy path it migrates once, leaving a compatibility symlink. So the Ansible `agent-compose` role is writing to the legacy location on mac and linux and getting away with it via that migration. Worth pointing the role at `~/.agent-compose` directly before #356 wires Windows in, rather than treating it as a Windows-only path difference. ## Follow-up, not in scope here 12 Windows test failures remain, all pre-existing on `main` and unrelated to `operating_context`: path separators baked into assertions, `HOME` vs `USERPROFILE` in `TestResolveSkillLoadPointsDefaultsWireClaudeAndCodex`, a stale palette snapshot, embedded-roster mismatches, and a `skillmount` verified/preserved split that `TestConvergeComposesRosterIntoCascade` now reaches for the first time. These want their own issue on `agent-compose`; none of them block `compose` from converging. Once #313 releases, the staged `~/.agent-compose/agent-compose.yaml` on kai-tower-3026 can drop its inert banner and take back the `operating_context` block its comment already spells out.
Author
Member

Landed and released. agent-compose#313 merged to main as 1395916, CI green, and the release workflow published v2.33.0 with a Windows amd64 binary.

kai-tower-3026 is upgraded (scoop update agent-compose, 2.32.0 to 2.33.0) and the shipped binary clears the repro:

> agent-compose version
v2.33.0
> agent-compose config validate probe.yaml
exit=0

A full compose against an isolated HOME converges on the released binary too:

cascade outputs=2 load-points=2 repository-plan=1 changed=5
skills  managed=336 load-points=2 ...
exit=0

Kai has not pointed the live host at the composer yet. ~/.agent-compose/agent-compose.yaml still carries its inert banner and the hand-maintained ~/.claude/CLAUDE.md @-include from group_vars/windows.yml is untouched, because activating it rewrites that load point and belongs to the #356 rollout rather than to this bug. The composer half of the blocker is gone; #356 owns the wiring.

Closing this. The 12 remaining Windows test failures are unrelated to operating_context and want their own agent-compose issue.

Landed and released. [agent-compose#313](https://forgejo.coilysiren.me/coilyco-flight-deck/agent-compose/pulls/313) merged to `main` as `1395916`, CI green, and the release workflow published **v2.33.0** with a Windows amd64 binary. kai-tower-3026 is upgraded (`scoop update agent-compose`, 2.32.0 to 2.33.0) and the shipped binary clears the repro: ``` > agent-compose version v2.33.0 > agent-compose config validate probe.yaml exit=0 ``` A full `compose` against an isolated `HOME` converges on the released binary too: ``` cascade outputs=2 load-points=2 repository-plan=1 changed=5 skills managed=336 load-points=2 ... exit=0 ``` Kai has not pointed the live host at the composer yet. `~/.agent-compose/agent-compose.yaml` still carries its inert banner and the hand-maintained `~/.claude/CLAUDE.md` @-include from `group_vars/windows.yml` is untouched, because activating it rewrites that load point and belongs to the #356 rollout rather than to this bug. The composer half of the blocker is gone; #356 owns the wiring. Closing this. The 12 remaining Windows test failures are unrelated to `operating_context` and want their own `agent-compose` issue.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/infrastructure#887
No description provided.