-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocument-anatomy.html
More file actions
1174 lines (1075 loc) · 85.4 KB
/
Copy pathdocument-anatomy.html
File metadata and controls
1174 lines (1075 loc) · 85.4 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
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "w6nnwbv1pd");
</script>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Document anatomy — AgenticFlowX</title>
<meta name="description" content="What's inside a spec, design, and tasks file — every section, every anchor, and the notation that keeps it deterministic. Flexible sections, fixed node IDs." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400..600;1,9..144,400..600&family=Geist:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
<style>
:root {
--bg: #f5efdf; --bg-subtle: #efe7d0; --bg-element: #fbf6e7; --bg-raised: #fffbed;
--border: rgba(20, 27, 53, 0.10); --border-strong: rgba(20, 27, 53, 0.16);
--text: #141b35; --text-secondary: #3d4256; --text-tertiary: #6b6450; --text-muted: #8a8069;
--accent: #a07e39; --accent-bright: #d4a656; --accent-deep: #6e5724; --moss: #4a6e5a; --ember: #a84c30;
--mono: 'JetBrains Mono', 'SF Mono', monospace;
--sans: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--serif: 'Fraunces', 'Iowan Old Style', Palatino, Georgia, serif;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; background-color: var(--bg); overflow-x: clip;
background-image: radial-gradient(1300px 700px at 88% -18%, rgba(212,166,86,0.06), transparent 60%), radial-gradient(1100px 780px at -8% 4%, rgba(74,110,90,0.045), transparent 58%);
background-attachment: fixed; background-repeat: no-repeat;
color: var(--text); font-family: var(--sans); font-size: 16px; line-height: 1.65; -webkit-font-smoothing: antialiased; }
a { color: inherit; text-decoration: none; }
.wrap-wide { width: min(1400px, 94vw); margin: 0 auto; }
.mono { font-family: var(--mono); }
nav.site { position: sticky; top: 0; z-index: 60; background: rgba(245,239,223,0.88); backdrop-filter: blur(8px); border-bottom: 1px solid transparent; transition: background .25s ease, backdrop-filter .25s ease, border-color .25s ease, box-shadow .25s ease; }
nav.site.scrolled { background: rgba(245,239,223,0.66); backdrop-filter: blur(18px) saturate(1.15); border-bottom-color: var(--border); box-shadow: 0 8px 30px -22px rgba(20,27,53,0.5); }
.nav-in { display: flex; align-items: center; height: 58px; gap: 18px; }
.nav-name { font-family: var(--serif); font-weight: 600; font-size: 17px; }
.nav-name a { display: inline-flex; align-items: center; gap: 10px; }
.nav-glyph { width: 34px; height: auto; display: block; }
.nav-name a:hover { color: var(--ember); }
.nav-crumb { font-family: var(--mono); font-size: 12px; color: var(--text-muted); }
.nav-crumb a:hover { color: var(--ember); } .nav-crumb b { color: var(--accent-deep); font-weight: 500; }
.nav-right { display: flex; align-items: center; gap: 16px; margin-left: auto; }
.nav-right a.ghost { font-size: 13.5px; color: var(--text-secondary); }
.nav-right a.ghost:hover { color: var(--ember); }
@media (max-width: 640px) { .nav-right a.ghost { display: none; } .nav-crumb { display: none; } }
.btn-primary { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.95rem; background: var(--text); color: var(--bg-raised); font-weight: 500; font-size: 0.87rem; border-radius: 6px; border: 1px solid var(--text); transition: background .15s; }
.btn-primary:hover { background: var(--ember); border-color: var(--ember); }
.nav-burger { display: none; align-items: center; justify-content: center; width: 38px; height: 38px; margin-left: 4px; border: 1px solid var(--border-strong); border-radius: 8px; background: transparent; color: var(--text); cursor: pointer; }
.nav-burger svg { width: 19px; height: 19px; }
/* mobile section rail — horizontal chip nav; gives phones a visible depth signal the hamburger can't */
.rail { display: none; }
@media (max-width: 920px) {
.rail { display: block; position: sticky; top: 58px; z-index: 54; background: rgba(245,239,223,0.94); backdrop-filter: blur(12px) saturate(1.1); border-bottom: 1px solid var(--border); }
.rail-in { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; padding: 9px 16px; -webkit-overflow-scrolling: touch; }
.rail-in::-webkit-scrollbar { display: none; }
.rail a { flex: 0 0 auto; font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.01em; color: var(--text-secondary); padding: 5px 11px; border: 1px solid var(--border-strong); border-radius: 999px; background: var(--bg-element); white-space: nowrap; transition: color .15s, background .15s, border-color .15s; }
.rail a.active { color: var(--bg-raised); background: var(--text); border-color: var(--text); }
}
.docs { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: clamp(32px, 4vw, 60px); padding: clamp(32px, 4vw, 48px) 0 72px; }
@media (max-width: 920px) {
.docs { grid-template-columns: 1fr; }
.nav-burger { display: inline-flex; }
.sidebar { position: fixed; top: 58px; left: 0; right: 0; z-index: 55; display: none; max-height: calc(100vh - 58px); overflow-y: auto; padding: 10px 22px 22px; border-right: none; background: rgba(245,239,223,0.98); backdrop-filter: blur(16px); border-bottom: 1px solid var(--border-strong); box-shadow: 0 22px 44px -26px rgba(20,27,53,0.45); }
.sidebar.open { display: block; }
}
.sidebar { position: sticky; top: 82px; align-self: start; max-height: calc(100vh - 100px); overflow-y: auto; padding-right: 18px; border-right: 1px solid var(--border); font-size: 13.5px; }
.sb-group { margin-bottom: 22px; }
.sb-label { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); margin: 0 0 6px; }
.sidebar a { display: block; padding: 5px 0 5px 12px; border-left: 2px solid transparent; color: var(--text-secondary); transition: color .15s, border-color .15s; }
.sidebar a:hover { color: var(--ember); }
.sidebar a.active { color: var(--ember); border-left-color: var(--ember); }
.sb-cta { margin-top: 26px; padding: 14px 16px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-element); font-size: 12.5px; color: var(--text-secondary); }
.sb-cta a { display: inline; padding: 0; border: none; color: var(--accent-deep); } .sb-cta a:hover { color: var(--ember); }
article { min-width: 0; max-width: 1040px; }
.doc-head { padding-bottom: 26px; border-bottom: 1px solid var(--border-strong); }
.doc-head .kicker { font-family: var(--mono); font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ember); margin: 0 0 12px; }
h1 { font-family: var(--serif); font-weight: 500; font-size: clamp(1.9rem, 3.4vw, 2.6rem); line-height: 1.1; margin: 0; letter-spacing: -0.01em; }
h1 em { color: var(--ember); font-style: italic; }
.doc-head p { margin: 12px 0 0; color: var(--text-secondary); font-size: 1.02rem; max-width: 680px; }
.doc-primer { margin-top: 24px; border: 1px solid var(--accent); border-radius: 14px; background: linear-gradient(180deg, rgba(160,126,57,0.08), rgba(160,126,57,0.025)); padding: 22px 24px; }
.doc-primer .dp-label { margin: 0; font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--ember); }
.doc-primer h2 { margin-top: 7px; font-size: 1.45rem; }
.doc-primer p { margin: 7px 0 0; max-width: none; }
.doc-primer ul { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px 24px; padding: 0; margin: 16px 0 0; list-style: none; }
.doc-primer li { position: relative; padding-left: 18px; color: var(--text-secondary); font-size: 13.5px; }
.doc-primer li::before { content: "→"; position: absolute; left: 0; color: var(--ember); font-family: var(--mono); }
@media (max-width: 640px) {
.wrap-wide { width: min(100% - 24px, 1400px); }
.docs { padding-top: 24px; padding-bottom: 48px; }
article section { padding-top: 38px; }
.doc-head { padding-bottom: 20px; }
h1 { font-size: clamp(2rem, 10vw, 2.6rem); }
.doc-head p, .sec-lede, article p { font-size: 14px; }
.doc-primer { margin-top: 20px; padding: 18px; }
.doc-primer h2 { font-size: 1.3rem; }
.doc-primer ul { grid-template-columns: 1fr; gap: 6px; }
.anatomy { gap: 12px; }
.an-code { font-size: 11px; padding: 13px 14px; }
.rulebox { padding: 16px 17px; }
.doc-next { flex-direction: column; }
.doc-next a { max-width: none; }
}
article section { padding-top: clamp(44px, 5.5vw, 60px); }
h2 { font-family: var(--serif); font-weight: 500; font-size: 1.7rem; margin: 0 0 4px; letter-spacing: -0.005em; scroll-margin-top: 90px; }
h2 .n { font-family: var(--mono); font-size: 0.72em; color: var(--ember); margin-right: 10px; }
h2 .file { font-family: var(--mono); font-size: 0.62em; color: var(--accent-deep); background: var(--bg-subtle); border: 1px solid var(--border); border-radius: 5px; padding: 2px 8px; margin-left: 10px; vertical-align: 3px; }
.sec-lede { color: var(--text-secondary); font-size: 15px; margin: 8px 0 0; max-width: 700px; }
h3 { font-family: var(--serif); font-weight: 600; font-size: 1.2rem; margin: 30px 0 8px; scroll-margin-top: 90px; }
article p { color: var(--text-secondary); font-size: 14.5px; max-width: 700px; }
article a.tx { color: var(--accent-deep); } article a.tx:hover { color: var(--ember); }
#credits .cmd-table a, #credits .callout a { text-decoration: underline; text-decoration-color: rgba(160,126,57,0.5); text-underline-offset: 3px; text-decoration-thickness: 1px; transition: text-decoration-color .15s, color .15s; }
#credits .cmd-table a:hover, #credits .callout a:hover { text-decoration-color: var(--ember); }
code { font-family: var(--mono); font-size: 0.85em; background: var(--bg-subtle); border: 1px solid var(--border); border-radius: 4px; padding: 1px 6px; color: var(--accent-deep); }
.diagram { margin: clamp(24px,3.5vw,36px) 0 0; border: 1px solid var(--border); border-radius: 12px; background: var(--bg-raised); padding: 10px 14px; }
.diagram img { display: block; width: 100%; height: auto; }
.shot-cap { margin-top: 10px; font-size: 12.5px; color: var(--text-tertiary); }
.gen { font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); border: 1px solid var(--border-strong); border-radius: 4px; padding: 1px 6px; margin-left: 8px; vertical-align: 1px; }
/* anatomy rows — markup left, explanation right */
.anatomy { display: grid; grid-template-columns: minmax(0,1.04fr) minmax(0,0.96fr); gap: 26px; align-items: start; margin-top: 22px; padding-top: 22px; border-top: 1px solid var(--border); }
.anatomy.first { border-top: none; padding-top: 0; }
@media (max-width: 900px) { .anatomy { grid-template-columns: 1fr; gap: 14px; } }
.an-code { background: #0d0f16; border: 1px solid var(--border-strong); border-radius: 10px; padding: 15px 17px; overflow-x: auto; font-family: var(--mono); font-size: 12.5px; line-height: 1.75; color: #c7cdda; white-space: pre; box-shadow: 0 20px 44px -34px rgba(20,27,53,0.5); }
.an-code .a { color: #e0b872; } .an-code .c { color: #6c7488; } .an-code .h { color: #ece7d8; font-weight: 600; } .an-code .g { color: #8fc9a3; } .an-code .b { color: #7fb8e0; }
.an-explain h4 { font-family: var(--serif); font-weight: 600; font-size: 1.14rem; margin: 0 0 8px; color: var(--text); }
.an-explain p { font-size: 14px; color: var(--text-secondary); margin: 0 0 8px; max-width: none; }
.an-explain ul { font-size: 13.5px; color: var(--text-secondary); padding-left: 18px; margin: 8px 0 0; }
.an-explain ul li { margin: 4px 0; }
.an-explain ul b { color: var(--text); font-weight: 500; }
.an-tag { display: inline-block; font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase; border-radius: 999px; padding: 3px 10px; margin-bottom: 10px; }
.an-tag.req { color: var(--ember); border: 1px solid rgba(168,76,48,0.4); background: rgba(168,76,48,0.06); }
.an-tag.opt { color: var(--moss); border: 1px solid rgba(74,110,90,0.4); background: rgba(74,110,90,0.06); }
/* specimen — one spec shown raw and rendered, before the dissection */
.specimen { margin-top: 30px; }
.specimen .sp-head { margin-bottom: 16px; }
.specimen .sp-kick { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--ember); margin: 0 0 6px; }
.specimen .sp-sub { font-size: 14px; color: var(--text-secondary); margin: 0; max-width: 720px; }
.sp-grid { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 20px; align-items: stretch; }
@media (max-width: 900px) { .sp-grid { grid-template-columns: 1fr; gap: 16px; } }
.sp-pane { display: flex; flex-direction: column; min-width: 0; }
.sp-tag { font-family: var(--mono); font-size: 10.5px; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); margin: 0 0 8px; }
.sp-tag b { color: var(--text-secondary); font-weight: 600; }
.sp-raw { flex: 1; margin: 0; background: #0d0f16; border: 1px solid var(--border-strong); border-radius: 10px; padding: 15px 17px; overflow-x: auto; font-family: var(--mono); font-size: 12px; line-height: 1.7; color: #c7cdda; white-space: pre; box-shadow: 0 20px 44px -34px rgba(20,27,53,0.5); }
.sp-raw .a { color: #e0b872; } .sp-raw .c { color: #6c7488; } .sp-raw .h { color: #ece7d8; font-weight: 600; } .sp-raw .g { color: #8fc9a3; } .sp-raw .b { color: #7fb8e0; }
.sp-doc { flex: 1; background: var(--bg-raised); border: 1px solid var(--border-strong); border-radius: 10px; padding: 17px 19px; box-shadow: 0 20px 44px -34px rgba(20,27,53,0.35); }
.sp-doc .sp-path { font-family: var(--mono); font-size: 10px; color: var(--text-muted); border-bottom: 1px solid var(--border); padding-bottom: 9px; margin-bottom: 12px; overflow-x: auto; white-space: nowrap; }
.sp-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 13px; }
.sp-chips span { font-family: var(--mono); font-size: 10px; letter-spacing: .04em; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border-strong); background: var(--bg-element); color: var(--text-secondary); }
.sp-chips span.on { background: var(--moss); border-color: var(--moss); color: #fff; }
.sp-doc h5 { font-family: var(--serif); font-weight: 600; font-size: 1.06rem; margin: 0; color: var(--text); line-height: 1.25; }
.sp-doc h6 { font-family: var(--mono); font-weight: 500; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; margin: 15px 0 5px; color: var(--ember); }
.sp-doc p { font-size: 12.5px; color: var(--text-secondary); margin: 0; max-width: none; line-height: 1.55; }
.sp-table { width: 100%; border-collapse: collapse; margin-top: 4px; font-size: 11.5px; }
.sp-table th, .sp-table td { text-align: left; padding: 5px 8px; border: 1px solid var(--border); }
.sp-table th { background: var(--bg-element); font-family: var(--mono); font-size: 9.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-tertiary); font-weight: 500; }
.sp-table td code { font-size: 11px; color: var(--accent-deep); }
.sp-ac { list-style: none; padding: 0; margin: 5px 0 0; font-size: 12.5px; color: var(--text-secondary); }
.sp-ac li { position: relative; padding-left: 23px; margin: 5px 0; }
.sp-ac li::before { content: ""; position: absolute; left: 0; top: 1px; width: 14px; height: 14px; border-radius: 4px; border: 1.5px solid var(--border-strong); box-sizing: border-box; }
.sp-ac li.done { color: var(--text-tertiary); }
.sp-ac li.done::before { content: "✓"; background: var(--moss); border-color: var(--moss); color: #fff; font-size: 10px; line-height: 13px; text-align: center; }
.sp-cap { margin: 14px 0 0; font-size: 13.5px; color: var(--text-tertiary); max-width: 760px; }
.sp-cap b { color: var(--text); font-weight: 500; }
.rulebox { border: 1px solid var(--border-strong); border-left: 3px solid var(--accent); border-radius: 10px; background: var(--bg-element); padding: 18px 22px; margin-top: 22px; max-width: 760px; }
.rulebox h4 { font-family: var(--serif); font-weight: 600; font-size: 1.1rem; margin: 0 0 10px; }
.rulebox .r { font-size: 14px; color: var(--text-secondary); margin: 6px 0; }
.rulebox .r b { color: var(--text); font-weight: 500; }
.rulebox .ok, .rulebox .no { font-family: var(--mono); font-size: 12.5px; padding: 4px 0; }
.rulebox .ok { color: var(--moss); } .rulebox .no { color: var(--ember); }
.tscroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.cmd-table { width: 100%; border-collapse: collapse; margin-top: 16px; font-size: 13px; }
.cmd-table th { text-align: left; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-muted); font-weight: 500; padding: 8px 14px 8px 0; border-bottom: 1.5px solid var(--border-strong); }
.cmd-table td { padding: 10px 14px 10px 0; border-bottom: 1px solid var(--border); vertical-align: top; color: var(--text-secondary); }
.cmd-table td:first-child { white-space: nowrap; }
.cmd-table code { white-space: nowrap; }
.cmd-table td.opt { color: var(--moss); font-family: var(--mono); font-size: 11px; }
.callout { border: 1px solid rgba(160,126,57,0.35); background: rgba(160,126,57,0.06); border-radius: 10px; padding: 14px 17px; font-size: 14px; color: var(--text-secondary); margin: 22px 0 0; max-width: 720px; }
.callout b { color: var(--accent-deep); }
.doc-next { display: flex; justify-content: space-between; gap: 14px; margin-top: clamp(48px, 6vw, 64px); padding-top: 22px; border-top: 1px solid var(--border-strong); }
.doc-next a { display: block; padding: 14px 18px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-element); transition: border-color .15s; max-width: 46%; }
.doc-next a:hover { border-color: var(--ember); }
.doc-next .lbl { font-family: var(--mono); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); }
.doc-next .ttl { display: block; font-family: var(--serif); font-weight: 600; font-size: 1rem; color: var(--text); margin-top: 3px; }
footer { border-top: 1px solid var(--border); padding: 30px 0; color: var(--text-tertiary); font-size: 13px; }
.foot-in { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 16px; }
.foot-links { display: flex; flex-wrap: wrap; gap: 20px; } .foot-links a:hover { color: var(--ember); }
/* pin annotation — real screenshot + numbered pins ↔ legend */
.anno { position: relative; border-radius: 10px; overflow: hidden; border: 1px solid var(--border-strong); background: #0c1017; box-shadow: 0 28px 64px -44px rgba(20,27,53,0.5); margin-top: clamp(24px,3.5vw,36px); }
.anno > img { display: block; width: 100%; height: auto; }
.anno-pin { position: absolute; transform: translate(-50%, -50%); width: 28px; height: 28px; border-radius: 50%; background: var(--accent-bright); color: #14161e; font-family: var(--mono); font-weight: 600; font-size: 12.5px; display: grid; place-items: center; border: 2px solid #fffbed; box-shadow: 0 2px 10px rgba(0,0,0,.55); z-index: 3; transition: transform .15s, background .15s; }
.anno-pin::before { content: ""; position: absolute; inset: -5px; border-radius: 50%; border: 1.5px solid var(--accent-bright); opacity: .55; animation: anno-ping 2.6s ease-out infinite; }
.anno-pin:nth-of-type(2)::before { animation-delay: .3s; } .anno-pin:nth-of-type(3)::before { animation-delay: .6s; }
.anno-pin:nth-of-type(4)::before { animation-delay: .9s; } .anno-pin:nth-of-type(5)::before { animation-delay: 1.2s; }
.anno-pin:nth-of-type(6)::before { animation-delay: 1.5s; } .anno-pin:nth-of-type(7)::before { animation-delay: 1.8s; }
.anno-pin:nth-of-type(8)::before { animation-delay: 2.1s; }
.anno-pin.lit { transform: translate(-50%, -50%) scale(1.35); background: var(--ember); z-index: 4; }
@keyframes anno-ping { 0% { transform: scale(1); opacity: .55; } 70%, 100% { transform: scale(2); opacity: 0; } }
.anno-legend { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 30px; margin-top: 24px; }
@media (max-width: 720px) { .anno-legend { grid-template-columns: 1fr; } }
.anno-item { display: grid; grid-template-columns: 28px 1fr; gap: 12px; padding: 12px 10px; border-radius: 10px; transition: background .15s; cursor: default; }
.anno-item:hover { background: var(--bg-element); }
.anno-item .num { width: 28px; height: 28px; border-radius: 50%; background: var(--bg-raised); border: 1.5px solid var(--accent); color: var(--accent-deep); font-family: var(--mono); font-weight: 600; font-size: 12.5px; display: grid; place-items: center; }
.anno-item h5 { font-family: var(--serif); font-weight: 600; font-size: 1rem; margin: 2px 0 3px; color: var(--text); }
.anno-item p { font-size: 13px; color: var(--text-secondary); margin: 0; max-width: none; }
.anno-item .do { display: block; margin-top: 5px; font-family: var(--mono); font-size: 11px; color: var(--ember); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } * { transition: none !important; } .anno-pin::before { animation: none; } }
</style>
</head>
<body>
<nav class="site" aria-label="Site">
<div class="wrap-wide nav-in">
<span class="nav-name"><a href="index.html"><img class="nav-glyph" src="assets/afx-glyph.svg" alt="" /> AgenticFlowX</a></span>
<span class="nav-crumb">/ <a href="sdd-guide.html">SDD guide</a> / <b>Document anatomy</b></span>
<div class="nav-right">
<a class="ghost" href="sdd-guide.html">← SDD guide</a>
<a class="ghost" href="https://github.com/AgenticFlowX/agenticflowx" target="_blank" rel="noopener noreferrer">GitHub</a>
<a class="btn-primary" href="https://marketplace.visualstudio.com/items?itemName=AgenticFlowX.agenticflowx" target="_blank" rel="noopener noreferrer">Install</a>
<button class="nav-burger" aria-label="Menu" aria-expanded="false" aria-controls="doc-nav">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="4" y1="7" x2="20" y2="7"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="17" x2="20" y2="17"/></svg>
</button>
</div>
</div>
</nav>
<nav class="rail" aria-label="Jump to section">
<div class="rail-in">
<a href="#idea">The idea</a>
<a href="#spec">spec.md</a>
<a href="#design-rules">design.md</a>
<a href="#tasks">tasks.md</a>
<a href="#see">@see</a>
<a href="#shapes">Three shapes</a>
<a href="#stepper">Stepper</a>
<a href="#previewer">Previewer</a>
<a href="#cheat">Cheat sheet</a>
</div>
</nav>
<div class="wrap-wide docs">
<aside class="sidebar" id="doc-nav" aria-label="On this page">
<div class="sb-group">
<p class="sb-label">The idea</p>
<a href="#idea">Flexible sections, fixed anchors</a>
</div>
<div class="sb-group">
<p class="sb-label">spec.md · the WHAT</p>
<a href="#spec">Anatomy</a>
<a href="#spec-ids">Requirement IDs</a>
</div>
<div class="sb-group">
<p class="sb-label">design.md · the HOW</p>
<a href="#design-rules">The node-ID rules</a>
<a href="#design-sections">The sections</a>
</div>
<div class="sb-group">
<p class="sb-label">tasks.md · the WHEN</p>
<a href="#tasks">Task anatomy</a>
<a href="#tasks-sessions">Work Sessions</a>
</div>
<div class="sb-group">
<p class="sb-label">The thread</p>
<a href="#see">The @see contract</a>
</div>
<div class="sb-group">
<p class="sb-label">Dash · sprint · full</p>
<a href="#shapes">The three shapes</a>
<a href="#ex-dash">A worked Dash</a>
<a href="#ex-sprint">A worked Sprint</a>
<a href="#ex-full">A worked Full spec</a>
</div>
<div class="sb-group">
<p class="sb-label">In the extension</p>
<a href="#stepper">How the stepper reads it</a>
<a href="#previewer">How the previewer renders it</a>
</div>
<div class="sb-group">
<p class="sb-label">Reference</p>
<a href="#credits">Where the notation comes from</a>
<a href="#cheat">Notation cheat sheet</a>
</div>
<div class="sb-cta">Prefer the walk-through? The <a href="sdd-guide.html">SDD guide</a> shows these in the extension, click by click.</div>
</aside>
<article>
<div class="doc-head">
<p class="kicker">Document anatomy</p>
<h1>Every section, every anchor, <em>explained</em>.</h1>
<p>A spec, design, and tasks file are plain markdown — but markdown with stable node IDs, so sections can flex while your code, tools, and reviews keep resolving against them. This page maps the required sections and the anchors that hold them together — <code>[FR-1]</code>, <code>[DES-ARCH]</code>, <code>### 2.1</code>, <code>@see</code> — with real examples for each.</p>
</div>
<div class="doc-primer">
<p class="dp-label">Read this first · the 30-second version</p>
<h2>Plain markdown, durable anchors.</h2>
<p>AFX keeps the writing human and the references machine-stable. You can change the prose; the IDs keep the workflow, review, and code links attached.</p>
<ul>
<li><b>Spec</b> says what must be true: <code>[FR-1]</code></li>
<li><b>Design</b> says how it works: <code>[DES-ARCH]</code></li>
<li><b>Tasks</b> turns both into checkable work: <code>### 2.1</code></li>
<li><b>Code</b> points back with <code>@see</code>; the journal keeps why</li>
</ul>
</div>
<!-- SPECIMEN: one spec, both ways -->
<div class="specimen">
<div class="sp-head">
<p class="sp-kick">See one whole, first</p>
<p class="sp-sub">The same <code>spec.md</code>, two ways. Left is the plain markdown that lives in your repo. Right is what the previewer, the extension, and your reviewers see — parsed straight from the anchors, no database, no export step. Everything below this is <em>how</em> that mapping works.</p>
</div>
<div class="sp-grid">
<div class="sp-pane">
<p class="sp-tag"><b>spec.md</b> — what you (and the agent) write</p>
<div class="sp-raw"><span class="c">---</span>
<span class="b">afx</span>: <span class="g">true</span>
<span class="b">type</span>: <span class="a">SPEC</span>
<span class="b">status</span>: <span class="a">Approved</span>
<span class="b">owner</span>: <span class="g">"@rix"</span>
<span class="b">version</span>: <span class="g">"1.0"</span>
<span class="c">---</span>
<span class="h"># Room Export — Product Specification</span>
<span class="h">## Problem Statement</span>
Treasurers need a room's ledger outside
the app — for taxes, disputes, receipts.
<span class="h">## Requirements</span>
| ID | Requirement | Priority |
| ----- | ---------------------- | -------- |
| <span class="a">FR-1</span> | Export ledger as CSV | Must |
| <span class="a">FR-2</span> | Stream rows, no buffer | Must |
| <span class="a">NFR-1</span> | 10k rows under 2s | Should |
<span class="h">## Acceptance Criteria</span>
- [<span class="g">x</span>] Opens cleanly in Excel & Sheets
- [ ] Streaming verified at 10k rows</div>
</div>
<div class="sp-pane">
<p class="sp-tag"><b>Rendered</b> — chips, tables, live checkboxes</p>
<div class="sp-doc" aria-label="Rendered spec.md">
<div class="sp-path">docs/specs/room-export/spec.md</div>
<div class="sp-chips"><span>SPEC</span><span class="on">Approved</span><span>v1.0</span><span>@rix</span></div>
<h5>Room Export — Product Specification</h5>
<h6>Problem Statement</h6>
<p>Treasurers need a room's ledger outside the app — for taxes, disputes, receipts.</p>
<h6>Requirements</h6>
<table class="sp-table">
<thead><tr><th>ID</th><th>Requirement</th><th>Priority</th></tr></thead>
<tbody>
<tr><td><code>FR-1</code></td><td>Export ledger as CSV</td><td>Must</td></tr>
<tr><td><code>FR-2</code></td><td>Stream rows, no buffer</td><td>Must</td></tr>
<tr><td><code>NFR-1</code></td><td>10k rows under 2s</td><td>Should</td></tr>
</tbody>
</table>
<h6>Acceptance Criteria</h6>
<ul class="sp-ac">
<li class="done">Opens cleanly in Excel & Sheets</li>
<li>Streaming verified at 10k rows</li>
</ul>
</div>
</div>
</div>
<p class="sp-cap"><b>Same bytes on disk.</b> The frontmatter becomes status chips, <code>##</code> headings become sections, the pipe table stays a table, and <code>- [x]</code> becomes a checkbox you can actually toggle. The rest of this page is the notation that keeps that mapping deterministic.</p>
</div>
<!-- THE IDEA -->
<section id="idea">
<h2><span class="n">01</span>Flexible sections, fixed anchors</h2>
<p class="sec-lede">AI output is fuzzy; a parser can't be. AFX threads the needle by making the <em>headings</em> free and the <em>IDs</em> fixed. You can rename, reorder, or add a section — as long as its node ID is present and unique, every reference to it still resolves.</p>
<div class="diagram"><img src="assets/illustrations/notation-contract.svg" alt="One requirement ID FR-1 flows from spec to design to tasks to a @see comment in code; sections can move but the ID stays the anchor every tool parses against" loading="lazy" decoding="async" /></div>
<p class="shot-cap">The same <code>[FR-1]</code> resolves from spec → design → tasks → code. That's the contract. <span class="gen">illustration</span></p>
<div class="callout"><b>Why this matters:</b> the format is <em>strict by design</em>. The AFX CLI, the VS Code extension, and CodeLens all <em>parse</em> these files to pull out sections and IDs. A missing or duplicated anchor doesn't error loudly — it fails silently in the tool that renders it. The rules below are what keep the whole system deterministic.</div>
<div class="rulebox" style="border-left-color:var(--moss)">
<h4>Written in the present tense</h4>
<p class="r">A spec isn't a plan for what the code <em>will</em> do. It's a <b>living document</b> — a 1:1 reflection of what the code <em>does, now</em>. So it's written in the present tense.</p>
<div class="ok">✓ "Export streams rows without buffering." · design sections carry <b>as-built</b> truth · <code>spec.md</code> and <code>design.md</code> represent current product and technical truth</div>
<div class="no">✕ "The system shall export…" · "We will add…" · past-tense changelog prose — that history lives in <code>journal.md</code>, not the spec</div>
<p class="r" style="margin-top:10px">When the code changes, you <em>refine</em> the doc so the two never drift. That's why the AFX status lifecycle is <code>Draft → Approved → Superseded</code> — a doc is either current truth or explicitly retired. ("Living" is the principle, not a status value.)</p>
</div>
</section>
<!-- SPEC -->
<section id="spec">
<h2><span class="n">02</span>spec.md<span class="file">the WHAT</span></h2>
<p class="sec-lede">Requirements only — no implementation. Eight <code>##</code> sections, in order. Custom sections are allowed; the required eight must not be dropped. Code links back to the IDs defined here, forever.</p>
<div class="anatomy first">
<div class="an-code"><span class="c">---</span>
<span class="b">afx</span>: <span class="g">true</span>
<span class="b">type</span>: <span class="a">SPEC</span>
<span class="b">status</span>: <span class="a">Draft</span> <span class="c"># Draft → Approved → Superseded</span>
<span class="b">owner</span>: <span class="g">"@rix"</span>
<span class="b">version</span>: <span class="g">"1.0"</span>
<span class="c">---</span>
<span class="h"># Room Export — Product Specification</span></div>
<div class="an-explain">
<span class="an-tag req">Frontmatter · required</span>
<h4>The header the tools read first</h4>
<p>YAML frontmatter is the single source of truth for state — never repeated as bold lines in the body. <code>afx: true</code> marks it AFX-owned; <code>type</code> tells the parser which template this is.</p>
<ul>
<li><b>status</b> drives the workflow: <code>Draft → Approved → Superseded</code>. Approving freezes the spec and unlocks design.</li>
<li><b>version</b> bumps when scope changes — which reverts status to Draft.</li>
</ul>
</div>
</div>
<div class="anatomy">
<div class="an-code"><span class="h">## References</span>
- <span class="a">**Proposal**</span>: [RFC-001](../../proposals/…)
- <span class="a">**Research**</span>: [RES-market](../research/…)
<span class="h">## Problem Statement</span>
Members can't get their ledger out of the room…
<span class="h">## User Stories</span>
<span class="h">### Primary Users</span>
<span class="h">### Stories</span>
**As a** member **I want** … **So that** …</div>
<div class="an-explain">
<span class="an-tag req">Sections 1–3 · required</span>
<h4>Context, then the problem, then who</h4>
<p><b>References</b> link upstream — proposals, research, ADRs — so the spec isn't a floating artifact. <b>Problem Statement</b> is the "why now." <b>User Stories</b> keeps two fixed sub-headings, <code>### Primary Users</code> and <code>### Stories</code>, so the parser can find them.</p>
</div>
</div>
<div class="anatomy">
<div class="an-code"><span class="h">## Requirements</span>
<span class="h">### Functional Requirements</span>
| ID | Requirement | Priority |
| ---- | ------------------ | ------------ |
| <span class="g">FR-1</span> | Export as CSV | Must Have |
| <span class="g">FR-2</span> | Filter by date | Must Have |
| <span class="g">FR-3</span> | Include totals row | Should Have |
<span class="h">### Non-Functional Requirements</span>
| ID | Requirement | Target |
| ----- | ----------- | --------------- |
| <span class="g">NFR-1</span> | Performance | 100k rows < 5s |</div>
<div class="an-explain">
<span class="an-tag req">The star section · required</span>
<h4>Requirements — where the IDs are born</h4>
<p>Every requirement gets a stable ID. This is the anchor that code, design, and tasks all point back to.</p>
<ul>
<li><b>Format:</b> <code>FR-N</code> (functional) and <code>NFR-N</code> (non-functional).</li>
<li><b>Sequential, no gaps</b> — <code>FR-1, FR-2, FR-3</code>, never skipping. The validator enforces this.</li>
<li><b>Priority</b> is a fixed vocabulary: <code>Must Have · Should Have · Nice to Have</code>.</li>
<li>Delete a requirement? Retire the ID, don't renumber — old <code>@see</code> links must not silently repoint.</li>
</ul>
</div>
</div>
<div class="anatomy">
<div class="an-code"><span class="h">## Acceptance Criteria</span>
<span class="h">### CSV export</span>
- [ ] Opens in Excel with correct encoding
- [ ] Totals row matches the ledger sum
<span class="h">## Non-Goals (Out of Scope)</span>
- PDF export · scheduled exports
<span class="h">## Open Questions</span>
| # | Question | Status |
| - | --------------------- | -------- |
| 1 | Include soft-deleted? | <span class="a">Open</span> |
<span class="h">## Dependencies</span>
- Requires the ledger read model</div>
<div class="an-explain">
<span class="an-tag req">Sections 5–8 · required</span>
<h4>What "done" means, and what it isn't</h4>
<ul>
<li><b>Acceptance Criteria</b> — checkboxes grouped per capability. These become the verifier's checklist.</li>
<li><b>Non-Goals</b> — the scope fence, written down so nobody "helpfully" builds it.</li>
<li><b>Open Questions</b> — a tracked table with a <code>Status</code> column; the previewer nudges you to resolve them.</li>
<li><b>Dependencies</b> — what must exist first.</li>
</ul>
</div>
</div>
<div class="anatomy">
<div class="an-code"><span class="h">## Appendix</span>
<span class="h">### Wireframes / Mockups</span>
<span class="h">### Data Examples</span>
<span class="h">### Glossary</span>
| Term | Definition |
| ---- | ---------- |</div>
<div class="an-explain">
<span class="an-tag opt">Optional</span>
<h4>Appendix — and your own sections</h4>
<p>The <b>Appendix</b> is optional. So is any custom <code>##</code> you add — a "Metrics" section, a "Rollout" note, whatever the feature needs. The one rule: the required eight stay present and in order. Everything else is yours.</p>
</div>
</div>
<div class="rulebox" id="spec-ids">
<h4>Requirement ID notation</h4>
<p class="r"><b>Functional:</b> <code>FR-1</code>, <code>FR-2</code> … · <b>Non-functional:</b> <code>NFR-1</code>, <code>NFR-2</code> …</p>
<div class="ok">✓ Sequential, no gaps · unique · referenced by @see everywhere</div>
<div class="no">✕ Don't renumber on delete · don't reuse a retired ID · don't skip numbers</div>
</div>
</section>
<!-- DESIGN -->
<section id="design-rules">
<h2><span class="n">03</span>design.md<span class="file">the HOW</span></h2>
<p class="sec-lede">Architecture, with a <code>[DES-ID]</code> on every section. This is the most "notation-heavy" file — because it's the one the extension parses hardest to render. Get the anchors right and the tools light up; get them wrong and sections vanish silently.</p>
<div class="rulebox">
<h4>The node-ID rules — memorise these five</h4>
<div class="ok">✓ Every <code>##</code> heading starts with a bracketed ID: <code>## [DES-API] API Contracts</code></div>
<div class="ok">✓ Uppercase kebab-case, inside square brackets: <code>[DES-API]</code>, <code>[DES-ROLLOUT]</code></div>
<div class="ok">✓ Unique within the file — no duplicate IDs</div>
<div class="ok">✓ <code>##</code> and <code>###</code> are captured by parsers; <code>####</code> and deeper are <b>not</b></div>
<div class="ok">✓ Custom <code>##</code> allowed — same anchored form, a fresh unique ID</div>
<div class="no">✕ <code>## API Contracts</code> (no ID) · <code>## API Contracts [DES-API]</code> (ID at end) · <code>## [des-api]</code> (lowercase)</div>
</div>
<div class="anatomy" style="margin-top:26px">
<div class="an-code"><span class="c">---</span>
<span class="b">type</span>: <span class="a">DESIGN</span>
<span class="b">spec</span>: <span class="g">spec.md</span> <span class="c"># mandatory backlink</span>
<span class="c">---</span>
<span class="h">## <span class="a">[DES-OVR]</span> Overview</span>
<span class="h">## <span class="a">[DES-ARCH]</span> Architecture</span>
<span class="h">### System Context</span>
<span class="h">### Component Diagram</span></div>
<div class="an-explain">
<h4>An ID leads every section</h4>
<p>The heading text is free — call it "Architecture" or "How it hangs together" — but the ID at the front is the handle. <code>### System Context</code> and other sub-sections don't need an ID; they're captured as children of their parent <code>##</code>.</p>
<p>The <code>spec: spec.md</code> backlink is mandatory — it's how the design knows which requirements it serves.</p>
</div>
</div>
</section>
<section id="design-sections" style="padding-top:34px">
<h3 style="margin-top:0">The 12 required sections (+ 2 optional)</h3>
<p>Every <code>design.md</code> carries these anchors. Add your own between them freely.</p>
<div class="tscroll">
<table class="cmd-table" style="min-width:640px">
<thead><tr><th>Node ID</th><th>Section</th><th>What lives here</th></tr></thead>
<tbody>
<tr><td><code>[DES-OVR]</code></td><td>Overview</td><td>2–3 sentence summary of the approach</td></tr>
<tr><td><code>[DES-ARCH]</code></td><td>Architecture</td><td>System context, component diagram</td></tr>
<tr><td><code>[DES-UI]</code></td><td>User Interface & UX</td><td>This feature's component composition</td></tr>
<tr><td><code>[DES-DEC]</code></td><td>Key Decisions</td><td>Decision · options · choice · rationale table</td></tr>
<tr><td><code>[DES-DATA]</code></td><td>Data Model</td><td>Schema, TypeScript interfaces, enums</td></tr>
<tr><td><code>[DES-API]</code></td><td>API Contracts</td><td>Server actions, input/output types</td></tr>
<tr><td><code>[DES-FILES]</code></td><td>File Structure</td><td>New files to create, files to modify</td></tr>
<tr><td><code>[DES-DEPS]</code></td><td>Dependencies</td><td>External & internal packages</td></tr>
<tr><td><code>[DES-SEC]</code></td><td>Security Considerations</td><td>Auth, boundaries, data handling</td></tr>
<tr><td><code>[DES-ERR]</code></td><td>Error Handling</td><td>Failure modes and responses</td></tr>
<tr><td><code>[DES-TEST]</code></td><td>Testing Strategy</td><td>Unit & integration approach</td></tr>
<tr><td><code>[DES-ROLLOUT]</code></td><td>Migration / Rollout</td><td>Phased plan and rollback</td></tr>
<tr><td class="opt">[DES-REFS]</td><td class="opt">File Reference Map</td><td class="opt">optional</td></tr>
<tr><td class="opt">[DES-QUESTIONS]</td><td class="opt">Open Technical Questions</td><td class="opt">optional</td></tr>
</tbody>
</table>
</div>
<div class="anatomy" style="margin-top:26px">
<div class="an-code"><span class="h">## <span class="a">[DES-DEC]</span> Key Decisions</span>
| Decision | Options | Choice | Rationale |
| ---------- | ------- | ------ | ------------- |
| CSV writer | buffer, <span class="g">stream</span> | stream | Const memory |
<span class="h">## <span class="a">[DES-API]</span> API Contracts</span>
```typescript
export async function exportRoomCsv(
roomId: string, range: DateRange
): Promise<Stream>
```</div>
<div class="an-explain">
<h4>The sections that carry weight</h4>
<p><b>[DES-DEC]</b> is where the "why" is pinned — a table of what you chose <em>and what you rejected</em>. It's the antidote to "we lost the argument": the reasoning is on the record, anchored.</p>
<p><b>[DES-API]</b>, <b>[DES-DATA]</b>, and <b>[DES-FILES]</b> hold the concrete contracts — signatures, schemas, exact file paths — so the implementation has a target, not a vibe.</p>
</div>
</div>
</section>
<!-- TASKS -->
<section id="tasks">
<h2><span class="n">04</span>tasks.md<span class="file">the WHEN</span></h2>
<p class="sec-lede">The execution plan. Hierarchical task IDs, each group scoped to files and linked back to the spec and design it implements. This is where the anchors get <em>used</em>.</p>
<div class="anatomy first">
<div class="an-code"><span class="h">## Phase 1: CSV export</span>
<span class="c">> GitHub Issue #42 | Ref: [DES-API], [FR-1]</span>
<span class="h">### <span class="a">1.1</span> Streaming CSV writer</span>
<span class="c"><!-- files: src/export/csv-writer.ts --></span>
<span class="c"><!-- @see design.md <span class="a">[DES-ARCH]</span> | spec.md <span class="a">[FR-1]</span> --></span>
- [ ] Writes rows without buffering
- [ ] Regression test at 100k rows
<span class="h">### <span class="a">1.2</span> Date-range filter</span>
<span class="c"><!-- files: src/export/filter.ts --></span>
- [ ] Honours room timezone <span class="c">// [FR-2]</span></div>
<div class="an-explain">
<span class="an-tag req">Task group anatomy</span>
<h4>Every group is a provable slice</h4>
<ul>
<li><b>Task ID</b> <code>### N.N</code> — phase-dot-task. <code>1.1</code>, <code>1.2</code>, then <code>2.1</code>…</li>
<li><b>File scope</b> — <code><!-- files: … --></code> names exactly what this task may touch. The drift guardrail holds the agent to it.</li>
<li><b>The <code>@see</code> line</b> — links the task to its design section and requirement. This is the anchor made actionable.</li>
<li><b>Checkboxes</b> — completion criteria the verifier checks against.</li>
</ul>
</div>
</div>
<div class="rulebox">
<h4>Task numbering & reference notation</h4>
<p class="r"><b>Numbering:</b> <code>0.x</code> pre-cleanup · <code>1.x</code>, <code>2.x</code>, <code>n.x</code> phases. Task groups are <code>### N.N</code>.</p>
<p class="r"><b>References use node IDs:</b> <code>[FR-1]</code> spec · <code>[NFR-2]</code> spec · <code>[DES-API]</code> design · <code>[1.1]</code> another task.</p>
</div>
</section>
<section id="tasks-sessions" style="padding-top:34px">
<h3 style="margin-top:0">Work Sessions — the two-signature ledger</h3>
<div class="anatomy first">
<div class="an-code"><span class="h">## Work Sessions</span>
| Date | Task | Action | Files Modified | Agent | Human |
| ---------- | ---- | ----------- | ------------------- | ----- | ----- |
| 2026-07-19 | 1.1 | Implemented | src/export/csv-writer.ts | <span class="g">[x]</span> | [ ] |
| 2026-07-19 | 1.1 | Verified | - | <span class="g">[x]</span> | <span class="g">[x]</span> |</div>
<div class="an-explain">
<span class="an-tag req">Always the last section · append-only</span>
<h4>Nothing ships on the model's word</h4>
<p>A fixed six-column schema — <code>Date · Task · Action · Files Modified · Agent · Human</code> — appended to, never rewritten. Every row carries two checkboxes.</p>
<ul>
<li><b>Agent <code>[x]</code></b> lands when <code>verify</code> passes its evidence checks.</li>
<li><b>Human <code>[x]</code></b> is yours to give. A task is done only when both are ticked.</li>
</ul>
<p>The same schema appears in Dash and Sprint files — which is why graduating up the ladder is lossless.</p>
</div>
</div>
</section>
<!-- THE @see CONTRACT -->
<section id="see">
<h2><span class="n">05</span>The <span class="mono" style="font-size:0.8em">@see</span> contract</h2>
<p class="sec-lede">All that notation exists for one payoff: code that points back at the requirement that justified it — and stays pointed even as the docs evolve.</p>
<div class="anatomy first">
<div class="an-code"><span class="c">/**</span>
<span class="c"> * Streams a room's ledger as CSV.</span>
<span class="c"> *</span>
<span class="c"> * @see</span> docs/specs/room-export/spec.md <span class="a">[FR-1]</span>
<span class="c"> * @see</span> docs/specs/room-export/design.md <span class="a">[DES-ARCH]</span>
<span class="c"> */</span>
export function exportRoomCsv(…) { … }</div>
<div class="an-explain">
<h4>The editor reads every ID on the line</h4>
<p>The extension parses the <code>@see</code> path <em>and</em> each bracketed ID separately — and a single line can carry several, like <code>spec.md [FR-2] [FR-3]</code>. Each one becomes live:</p>
<ul>
<li><b>Hover</b> a <code>[FR-3]</code> → a preview of that exact section pops up inline — you read the requirement without leaving the file.</li>
<li><b>Click</b> it → the cursor jumps straight to that table row or heading in the spec. Not just the file — the exact line.</li>
<li><b>Rename-proof:</b> it resolves the ID against the row or heading, so renaming the section title changes nothing. The hover and the jump still land as long as the ID is intact.</li>
<li><b>A broken link</b> (retired ID, moved file) surfaces as a diagnostic, not silent rot — and <code>/afx-check trace</code> audits every <code>@see</code> in CI, headless.</li>
</ul>
</div>
</div>
<div class="callout"><b>The whole loop, closed:</b> requirement <code>[FR-1]</code> in the spec → a decision <code>[DES-ARCH]</code> in the design → a task <code>1.1</code> that implements it → a function that <code>@see</code>s both. Change a heading, and it all still resolves — because you anchored to the ID, not the words.</div>
</section>
<!-- THE THREE SHAPES -->
<section id="shapes">
<h2><span class="n">06</span>The three shapes — <em style="font-style:normal;color:var(--ember)">dash · sprint · full</em></h2>
<p class="sec-lede">The same notation scales down. A one-line bug fix doesn't need four files, and a quarter-long feature shouldn't live in one. AFX gives the exact same anchors — <code>FR-N</code>, <code>[DES-*]</code>, <code>### N.N</code>, <code>@see</code>, the two-signature Work Sessions ledger — in three sizes. Pick the smallest that fits; graduate up when scope grows, and nothing is lost.</p>
<div class="tscroll">
<table class="cmd-table" style="min-width:820px">
<thead><tr><th> </th><th>Dash</th><th>Sprint</th><th>Full</th></tr></thead>
<tbody>
<tr>
<td><b>Frontmatter <code>type</code></b></td>
<td><code>DASH</code></td>
<td><code>SPRINT</code></td>
<td><code>SPEC</code> · <code>DESIGN</code> · <code>TASKS</code></td>
</tr>
<tr>
<td><b>Files on disk</b></td>
<td>one — <code><feature>.md</code></td>
<td>one — <code><feature>.md</code> + <code>journal.md</code></td>
<td>four — <code>spec.md</code> · <code>design.md</code> · <code>tasks.md</code> · <code>journal.md</code></td>
</tr>
<tr>
<td><b>Sections</b></td>
<td>Purpose · Tasks · Work Sessions</td>
<td>References · Spec · Design · Tasks · Work Sessions (one doc)</td>
<td>The full 8 / 12+2 / task set, split across files</td>
</tr>
<tr>
<td><b>Approval gating</b></td>
<td class="opt">none — just do the work</td>
<td>frontmatter <code>approval:</code> block — spec → design → tasks, in order</td>
<td>per-file <code>status:</code> — <code>Draft → Approved → Superseded</code></td>
</tr>
<tr>
<td><b>Best for</b></td>
<td>surgical fix, known-scope bug, focused refactor, small config/test work</td>
<td>a real feature that's still small — needs spec + design + tasks, fast</td>
<td>larger, multi-phase, strategic work worth splitting</td>
</tr>
<tr>
<td><b>Same anchors?</b></td>
<td colspan="3" style="color:var(--moss)">Yes — identical <code>FR-N</code> / <code>[DES-*]</code> / <code>### N.N</code> / <code>@see</code> notation and the same six-column Work Sessions ledger in all three.</td>
</tr>
<tr>
<td><b>Graduates to</b></td>
<td>Sprint or Full</td>
<td>Full (<code>/afx-sprint graduate</code>)</td>
<td class="opt">— (the ceiling)</td>
</tr>
</tbody>
</table>
</div>
<p class="shot-cap">All three live under <code>docs/specs/<feature>/</code>. Graduation is a lossless promote: IDs and Work Sessions carry over verbatim, only <code>@see</code> paths retarget.</p>
<!-- where the files land -->
<h3 id="shapes-tree">Where the files land</h3>
<p>Everything sits in one feature folder. The shape decides how many files — never where they go.</p>
<div class="anatomy first">
<div class="an-code"><span class="c"># Dash — one file</span>
docs/specs/room-export/
└─ <span class="a">room-export.md</span> <span class="c"># type: DASH</span>
<span class="c"># Sprint — one file + journal</span>
docs/specs/room-export/
├─ <span class="a">room-export.md</span> <span class="c"># type: SPRINT</span>
└─ <span class="b">journal.md</span> <span class="c"># session continuity</span>
<span class="c"># Full — the four-file set</span>
docs/specs/room-export/
├─ <span class="a">spec.md</span> <span class="c"># type: SPEC</span>
├─ <span class="a">design.md</span> <span class="c"># type: DESIGN</span>
├─ <span class="a">tasks.md</span> <span class="c"># type: TASKS</span>
└─ <span class="b">journal.md</span></div>
<div class="an-explain">
<h4>One folder, one feature</h4>
<p>The folder name is the feature slug. A <b>Dash</b> and a <b>Sprint</b> both use <code><feature>.md</code> as the single doc — the <code>type:</code> in frontmatter is what tells them apart. Graduating a Sprint to Full keeps the folder and adds the split files beside it.</p>
<ul>
<li><b>journal.md</b> appears from Sprint up — it's the append-only session log that keeps continuity across agent runs.</li>
<li>ADRs and research live one level deeper, under <code>research/</code> — covered in <a class="tx" href="#credits">the sources below</a>.</li>
</ul>
</div>
</div>
<!-- DASH EXAMPLE -->
<h3 id="ex-dash">A worked Dash</h3>
<p>The lightest shape: a purpose, a task group, and the ledger. No status, no gates — for when the scope is already clear.</p>
<div class="anatomy first">
<div class="an-code"><span class="c">---</span>
<span class="b">afx</span>: <span class="g">true</span>
<span class="b">type</span>: <span class="a">DASH</span>
<span class="b">owner</span>: <span class="g">"@rix"</span>
<span class="b">tags</span>: [<span class="g">"room-export"</span>]
<span class="c">---</span>
<span class="h"># Room Export — CSV button</span>
<span class="h">## Purpose</span>
Members can't get their ledger out. Add a
CSV export button to the room menu.
- <span class="a">**Observed**</span>: no way to export the ledger
- <span class="a">**Expected**</span>: one click → CSV download
- <span class="a">**Scope**</span>: CSV only; no PDF, no schedule
<span class="h">## Tasks</span>
<span class="h">### <span class="a">1.1</span> CSV export button</span>
<span class="c"><!-- files: src/room/export.ts --></span>
- [ ] Streams rows without buffering
- [ ] Totals row matches the ledger sum
<span class="h">## Work Sessions</span>
| Date | Task | Action | Files Modified | Agent | Human |
| ---- | ---- | ------ | -------------- | ----- | ----- |</div>
<div class="an-explain">
<span class="an-tag opt">Two authored sections + one generated</span>
<h4>Structure without ceremony</h4>
<p><b>Purpose</b> replaces the whole spec — the "why", with optional <code>Observed / Expected / Scope</code> labels that suit a bug. <b>Tasks</b> uses the exact same <code>### N.N</code> groups and <code><!-- files: --></code> scope as a full tasks file.</p>
<ul>
<li>No <code>status</code>, no <code>approval</code> block — a Dash is trusted to just move.</li>
<li><b>Work Sessions</b> is still the same six-column ledger, appended by <code>/afx-dash code</code> and <code>verify</code>.</li>
<li>Outgrows itself? <code>type: DASH → SPRINT</code> in place, or graduate to the four-file set — task IDs and ledger untouched.</li>
</ul>
</div>
</div>
<!-- SPRINT EXAMPLE -->
<h3 id="ex-sprint">A worked Sprint</h3>
<p>Spec, design, and tasks in one document — separated by HTML-comment markers the tools parse against, gated by an <code>approval:</code> block in the frontmatter.</p>
<div class="anatomy first">
<div class="an-code"><span class="c">---</span>
<span class="b">afx</span>: <span class="g">true</span>
<span class="b">type</span>: <span class="a">SPRINT</span>
<span class="b">status</span>: <span class="a">Draft</span>
<span class="b">approval</span>:
<span class="b">spec</span>: <span class="a">Approved</span> <span class="c"># Draft | Approved</span>
<span class="b">design</span>: <span class="a">Draft</span> <span class="c"># gated on spec: Approved</span>
<span class="b">tasks</span>: <span class="a">Draft</span> <span class="c"># gated on design: Approved</span>
<span class="c">---</span>
<span class="h"># Room Export — Sprint Brief</span>
<span class="c"><!-- SPRINT-SECTION-START: SPEC --></span>
<span class="h">## 1. Spec</span>
<span class="h">### Requirements</span>
| ID | Requirement | Priority |
| <span class="g">FR-1</span> | Export as CSV | Must Have |
<span class="c"><!-- SPRINT-SECTION-END: SPEC --></span>
<span class="c"><!-- SPRINT-SECTION-START: DESIGN --></span>
<span class="h">## 2. Design</span>
<span class="h">### <span class="a">[DES-ARCH]</span> Architecture</span>
<span class="c"><!-- SPRINT-SECTION-END: DESIGN --></span>
<span class="c"><!-- SPRINT-SECTION-START: TASKS --></span>
<span class="h">## 3. Tasks</span>
<span class="h">#### <span class="a">1.1</span> CSV writer</span>
<span class="c"><!-- @see room-export.md [FR-1] [DES-ARCH] --></span>
<span class="c"><!-- SPRINT-SECTION-END: TASKS --></span>
<span class="h">## 4. Work Sessions</span></div>
<div class="an-explain">
<span class="an-tag req">One doc · four numbered sections</span>
<h4>The whole loop in a single file</h4>
<ul>
<li><b>Section markers</b> — <code><!-- SPRINT-SECTION-START: SPEC --></code> … <code>END</code> wrap each stage. They're what the graduate command extracts against, and what the extension reads to fill the stepper.</li>
<li><b>Headings promote one level</b> — <code>### Requirements</code> here becomes <code>## Requirements</code> when it graduates to <code>spec.md</code>. The IDs don't move.</li>
<li><b>The <code>approval:</code> block</b> gates in order: design can't be approved until <code>spec: Approved</code>, tasks until <code>design: Approved</code>.</li>
<li>The <code>@see</code> line points at <code>room-export.md</code> while it's a sprint; <code>graduate</code> retargets it to <code>design.md</code> / <code>spec.md</code>.</li>
</ul>
<p style="margin-top:10px">Sections 1–4 mirror the parent templates exactly — which is what makes <code>/afx-sprint graduate</code> a clean extract-and-promote rather than a rewrite.</p>
</div>
</div>
<!-- FULL EXAMPLE -->
<h3 id="ex-full">A worked Full spec</h3>
<p>Nothing new to learn — the full shape is simply the three files you've already met on this page, split out and gated independently. Sections <a class="tx" href="#spec">02</a>, <a class="tx" href="#design-rules">03</a>, and <a class="tx" href="#tasks">04</a> above <em>are</em> the worked full example, dissected row by row.</p>
<div class="rulebox">
<h4>What "graduating to full" actually does</h4>
<p class="r"><b>1.</b> Splits the one doc into <code>spec.md</code> · <code>design.md</code> · <code>tasks.md</code>, promoting each section's heading level back up.</p>
<p class="r"><b>2.</b> Keeps every <code>FR-N</code>, <code>[DES-*]</code>, <code>### N.N</code> ID and the entire Work Sessions ledger <b>verbatim</b>.</p>
<p class="r"><b>3.</b> Retargets <code>@see</code> paths from <code><feature>.md</code> to the new <code>spec.md</code> / <code>design.md</code>, preserving the node IDs.</p>
<p class="r"><b>4.</b> Archives the original as <code><feature>.md.archived</code> — never deletes it, never leaves two competing sources of truth.</p>
<div class="ok">✓ Because the notation is identical at every size, moving up the ladder never touches your code's <code>@see</code> IDs — only the paths beside them.</div>
</div>
<div class="callout"><b>The rule of thumb:</b> start at the smallest shape you're sure covers the work. A Dash that grows a design discussion wants to be a Sprint; a Sprint sprouting phases and multiple approvers wants to be Full. You never pay the four-file tax until the work earns it.</div>
<div class="callout" style="border-color:rgba(168,76,48,0.35);background:rgba(168,76,48,0.05)"><b>The honest trade-off:</b> structure buys determinism, and determinism costs tokens. Every refinement — spec → design → tasks, plus each review pass — is another model round-trip. That's the real dial these three shapes give you: a Dash is cheap and loose; a full spec is more deliberate, more reproducible, and more expensive to produce. Spend the ceremony where you genuinely need the same output twice — a payments flow, a migration, a public API — and stay light everywhere else. SDD isn't "always do the most"; it's "match the rigour to the risk."</div>
</section>
<!-- STEPPER -->
<section id="stepper">
<h2><span class="n">07</span>How the stepper reads it</h2>
<p class="sec-lede">The notation isn't just for humans and CI — the VS Code extension parses these same files live. Open an SDD doc and a four-pill stepper appears in the chat composer: <b>Spec · Design · Tasks · Work</b>. It's not decoration; every pill's colour is read straight from the frontmatter, and clicking one navigates the workflow.</p>
<div class="anno">
<img src="assets/screenshots/chat-spec-stepper.png" alt="The AFX chat composer showing the four-pill SDD stepper — Spec active in brass, Design in soft brass, Tasks and Work dotted — with an intent label and a row of stage commands below" loading="lazy" decoding="async" />
<span class="anno-pin" data-n="1" style="left:4%;top:72%">1</span>
<span class="anno-pin" data-n="2" style="left:3%;top:75.8%">2</span>
<span class="anno-pin" data-n="3" style="left:34.3%;top:75.8%">3</span>
<span class="anno-pin" data-n="4" style="left:72%;top:75.8%">4</span>
<span class="anno-pin" data-n="5" style="left:50%;top:79.1%">5</span>
<span class="anno-pin" data-n="6" style="left:16%;top:82.3%">6</span>
</div>
<div class="anno-legend">
<div class="anno-item" data-n="1"><span class="num">1</span><div><h5>The active doc</h5><p><code>SPEC.MD · DRAFT</code>, with a Preview toggle. The stepper always reflects whichever SDD file is open in the editor.</p></div></div>
<div class="anno-item" data-n="2"><span class="num">2</span><div><h5>Spec — active</h5><p>The open file: it carries the brass ring. Its fill tone is read from this file's <code>status</code> — soft for <code>Draft</code>, solid once approved.</p></div></div>
<div class="anno-item" data-n="3"><span class="num">3</span><div><h5>Design — reached</h5><p>Filled, and the connector into it is solid, because the host read the sibling <code>design.md</code>'s frontmatter — it's on the ladder, not pending.</p></div></div>
<div class="anno-item" data-n="4"><span class="num">4</span><div><h5>Tasks & Work — pending</h5><p>Dotted and muted: no status read yet. Once tasks exist, this pill shows a live <code>done/total</code> count and the line to it fills in.</p></div></div>
<div class="anno-item" data-n="5"><span class="num">5</span><div><h5>The intent label</h5><p>Names the current stage's job — <em>"Spec — clarify requirements, acceptance, and scope."</em> It changes with the active pill.</p></div></div>
<div class="anno-item" data-n="6"><span class="num">6</span><div><h5>The stage commands</h5><p>The <code>/afx-*</code> commands for this stage as buttons. <span class="mono" style="color:var(--accent-deep)">✎ Draft</span> ones edit in the composer first; <span class="mono" style="color:var(--ember)">⚡ Auto</span> ones run immediately.</p></div></div>
</div>
<p class="shot-cap">Real capture — the chat composer with a spec open. <span class="gen">annotated screenshot</span></p>
<h3 id="stepper-parse">Where each pill's colour comes from</h3>
<p>The host reads the frontmatter and turns it into a pill status. There's no hidden state — the file <em>is</em> the state.</p>
<div class="tscroll">
<table class="cmd-table" style="min-width:680px">
<thead><tr><th>Frontmatter says</th><th>Pill becomes</th><th>Looks like</th></tr></thead>
<tbody>
<tr><td><code>status: Approved</code></td><td><b>approved</b></td><td>solid brass, filled connector</td></tr>
<tr><td><code>status: Draft</code></td><td><b>draft</b></td><td>soft brass</td></tr>
<tr><td>gated on an unapproved upstream</td><td><b>blocked</b></td><td>amber</td></tr>
<tr><td>tasks with open checkboxes</td><td><b>progress</b></td><td>brass with a <code>3/8</code> count · gradient line</td></tr>
<tr><td>no file / no status read</td><td><b>pending</b></td><td>dotted, muted</td></tr>
</tbody>
</table>
</div>
<div class="anatomy first" style="margin-top:26px">
<div class="an-code"><span class="c"># Standard 4-file — reads each sibling's status</span>
spec.md <span class="b">status</span>: <span class="a">Approved</span> <span class="c">→ pill 1 solid</span>
design.md <span class="b">status</span>: <span class="a">Draft</span> <span class="c">→ pill 2 soft</span>
tasks.md <span class="a">3/8</span> boxes ticked <span class="c">→ pill 3 progress</span>
<span class="c"># Sprint single-file — reads the approval block</span>
<span class="b">approval</span>:
<span class="b">spec</span>: <span class="a">Approved</span> <span class="c">→ pill 1 solid</span>
<span class="b">design</span>: <span class="a">Draft</span> <span class="c">→ pill 2 soft</span>
<span class="b">tasks</span>: <span class="a">Draft</span> <span class="c">→ pill 3 draft</span></div>
<div class="an-explain">
<h4>Two files, one stepper</h4>
<p>In the <b>standard 4-file</b> layout each pill reads its own file's <code>status</code>. In <b>sprint</b> mode the four pills read the single <code>approval:</code> block instead — same four stages, one document.</p>
<ul>
<li><b>Clicking a pill</b> navigates: in 4-file mode it opens the sibling <code>spec.md</code> / <code>design.md</code> / <code>tasks.md</code>; in sprint mode it jumps to that <code>SPRINT-SECTION</code> heading in the one file.</li>
<li><b>The connector line</b> fills solid up to the last reached stage, and shows a gradient across Tasks as the checkboxes complete.</li>
<li>This is the same node-ID discipline paying off: the stepper is just a parser reading anchors and frontmatter you can see in plain text.</li>
</ul>
</div>
</div>
</section>
<!-- PREVIEWER -->
<section id="previewer">
<h2><span class="n">08</span>How the previewer renders it</h2>
<p class="sec-lede">Open an AFX doc in the workbench previewer and it's more than pretty markdown. The frontmatter becomes status chips; the checkboxes become live toggles; and — because the parser knows what each section <em>is</em> — the right <code>/afx-*</code> commands hang off the right parts of the document as small toolbars. The notation is what makes that possible.</p>
<div class="anno">
<img src="assets/screenshots/previewer-sprint.png" alt="The AFX workbench previewer rendering a sprint document: metadata chips in the header, inline task-row commands, live checkboxes, node-ID links, a Work Sessions sign-off toolbar, the sessions table, and a Quality pulse rail with a Refinement coach" loading="lazy" decoding="async" />
<span class="anno-pin" data-n="1" style="left:61%;top:2.3%">1</span>
<span class="anno-pin" data-n="2" style="left:22%;top:12%">2</span>
<span class="anno-pin" data-n="3" style="left:18.5%;top:32.6%">3</span>
<span class="anno-pin" data-n="4" style="left:41%;top:54%">4</span>
<span class="anno-pin" data-n="5" style="left:44%;top:66.8%">5</span>
<span class="anno-pin" data-n="6" style="left:54%;top:84.4%">6</span>
<span class="anno-pin" data-n="7" style="left:91.5%;top:27%">7</span>
<span class="anno-pin" data-n="8" style="left:88%;top:85.5%">8</span>
</div>
<div class="anno-legend">
<div class="anno-item" data-n="1"><span class="num">1</span><div><h5>Frontmatter → status chips</h5><p>The YAML header is stripped from the body and re-shown as chips: <code>type · status · version · owner · updated</code>. Never duplicated as bold lines.</p></div></div>
<div class="anno-item" data-n="2"><span class="num">2</span><div><h5>Inline task commands</h5><p>Each phase and task group gets a <code>Code Phase 2</code> / <code>Code 2.1</code> button, built from its <code>### N.N</code> ID — draft a coding command for exactly that task.</p></div></div>
<div class="anno-item" data-n="3"><span class="num">3</span><div><h5>Live checkboxes</h5><p>Acceptance and task boxes are real inputs. Ticking one writes straight back to the markdown — not a read-only render.</p></div></div>
<div class="anno-item" data-n="4"><span class="num">4</span><div><h5>Node-ID links</h5><p><code>[FR-1]</code>, <code>[DES-UI]</code> in the Cross-Reference Index resolve as links — the same anchors code <code>@see</code>s.</p></div></div>
<div class="anno-item" data-n="5"><span class="num">5</span><div><h5>The sign-off toolbar</h5><p><b>Sessions:</b> Approve, Agent all, Human all. The two-signature ledger, made clickable. <span class="do">Approve ticks Human on every Agent-verified row</span></p></div></div>
<div class="anno-item" data-n="6"><span class="num">6</span><div><h5>Work Sessions table</h5><p>The append-only ledger itself, with its Agent and Human columns — the source of truth the toolbar above acts on.</p></div></div>
<div class="anno-item" data-n="7"><span class="num">7</span><div><h5>Quality pulse</h5><p>A scorecard — Strategy · Structure · Clarity · Completeness — plus a plain-English list of what's still to review. Read from the doc, live.</p></div></div>
<div class="anno-item" data-n="8"><span class="num">8</span><div><h5>Refinement coach</h5><p>Turns a gap into an action: here it spots open tasks and offers a <code>Verify tasks</code> button that runs the command for you.</p></div></div>
</div>
<p class="shot-cap">Real capture — a sprint doc in the workbench previewer. <span class="gen">annotated screenshot</span></p>
<h3 id="previewer-render">What it does under the hood</h3>
<p>The body is rendered with a standard markdown engine (react-markdown + GFM), so tables, checklists, and code fences all render correctly. On top of that, three things make it an <em>AFX</em> previewer:</p>
<ul style="color:var(--text-secondary);font-size:14.5px;max-width:720px;padding-left:20px">
<li style="margin:6px 0"><b>Frontmatter is lifted out</b> and shown as status chips, never left as raw YAML in the body.</li>
<li style="margin:6px 0"><b>Checkboxes are wired</b> — task and Work Sessions ticks write back to the file.</li>
<li style="margin:6px 0"><b>Sections are recognised</b> and given command toolbars. This is where the node IDs earn their keep.</li>
</ul>
<h3 id="previewer-toolbars">The toolbars, and where they attach</h3>
<p>A toolbar is a row of the stage's <code>/afx-*</code> commands. Each button is either <span class="mono" style="color:var(--accent-deep)">✎ Draft</span> (drops the command into the chat composer to edit) or <span class="mono" style="color:var(--ember)">⚡ Auto</span> (runs it immediately). They attach at three levels:</p>
<div class="tscroll">
<table class="cmd-table" style="min-width:720px">
<thead><tr><th>Scope</th><th>Attaches to</th><th>How it's matched</th></tr></thead>
<tbody>
<tr><td><b>Document</b></td><td>the whole file, in the header</td><td>the frontmatter <code>type</code> (SPEC / DESIGN / TASKS / SPRINT / …)</td></tr>
<tr><td><b>Section</b></td><td>the top of each major section</td><td>the <code>SPRINT-SECTION</code> kind, or the heading's text</td></tr>
<tr><td><b>Inline</b></td><td>after a heading, phase, or task group</td><td>the <code>### N.N</code> task ID or the heading slug</td></tr>
</tbody>
</table>
</div>
<h3 id="previewer-mapping">What each section offers</h3>
<p>Because the parser knows a Tasks section from a Spec section, it can offer the commands that actually apply there. In a <b>sprint</b> doc the section toolbars map by kind:</p>
<div class="tscroll">
<table class="cmd-table" style="min-width:760px">
<thead><tr><th>Sprint section</th><th>Draft ✎</th><th>Auto ⚡</th></tr></thead>
<tbody>
<tr><td><code>## 1. Spec</code></td><td>Refine spec · Author design</td><td>Verify spec · Approve spec</td></tr>
<tr><td><code>## 2. Design</code></td><td>Refine design · Author tasks</td><td>Verify design · Approve design</td></tr>
<tr><td><code>## 3. Tasks</code></td><td>Refine tasks · Code tasks · Graduate</td><td>Verify tasks · Approve tasks</td></tr>
<tr><td><code>## 4. Work Sessions</code></td><td colspan="2">the sign-off toolbar (Approve · Agent all · Human all)</td></tr>