jspurefix 5.11.3 → 5.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.gitattributes +4 -0
  2. package/dist/buffer/index.d.ts +6 -0
  3. package/dist/buffer/index.js +6 -0
  4. package/dist/buffer/index.js.map +1 -1
  5. package/dist/collections/index.d.ts +1 -0
  6. package/dist/collections/index.js +16 -0
  7. package/dist/collections/index.js.map +1 -1
  8. package/dist/dictionary/contained/index.d.ts +3 -0
  9. package/dist/dictionary/contained/index.js +3 -0
  10. package/dist/dictionary/contained/index.js.map +1 -1
  11. package/dist/dictionary/index.d.ts +5 -0
  12. package/dist/dictionary/index.js +5 -0
  13. package/dist/dictionary/index.js.map +1 -1
  14. package/dist/generator/applicability.d.ts +3 -0
  15. package/dist/generator/applicability.js +48 -0
  16. package/dist/generator/applicability.js.map +1 -0
  17. package/dist/generator/case-builder.d.ts +34 -0
  18. package/dist/generator/case-builder.js +111 -0
  19. package/dist/generator/case-builder.js.map +1 -0
  20. package/dist/generator/corpus-store.d.ts +43 -0
  21. package/dist/generator/corpus-store.js +115 -0
  22. package/dist/generator/corpus-store.js.map +1 -0
  23. package/dist/generator/index.d.ts +12 -0
  24. package/dist/generator/index.js +29 -0
  25. package/dist/generator/index.js.map +1 -0
  26. package/dist/generator/instrument-universe.d.ts +54 -0
  27. package/dist/generator/instrument-universe.js +286 -0
  28. package/dist/generator/instrument-universe.js.map +1 -0
  29. package/dist/generator/layout.d.ts +46 -0
  30. package/dist/generator/layout.js +208 -0
  31. package/dist/generator/layout.js.map +1 -0
  32. package/dist/generator/market-context.d.ts +64 -0
  33. package/dist/generator/market-context.js +119 -0
  34. package/dist/generator/market-context.js.map +1 -0
  35. package/dist/generator/outcome.d.ts +16 -0
  36. package/dist/generator/outcome.js +71 -0
  37. package/dist/generator/outcome.js.map +1 -0
  38. package/dist/generator/realistic-generator.d.ts +53 -0
  39. package/dist/generator/realistic-generator.js +275 -0
  40. package/dist/generator/realistic-generator.js.map +1 -0
  41. package/dist/generator/rng.d.ts +12 -0
  42. package/dist/generator/rng.js +60 -0
  43. package/dist/generator/rng.js.map +1 -0
  44. package/dist/generator/scatter.d.ts +50 -0
  45. package/dist/generator/scatter.js +285 -0
  46. package/dist/generator/scatter.js.map +1 -0
  47. package/dist/generator/scenarios.d.ts +13 -0
  48. package/dist/generator/scenarios.js +139 -0
  49. package/dist/generator/scenarios.js.map +1 -0
  50. package/dist/generator/value-conventions.d.ts +15 -0
  51. package/dist/generator/value-conventions.js +217 -0
  52. package/dist/generator/value-conventions.js.map +1 -0
  53. package/dist/index.d.ts +6 -0
  54. package/dist/index.js +4 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/jsfix-cmd.d.ts +5 -0
  57. package/dist/jsfix-cmd.js +161 -0
  58. package/dist/jsfix-cmd.js.map +1 -1
  59. package/dist/store/fix-msg-ascii-store-resend.js +2 -2
  60. package/dist/store/fix-msg-ascii-store-resend.js.map +1 -1
  61. package/dist/store/fix-msg-store-record.d.ts +1 -1
  62. package/dist/store/fix-msg-store-record.js.map +1 -1
  63. package/dist/store/index.d.ts +1 -0
  64. package/dist/store/index.js +1 -0
  65. package/dist/store/index.js.map +1 -1
  66. package/dist/transport/index.d.ts +7 -0
  67. package/dist/transport/index.js +7 -0
  68. package/dist/transport/index.js.map +1 -1
  69. package/dist/transport/session/index.d.ts +1 -0
  70. package/dist/transport/session/index.js +1 -0
  71. package/dist/transport/session/index.js.map +1 -1
  72. package/docs/generated-cases.md +237 -0
  73. package/docs/scattered-components.md +60 -5
  74. package/jsfix.test_client.txt +62 -62
  75. package/jsfix.test_server.txt +72 -72
  76. package/package.json +5 -2
  77. package/src/buffer/index.ts +6 -0
  78. package/src/collections/index.ts +1 -1
  79. package/src/dictionary/contained/index.ts +3 -0
  80. package/src/dictionary/index.ts +5 -0
  81. package/src/generator/applicability.ts +87 -0
  82. package/src/generator/case-builder.ts +167 -0
  83. package/src/generator/corpus-store.ts +166 -0
  84. package/src/generator/index.ts +12 -0
  85. package/src/generator/instrument-universe.ts +347 -0
  86. package/src/generator/layout.ts +279 -0
  87. package/src/generator/market-context.ts +207 -0
  88. package/src/generator/outcome.ts +102 -0
  89. package/src/generator/realistic-generator.ts +404 -0
  90. package/src/generator/rng.ts +92 -0
  91. package/src/generator/scatter.ts +432 -0
  92. package/src/generator/scenarios.ts +167 -0
  93. package/src/generator/value-conventions.ts +295 -0
  94. package/src/index.ts +12 -0
  95. package/src/jsfix-cmd.ts +204 -0
  96. package/src/store/fix-msg-ascii-store-resend.ts +2 -1
  97. package/src/store/fix-msg-store-record.ts +4 -1
  98. package/src/store/index.ts +1 -0
  99. package/src/transport/index.ts +10 -0
  100. package/src/transport/session/index.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAwB;AACxB,+CAA4B;AAC5B,iDAA8B;AAC9B,kDAA+B;AAC/B,oDAAiC;AACjC,gDAA6B;AAC7B,kDAA+B;AAC/B,gDAA6B;AAC7B,oDAAiC;AACjC,wDAAqC;AAGrC,oEAAiD;AACjD,oEAAiD;AACjD,4CAAyB","sourcesContent":["export * from './duplex'\nexport * from './fix-entity'\nexport * from './fix-acceptor'\nexport * from './fix-initiator'\nexport * from './msg-transmitter'\nexport * from './msg-payload'\nexport * from './send-callback'\nexport * from './tick-action'\nexport * from './msg-application'\nexport * from './ascii/ascii-session'\n// the factory an application extends to customise its Logon or header - it belongs\n// at the root alongside ASessionMsgFactory, not behind a dist/ path\nexport * from './ascii/ascii-session-msg-factory'\nexport * from './fixml/fixml-session-msg-factory'\nexport * from './session'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAwB;AACxB,+CAA4B;AAC5B,iDAA8B;AAC9B,kDAA+B;AAC/B,oDAAiC;AACjC,gDAA6B;AAC7B,kDAA+B;AAC/B,gDAA6B;AAC7B,oDAAiC;AACjC,wDAAqC;AAGrC,oEAAiD;AACjD,oEAAiD;AACjD,4CAAyB;AACzB,4CAAyB;AAIzB,kEAA+C;AAC/C,oDAAiC;AACjC,oEAAiD;AACjD,sDAAmC;AACnC,sDAAmC;AACnC,oDAAiC","sourcesContent":["export * from './duplex'\nexport * from './fix-entity'\nexport * from './fix-acceptor'\nexport * from './fix-initiator'\nexport * from './msg-transmitter'\nexport * from './msg-payload'\nexport * from './send-callback'\nexport * from './tick-action'\nexport * from './msg-application'\nexport * from './ascii/ascii-session'\n// the factory an application extends to customise its Logon or header - it belongs\n// at the root alongside ASessionMsgFactory, not behind a dist/ path\nexport * from './ascii/ascii-session-msg-factory'\nexport * from './fixml/fixml-session-msg-factory'\nexport * from './session'\nexport * from './factory'\n// the transport description types an application writes in its session JSON -\n// the concrete tcp/http entities stay out of the root barrel so that requiring\n// jspurefix does not drag express in behind them\nexport * from './tcp/tcp-transport-description'\nexport * from './tcp/tls-options'\nexport * from './http/http-transport-description'\nexport * from './http/http-adapter'\nexport * from './http/html-options'\nexport * from './http/html-route'\n"]}
