Skip to content

Commit c6ce7fc

Browse files
dmealingclaude
andcommitted
fix(csharp): pre-merge review — JsonSource length-1 invariant + null-source fallback
Two cross-port drifts surfaced by the pre-merge review of FR5a, both worth fixing now while only two ports (TS + C#) have to stay in sync. 1. Parser.cs ParseState.CurrentSource() returned a JsonSource with an empty Files array when the parser was invoked without a source id (e.g. parsing from a string buffer in tests). That violates the FR5a length-1 invariant and produces an envelope shape no other port emits. TS reference (parser-core.ts:104-114) falls back to the default CodeSource in the same case. C# now matches. 2. ErrorSource.cs JsonSource.Files was typed IReadOnlyList<string> with a Javadoc invariant ("FR5a invariant: exactly one file") that the type didn't enforce. Combined with #1 the type system allowed the bad shape through. Added a constructor-init validator that throws ArgumentException when Files.Count != 1, pointing callers at MergedSource for multi-file provenance (FR5c). Verification: C# all 4 projects 561/561 still green (Conformance 302 including the 26 new FR5a tests + Render 88 + Codegen 164 + Cli 7). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8f41f31 commit c6ce7fc

2 files changed

Lines changed: 17 additions & 3 deletions

File tree

server/csharp/MetaObjects/Parser.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,15 @@ private sealed class ParseState
130130

131131
/// <summary>
132132
/// Build a <see cref="JsonSource"/> envelope for the current location.
133-
/// The <c>Files</c> list is a length-1 array of <see cref="Source"/>
134-
/// (the source id / file path), or an empty list when none is available.
133+
/// When <see cref="Source"/> is null (parser invoked without a source id,
134+
/// e.g. from a string buffer in tests), fall back to <see cref="CodeSource"/>
135+
/// — emitting a JsonSource with an empty file list would violate the
136+
/// FR5a length-1 invariant and produce an envelope shape no other port
137+
/// emits. Matches the TS reference (parser-core.ts:104-114).
135138
/// </summary>
136139
public ErrorSource CurrentSource() =>
137140
Source is null
138-
? new JsonSource(Array.Empty<string>(), Builder.ToString())
141+
? CodeSource.Default
139142
: new JsonSource(new[] { Source }, Builder.ToString());
140143
}
141144

server/csharp/MetaObjects/Source/ErrorSource.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ public abstract record ErrorSource
2828
/// <param name="JsonPath">Canonical JSONPath string for the node within <paramref name="Files"/>[0].</param>
2929
public sealed record JsonSource(IReadOnlyList<string> Files, string JsonPath) : ErrorSource
3030
{
31+
/// <summary>
32+
/// FR5a invariant: exactly one file. Multi-file provenance lives on
33+
/// <see cref="MergedSource"/> (FR5c). Enforced at construction so the
34+
/// type can be trusted by cross-port comparison.
35+
/// </summary>
36+
public IReadOnlyList<string> Files { get; init; } = Files.Count == 1
37+
? Files
38+
: throw new ArgumentException(
39+
$"JsonSource requires exactly one file path; got {Files.Count}. " +
40+
"Use MergedSource for multi-file provenance.", nameof(Files));
41+
3142
/// <inheritdoc/>
3243
public override string Format => "json";
3344
}

0 commit comments

Comments
 (0)