Skip to content

Commit 2ec6e37

Browse files
dmealingclaude
andcommitted
fix: close two audit-residual gaps — Python YAML byte-compare + LF .gitattributes
Two persistent gaps surfaced by the conformance-framework QA audit (2026-05-26). Neither blocks current tests, but both are real contract-violation footguns waiting to surface. 1. Python YAML conformance was comparing parsed JSON trees, not canonical bytes. The canonical-serializer contract (spec/conformance-tests.md:54-84) specifies 2-space indent, alphabetically-sorted @-attrs, declaration-ordered children, and a trailing newline — ALL part of the cross-port byte-exact contract. A tree compare would silently pass regressions in any of these. TS does `expect(got).toBe(want)`; C# + Java do trimmed-string compare. Python now matches: byte-compare with only trailing-newline normalization. 2. Added a top-level .gitattributes forcing LF line endings on fixtures/** and *.mustache files. Without this, a Windows clone with default `core.autocrlf=true` silently converts these to CRLF on checkout, then byte-equality assertions fail for filesystem reasons rather than engine reasons. Render-conformance is particularly exposed: its whole reason-for-being is byte-exact rendered output to defend prompt-cache hits. Verification: - Python YAML conformance: 6/6 still green (assertion shape changed, fixtures already correct under LF) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent bd0bcb8 commit 2ec6e37

2 files changed

Lines changed: 28 additions & 6 deletions

File tree

.gitattributes

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Conformance fixtures rely on byte-exact cross-port comparison. Force LF on
2+
# all fixture inputs/outputs so a Windows clone with default
3+
# `core.autocrlf=true` doesn't silently rewrite line endings and break the
4+
# byte-equality gate for filesystem reasons rather than engine reasons.
5+
#
6+
# Files affected:
7+
# - fixtures/conformance/*/expected.json + input/*.json (canonical metamodel)
8+
# - fixtures/yaml-conformance/*/{input.yaml,expected.json}
9+
# - fixtures/render-conformance/*/{expected.txt,template.mustache,payload.json,meta.json}
10+
# - fixtures/verify-conformance/*/{template.mustache,payload.json,expected-drift.json,options.json,partials/*.mustache}
11+
# - fixtures/persistence-conformance/**/*.yaml
12+
fixtures/** text eol=lf
13+
14+
# Mustache templates carry cross-port byte-exact contracts elsewhere too
15+
# (test resources, snapshots).
16+
*.mustache text eol=lf

server/python/tests/conformance/test_yaml_conformance.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,16 @@ def test_yaml_conformance(fix: YamlFixture) -> None:
122122
assert codes == [], (
123123
f"{fix.name}: expected no errors but got {codes}"
124124
)
125-
want_tree = json.loads((fix.dir / "expected.json").read_text())
126-
got_tree = json.loads(canonical)
127-
assert got_tree == want_tree, (
128-
f"{fix.name}: canonical mismatch\n"
129-
f" want: {json.dumps(want_tree, indent=2, sort_keys=True)}\n"
130-
f" got: {json.dumps(got_tree, indent=2, sort_keys=True)}"
125+
# Byte-compare per the canonical-serializer contract
126+
# (spec/conformance-tests.md): 2-space indent, sorted @-attrs,
127+
# declaration-ordered children, trailing newline are ALL part of the
128+
# cross-port contract. A tree compare would silently pass regressions
129+
# in any of these. Trim only the trailing newline so authors don't
130+
# have to fight editors-that-trim. TS does `expect(got).toBe(want)`;
131+
# C# + Java do trimmed-string compare. Python now matches.
132+
want = (fix.dir / "expected.json").read_text(encoding="utf-8")
133+
assert canonical.strip() == want.strip(), (
134+
f"{fix.name}: canonical byte mismatch\n"
135+
f"--- want ---\n{want}\n"
136+
f"--- got ---\n{canonical}"
131137
)

0 commit comments

Comments
 (0)