@@ -12,3 +12,4 @@ export * from './resend-request-manager';
12
12
  export * from './fix-clock';
13
13
  export * from './session-registry';
14
14
  export * from './clone-session-description';
15
+ export * from './fix-session-state-args';
@@ -28,4 +28,5 @@ __exportStar(require("./resend-request-manager"), exports);
28
28
  __exportStar(require("./fix-clock"), exports);
29
29
  __exportStar(require("./session-registry"), exports);
30
30
  __exportStar(require("./clone-session-description"), exports);
31
+ __exportStar(require("./fix-session-state-args"), exports);
31
32
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transport/session/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAuC;AACvC,gDAA6B;AAC7B,sDAAmC;AACnC,qDAAkC;AAClC,2DAAwC;AACxC,wDAAqC;AACrC,wDAAqC;AACrC,kDAA+B;AAC/B,iEAA8C;AAC9C,2DAAwC;AACxC,2DAAwC;AACxC,8CAA2B;AAC3B,qDAAkC;AAClC,8DAA2C","sourcesContent":["export * from './a-session-msg-factory'\nexport * from './fix-session'\nexport * from './fix-session-state'\nexport * from './make-fix-session'\nexport * from './session-50-description'\nexport * from './session-description'\nexport * from './session-msg-factory'\nexport * from './session-state'\nexport * from './session-sequence-coordinator'\nexport * from './session-sequence-store'\nexport * from './resend-request-manager'\nexport * from './fix-clock'\nexport * from './session-registry'\nexport * from './clone-session-description'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transport/session/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAuC;AACvC,gDAA6B;AAC7B,sDAAmC;AACnC,qDAAkC;AAClC,2DAAwC;AACxC,wDAAqC;AACrC,wDAAqC;AACrC,kDAA+B;AAC/B,iEAA8C;AAC9C,2DAAwC;AACxC,2DAAwC;AACxC,8CAA2B;AAC3B,qDAAkC;AAClC,8DAA2C;AAC3C,2DAAwC","sourcesContent":["export * from './a-session-msg-factory'\nexport * from './fix-session'\nexport * from './fix-session-state'\nexport * from './make-fix-session'\nexport * from './session-50-description'\nexport * from './session-description'\nexport * from './session-msg-factory'\nexport * from './session-state'\nexport * from './session-sequence-coordinator'\nexport * from './session-sequence-store'\nexport * from './resend-request-manager'\nexport * from './fix-clock'\nexport * from './session-registry'\nexport * from './clone-session-description'\nexport * from './fix-session-state-args'\n"]}
@@ -0,0 +1,237 @@
1
+ # Generated cases — measuring the scattered component problem
2
+
3
+ Companion to [`scattered-components.md`](scattered-components.md). That note argues from
4
+ the shape of the dictionary; this one reports what actually happens when messages of that
5
+ shape are put through the parser.
6
+
7
+ Everything here is produced by `src/generator/`, reachable from the command line:
8
+
9
+ ```
10
+ npm run scenarios
11
+ npm run scenario -- --scenario=spread-trade-capture
12
+ npm run doll -- --scenario=spread-trade-capture --russian-doll=2
13
+ ```
14
+
15
+ ## How a case is built
16
+
17
+ Four steps, and the third is the one that makes the results worth anything.
18
+
19
+ 1. **Generate** an object for a message type, populated to look like traffic rather than
20
+ to exercise the dictionary — a book of real instruments, prices on tick, quantities in
21
+ whole lots, dates that agree with each other, and optional fields chosen by how likely
22
+ a real counterparty is to send them.
23
+ 2. **Encode** it. The encoder emits in dictionary order, so the result is contiguous by
24
+ construction.
25
+ 3. **Parse the canonical encoding.** This is the oracle. There is no hand-written
26
+ expectation anywhere in the corpus that could itself be wrong.
27
+ 4. **Permute the body tokens** so that one or more components become non-adjacent, in a
28
+ way the specification still permits, and parse that. It is the same message, so any
29
+ difference is the engine losing information.
30
+
31
+ Because step 4 only re-orders, the byte count and the sum of the bytes are unchanged, so
32
+ `BodyLength` and `CheckSum` remain correct without being recomputed. The only ordering
33
+ the wire genuinely constrains is honoured: a repeating group run is atomic and moves as a
34
+ block, and the delimiter stays first inside each instance.
35
+
36
+ ## What `--russian-doll n` means
37
+
38
+ A **site** is one component made non-adjacent within its level. Depth `n` asks for `n`
39
+ sites along one path into the message. Each site records its **structure depth** — how
40
+ many group instance bodies enclose it — and that number turns out to be the whole story.
41
+
42
+ ## The measurement — repo44
43
+
44
+ Ten scenarios against `repo44`, forty seeds each, doll depths 1 to 3: 1,200 cases.
45
+
46
+ | Sites at | Cases | Scattered parse matches the oracle |
47
+ | --- | --- | --- |
48
+ | structure depth 0 (message body) | 749 | **749** |
49
+ | structure depth 1 (inside a group instance) | 238 | **0** |
50
+ | no scatterable site in the message | 213 | — |
51
+
52
+ Two things stand out.
53
+
54
+ **The shipped repair holds completely at depth 0.** Every case the generator could
55
+ construct with a component scattered in the message body round-tripped exactly, across
56
+ equities, futures, options, bonds, spreads, market data and allocations. Eight years
57
+ without a complaint looks less like luck than it did.
58
+
59
+ **Nothing at depth 1 survives, and nothing at depth 1 is rejected either.** Not one case
60
+ in 238 produced a parse error. The message is accepted, `invalid()`, `missing()` and
61
+ `undefinedForMsg()` all report nothing wrong, and the application is handed a component
62
+ with fields missing. This is the outcome the design note calls strictly worse than the
63
+ bug being fixed, and it is what actually happens today.
64
+
65
+ A depth-1 case reduced to its essentials — an ICE Brent calendar spread whose
66
+ `InstrumentLeg` is scattered inside its leg group instance:
67
+
68
+ ```
69
+ canonical ...|555=2|600=BRN|607=217|611=20260822|623=1|624=1|539=1|...
70
+ scattered ...|555=2|600=BRN|539=1|524=NPID-60012|566=71.32|607=217|611=20260822|623=1|624=1|...
71
+
72
+ InstrumentLeg.LegProduct canonical 217 scattered absent
73
+ InstrumentLeg.LegMaturityDate canonical 20260822 scattered absent
74
+ InstrumentLeg.LegRatioQty canonical 1 scattered absent
75
+ InstrumentLeg.LegSide canonical 1 scattered absent
76
+ ```
77
+
78
+ On `repo44` the loss is always a subset — no case produced a *wrong* value, only a
79
+ missing one. That does not survive contact with the next dictionary.
80
+
81
+ ## The measurement — qf50sp2
82
+
83
+ The same sweep against the QuickFIX FIX50SP2 dictionary, 750 cases, is a different and
84
+ less comfortable picture.
85
+
86
+ | Sites at | Contended tag on the wire | Cases | Match the oracle |
87
+ | --- | --- | --- | --- |
88
+ | structure depth 0 | yes | 319 | 174 |
89
+ | structure depth 0 | no | 181 | 176 |
90
+ | structure depth 1 | yes | 164 | 36 |
91
+ | structure depth 1 | no | 62 | 0 |
92
+
93
+ **Depth 0 is no longer safe.** 150 of 500 message-body cases produce a different object,
94
+ and 145 of those 150 are the collision phase 0 predicted. The QuickFIX FIX50SP2 dictionary
95
+ declares `Currency(15)` inside `Instrument` *and* again beside it at message level, in 57
96
+ places. When `Instrument` arrives scattered, the shipped repair attributes the message
97
+ level `Currency` to `Instrument`:
98
+
99
+ ```
100
+ Instrument.Currency canonical absent scattered "USD"
101
+ ```
102
+
103
+ That is a different failure from the depth-1 one, and a worse one. At depth 1 the engine
104
+ loses a field, which a careful application can notice. Here it **invents** one that looks
105
+ entirely legitimate. This is the shipped repair, in the library today, on a dictionary the
106
+ library ships — pinned by
107
+ [`src/test/ascii/fragment-contention.test.ts`](../src/test/ascii/fragment-contention.test.ts).
108
+
109
+ It also settles open decision 1 in the design note from the evidence rather than from
110
+ taste: the collision map is not a nicety for the generalisation, it is missing from the
111
+ repair that already exists.
112
+
113
+ ## The true russian doll does exist — just not in 4.4
114
+
115
+ **Standard FIX 4.4 almost never offers a component nested inside another component where
116
+ both own two or more tags of their own.** The components inside `Instrument` are group
117
+ wrappers owning a single `NoXXX`, so the literal doll of the design note —
118
+ `SecurityXLinkage` scattered within a scattered `Instrument` — has no instance in `repo44`
119
+ at all. The reachable second level there is a component scattered inside a *group
120
+ instance*: a different axis, the same defect, since detection is populated only while
121
+ `structureStack.length === 1`.
122
+
123
+ FIX 5.0SP2 does have it. `Instrument.PricingDateTime` and `Instrument.OptionExercise` are
124
+ components inside `Instrument` carrying several tags each, and every generated case that
125
+ scattered one inside a scattered `Instrument` produced a wrong object — at structure depth
126
+ 0, where the repair is supposed to work. These are the remaining 5 depth-0 failures that
127
+ the `Currency` collision does not explain.
128
+
129
+ So phase 3 has two independent things to handle, and a dictionary to test each against:
130
+ recursion into nested components (5.0SP2) and repair below the message body (either).
131
+
132
+ ## Which shapes a dictionary can express
133
+
134
+ The cases with no site at all are not a failure of the tool. `InstrmtMDReqGrp` in FIX 4.4
135
+ contains nothing but `Instrument`, so there is no sibling field to interleave and the
136
+ component cannot be made non-adjacent. Worth knowing before designing a repair for a shape
137
+ the wire cannot carry.
138
+
139
+ ## The corpus
140
+
141
+ `data/corpus/` holds edge cases as **bytes**, not as recipes. One directory per case:
142
+
143
+ ```
144
+ data/corpus/<name>/case.json metadata, the scatter plan, and the expected outcome
145
+ data/corpus/<name>/canonical.txt the contiguous encoding - the oracle
146
+ data/corpus/<name>/scattered.txt the same bytes, legally re-ordered
147
+ ```
148
+
149
+ The generator is how a case is *found*; it is not what a case *is*. A seed reproduces a
150
+ message only while the generator is unchanged — move one probability in the value
151
+ conventions and the random stream reshuffles, so every seed yields a different message and
152
+ every recorded expectation quietly stops describing what it was written for. A corpus that
153
+ has to be regenerated to be read is not a corpus. `case.json` records the seed under
154
+ `origin` as provenance, not as a way to rebuild.
155
+
156
+ Each entry records not just *whether* the engine copes but *how* it fails, so a change in
157
+ the shape of the damage fails the test too:
158
+
159
+ | outcome | meaning |
160
+ | --- | --- |
161
+ | `round-trips` | the scattered ordering parses to the same object |
162
+ | `loses-fields` | parses with fields dropped; every value present is still correct |
163
+ | `mis-attributes` | parses, and carries a value the message never said — the dangerous one |
164
+ | `rejected` | refused outright, which for a legal ordering is a defect, but a loud one |
165
+
166
+ ### What is in it
167
+
168
+ | case | dictionary | outcome |
169
+ | --- | --- | --- |
170
+ | `instrument-scattered-in-body` | repo44 | round-trips |
171
+ | `equity-fill-instrument-scattered` | repo44 | round-trips |
172
+ | `bond-instrument-scattered` | repo44 | round-trips |
173
+ | `allocation-instrument-scattered` | repo44 | round-trips |
174
+ | `leg-scattered-in-group-instance` | repo44 | loses-fields |
175
+ | `md-entry-instrument-scattered` | repo44 | loses-fields |
176
+ | `currency-inside-instrument` | qf50sp2 | round-trips |
177
+ | `currency-claimed-by-sibling` | qf50sp2 | **mis-attributes** |
178
+ | `nested-component-in-instrument` | qf50sp2 | loses-fields |
179
+
180
+ Four cases where the engine is right, four where it drops data, one where it invents data —
181
+ across two dictionaries, at both structure depths, in five message types.
182
+
183
+ ### Growing it
184
+
185
+ ```
186
+ npm run doll -- --scenario=spread-trade-capture --russian-doll=2 --seed=1 --as-of=2026-01-15T10:30:00Z --corpus-add=my-case --note="what this shows"
187
+ ```
188
+
189
+ The tool prints what it found, writes the three files, and tells you what outcome it
190
+ recorded. **Read it before committing** — the recorded expectation is an assertion about
191
+ this engine, and nothing checks that a human agreed with it.
192
+
193
+ A case whose outcome is not `round-trips` is a defect described precisely enough that
194
+ fixing it will fail this test. That is the point. When one is fixed, update the entry
195
+ rather than deleting it.
196
+
197
+ ## The tests
198
+
199
+ Three files, doing three different jobs.
200
+
201
+ [`corpus.test.ts`](../src/test/ascii/corpus.test.ts) walks `data/corpus/` and asserts each
202
+ entry still behaves as recorded, down to which leaf paths are missing or added. It also
203
+ asserts the corpus stays worth having — non-empty, covering more than one dictionary, both
204
+ structure depths, and all three of working / losing / inventing.
205
+
206
+ [`generated-scatter.test.ts`](../src/test/ascii/generated-scatter.test.ts) is breadth. It
207
+ sweeps 400 freshly generated cases on every run — 272 message-body sites over 8 scenarios,
208
+ 62 below-body sites over 4 — and asserts the *rule*: everything at structure depth 0 round
209
+ trips, nothing below it does, and nothing is ever rejected. Every assertion is over a
210
+ counted population with a floor, so a generator that stopped producing sites fails the
211
+ suite instead of passing it quietly.
212
+
213
+ > An earlier version of that file used `test.each` with an early `return` for cases the
214
+ > generator could not produce. Eight of its forty tests passed while asserting nothing at
215
+ > all. Counted populations with floors are the fix, and the lesson generalises: a test that
216
+ > can skip itself is a test that can stop testing.
217
+
218
+ [`fragment-contention.test.ts`](../src/test/ascii/fragment-contention.test.ts) proves the
219
+ `qf50sp2` `Currency` defect is systemic rather than one unlucky message, and that it fires
220
+ only where the collision is declared: every value the engine invents is that one contended
221
+ tag, and a message not carrying it is never harmed.
222
+
223
+ Both non-round-trip groups are written to be **inverted, not deleted**, when the repair is
224
+ generalised. They are the acceptance criteria for phase 3.
225
+
226
+ ## Reproducing a case
227
+
228
+ A seed fixes every choice the generator makes, but dates are read off the wall clock
229
+ unless anchored, so both are needed:
230
+
231
+ ```
232
+ npm run doll -- --scenario=spread-trade-capture --russian-doll=2 \
233
+ --seed=42 --as-of=2026-01-15T10:30:00Z --unit
234
+ ```
235
+
236
+ `--unit` writes `canonical.txt`, `fix.txt` (the scattered ordering), `object.json`,
237
+ `plan.json` and `case.txt` into the working directory.
@@ -1,12 +1,18 @@
1
1
  # Scattered components — analysis and plan of attack
