-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.fusa-reqs.json
More file actions
1165 lines (1165 loc) · 54.8 KB
/
Copy path.fusa-reqs.json
File metadata and controls
1165 lines (1165 loc) · 54.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{
"version": "1",
"requirements": [
{
"id": "REQ-LIN-001",
"title": "ValidateFrame rejects ID > 0x3F",
"description": "ValidateFrame shall return an error when Frame.ID exceeds MaxID (0x3F).",
"asil": "ASIL-B",
"rationale": "SG-02: prevents an invalid identifier from being transmitted or processed.",
"tags": ["core", "validation"]
},
{
"id": "REQ-LIN-002",
"title": "ValidateFrame rejects empty data",
"description": "ValidateFrame shall return an error when Frame.Data has zero length.",
"asil": "ASIL-B",
"rationale": "SG-01: a zero-length LIN frame is malformed and has no defined wire format.",
"tags": ["core", "validation"]
},
{
"id": "REQ-LIN-003",
"title": "ValidateFrame rejects oversized data",
"description": "ValidateFrame shall return an error when len(Frame.Data) exceeds MaxDataLen (8).",
"asil": "ASIL-B",
"rationale": "SG-01: LIN 2.x limits payloads to 8 bytes; exceeding this is a protocol violation.",
"tags": ["core", "validation"]
},
{
"id": "REQ-LIN-004",
"title": "ProtectID parity bit P0",
"description": "ProtectID shall compute P0 = ID0 XOR ID1 XOR ID2 XOR ID4 and place it in bit 6 of the returned PID byte.",
"asil": "ASIL-B",
"rationale": "SG-01: P0 is mandated by LIN 2.x §2.3.1 for frame identifier error detection.",
"tags": ["core", "pid"]
},
{
"id": "REQ-LIN-005",
"title": "ProtectID parity bit P1",
"description": "ProtectID shall compute P1 = NOT(ID1 XOR ID3 XOR ID4 XOR ID5) and place it in bit 7 of the returned PID byte.",
"asil": "ASIL-B",
"rationale": "SG-01: P1 is mandated by LIN 2.x §2.3.1 for frame identifier error detection.",
"tags": ["core", "pid"]
},
{
"id": "REQ-LIN-006",
"title": "VerifyPID accepts correct parity",
"description": "VerifyPID shall return the raw 6-bit ID and nil error when the parity bits in the PID are correct.",
"asil": "ASIL-B",
"rationale": "SG-01: correct PID passes without false rejection.",
"tags": ["core", "pid"]
},
{
"id": "REQ-LIN-007",
"title": "VerifyPID rejects incorrect parity",
"description": "VerifyPID shall return an error when the parity bits in the supplied PID do not match those computed by ProtectID for the embedded 6-bit ID.",
"asil": "ASIL-B",
"rationale": "SG-01: a corrupted PID must be detected and rejected.",
"tags": ["core", "pid"]
},
{
"id": "REQ-LIN-008",
"title": "Classic checksum covers data bytes only",
"description": "CalcChecksum with ClassicChecksum shall sum the data bytes only (excluding PID) using inverted carry-around 8-bit addition.",
"asil": "ASIL-B",
"rationale": "SG-02: LIN 1.x classic checksum algorithm per LIN spec §2.3.2.",
"tags": ["core", "checksum"]
},
{
"id": "REQ-LIN-009",
"title": "Enhanced checksum includes PID",
"description": "CalcChecksum with EnhancedChecksum shall include the PID byte in the sum alongside the data bytes, using inverted carry-around 8-bit addition.",
"asil": "ASIL-B",
"rationale": "SG-02: LIN 2.x enhanced checksum algorithm per LIN spec §2.3.2.",
"tags": ["core", "checksum"]
},
{
"id": "REQ-LIN-010",
"title": "Checksum carry-around inversion",
"description": "CalcChecksum shall apply carry-around addition (sum > 0xFF wraps by subtracting 0xFF, not 0x100) and invert the final sum (0xFF - sum).",
"asil": "ASIL-B",
"rationale": "SG-02: correct carry-around is required for interoperability with LIN hardware.",
"tags": ["core", "checksum"]
},
{
"id": "REQ-LIN-011",
"title": "Bus.Publish registers slave response",
"description": "Bus.Publish shall register a response payload for the given frame ID so that the next SendHeader for that ID returns the registered data.",
"asil": "ASIL-B",
"rationale": "Core interface contract: slave response registration.",
"tags": ["core", "interface"]
},
{
"id": "REQ-LIN-012",
"title": "Bus.Subscribe delivers matching frames",
"description": "Bus.Subscribe shall return a channel that receives every Frame whose ID matches any supplied Filter, or all Frames when no filters are supplied.",
"asil": "ASIL-B",
"rationale": "Core interface contract: frame observation.",
"tags": ["core", "interface"]
},
{
"id": "REQ-LIN-013",
"title": "MasterBus.SendHeader returns synthesised Frame",
"description": "MasterBus.SendHeader shall trigger a frame exchange for the given ID and return the resulting Frame including correct PID and checksum.",
"asil": "ASIL-B",
"rationale": "Core interface contract: master-driven frame exchange.",
"tags": ["core", "interface"]
},
{
"id": "REQ-LIN-014",
"title": "MasterBus.SendHeader returns ErrNoResponse",
"description": "MasterBus.SendHeader shall return ErrNoResponse when no slave has registered a response for the requested frame ID.",
"asil": "ASIL-B",
"rationale": "Absence of a slave response is a distinct, detectable condition.",
"tags": ["core", "interface"]
},
{
"id": "REQ-LIN-015",
"title": "ValidateFrame accepts ID = 0x3F",
"description": "ValidateFrame shall return nil error when Frame.ID equals MaxID (0x3F), the boundary maximum.",
"asil": "ASIL-B",
"rationale": "SG-05: boundary condition — ID 0x3F is a valid LIN identifier and must not be falsely rejected.",
"tags": ["core", "validation", "boundary"]
},
{
"id": "REQ-LIN-016",
"title": "ValidateFrame accepts data length 1",
"description": "ValidateFrame shall return nil error when len(Frame.Data) equals 1, the boundary minimum.",
"asil": "ASIL-B",
"rationale": "SG-05: boundary condition — a 1-byte payload is the smallest valid LIN frame.",
"tags": ["core", "validation", "boundary"]
},
{
"id": "REQ-LIN-017",
"title": "ValidateFrame accepts data length 8",
"description": "ValidateFrame shall return nil error when len(Frame.Data) equals MaxDataLen (8), the boundary maximum.",
"asil": "ASIL-B",
"rationale": "SG-05: boundary condition — an 8-byte payload is the largest valid LIN frame.",
"tags": ["core", "validation", "boundary"]
},
{
"id": "REQ-LIN-018",
"title": "ProtectID preserves lower 6 bits",
"description": "For any id in 0x00–0x3F, (ProtectID(id) & 0x3F) shall equal id.",
"asil": "ASIL-B",
"rationale": "SG-01: the PID must carry the original ID in bits 0–5 so receivers can extract it.",
"tags": ["core", "pid", "boundary"]
},
{
"id": "REQ-LIN-019",
"title": "Bus.Publish with nil removes registration",
"description": "Bus.Publish(id, nil) shall remove any previously registered response for id; subsequent SendHeader for that id shall return ErrNoResponse.",
"asil": "ASIL-B",
"rationale": "Slaves must be able to withdraw a response (e.g., entering sleep mode) without restarting the bus.",
"tags": ["core", "interface"]
},
{
"id": "REQ-LIN-020",
"title": "Bus.Subscribe with All=true delivers every frame",
"description": "A Filter with All=true shall match every Frame regardless of ID; Bus.Subscribe with such a filter delivers all frames on the bus.",
"asil": "ASIL-B",
"rationale": "Bus monitoring and logging require all-frame visibility.",
"tags": ["core", "interface", "filter"]
},
{
"id": "REQ-LIN-021",
"title": "ErrNoResponse is a non-nil sentinel error",
"description": "ErrNoResponse shall be a non-nil error value that callers can test with errors.Is(err, lin.ErrNoResponse).",
"asil": "ASIL-B",
"rationale": "Callers must distinguish absent-slave from other error conditions; a sentinel enables precise error handling.",
"tags": ["core", "error"]
},
{
"id": "REQ-VIRT-001",
"title": "New returns initialised bus",
"description": "virtual.New shall return a Bus with an empty response table, no subscribers, and nil error.",
"asil": "ASIL-B",
"rationale": "Deterministic initial state required for predictable test behaviour.",
"tags": ["virtual"]
},
{
"id": "REQ-VIRT-002",
"title": "Publish stores response",
"description": "Publish(id, data) shall atomically store a copy of data as the response for id, replacing any prior entry.",
"asil": "ASIL-B",
"rationale": "A stale or partial response must never be served; atomic replacement ensures consistency.",
"tags": ["virtual", "publish"]
},
{
"id": "REQ-VIRT-003",
"title": "Publish(nil) removes response",
"description": "Publish(id, nil) shall remove the registered response for id so that subsequent SendHeader returns ErrNoResponse.",
"asil": "ASIL-B",
"rationale": "Slaves must be able to withdraw a response (e.g., during sleep mode).",
"tags": ["virtual", "publish"]
},
{
"id": "REQ-VIRT-004",
"title": "Publish rejects ID > MaxID",
"description": "Publish shall return an error when id > 0x3F.",
"asil": "ASIL-B",
"rationale": "REQ-LIN-001 boundary enforced at the transport layer.",
"tags": ["virtual", "validation"]
},
{
"id": "REQ-VIRT-005",
"title": "Publish after Close returns error",
"description": "Publish shall return an error when called after Close.",
"asil": "ASIL-B",
"rationale": "Operations on a closed bus must fail deterministically, not silently succeed.",
"tags": ["virtual", "lifecycle"]
},
{
"id": "REQ-VIRT-006",
"title": "SendHeader computes correct PID",
"description": "SendHeader shall set Frame.ID to the requested id and compute the PID using ProtectID before broadcasting.",
"asil": "ASIL-B",
"rationale": "SG-01: the PID in the synthesised frame must reflect ProtectID output.",
"tags": ["virtual", "pid"]
},
{
"id": "REQ-VIRT-007",
"title": "SendHeader computes correct checksum",
"description": "SendHeader shall set Frame.Checksum using CalcChecksum with the response's ChecksumType.",
"asil": "ASIL-B",
"rationale": "SG-02: checksum in the synthesised frame must be verifiable by subscribers.",
"tags": ["virtual", "checksum"]
},
{
"id": "REQ-VIRT-008",
"title": "SendHeader broadcasts to subscribers",
"description": "SendHeader shall deliver the synthesised Frame to every subscriber whose filter matches the frame ID.",
"asil": "ASIL-B",
"rationale": "All observers must receive the same frame data.",
"tags": ["virtual", "broadcast"]
},
{
"id": "REQ-VIRT-009",
"title": "SendHeader returns ErrNoResponse",
"description": "SendHeader shall return ErrNoResponse when no response is registered for the requested id.",
"asil": "ASIL-B",
"rationale": "REQ-LIN-014 enforced at the virtual transport layer.",
"tags": ["virtual", "error"]
},
{
"id": "REQ-VIRT-010",
"title": "SendHeader rejects ID > MaxID",
"description": "SendHeader shall return an error when id > 0x3F.",
"asil": "ASIL-B",
"rationale": "REQ-LIN-001 boundary enforced at the transport layer.",
"tags": ["virtual", "validation"]
},
{
"id": "REQ-VIRT-011",
"title": "Subscribe exact filter isolates by ID",
"description": "A subscriber with Filter{ID: x} shall not receive frames whose ID differs from x.",
"asil": "ASIL-B",
"rationale": "SG-05: cross-frame-ID leakage would cause application code to act on the wrong data.",
"tags": ["virtual", "filter", "isolation"]
},
{
"id": "REQ-VIRT-012",
"title": "Subscribe All filter receives every frame",
"description": "A subscriber with Filter{All: true} shall receive every frame regardless of ID.",
"asil": "ASIL-B",
"rationale": "Bus monitoring requires all-frames subscription.",
"tags": ["virtual", "filter"]
},
{
"id": "REQ-VIRT-013",
"title": "Full subscriber channel drops frames without blocking",
"description": "When a subscriber channel is full, SendHeader shall drop the frame for that subscriber rather than blocking.",
"asil": "ASIL-B",
"rationale": "A slow subscriber must not stall the master schedule; mirrors real LIN hardware behaviour.",
"tags": ["virtual", "backpressure"]
},
{
"id": "REQ-VIRT-014",
"title": "Multiple subscribers each receive independently",
"description": "When multiple subscribers match a frame, each shall receive a copy independently.",
"asil": "ASIL-B",
"rationale": "Independent observers must not interfere with each other.",
"tags": ["virtual", "isolation"]
},
{
"id": "REQ-VIRT-015",
"title": "Close closes all subscriber channels",
"description": "Close shall close every subscriber channel so that range-loops over those channels terminate.",
"asil": "ASIL-B",
"rationale": "Resource cleanup; prevents goroutine leaks in consumers.",
"tags": ["virtual", "lifecycle"]
},
{
"id": "REQ-VIRT-016",
"title": "Close is idempotent",
"description": "Close shall return nil and have no observable effect when called a second time.",
"asil": "ASIL-B",
"rationale": "Idempotent Close prevents double-close panics in defer chains.",
"tags": ["virtual", "lifecycle"]
},
{
"id": "REQ-VIRT-017",
"title": "SendHeader after Close returns error",
"description": "SendHeader shall return an error when called after Close.",
"asil": "ASIL-B",
"rationale": "Operations on a closed bus must fail deterministically.",
"tags": ["virtual", "lifecycle"]
},
{
"id": "REQ-VIRT-018",
"title": "Concurrent access is data-race free",
"description": "Publish, SendHeader, Subscribe, and Close may be called concurrently from multiple goroutines without data races.",
"asil": "ASIL-B",
"rationale": "Real-time LIN systems often drive the bus from one goroutine while subscribing from another; a data race would be undefined behaviour.",
"tags": ["virtual", "concurrency"]
},
{
"id": "REQ-VIRT-019",
"title": "Publish stores a defensive copy",
"description": "Publish shall copy the supplied data slice; subsequent mutation of the caller's slice shall not affect the stored response.",
"asil": "ASIL-B",
"rationale": "Shared mutable state between caller and bus would corrupt responses under concurrent use.",
"tags": ["virtual", "safety", "copy"]
},
{
"id": "REQ-LDF-001",
"title": "Parse extracts protocol version",
"description": "Parse shall populate DB.ProtocolVersion from the LIN_protocol_version field.",
"asil": "ASIL-A",
"rationale": "Protocol version determines which checksum algorithm and features apply.",
"tags": ["ldf"]
},
{
"id": "REQ-LDF-002",
"title": "Parse extracts baud rate",
"description": "Parse shall populate DB.Speed (kbps) from the LIN_speed field.",
"asil": "ASIL-A",
"rationale": "Incorrect baud rate causes all frame exchanges to fail.",
"tags": ["ldf"]
},
{
"id": "REQ-LDF-003",
"title": "Parse extracts master node name",
"description": "Parse shall populate DB.MasterNode from the Nodes Master declaration.",
"asil": "ASIL-A",
"rationale": "Master node identity is required for publisher validation.",
"tags": ["ldf", "nodes"]
},
{
"id": "REQ-LDF-004",
"title": "Parse extracts slave node list",
"description": "Parse shall populate DB.SlaveNodes from the Nodes Slaves declaration.",
"asil": "ASIL-A",
"rationale": "Slave list is used to validate frame publisher declarations.",
"tags": ["ldf", "nodes"]
},
{
"id": "REQ-LDF-005",
"title": "Parse populates frame descriptors",
"description": "Parse shall populate frame descriptors (name, ID, publisher, length) from the LDF Frames section.",
"asil": "ASIL-A",
"rationale": "SG-03: incorrect frame descriptor leads to wrong signal decoding.",
"tags": ["ldf", "frame"]
},
{
"id": "REQ-LDF-006",
"title": "Parse populates signal-to-bit-offset mappings",
"description": "Parse shall populate SignalRef entries (signal name and bit offset) for each frame.",
"asil": "ASIL-A",
"rationale": "SG-03: a wrong bit offset produces a shifted signal value.",
"tags": ["ldf", "frame", "signal"]
},
{
"id": "REQ-LDF-007",
"title": "Parse populates signal bit width",
"description": "Parse shall populate Signal.BitWidth from the signal definition in the LDF Signals section.",
"asil": "ASIL-A",
"rationale": "SG-03: wrong bit width truncates or extends the extracted value.",
"tags": ["ldf", "signal"]
},
{
"id": "REQ-LDF-008",
"title": "Parse populates signal publisher",
"description": "Parse shall populate Signal.Publisher from the signal definition.",
"asil": "ASIL-A",
"rationale": "Publisher field identifies which node is responsible for the signal value.",
"tags": ["ldf", "signal"]
},
{
"id": "REQ-LDF-009",
"title": "Decode uses LSB-first Intel byte order",
"description": "Decode shall extract signal values using LSB-first (Intel) bit ordering: bit 0 of the signal maps to bitOffset in the payload.",
"asil": "ASIL-A",
"rationale": "SG-03: wrong byte order inverts every multi-bit signal value.",
"tags": ["ldf", "decode"]
},
{
"id": "REQ-LDF-010",
"title": "Decode returns nil for unknown frame ID",
"description": "Decode shall return nil when the frame ID is not present in the parsed LDF.",
"asil": "ASIL-A",
"rationale": "Caller must be able to distinguish missing-frame from empty-signal-set.",
"tags": ["ldf", "decode"]
},
{
"id": "REQ-LDF-011",
"title": "Parse populates schedule table entries",
"description": "Parse shall populate schedule tables with ScheduleEntry{ID, DelayMs} from the LDF Schedule_tables section.",
"asil": "ASIL-A",
"rationale": "Schedule tables drive master timing; incorrect delay affects bus load.",
"tags": ["ldf", "schedule"]
},
{
"id": "REQ-LDF-012",
"title": "Frame() returns nil for unknown ID",
"description": "DB.Frame() shall return nil when no frame with the given ID was declared in the LDF.",
"asil": "ASIL-A",
"rationale": "Callers must be able to detect missing frame descriptors without panicking.",
"tags": ["ldf", "frame"]
},
{
"id": "REQ-LDF-013",
"title": "Signal() returns nil for unknown name",
"description": "DB.Signal() shall return nil when no signal with the given name was declared in the LDF.",
"asil": "ASIL-A",
"rationale": "Callers must be able to detect missing signal definitions without panicking.",
"tags": ["ldf", "signal"]
},
{
"id": "REQ-LDF-014",
"title": "Parse does not panic on arbitrary input",
"description": "Parse shall return a non-nil DB (possibly empty) and a non-nil error (or nil) for any input; it shall never panic.",
"asil": "ASIL-A",
"rationale": "SG-05: a panic on malformed LDF would crash the application; robust parsing is required.",
"tags": ["ldf", "robustness"]
},
{
"id": "REQ-LDF-015",
"title": "Frames() returns a defensive copy",
"description": "DB.Frames() shall return a new map; mutations to the returned map shall not affect DB's internal frame table.",
"asil": "ASIL-A",
"rationale": "Shared internal state could be corrupted by callers; defensive copy ensures immutability.",
"tags": ["ldf", "copy"]
},
{
"id": "REQ-MASTER-001",
"title": "New returns non-nil Node",
"description": "master.New shall return a non-nil *Node backed by the supplied MasterBus.",
"asil": "ASIL-B",
"rationale": "Callers must not need to nil-check the returned Node.",
"tags": ["master"]
},
{
"id": "REQ-MASTER-002",
"title": "Node.SendHeader delegates to bus",
"description": "Node.SendHeader shall call bus.SendHeader and return the Frame and error unchanged.",
"asil": "ASIL-B",
"rationale": "The master node must not alter or suppress the frame returned by the bus.",
"tags": ["master"]
},
{
"id": "REQ-MASTER-003",
"title": "Run iterates schedule in order",
"description": "Node.Run shall process schedule entries in the order they appear in the schedule table, restarting from the first entry after the last.",
"asil": "ASIL-B",
"rationale": "SG-01: out-of-order header transmission violates the LIN schedule contract.",
"tags": ["master", "schedule"]
},
{
"id": "REQ-MASTER-004",
"title": "Run calls SendHeader for each slot",
"description": "For each schedule slot, Run shall call MasterBus.SendHeader with the slot's frame ID.",
"asil": "ASIL-B",
"rationale": "SG-01: every slot must trigger a frame exchange.",
"tags": ["master", "schedule"]
},
{
"id": "REQ-MASTER-005",
"title": "Run waits slot delay between exchanges",
"description": "Run shall wait slot.DelayMs milliseconds after each frame exchange before processing the next slot.",
"asil": "ASIL-B",
"rationale": "Slot timing enforces bus load constraints.",
"tags": ["master", "timing"]
},
{
"id": "REQ-MASTER-006",
"title": "Run invokes OnFrame on success",
"description": "Run shall call the OnFrame callback with the received Frame when SendHeader succeeds.",
"asil": "ASIL-B",
"rationale": "Application code receives data via OnFrame; omitting the call silently drops data.",
"tags": ["master", "callback"]
},
{
"id": "REQ-MASTER-007",
"title": "Run invokes OnError on failure",
"description": "Run shall call the OnError callback when SendHeader returns a non-nil error.",
"asil": "ASIL-B",
"rationale": "Application code must be notified of absent slave responses.",
"tags": ["master", "callback"]
},
{
"id": "REQ-MASTER-008",
"title": "Run returns on context cancellation",
"description": "Run shall return ctx.Err() when ctx is cancelled or expires.",
"asil": "ASIL-B",
"rationale": "Graceful shutdown requires context propagation.",
"tags": ["master", "lifecycle"]
},
{
"id": "REQ-MASTER-009",
"title": "Run treats an empty schedule as a no-op success",
"description": "Run shall return success immediately (without invoking the bus) when the schedule table is empty, rather than returning an error.",
"asil": "ASIL-B",
"rationale": "RELAY §8.3: an empty schedule table is valid and simply disables scheduled transmission; it is not a programming error, so Run must not fail callers that have not yet configured a schedule.",
"tags": ["master", "validation"]
},
{
"id": "REQ-MASTER-010",
"title": "SetSchedule accepts an empty schedule",
"description": "SetSchedule shall accept a nil or zero-length slice and succeed, disabling scheduled transmission (REQ-MASTER-009) until a non-empty schedule is set.",
"asil": "ASIL-B",
"rationale": "RELAY §8.3: an empty schedule table is valid; SetSchedule must not reject it.",
"tags": ["master", "validation"]
},
{
"id": "REQ-MASTER-011",
"title": "SetSchedule rejects invalid frame ID",
"description": "SetSchedule shall return an error when any entry has ID > MaxID (0x3F).",
"asil": "ASIL-B",
"rationale": "REQ-LIN-001 boundary enforced at schedule configuration time.",
"tags": ["master", "validation"]
},
{
"id": "REQ-MASTER-012",
"title": "SetSchedule stores a defensive copy",
"description": "SetSchedule shall copy the supplied slice; subsequent mutation of the caller's slice shall not affect the active schedule.",
"asil": "ASIL-B",
"rationale": "Shared mutable schedule data could cause non-deterministic slot ordering.",
"tags": ["master", "copy"]
},
{
"id": "REQ-MASTER-013",
"title": "Run continues after per-slot errors",
"description": "Run shall continue to the next schedule slot when SendHeader returns an error (e.g., ErrNoResponse); it shall not abort the schedule.",
"asil": "ASIL-B",
"rationale": "A temporarily absent slave must not halt the entire LIN schedule.",
"tags": ["master", "resilience"]
},
{
"id": "REQ-SLAVE-001",
"title": "New returns ready slave node",
"description": "slave.New shall return a non-nil Node with no registered responses.",
"asil": "ASIL-B",
"rationale": "Deterministic initial state required.",
"tags": ["slave"]
},
{
"id": "REQ-SLAVE-002",
"title": "SetResponse registers response via Publish",
"description": "SetResponse shall call bus.Publish(id, data) and track id in its registered ID set.",
"asil": "ASIL-B",
"rationale": "Slave response registration must delegate to the underlying bus.",
"tags": ["slave", "publish"]
},
{
"id": "REQ-SLAVE-003",
"title": "SetResponse(nil) removes registration",
"description": "SetResponse(id, nil) shall call bus.Publish(id, nil) and remove id from the registered ID set.",
"asil": "ASIL-B",
"rationale": "Slave must be able to withdraw a response.",
"tags": ["slave", "publish"]
},
{
"id": "REQ-SLAVE-004",
"title": "SetResponse rejects ID > MaxID",
"description": "SetResponse shall return an error when id > 0x3F without calling bus.Publish.",
"asil": "ASIL-B",
"rationale": "REQ-LIN-001 boundary enforced at the slave API.",
"tags": ["slave", "validation"]
},
{
"id": "REQ-SLAVE-005",
"title": "RegisteredIDs reflects current state",
"description": "RegisteredIDs shall return exactly the set of IDs for which a non-nil response is currently registered.",
"asil": "ASIL-B",
"rationale": "Application code uses RegisteredIDs for diagnostics and health checks.",
"tags": ["slave"]
},
{
"id": "REQ-SLAVE-006",
"title": "Subscribe delegates to bus",
"description": "Subscribe shall delegate to bus.Subscribe and return the resulting channel and error unchanged.",
"asil": "ASIL-B",
"rationale": "Slave observes the bus to monitor master requests and peer responses.",
"tags": ["slave"]
},
{
"id": "REQ-SLAVE-007",
"title": "RegisteredIDs returns empty slice when none registered",
"description": "RegisteredIDs shall return an empty (not nil) slice when no responses are currently registered.",
"asil": "ASIL-B",
"rationale": "Callers must be able to use len() on the result without nil checks.",
"tags": ["slave", "boundary"]
},
{
"id": "REQ-SLAVE-008",
"title": "SetResponse overwrites previous registration for same ID",
"description": "When SetResponse is called for an ID that already has a registration, the new data shall replace the previous registration.",
"asil": "ASIL-B",
"rationale": "Slaves must be able to update responses (e.g., refreshed sensor values) without removing and re-registering.",
"tags": ["slave", "update"]
},
{
"id": "REQ-SAFETY-001",
"title": "DataID embedded in header bytes 0-1",
"description": "Protect shall write Config.DataID as a little-endian uint16 into bytes 0-1 of the protected payload.",
"asil": "ASIL-B",
"rationale": "DataID is included in the CRC computation and identifies the logical data element.",
"tags": ["safety", "header"]
},
{
"id": "REQ-SAFETY-002",
"title": "SourceID embedded in header bytes 2-3",
"description": "Protect shall write Config.SourceID as a little-endian uint16 into bytes 2-3 of the protected payload.",
"asil": "ASIL-B",
"rationale": "SourceID is included in the CRC computation and identifies the sender node.",
"tags": ["safety", "header"]
},
{
"id": "REQ-SAFETY-003",
"title": "SequenceCounter starts at 0 and increments",
"description": "The Protector's SequenceCounter shall start at 0 and increment by 1 with each Protect call.",
"asil": "ASIL-B",
"rationale": "Monotonically increasing counter enables detection of missed or replayed frames.",
"tags": ["safety", "counter"]
},
{
"id": "REQ-SAFETY-004",
"title": "SequenceCounter embedded in header bytes 4-7",
"description": "Protect shall write the current SequenceCounter as a little-endian uint32 into bytes 4-7.",
"asil": "ASIL-B",
"rationale": "SG-04: counter must be present in the wire format for gap detection.",
"tags": ["safety", "header", "counter"]
},
{
"id": "REQ-SAFETY-005",
"title": "CRC computed over header and payload",
"description": "Protect shall compute CRC-16/CCITT-FALSE (poly=0x1021, init=0xFFFF) over the header with CRC slot zeroed, concatenated with the payload.",
"asil": "ASIL-B",
"rationale": "SG-04: CRC must cover all bytes including DataID and SourceID.",
"tags": ["safety", "crc"]
},
{
"id": "REQ-SAFETY-006",
"title": "CRC embedded in header bytes 8-9",
"description": "Protect shall write the computed CRC as a little-endian uint16 into bytes 8-9.",
"asil": "ASIL-B",
"rationale": "SG-04: CRC must be transmitted so the receiver can verify it.",
"tags": ["safety", "header", "crc"]
},
{
"id": "REQ-SAFETY-007",
"title": "Unwrap returns ErrHeaderTooShort for payload < 10 bytes",
"description": "Unwrap shall return E2EError{Kind: ErrHeaderTooShort} when the input is shorter than 10 bytes.",
"asil": "ASIL-B",
"rationale": "SG-04: truncated header cannot be validated; must be rejected.",
"tags": ["safety", "receiver", "error"]
},
{
"id": "REQ-SAFETY-008",
"title": "Unwrap detects byte corruption via CRC",
"description": "Unwrap shall return E2EError{Kind: ErrCRCMismatch} when the recomputed CRC does not match bytes 8-9.",
"asil": "ASIL-B",
"rationale": "SG-04: any single-byte corruption in header or payload must be detected.",
"tags": ["safety", "receiver", "crc"]
},
{
"id": "REQ-SAFETY-009",
"title": "Unwrap detects sequence gap",
"description": "Unwrap shall return E2EError{Kind: ErrSequenceGap} when the received counter is not exactly lastSeq+1 (after the first received message).",
"asil": "ASIL-B",
"rationale": "SG-04: a gap indicates a missed or replayed frame.",
"tags": ["safety", "receiver", "counter"]
},
{
"id": "REQ-SAFETY-010",
"title": "Unwrap returns original payload on success",
"description": "Unwrap shall strip the 10-byte header and return a copy of the original payload when all checks pass.",
"asil": "ASIL-B",
"rationale": "Application code must receive the original payload unchanged.",
"tags": ["safety", "receiver"]
},
{
"id": "REQ-SAFETY-011",
"title": "Protect/Unwrap round-trip preserves payload",
"description": "For any payload p, Unwrap(Protect(p)) shall return a byte-for-byte copy of p with nil error when Protector and Receiver share the same Config.",
"asil": "ASIL-B",
"rationale": "End-to-end correctness: protection must be lossless.",
"tags": ["safety", "roundtrip"]
},
{
"id": "REQ-SAFETY-012",
"title": "Protect output length equals 10 + len(payload)",
"description": "The byte slice returned by Protect shall have length exactly headerSize (10) + len(payload).",
"asil": "ASIL-B",
"rationale": "SG-04: receivers must be able to compute the expected output length to detect truncation.",
"tags": ["safety", "length"]
},
{
"id": "REQ-SAFETY-013",
"title": "Unwrap accepts first message with any counter value",
"description": "A newly created Receiver shall accept the first Unwrap call regardless of the counter value in the header.",
"asil": "ASIL-B",
"rationale": "On cold start the receiver does not know the protector's initial counter; the first message seeds the sequence.",
"tags": ["safety", "receiver", "counter"]
},
{
"id": "REQ-SAFETY-014",
"title": "Protect is safe for concurrent calls",
"description": "Protect may be called concurrently from multiple goroutines; each call shall receive a unique, monotonically increasing counter value.",
"asil": "ASIL-B",
"rationale": "SG-04: a data race on the counter could produce duplicate counters, defeating gap detection.",
"tags": ["safety", "concurrency"]
},
{
"id": "REQ-SAFETY-015",
"title": "Unwrap returns an independent payload copy",
"description": "The payload returned by Unwrap shall be a fresh copy; mutations to it shall not affect future Unwrap calls or internal state.",
"asil": "ASIL-B",
"rationale": "Shared payload memory between receiver and caller could corrupt subsequent receives.",
"tags": ["safety", "copy"]
},
{
"id": "REQ-SEOOC-001",
"title": "Integrating system provides physical LIN layer",
"description": "The system integrating go-LIN shall provide a correct LIN physical layer (transceiver, break detection, bit timing). go-LIN operates above the physical layer.",
"asil": "ASIL-B",
"rationale": "Physical-layer faults are outside go-LIN scope; must be addressed by the integrating system.",
"tags": ["seooc", "assumption"]
},
{
"id": "REQ-SEOOC-002",
"title": "Integrating system calls ValidateFrame on external data",
"description": "The integrating system shall call ValidateFrame on any frame received from external hardware before passing it to go-LIN APIs.",
"asil": "ASIL-B",
"rationale": "go-LIN validates at API boundaries; raw hardware data must be validated by the caller first.",
"tags": ["seooc", "assumption"]
},
{
"id": "REQ-SEOOC-003",
"title": "Integrating system validates frame ID semantics",
"description": "The integrating system shall verify that a received frame ID corresponds to the intended actuator or sensor before acting on the payload.",
"asil": "ASIL-B",
"rationale": "go-LIN delivers frames as received; semantic validation is the application's responsibility.",
"tags": ["seooc", "assumption"]
},
{
"id": "REQ-SEOOC-004",
"title": "Integration: virtual bus delivers E2E payload intact",
"description": "When Protect(payload) is published on the virtual bus and retrieved via SendHeader + Unwrap, the result shall equal payload with nil error.",
"asil": "ASIL-B",
"rationale": "End-to-end integration test of virtual transport + safety layer together.",
"tags": ["seooc", "integration"]
},
{
"id": "REQ-SEOOC-005",
"title": "Integration: master-slave round-trip via virtual bus",
"description": "A slave registered via slave.SetResponse shall have its payload returned by master.Node.SendHeader via the virtual bus.",
"asil": "ASIL-B",
"rationale": "Verifies the master-slave integration contract using the virtual transport.",
"tags": ["seooc", "integration"]
},
{
"id": "REQ-SEOOC-006",
"title": "Integration: LDF schedule IDs are valid",
"description": "LDF schedule table entries shall reference only frame IDs that appear in the Frames section and are within 0x00–0x3F.",
"asil": "ASIL-A",
"rationale": "A schedule entry referencing an undeclared ID would cause ErrNoResponse on every cycle.",
"tags": ["seooc", "integration", "ldf"]
},
{
"id": "REQ-SEOOC-007",
"title": "Integrating system handles ErrNoResponse safely",
"description": "The integrating system shall handle lin.ErrNoResponse without propagating the absence of a slave as a safety-critical output change.",
"asil": "ASIL-B",
"rationale": "A missing slave response is an operational condition; the application must decide the safe-state action.",
"tags": ["seooc", "assumption"]
},
{
"id": "REQ-SEOOC-008",
"title": "Integrating system routes safety-critical frames through safety package",
"description": "The integrating system shall apply safety.Protect/Unwrap to all frame payloads classified as safety-critical (ASIL >= ASIL-B).",
"asil": "ASIL-B",
"rationale": "go-LIN provides E2E protection but does not enforce which frames use it; the application must apply it to safety-relevant data.",
"tags": ["seooc", "assumption"]
},
{
"id": "REQ-SEOOC-009",
"title": "Integrating system provides monotonic clock for timing",
"description": "The integrating system shall provide a monotonic clock with at least 1 ms resolution for LIN schedule slot timing.",
"asil": "ASIL-B",
"rationale": "go-LIN uses time.After for slot delays; clock accuracy below 1 ms will cause schedule timing jitter.",
"tags": ["seooc", "assumption", "timing"]
},
{
"id": "REQ-ADAPT-001",
"title": "Adapt returns a LIN relay.Node",
"description": "Adapt shall return a non-nil relay.Node whose Protocol method reports relay.LIN.",
"asil": "ASIL-B",
"rationale": "RELAY §10.3/§13.7: the protocol-agnostic application layer must observe the correct protocol tag to route LIN traffic.",
"tags": ["adapt", "relay"]
},
{
"id": "REQ-ADAPT-002",
"title": "linNode.Send publishes payload for a valid frame ID",
"description": "linNode.Send shall publish msg.Payload as the slave response for the frame ID parsed from msg.ID when the ID is in range 0-63.",
"asil": "ASIL-B",
"rationale": "RELAY §10.3: the adapter egress path must faithfully forward application payloads onto the LIN bus.",
"tags": ["adapt", "relay"]
},
{
"id": "REQ-ADAPT-003",
"title": "linNode.Send rejects an out-of-range frame ID",
"description": "linNode.Send shall return a non-nil error and shall not publish when msg.ID is not a decimal string in range 0-63.",
"asil": "ASIL-B",
"rationale": "SG-05: an invalid identifier supplied through the adapter must not be transmitted.",
"tags": ["adapt", "relay", "validation"]
},
{
"id": "REQ-ADAPT-004",
"title": "linNode.Subscribe converts frames to relay.Message",
"description": "linNode.Subscribe shall return a channel that yields one relay.Message per received frame, each carrying the frame's canonical ToMessage() conversion.",
"asil": "ASIL-B",
"rationale": "RELAY §10.5: the adapter ingress path must deliver every received frame to the application as a canonical envelope.",
"tags": ["adapt", "relay"]
},
{
"id": "REQ-ADAPT-005",
"title": "linNode.Close closes the underlying bus",
"description": "linNode.Close shall close the wrapped Bus and return its error unchanged.",
"asil": "ASIL-B",
"rationale": "RELAY §6 lifecycle: closing the node must release the underlying transport so no further frames are processed.",
"tags": ["adapt", "relay", "lifecycle"]
},
{
"id": "REQ-MOCK-001",
"title": "mock.New returns a MasterBus-satisfying bus",
"description": "mock.New shall return a non-nil bus that satisfies lin.MasterBus (Publish, Subscribe, SendHeader, SetSchedule, Close).",
"asil": "ASIL-B",
"rationale": "RELAY §7 rule 4 / §13.7: a mandatory mock transport must implement the full bus contract so applications can be tested without hardware.",
"tags": ["mock", "relay"]
},
{
"id": "REQ-RELAY-001",
"title": "Protocol enum values are defined and stable",
"description": "relay::Protocol shall define CAN=1, DDS=2, LIN=3, MQTT=4, RCP=5, SOMEIP=6 as stable integer values matching RELAY spec v1.11 §3.",
"asil": "ASIL-B",
"rationale": "Protocol values are wire-format identifiers; changing them breaks cross-language interop.",
"tags": ["relay", "protocol"]
},
{
"id": "REQ-RELAY-002",
"title": "Protocol enum is an int-backed enum class",
"description": "relay::Protocol shall be an enum class backed by int to prevent implicit integer promotion and enable type-safe switching.",
"asil": "ASIL-B",
"rationale": "Type safety prevents accidental protocol ID confusion at compile time.",
"tags": ["relay", "protocol"]
},
{
"id": "REQ-RELAY-003",
"title": "to_string(Protocol) returns canonical name",
"description": "relay::to_string(Protocol p) shall return the canonical uppercase protocol name (e.g., 'LIN', 'CAN') and 'unknown' for unrecognised values.",
"asil": "ASIL-B",
"rationale": "RELAY §3.1: canonical names are used in log output and diagnostic fields.",
"tags": ["relay", "protocol"]
},
{
"id": "REQ-RELAY-004",
"title": "Version struct has major/minor/patch fields",
"description": "relay::Version shall provide int fields major, minor, and patch with equality comparison.",
"asil": "ASIL-B",
"rationale": "RELAY §4: version negotiation requires structured version comparison.",
"tags": ["relay", "version"]
},
{
"id": "REQ-RELAY-005",
"title": "Version::to_string returns 'major.minor.patch'",
"description": "Version::to_string() shall return a string formatted as '<major>.<minor>.<patch>'.",
"asil": "ASIL-B",
"rationale": "RELAY §4.1: canonical version string format for diagnostics and conformance checks.",
"tags": ["relay", "version"]
},
{
"id": "REQ-RELAY-006",
"title": "Message struct fields: protocol, version, id, payload, timestamp, seq, meta",
"description": "relay::Message shall provide fields: Protocol protocol, Version version, std::string id, std::vector<uint8_t> payload, system_clock::time_point timestamp, uint64_t seq, unordered_map<string,string> meta.",
"asil": "ASIL-B",
"rationale": "RELAY §5: canonical message envelope for all protocol bindings.",
"tags": ["relay", "message"]
},
{
"id": "REQ-RELAY-007",
"title": "Message id is a string (protocol-specific format)",
"description": "Message::id shall be a std::string whose format is defined per protocol binding (e.g., decimal integer for LIN, hex for CAN).",
"asil": "ASIL-B",
"rationale": "RELAY §5.2: string ID allows each protocol to use its native identifier notation.",
"tags": ["relay", "message"]
},
{
"id": "REQ-RELAY-008",
"title": "Errc::closed is error code 1",
"description": "relay::Errc::closed shall have integer value 1.",
"asil": "ASIL-B",
"rationale": "RELAY §9.1: stable error code values required for cross-language compatibility.",
"tags": ["relay", "error"]
},
{
"id": "REQ-RELAY-009",
"title": "Errc::not_connected is error code 2",
"description": "relay::Errc::not_connected shall have integer value 2.",
"asil": "ASIL-B",
"rationale": "RELAY §9.1: stable error code values.",
"tags": ["relay", "error"]
},
{
"id": "REQ-RELAY-010",
"title": "Errc::timeout is error code 3",
"description": "relay::Errc::timeout shall have integer value 3.",
"asil": "ASIL-B",
"rationale": "RELAY §9.1: stable error code values.",
"tags": ["relay", "error"]
},
{
"id": "REQ-RELAY-011",
"title": "Errc::payload_too_large is error code 4",
"description": "relay::Errc::payload_too_large shall have integer value 4.",
"asil": "ASIL-B",
"rationale": "RELAY §9.1: stable error code values.",
"tags": ["relay", "error"]
},
{
"id": "REQ-RELAY-012",
"title": "Errc is an int-backed enum class",
"description": "relay::Errc shall be an enum class backed by int, compatible with std::error_code.",
"asil": "ASIL-B",
"rationale": "Enables implicit std::error_code construction via is_error_code_enum specialisation.",
"tags": ["relay", "error"]
},
{
"id": "REQ-RELAY-013",
"title": "INode abstract interface: protocol(), send(), subscribe(), close()",
"description": "relay::INode shall declare pure virtual methods: protocol() const noexcept, send(Message), subscribe(vector<SubscriberOption>), and close().",
"asil": "ASIL-B",
"rationale": "RELAY §6: INode is the mandatory base interface for all protocol adapters.",
"tags": ["relay", "interface"]
},
{
"id": "REQ-RELAY-014",
"title": "ICaller extends INode with call()",
"description": "relay::ICaller shall extend INode with a call(Message req, milliseconds timeout) method returning pair<Message, error_code>.",
"asil": "ASIL-B",
"rationale": "RELAY §6.4: synchronous request/response pattern for RPC-style protocols.",
"tags": ["relay", "interface"]
},
{
"id": "REQ-RELAY-015",
"title": "BackPressurePolicy enum: DropNewest=0, DropOldest=1, Block=2",
"description": "relay::BackPressurePolicy shall define DropNewest=0, DropOldest=1, Block=2.",
"asil": "ASIL-B",
"rationale": "RELAY §8: backpressure policies allow callers to choose between latency and reliability tradeoffs.",
"tags": ["relay", "backpressure"]
},
{
"id": "REQ-RELAY-016",
"title": "SubscriberConfig holds channel depth, backpressure, event_id, topic_name",
"description": "relay::SubscriberConfig shall hold: int chan_depth, BackPressurePolicy back_pressure, uint32_t event_id, string topic_name.",
"asil": "ASIL-B",
"rationale": "RELAY §8.1: subscriber configuration captures all protocol-agnostic subscriber settings.",
"tags": ["relay", "subscriber"]
},
{
"id": "REQ-RELAY-017",
"title": "SubscriberOption is a std::function<void(SubscriberConfig&)>",
"description": "relay::SubscriberOption shall be typedef'd as std::function<void(SubscriberConfig&)>.",
"asil": "ASIL-B",
"rationale": "RELAY §8.2: functional option pattern allows composable subscriber configuration.",
"tags": ["relay", "subscriber"]
},
{
"id": "REQ-RELAY-018",
"title": "apply_options() folds options into SubscriberConfig",
"description": "relay::apply_options(opts) shall apply each option in order to a default SubscriberConfig and return the result.",
"asil": "ASIL-B",
"rationale": "RELAY §8.3: apply_options is the canonical way to construct a SubscriberConfig from options.",
"tags": ["relay", "subscriber"]
},
{
"id": "REQ-RELAY-019",
"title": "SubscriberConfig::effective_depth returns default when chan_depth is 0",
"description": "effective_depth(default_depth) shall return chan_depth when chan_depth > 0, else default_depth.",
"asil": "ASIL-B",