AtlatestRepositoryfolio

folio / tree / testtest-integrity-checker.sgl

1;;; The independent buried-id scanner ((folio integrity)).
2;;;
3;;; The withdrawn attempt's checker failed twice: first it shared the
4;;; parser's fence state machine (an unterminated fence blinded both sides
5;;; at once), then its rewrite applied one `task-id?` predicate to BOTH
6;;; sides of the comparison (anything the predicate wrongly rejected was
7;;; structurally invisible). This suite proves the replacement can see
8;;; precisely the things the parser cannot -- a checker that only finds what
9;;; the parser already finds is the same defect wearing a new coat.
11(import (sigil test)
12 (sigil struct)
13 (sigil string)
14 (sigil list)
15 (sigil dict)
16 (sigil fs)
17 (folio store)
18 (folio task)
19 (folio inbox)
20 (folio integrity)
21 (folio tools))
23;; The current LIVE ~/Ops/folio/inbox.md shape: a header, a multi-paragraph
24;; terminated comment documenting the burial history (with an id and a
25;; wikilink inside), and ZERO items. An empty-but-not-blank file is exactly
26;; where a boundary bug hides.
27(define live-inbox-shape
28 (string-join
29 (list "# Inbox"
30 ""
31 "<!--"
32 "Emptied 2026-08-02. Six further items folio could not see were"
33 "re-filed as real tasks."
34 ""
35 "The capture folio showed as t-6c4269 was only the FIRST LINE of a"
36 "multi-line item; the rest of it sat here with `{id: t-a979a6}`"
37 "invisible to every folio query."
38 ""
39 "That is the bug in [[t-a6ba]] behaving exactly as documented."
40 ""
41 "Capture items as `- [ ] <text>` on ONE line."
42 "-->")
43 "\n"))
45(test-group "raw scan: what it must see"
47 ;; RED ARM at unit level: the planted burial IS reported.
48 (test "a planted buried id is found, with its exact value and line"
49 (let ((occs (raw-task-ids "prose above\n\nSee refs. {id: t-a979a6, tags: x}")))
50 (assert-equal 1 (length occs))
51 (assert-equal 3 (car (car occs)))
52 (assert-equal "t-a979a6" (cdr (car occs)))))
54 ;; The checker must see what the PARSER CANNOT -- independence is only
55 ;; real if the two nets disagree on exactly these shapes.
56 (test "sees an id the parser cannot: no checkbox anywhere"
57 (assert-equal '("t-da90")
58 (map cdr (raw-task-ids "plain prose {id: t-da90}"))))
60 (test "sees an UPPERCASE id (no hex filtering)"
61 (assert-equal '("t-A8FD")
62 (map cdr (raw-task-ids "lost. {id: t-A8FD, priority: high}"))))
64 (test "sees a no-space id (no hard-coded needle)"
65 (assert-equal '("t-bbbb") (map cdr (raw-task-ids "lost. {id:t-bbbb}"))))
67 (test "extracts a long id whole, never a sliced phantom"
68 (assert-equal '("t-abcdef") (map cdr (raw-task-ids "x {id: t-abcdef}"))))
70 (test "reads the value to its delimiter, not a fixed width"
71 (assert-equal '("t-12") (map cdr (raw-task-ids "{id: t-12, tags: y}")))))
73(test-group "raw scan: what it must NOT see"
75 ;; Withdrawn defect 16: `parent-id:` matched an `id:` scan and produced a
76 ;; permanent, unclearable warning. The boundary test forbids it.
77 (test "parent-id is not an id"
78 (assert-equal '("t-1111")
79 (map cdr (raw-task-ids "- [ ] x {id: t-1111, parent-id: t-2222}"))))
81 (test "a foreign id value is out of scope by design"
82 (assert-equal '() (raw-task-ids "upstream {id: 3, port: 80} {id: JIRA-421}")))
84 (test "an id inside a TERMINATED comment is annotation, not a burial"
85 (assert-equal '() (raw-task-ids "<!-- recovered {id: t-a979a6} -->")))
87 (test "the live inbox shape is clean: comment id skipped, wikilink ignored"
88 (assert-equal '() (raw-task-ids live-inbox-shape)))
90 ;; The dual guard: an UNTERMINATED opener is NOT a comment. Skipping to
91 ;; end-of-file on it would rebuild the withdrawn attempt's unterminated-
92 ;; fence blindness; the failure direction here is over-reporting.
93 (test "an unterminated comment opener does not blind the scan"
94 (assert-equal '("t-yyyy")
95 (map cdr (raw-task-ids "<!--\nlost {id: t-yyyy}")))))
97(test-group "buried = raw minus accounted, both sides verbatim"
99 (test "a visible id is accounted for; a buried one is not"
100 (let ((buried (buried-task-ids
101 "- [ ] seen {id: t-0001}\nlost prose {id: t-0002}"
102 '("t-0001"))))
103 (assert-equal '("t-0002") (map cdr buried))))
105 ;; Named defect 2: no predicate on either side. An uppercase id passed
106 ;; verbatim as visible must cancel its own raw occurrence -- and stay
107 ;; reported when it is NOT visible.
108 (test "uppercase cancels only when actually visible"
109 (assert-equal '()
110 (buried-task-ids "- [ ] x {id: t-A8FD}" '("t-A8FD")))
111 (assert-equal '("t-A8FD")
112 (map cdr (buried-task-ids "lost {id: t-A8FD}" '()))))
114 ;; Defect 16's permanence lesson, live shape: a task whose TEXT quotes id
115 ;; examples (this redesign's own brief sits in ops-platform.md quoting
116 ;; `{id:t-bbbb}`). Content inside a visible block is reachable as that
117 ;; task's text -- reporting it forever trains the reader to ignore the
118 ;; banner. The SAME quote outside any block is genuinely buried.
119 (test "an id quoted inside a visible block is not a burial; outside it is"
120 (assert-equal '()
121 (buried-task-ids
122 "- [ ] the brief quotes {id:t-bbbb} and {id: t-XXXX} {id: t-b00c}"
123 '("t-b00c")
124 '((1 . 1))))
125 (assert-equal '("t-bbbb")
126 (map cdr (buried-task-ids
127 "loose prose quoting {id:t-bbbb}"
128 '("t-b00c")
129 '((3 . 5)))))))
131(test-group "end to end: the live estate shapes"
133 (test "the current live inbox shape is a no-op for the listing"
134 (call-with-temp-directory
135 (lambda (dir)
136 (let ((store (make-folio-store dir)))
137 (write-file-string (store-inbox-path store) live-inbox-shape)
138 (let ((out ((make-tool-inbox store) (dict))))
139 (assert-false (string-contains? out "INTEGRITY"))
140 ;; byte-identical afterwards: comment, id, wikilink all intact
141 (assert-equal live-inbox-shape
142 (read-file-string (store-inbox-path store))))))))
144 (test "grep-count and folio-count agree on the live inbox shape"
145 (call-with-temp-directory
146 (lambda (dir)
147 (let ((store (make-folio-store dir)))
148 (write-file-string (store-inbox-path store) live-inbox-shape)
149 ;; zero visible tasks, zero REPORTABLE raw ids: the one raw
150 ;; occurrence sits in a terminated comment, which is the
151 ;; documented annotation channel
152 (assert-equal 0 (length (read-inbox store)))
153 (assert-equal '() (raw-task-ids live-inbox-shape)))))))