2
2
 
3
- **Status: analysis, with phase 0 landed.** Nothing in the parser has changed. This note
4
- sets out a generalisation of the fragmented component repair already shipped, why it is
5
- believed tractable, what it would cost, and the order the work would land in. It is
3
+ **Status: analysis, with phases 0 and 0b landed.** Nothing in the parser has changed. This
4
+ note sets out a generalisation of the fragmented component repair already shipped, why it
5
+ is believed tractable, what it would cost, and the order the work would land in. It is
6
6
  written to port — the same shape applies to cspurefix.
7
7
 
8
- There is no urgency behind it. The library has been in use for eight years and exactly one
9
- counterparty has produced the shape that motivated the existing repair. This is the
8
+ Phase 0b has since measured the problem rather than reasoning about it, and turned up one
9
+ thing this note did not expect: **the repair that already shipped mis-attributes a
10
+ contended tag on the `qf50sp2` dictionary**, inventing an `Instrument.Currency` the
11
+ counterparty never sent. That is a live defect rather than a generalisation, and open
12
+ decision 1 below is really its fix. See [`generated-cases.md`](generated-cases.md).
13
+
14
+ There is still no urgency behind it. The library has been in use for eight years and
15
+ exactly one counterparty has produced the shape that motivated the existing repair. This is the
10
16
  argument being written down while it is fresh, not a queue-jumping defect.
