diff --git a/Models/Entities/ParsedField.cs b/Models/Entities/ParsedField.cs
index a7213c2..c7a2f9b 100644
--- a/Models/Entities/ParsedField.cs
+++ b/Models/Entities/ParsedField.cs
@@ -12,6 +12,24 @@ public class ParsedField
public bool IsRequired { get; set; }
public string DataType { get; set; }
public int Occurrence { get; set; } = 1;
+
+ ///
+ /// Total de ocorrências físicas reais existentes para o grupo (LineName+FieldName) deste
+ /// campo. Constante para todos os ParsedFields do mesmo grupo, incluindo o agregado
+ /// (Occurrence=0) gerado por AggregatePositionalGroupRepetitions. Default 1 (campo sem
+ /// repetição posicional).
+ ///
+ public int OccurrenceCount { get; set; } = 1;
+
+ ///
+ /// true apenas na entrada que representa o valor lógico final/agregado de uma LineElement
+ /// marcada IsPositionalGroupRepetition (gerada por AggregatePositionalGroupRepetitions,
+ /// Occurrence=0). false nos fragmentos físicos brutos (Occurrence >= 1) e em campos sem
+ /// repetição posicional. Permite ao consumidor (front-end) escolher a entrada correta sem
+ /// depender da convenção implícita "Occurrence==0".
+ ///
+ public bool IsAggregatedOccurrence { get; set; }
+
public bool IsMissing { get; set; }
public string LineSequence { get; set; }
public string FullPath => $"{LineName}.{FieldName}";
diff --git a/Services/Implementations/LayoutParserService .cs b/Services/Implementations/LayoutParserService .cs
index b0b26d4..c04e275 100644
--- a/Services/Implementations/LayoutParserService .cs
+++ b/Services/Implementations/LayoutParserService .cs
@@ -438,6 +438,13 @@ private void AggregatePositionalGroupRepetitions(Layout layout, List f.Occurrence).ToList();
var first = ordered[0];
+ int occurrenceCount = ordered.Count;
+
+ // Propaga a contagem real de ocorrências físicas também para os fragmentos
+ // brutos (Occurrence >= 1) já existentes em parsedFields — mesma instância por
+ // referência, então esta atualização é visível na lista final.
+ foreach (var raw in ordered)
+ raw.OccurrenceCount = occurrenceCount;
// Trim por fragmento antes de concatenar (não um trim único no resultado final):
// cada ocorrência física já teve TrimEnd aplicado por ApplyAlignment (padding de
@@ -465,6 +472,8 @@ private void AggregatePositionalGroupRepetitions(Layout layout, Listeea774e6409e10a9806015b49816b98a1fbb487550aa309469d9dc49dd1e2375.
+ ///
+ /// ✅ Recapturado por @lp-qa em 2026-08-26, rodando este teste contra a amostra real
+ /// (.claude/tmp/26072026/) DEPOIS do fix do Bug A (Length real) e da adição de
+ /// OccurrenceCount/IsAggregatedOccurrence (commit a330af2). Contagem também corrigida de
+ /// 704 para 705 nesta mesma validação (704 nunca foi confirmado contra a amostra real —
+ /// era herdado de um short-circuit; comportamento pré-existente, não introduzido pelo fix).
///
- private const string MqBaselineSha256 = "99e4688590b1ec6df01146bf3c43a3c429b1ecdaa03e6b75a2069ed45e690024";
+ private const string MqBaselineSha256 = "453e9a184e253d1b310f7814282ebfddb9ca5a99f25acc65ecae741060c8ecfd";
///
/// Issue #37: LINHA081 (IsPositionalGroupRepetition=true) forma o campo infCpl
@@ -110,6 +120,12 @@ public async Task LINHA081_agrega_infCpl_igual_ao_xml_esperado_do_gabarito_real(
Assert.NotNull(agregado);
Assert.Equal(infCplEsperado, agregado!.Value);
+ // Bug A + contrato novo: Length do agregado é o comprimento REAL do valor lógico
+ // concatenado, e o sinal explícito de "esta é a entrada final" está em
+ // IsAggregatedOccurrence — o front não precisa mais inferir via Occurrence==0.
+ Assert.Equal(infCplEsperado.Length, agregado.Length);
+ Assert.True(agregado.IsAggregatedOccurrence);
+ Assert.Equal(4, agregado.OccurrenceCount);
// Aditivo: os 4 fragmentos físicos (Occurrence 1..4) continuam intactos — é deles que
// ValidateLineOccurrences deriva o MinimalOccurrence/MaximumOccurrence de LINHA081.
@@ -119,6 +135,11 @@ public async Task LINHA081_agrega_infCpl_igual_ao_xml_esperado_do_gabarito_real(
.ToList();
Assert.Equal(4, fragmentos.Count);
Assert.Equal(new[] { 1, 2, 3, 4 }, fragmentos.Select(f => f.Occurrence));
+ // Bug A: cada fragmento bruto reporta o comprimento REAL do seu valor, não mais o
+ // tamanho máximo declarado no layout (500) — e nenhum é a entrada agregada.
+ Assert.All(fragmentos, f => Assert.Equal(f.Value.Length, f.Length));
+ Assert.All(fragmentos, f => Assert.False(f.IsAggregatedOccurrence));
+ Assert.All(fragmentos, f => Assert.Equal(4, f.OccurrenceCount));
}
///
@@ -248,6 +269,8 @@ private static string DumpCampos(List