Fix: Sort the contract manifest in byte order, not the machine's

The manifest is written in byte order, but both the runner and the manifest
contract discovered contracts with a bare `sort` and compared them with bash's
`<` -- and both of those follow LC_COLLATE. Under en_US.UTF-8 the collation
folds punctuation away, so `calendar_agenda_bridge_test.py` sorts before
`calendar-agenda-helper-contract` instead of after it, and eight pairs that
differ only by `-` against `_` come back out of order.

The effect was that `tests/setup/contract-manifest-contract` failed on this
machine, and `panama test` refused to run at all, with eight identical "paths
are not lexicographically sorted" findings and nothing naming which paths. A
gate whose answer depends on the machine's LANG is not a gate, so the sort and
the comparison are both pinned to byte order. LC_ALL rather than LC_COLLATE,
because an exported LC_ALL outranks it and would have put the bug back.

Claude-Session: https://claude.ai/code/session_017zzbtfnMLoYrB8WesqANFY
This commit is contained in:
Gabriel Brown
2026-08-27 14:47:47 -04:00
parent 1ae3825fda
commit 1ee42f2cb6
2 changed files with 15 additions and 2 deletions
+8 -1
View File
@@ -451,11 +451,16 @@ CONTRACT_CAPABILITIES=(hermetic live-host live-compositor live-desktop network p
contract_paths() {
local candidate
# The manifest is kept in byte order, so both the discovery sort and the
# comparison below have to be byte order too. A UTF-8 collation folds the
# punctuation away -- `calendar_agenda_bridge_test.py` sorts before
# `calendar-agenda-helper-contract` under en_US and after it under C -- and
# a gate that passes or fails on the machine's LANG is not a gate.
while IFS= read -r candidate; do
[[ -x "$candidate" || "$candidate" == *_test.py ]] || continue
printf 'tests/%s\n' "${candidate#"$PANAMA_DIR/tests/"}"
done < <(find "$PANAMA_DIR/tests" -type f \
-not -path '*/fixtures/*' -not -path '*__pycache__*' | sort)
-not -path '*/fixtures/*' -not -path '*__pycache__*' | LC_ALL=C sort)
}
contract_manifest_entries() {
@@ -478,6 +483,8 @@ validate_contract_manifest() {
require_contract_manifest || return 1
local manifest="$PANAMA_DIR/$CONTRACT_MANIFEST"
# Byte order, for the same reason contract_paths sorts in it.
local LC_ALL=C
local line capabilities path extra previous_comment="" previous_was_comment=0
local previous_path="" capability discovered
local -a capability_list=() findings=()