11
17
 
12
18
  ## Where this starts
@@ -204,6 +210,52 @@ committee is, and the broker dialects are the ones most likely to also send the
204
210
  messages this whole exercise is about. On the evidence here the collision map is not a
205
211
  defensive nicety at the edge of the design; it is load-bearing.
206
212
 
213
+ ### Phase 0b — how bad is it, measured *(landed)*
214
+
215
+ Phase 0 asked whether the dictionary permits a repair. This asks what the engine does
216
+ today when it meets the shape, which until now was reasoned about rather than observed.
217
+
218
+ `src/generator/` builds a message that reads like traffic, encodes it canonically, then
219
+ permutes the body tokens into a legal but scattered ordering. The canonical parse is the
220
+ oracle, so no expectation is hand written. Two dictionaries, ~2,000 cases, three findings —
221
+ and the first one is not about this plan at all.
222
+
223
+ **1. The shipped repair is already wrong on `qf50sp2`, and phase 0 said why.** That
224
+ dictionary declares `Currency(15)` inside `Instrument` and again beside it at message
225
+ level. When `Instrument` arrives scattered, the repair claims the *sibling's* tag:
226
+ `Instrument.Currency` appears in the parsed object carrying a value the counterparty sent
227
+ at message level. 145 of 500 message-body cases. This is not a failure of the
228
+ generalisation; it is a defect in the code that shipped, on a dictionary this library
229
+ distributes, and it moves open decision 1 from a design preference to a bug fix.
230
+
231
+ **2. At depth 1 nothing survives, and nothing is rejected.** On `repo44`, 749 of 749
232
+ message-body cases round trip exactly, and 0 of 238 cases one level down. Every one of the
233
+ 238 parsed cleanly, reported no missing or invalid fields, and handed the application a
234
+ component with fields dropped. Silently wrong data, exactly as feared.
235
+
236
+ **3. The literal russian doll is a FIX 5.0 shape, not a 4.4 one.** Standard FIX 4.4 has
237
+ essentially no component nested inside another component where both own two or more tags —
238
+ the components inside `Instrument` are group wrappers owning one `NoXXX`. FIX 5.0SP2 does:
239
+ `Instrument.PricingDateTime`, `Instrument.OptionExercise`. Every case scattering one of
240
+ those inside a scattered `Instrument` produced a wrong object, at structure depth 0.
241
+
242
+ So phase 3 has two separable obligations with a dictionary to test each against — recursion
243
+ into nested components (`qf50sp2`) and repair below the message body (either) — and phase 0
244
+ has produced a defect that need not wait for either.
245
+
246
+ See [`generated-cases.md`](generated-cases.md) for method, tables and worked examples.
247
+
248
+ Nine of these cases are frozen as bytes in `data/corpus/`, each recording not just whether
249
+ the engine copes but *how* it fails — `round-trips`, `loses-fields` or `mis-attributes`,
250
+ down to the leaf paths involved. Four are cases the engine gets right, four where it drops
251
+ data, one where it invents it. The corpus is meant to grow: `--corpus-add` on the generator
252
+ keeps whatever the tool turns up next.
253
+
254
+ Three tests: `corpus.test.ts` walks the frozen cases, `generated-scatter.test.ts` sweeps
255
+ 400 fresh ones each run and asserts the rule over counted populations, and
256
+ `fragment-contention.test.ts` shows the `Currency` mis-attribution is systemic. The
257
+ non-round-trip groups are written to be inverted, not deleted, when the repair lands.
258
+
207
259
  ### Phase 1 — the accessor seam
