v6.5.0
v6.5.0 — unreleased
The release automation ran correctly all the way to git push and died there. The first
fully green release pipeline in the project's history published crates, cut the tag, and
uploaded 32 assets. Its post-publication handoff then computed the successor, cut the branch,
committed the bump, and was refused:
! [remote rejected] v6.5.0 -> v6.5.0 (refusing to allow a GitHub App to create or
update workflow `.github/workflows/rust-wasm.yml` without `workflows` permission)
Fixed
- A workflow file was a version authority, and no token can push that.
rust-wasm.yml
hard-coded the WIT export string, soversion-bump.pyrewrote it every release and the
push became a workflow update. There is no permission to grant:workflowsis not in the
GITHUB_TOKENpermission vocabulary at all, so adding it to thepermissions:block
would be a syntax error, not a fix. The step now derives the version fromCargo.toml, the
single source of truth, and the authority is retired — 53 authorities down to 52, and no
workflow file carries a version literal. - The coupling cannot come back.
check-github-actions.pygained a rule that rejects any
workflow line containing the workspace version, naming the dead end in the message.
Falsified two ways: reintroducing the literal turns the checker red, and deleting the rule
turns its own suite red.test-version-bump.pynow carries a derived workflow in its
fixture and asserts the bump leaves it byte-identical; pinning that fixture to the current
version turns the test red.
Fixed — Windows is a supported platform, not a shipped stub (#321)
392 tests run: 392 passed. The Windows suite is green on both feature sets. It went
94 failures, 35, 20, 12, 6, 3, 0 — every step a real cross-platform defect, none of them a
test that needed relaxing.
The Windows binary this repository builds, packages, and publishes could not create
.shepherd/, could not bind a session, and could not store a run. Five families of
#[cfg(not(unix))] twins were Err("... unavailable on this platform"), and the first Windows
test run in the project's history reported 384 tests run: 290 passed, 94 failed. Every one is
now implemented.
-
crate::safe_fsis the new shared primitive layer for non-unix targets, and its module
docs state exactly what it does and does not guarantee rather than implying parity with the
descriptor-anchored unix side. Opens passFILE_FLAG_OPEN_REPARSE_POINT, so a leaf that is
or becomes a link yields the link itself and never an attacker's target; every ancestor is
checked withsymlink_metadata; publication isCREATE_NEWplusCreateHardLinkW, the same
two refusals unix gets fromO_EXCLandlinkat. The residual gap — an ancestor swapped
between its check and its use — is written down in the module rather than discovered later. -
Implemented:
wave_c_bootstrap(all five, soinitworks), the wholedispatch_store
ledger (so the hooks work, for Claude Code, Codex, and Pi alike),dispatch_scope
containment,compile's generated-tree check and materialize,wave_d_planningartifacts,
wave_b1_status_handoffrun states and handoffs, andresume_context. Each returns the SAME
error variants and messages as its unix twin, because callers and fixtures branch on them. -
The directory fsync is now a paired platform decision.
run/atomic.rsopened the parent
directory tosync_all()it; on Windowsstd::fs::File::opendoes not set
FILE_FLAG_BACKUP_SEMANTICS, so every store failed withAccess is denied. (os error 5).
The non-unix arm does nothing and says why: NTFS journals the rename, so there is no
unflushed directory entry the way there is on POSIX. A stated difference, not a swallowed
error. -
windows-latestis now a permanent CI axis, not a dispatch option, so this cannot
regress unobserved..cargo/config.tomlandscripts/setup.shadd a local
x86_64-pc-windows-gnucross-check so the Windows half type-checks in seconds instead of a
six-minute round trip. -
safe_fsships its own suite: no-clobber publishes once and leaves no temporary,
replace_atomicoverwrites where no-clobber refuses, an over-limit read fails instead of
truncating, absence and wrong-type stay distinguishable,ensure_directoryreports only what
it created, children are sorted and split by kind, removal is idempotent, and a real symlink
is refused rather than followed. -
The defects Windows found that had nothing to do with the stubs.
reject_symlink_path
stat'ed the bare drive prefix, so every run command died with
ERROR: inspect \\?\C:: Incorrect function.The layout manifest rendered OS-native
separators into a durable, sorted, compared artifact, so the same migration produced a
different manifest on each platform — and once the sources were canonical, the deepest-first
removal ordering countedMAIN_SEPARATORand collapsed to zero, removing parents before
their children.normalize_relativerefused every absolute Windows path because a backslash
is a smuggling attempt on unix and the separator there. Path identity needed
canonical_identity, because Windows spells one directory three ways —
\\?\C:\Users\runneradmin,C:\Users\runneradmin, andC:\Users\RUNNER~1. -
Three tests held a live SQLite connection across their fixture removal. Windows cannot
delete a directory containing an open handle; unix unlinks an open file happily, which is
why no amount of retrying would ever have helped. The teardown helper names the surviving
files now, which is what turned that from a guess into a diagnosis.
Changed — the model tier map
The team leads no longer pin a tier. A sprint spawned at the reasoning tier gets leads at that
tier; a sprint spawned cheaply gets cheap leads.
| Roles | Hint | Claude / Codex / Pi |
|---|---|---|
| root, planter | reasoning-high |
opus[1m] / reasoning-high / opus |
| engineer, conductor | inherit-caller |
inherit / inherit-caller |
| critic, coder, auditor, worker | standard |
sonnet / standard |
| discovery | economy |
haiku / economy at low effort |
discovery is the widest fan-out role in the flock, so it reaches for the economy tier that
all three harness profiles already defined and nothing had ever used. All nine stay overridable
through [models].
This was not safe to change on its own. compiler.rs built the Codex [agent_types] table
by skipping any role whose model_hint was inherit-caller, a proxy for "not dispatchable"
that was wrong in both directions: planter is dispatchable: false and Codex had been
advertising the role that holds ask-operator as spawnable, while any lead adopting
inherit-caller would have silently vanished from the table. It keys on dispatchable now.
Changed — organization identity
Identity fields renamed to pzzld-org ahead of the repository transfer. Every remaining FL03
string resolves something — the binstall pkg-url, both installers' release bases, the README
curl one-liner, the marketplace add lines — and GitHub's permanent post-transfer redirect
makes FL03 correct in both states while pzzld-org 404s until the move lands. #326 flips them
afterwards.
Notes
- The fixture pins a synthetic
9.9.9, never the live release. A fixture pinned to the real
version would make the test file a version authority, which is the exact coupling the rule
under test exists to prevent.