Skip to content

fix: pin case-insensitive matching to the invariant culture - #77

Merged
bgard68 merged 1 commit into
mainfrom
claude/culture-invariant-matching
Aug 25, 2026
Merged

fix: pin case-insensitive matching to the invariant culture#77
bgard68 merged 1 commit into
mainfrom
claude/culture-invariant-matching

Conversation

@bgard68

@bgard68 bgard68 commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Determinism is this project's central claim, and it has to hold across machines, not only across runs on one.

Five patterns used RegexOptions.IgnoreCase without CultureInvariant, which folds case using whatever culture the process happens to be running in.

Turkish is the standard counterexample: uppercase I lowercases to a dotless ı, so a pattern containing an i stops matching text that differs only in case. Every one of these patterns contains one — sentinel, uses. A deployment in tr-TR would have analysed the same workflow differently from one in en-US, and nothing would have reported the difference.

InvariantGlobalization is not set on any project, so the culture is whatever the host provides.

What is actually load-bearing

Only the acceptance directive. The others sit behind an ordinal prefix check their case folding cannot get past — UnpinnedActionRule matches uses: with StringComparison.Ordinal before its regex runs.

They were pinned anyway: a guard that is unreachable today becomes reachable the moment someone loosens the check in front of it.

File Patterns
WorkflowSuppressions 1 — the sentinel:accept directive
UnpinnedActionRule 2
SensitiveDataSanitizer 1
WorkflowPatchGenerator 1

A test I wrote and then deleted

The new test analyses an uppercased acceptance under both en-US and tr-TR.

It originally had a second case asserting that an uppercased USES: is still reported as unpinned. That premise was wrong — it failed in both cultures, because the rule filters on uses: ordinally before the regex runs, and GitHub Actions keys are case-sensitive, so USES: is not a step key at all. The rule is right to ignore it.

The test was removed rather than weakened to pass, and the reason is recorded in the file where the next reader will look for it.

Verification

324 tests pass, 195 in the infrastructure suite. The new test pins the invariant under both cultures by swapping CultureInfo.CurrentCulture around the assertion.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N7HjzX5e8Wv7PKENDo2Uj7


Generated by Claude Code

Determinism is this project's central claim, and it has to hold across machines
rather than only across runs on one. Five patterns used RegexOptions.IgnoreCase
without CultureInvariant, which folds case using whatever culture the process is
running in.

Turkish is the standard counterexample: uppercase I lowercases to a dotless i,
so a pattern containing an i stops matching text that differs only in case.
Every one of these patterns contains one - "sentinel", "uses". A deployment in
tr-TR would have analysed the same workflow differently from one in en-US, and
nothing would have reported the difference.

Only the acceptance directive is load-bearing today. The others sit behind an
ordinal prefix check that their case folding cannot get past. They were pinned
anyway, because a guard that is unreachable today is reachable the moment
someone loosens the check in front of it.

A test analyses an uppercased acceptance under both en-US and tr-TR. It was
written with a second case, asserting an uppercased "USES:" is still reported -
that premise was wrong in both cultures, because the rule filters on "uses:"
ordinally before the regex runs, and GitHub Actions keys are case-sensitive so
"USES:" is not a step key at all. The test was removed rather than weakened, and
why is recorded where the next reader will look.

195 infrastructure tests and 324 in total pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7HjzX5e8Wv7PKENDo2Uj7
@bgard68
bgard68 merged commit cf3592a into main Aug 25, 2026
10 checks passed
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.

2 participants