208
260
 
209
261
  Give `SegmentDescription` `owns(pos)` and `forEachPosition(cb)`; move the four `msg-view`
@@ -267,6 +319,9 @@ protocol errors into wrong objects.
267
319
  1. Should a collision found by phase 0 refuse repair for the colliding pair only, or for
268
320
  the whole set containing them? The `qf50sp2` result argues for the pair — refusing all
269
321
  of `NewOrderSingle` over one contended tag would give up a great deal for very little.
322
+ **No longer hypothetical:** phase 0b showed the shipped repair claiming the contended
323
+ `Currency` in 145 of 500 cases, so whichever answer is chosen has to be applied to the
324
+ existing repair and not only to the generalisation.
270
325
  2. Should the collision map be computed once at dictionary load, or lazily on first repair?
271
326
  3. Should a dictionary with collisions say so at load time? An operator onboarding a broker
272
327
  dialect would want to know before a live session does, and phase 0 is cheap enough to
@@ -1,62 +1,62 @@
1
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:21.385|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=106|
2
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:45:21.389|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=158|
3
- 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260816-15:45:21.390|7=1|16=1|10=237|
4
- 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260816-15:45:21.393|122=20260816-15:45:21.393|123=Y|36=2|10=111|
5
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:45:23.393|58=test_server confirming logout|10=089|
6
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:25.852|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=109|
7
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:45:25.853|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=158|
8
- 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260816-15:45:25.853|7=1|16=0|10=244|
9
- 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260816-15:45:25.855|122=20260816-15:45:25.855|123=Y|36=2|10=125|
10
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:45:27.855|58=test_server confirming logout|10=096|
11
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:30.443|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=101|
12
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:45:30.445|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=151|
13
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260816-15:45:30.445|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
14
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:30.879|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=114|
15
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:45:30.880|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=154|
16
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:45:31.883|58=test_server confirming logout|10=092|
17
- 8=FIX.4.4|9=0000101|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260816-15:45:32.446|58=test_client initiate logout|10=106|
18
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:34.248|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
19
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:45:34.249|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=157|
20
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:45:35.251|58=test_server confirming logout|10=085|
21
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:37.616|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=110|
22
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:45:37.618|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=160|
23
- 8=FIX.4.4|9=0000101|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260816-15:45:38.622|58=test_client initiate logout|10=105|
24
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:40.985|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=113|
25
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260816-15:45:40.986|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=166|
26
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:45:41.988|58=test_server confirming logout|10=099|
27
- 8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:44.346|98=0|141=Y|553=js-client|554=pwd-client|10=176|
28
- 8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:45:44.347|45=1|372=A|373=1|58=msgType A missing required tag 108|10=010|
29
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:48.795|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=120|
30
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:45:48.797|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=170|
31
- 8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:45:48.798|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=172|
32
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260816-15:45:50.798|58=test_server confirming logout|10=099|
33
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:53.367|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=111|
34
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:45:53.369|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=161|
35
- 8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:45:53.371|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=076|
36
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260816-15:45:55.371|58=test_server confirming logout|10=091|
37
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:45:57.369|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=117|
38
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:46:01.748|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
39
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:46:01.749|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=157|
40
- 8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:46:01.750|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=028|
41
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260816-15:46:03.751|58=test_server confirming logout|10=087|
42
- 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:46:06.108|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=053|
43
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:46:06.109|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=152|
44
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260816-15:46:08.113|112=Sun, 16 Aug 2026 15:46:08 GMT|10=158|
45
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260816-15:46:10.116|112=Sun, 16 Aug 2026 15:46:10 GMT|10=148|
46
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:46:12.109|58=test_server confirming logout|10=083|
47
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:46:14.465|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
48
- 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:46:14.466|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=107|
49
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:46:16.471|112=Sun, 16 Aug 2026 15:46:16 GMT|10=163|
50
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260816-15:46:18.473|112=Sun, 16 Aug 2026 15:46:18 GMT|10=170|
51
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260816-15:46:20.478|112=Sun, 16 Aug 2026 15:46:20 GMT|10=162|
52
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260816-15:46:22.468|58=test_server confirming logout|10=095|
53
- 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:46:25.026|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=053|
54
- 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:46:25.027|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=105|
55
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260816-15:46:27.031|112=Sun, 16 Aug 2026 15:46:27 GMT|10=159|
56
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260816-15:46:29.035|112=Sun, 16 Aug 2026 15:46:29 GMT|10=168|
57
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:46:30.037|112=Sun, 16 Aug 2026 15:46:30 GMT|10=153|
58
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260816-15:46:31.039|112=Sun, 16 Aug 2026 15:46:31 GMT|10=159|
59
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260816-15:46:33.029|58=test_server confirming logout|10=088|
60
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260816-15:48:24.476|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=113|
61
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260816-15:48:24.482|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=158|
62
- 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260816-15:48:25.486|58=test_server confirming logout|10=097|
1
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:34:32.252|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
2
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:34:32.257|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=156|
3
+ 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260827-17:34:32.258|7=1|16=1|10=244|
4
+ 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260827-17:34:32.262|122=20260827-17:34:32.261|123=Y|36=2|10=108|
5
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:34:34.261|58=test_server confirming logout|10=087|
6
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:34:36.884|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=118|
7
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:34:36.886|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=168|
8
+ 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260827-17:34:36.887|7=1|16=0|10=255|
9
+ 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260827-17:34:36.889|122=20260827-17:34:36.889|123=Y|36=2|10=147|
10
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:34:38.888|58=test_server confirming logout|10=106|
11
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:34:41.290|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=105|
12
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:34:41.292|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=155|
13
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260827-17:34:41.292|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
14
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:34:41.720|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
15
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:34:41.721|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=152|
16
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:34:42.723|58=test_server confirming logout|10=089|
17
+ 8=FIX.4.4|9=0000101|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260827-17:34:43.293|58=test_client initiate logout|10=110|
18
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:34:45.431|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=106|
19
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:34:45.432|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=155|
20
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:34:46.434|58=test_server confirming logout|10=092|
21
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:34:49.031|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=106|
22
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:34:49.032|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=155|
23
+ 8=FIX.4.4|9=0000101|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260827-17:34:50.033|58=test_client initiate logout|10=097|
24
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:34:52.431|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
25
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260827-17:34:52.432|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=157|
26
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:34:53.434|58=test_server confirming logout|10=090|
27
+ 8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:34:55.818|98=0|141=Y|553=js-client|554=pwd-client|10=184|
28
+ 8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:34:55.819|45=1|372=A|373=1|58=msgType A missing required tag 108|10=018|
29
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:35:00.292|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
30
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:35:00.294|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=153|
31
+ 8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:35:00.295|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=155|
32
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260827-17:35:02.295|58=test_server confirming logout|10=091|
33
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:35:04.876|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=115|
34
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:35:04.877|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=164|
35
+ 8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:35:04.878|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=087|
36
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260827-17:35:06.878|58=test_server confirming logout|10=102|
37
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:35:08.876|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=119|
38
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:35:13.271|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
39
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:35:13.272|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=153|
40
+ 8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:35:13.273|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=033|
41
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260827-17:35:15.275|58=test_server confirming logout|10=093|
42
+ 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:35:17.650|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=059|
43
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:35:17.651|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=158|
44
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260827-17:35:19.656|112=Thu, 27 Aug 2026 17:35:19 GMT|10=173|
45
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260827-17:35:21.658|112=Thu, 27 Aug 2026 17:35:21 GMT|10=162|
46
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:35:23.654|58=test_server confirming logout|10=092|
47
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:35:26.141|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
48
+ 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:35:26.146|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=107|
49
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:35:28.150|112=Thu, 27 Aug 2026 17:35:28 GMT|10=162|
50
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260827-17:35:30.153|112=Thu, 27 Aug 2026 17:35:30 GMT|10=152|
51
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260827-17:35:32.156|112=Thu, 27 Aug 2026 17:35:32 GMT|10=160|
52
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260827-17:35:34.148|58=test_server confirming logout|10=095|
53
+ 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:35:36.520|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=056|
54
+ 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:35:36.521|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=108|
55
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260827-17:35:38.525|112=Thu, 27 Aug 2026 17:35:38 GMT|10=170|
56
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260827-17:35:40.526|112=Thu, 27 Aug 2026 17:35:40 GMT|10=158|
57
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:35:41.531|112=Thu, 27 Aug 2026 17:35:41 GMT|10=155|
58
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260827-17:35:42.529|112=Thu, 27 Aug 2026 17:35:42 GMT|10=166|
59
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260827-17:35:44.522|58=test_server confirming logout|10=090|
60
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260827-17:37:54.447|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=116|
61
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260827-17:37:54.455|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=163|
62
+ 8=FIX.4.4|9=0000103|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260827-17:37:55.459|58=test_server confirming logout|10=102|