Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

### Fixed
- **A corrupted Claude de-duplication key no longer silently inflates Charts and Estimated API value.** The usage index treated invalid UTF-8 in a present `dedup_key` as "this record has no key", so the same transcript event could be counted twice if its counterpart still had one. Invalid UTF-8 now rejects the whole index the way a required string already does, and the next scan rebuilds it from the transcripts. A key that was never written is still encoded as the length sentinel, not as a failed decode.
- **A filesystem that cannot flock no longer writes settings unserialized.** The state-write lock treated every `flock` errno other than contention as "no lock needed" and continued, so an NFS/FUSE/SMB home without `lockd` let the tray and the CLI replace `api_keys.json` over each other. Only `ENOTSUP` — the filesystem saying it does not implement locking at all — now falls back, read from the errno rather than from how a given Rust version happens to decode it, and the fallback is an exclusive-create sibling that a second writer cannot take. `ENOLCK` is no longer treated as a broken filesystem: it also means the kernel lock table is full or `lockd` failed for that one call, while another process still holds a real flock, so falling back to the sibling would have let both writers through. It fails the write instead. An unknown errno already did. Because the sibling has no kernel-backed release, a leftover from a killed process still has to age out, but the age it has to reach is now two minutes rather than the ten seconds an acquirer waits — a holder doing slow work can no longer be outlasted and have its lock taken, and a lock file stamped in the future by a skewed server clock reads as held rather than as expired. A holder unlinks the sibling on release only while it is still the same file it created, so a takeover cannot cascade into deleting a third writer's lock. A `state-write.lock` the current user cannot open — left by a `sudo` run, or by another account — now fails the write and names the path to remove. It used to be unlinked and recreated, which put the running holder and the new one on two different inodes and let both writes proceed. A directory in the lock path still fails the write.
- **Windows stops handing an npm shim or a directory to the PTY as the Codex or Claude binary.** `where.exe` prints every match on PATH and the resolver took the first line, so an extensionless POSIX shim named `codex` sitting ahead of `codex.exe` went straight to `CreateProcessW` and failed with error 193. Every candidate is now examined, a native `.exe` or `.com` wins over a `.cmd` or `.bat` shim wherever the shim sits in the list, and directories and extensionless files are refused — for PATH results, for the `CODEX_BINARY` and `CLAUDE_BINARY` overrides, and for an explicit path. A `.cmd` or `.bat` shim now runs through `%COMSPEC% /d /c` instead of being launched directly, and its arguments are escaped for the interpreter rather than for `CreateProcessW`, because the two do not agree on `&`, `|`, or `>`. The few tails `cmd.exe` cannot express at all fail with a message rather than launch something other than what was asked for. Closes #270. Thanks @ITSMERNB!

## [Ceiling] 1.5.34 - 2026-08-18
Expand Down
Loading
Loading