AtlatestRepositoryfolio
1
;;; Regression fixtures from the WITHDRAWN 2026-08-02 block-parsing attempt.2
;;;3
;;; Two adversarial review rounds found sixteen data-loss defects in it, and4
;;; round two's fixes introduced four more of the same character. The four5
;;; named round-two regressions each get a fixture here, plus the destructive6
;;; shapes from the sixteen-defect table that apply to the redesign. These7
;;; are the known-bad inputs; every one destroyed (or hid) data under the8
;;; withdrawn code and must be inert under the syntactic-extent model.9
;;;10
;;; See archive/tasks/folio-inbox-integrity-and-bulk-moves ("Handoff to the11
;;; redesign worker") for the full classified table.13
(import (sigil test)14
(sigil struct)15
(sigil string)16
(sigil list)17
(sigil dict)18
(sigil fs)19
(sigil path)20
(folio store)21
(folio task)22
(folio id)23
(folio inbox)24
(folio project)25
(folio tools))27
(define (write-inbox! store content)28
(write-file-string (store-inbox-path store) content))30
(define (inbox-content store)31
(read-file-string (store-inbox-path store)))33
(test-group "named defect 1: #Notes deletion"35
;; Withdrawn round two: requiring a space after `#` fixed `#infra` but made36
;; `#Notes` a non-heading -- and removing ONE inbox item then deleted the37
;; #Notes section and both lines under it (5 lines -> `# Inbox`).38
(test "removing an item leaves every other line BYTE-IDENTICAL"39
(call-with-temp-directory40
(lambda (dir)41
(let ((store (make-folio-store dir)))42
(write-inbox! store43
(string-join44
(list "# Inbox"45
"- [ ] the item {id: t-aaaa}"46
"#Notes"47
"remember the milk"48
"remember the eggs")49
"\n"))50
(assert-true (inbox-remove-by-id! store "t-aaaa"))51
;; not "the section survives" -- the file must be EXACTLY the52
;; original minus the removed item's one line. A weaker assertion53
;; would have passed the withdrawn attempt's bug (file reduced to54
;; `# Inbox`) had it merely checked the item was gone.55
(assert-equal56
(string-join57
(list "# Inbox"58
"#Notes"59
"remember the milk"60
"remember the eggs")61
"\n")62
(inbox-content store))))))64
;; The dual: `#infra #urgent` as a hashtag body line. Under the withdrawn65
;; heading heuristic it split the block; here an UNINDENTED hashtag line66
;; belongs to no task and cannot be removed by a task operation, while an67
;; INDENTED one is body and travels with its item.68
(test "an unindented hashtag line survives an adjacent removal"69
(call-with-temp-directory70
(lambda (dir)71
(let ((store (make-folio-store dir)))72
(write-inbox! store73
(string-join74
(list "# Inbox"75
"- [ ] the item {id: t-aaab}"76
"#infra #urgent")77
"\n"))78
(assert-true (inbox-remove-by-id! store "t-aaab"))79
(assert-true (string-contains? (inbox-content store)80
"#infra #urgent")))))))82
(test-group "named defect 4: foreign ids are not folio's to touch"84
;; Withdrawn round two: a loosened backfill header-check DELETED85
;; `{id: JIRA-421}` from disk on a plain listing.86
(test "a task with a foreign id survives a listing byte-identically"87
(call-with-temp-directory88
(lambda (dir)89
(let ((store (make-folio-store dir)))90
(write-inbox! store "# Inbox\n- [ ] import the tracker {id: JIRA-421}\n")91
(let ((before (inbox-content store)))92
((make-tool-inbox store) (dict))93
(assert-equal before (inbox-content store)))))))95
;; Withdrawn round one, defect 1: `adopt-trailing-metadata` DELETED the96
;; body line `upstream {id: 3, port: 80}` from disk when the inbox was97
;; merely LISTED. Body lines are never metadata-parsed now.98
(test "a config fragment in a body survives a listing byte-identically"99
(call-with-temp-directory100
(lambda (dir)101
(let ((store (make-folio-store dir)))102
(inbox-add! store103
(string-join104
(list "proxy setup notes"105
"upstream {id: 3, port: 80}"106
"keep this verbatim")107
"\n")108
'((id . "t-abab")))109
(let ((before (inbox-content store))110
(out ((make-tool-inbox store) (dict))))111
(assert-equal before (inbox-content store))112
;; and the fragment is not reported as a burial113
(assert-false (string-contains? out "INTEGRITY"))114
;; the body round-trips115
(assert-true (string-contains?116
(folio-task-text (car (read-inbox store)))117
"upstream {id: 3, port: 80}")))))))119
(test "a config fragment survives COMPLETING its own task"120
(call-with-temp-directory121
(lambda (dir)122
(let ((store (make-folio-store dir)))123
(inbox-add! store "proxy notes\nupstream {id: 3, port: 80}"124
'((id . "t-abac")))125
(complete-task-in-store! store "t-abac")126
(let ((task (car (read-inbox store))))127
(assert-true (folio-task-done? task))128
(assert-true (string-contains? (folio-task-text task)129
"upstream {id: 3, port: 80}"))))))))131
(test-group "buried legacy shapes freeze normalisation instead of minting"133
;; The exact mechanism of every burial: a headless first line above a134
;; body whose LAST line carries the id. backfill must not mint a phantom135
;; onto the head; the listing must report the burial instead.136
(test "a legacy buried item is reported, and the file is not rewritten"137
(call-with-temp-directory138
(lambda (dir)139
(let ((store (make-folio-store dir)))140
(write-inbox! store141
(string-join142
(list "# Inbox"143
"- [ ] tighten sigil test discovery"144
""145
"Current behavior: it walks into deps."146
""147
"Cross-link the observation. {id: t-5506, tags: sigil}")148
"\n"))149
(let* ((before (inbox-content store))150
(out ((make-tool-inbox store) (dict))))151
;; loud: the burial is named with its id152
(assert-true (string-contains? out "INTEGRITY"))153
(assert-true (string-contains? out "t-5506"))154
;; frozen: no phantom mint, no rewrite of any kind155
(assert-equal before (inbox-content store)))))))157
;; Named defect 2 (task-id? on both sides): an UPPERCASE-hex id was158
;; invisible to the withdrawn check because a predicate filtered both159
;; sides of its comparison. Buried uppercase must be reported.160
(test "a buried UPPERCASE id is reported"161
(call-with-temp-directory162
(lambda (dir)163
(let ((store (make-folio-store dir)))164
(write-inbox! store165
"# Inbox\nlost prose capture. {id: t-A8FD, priority: high}\n")166
(let ((out ((make-tool-inbox store) (dict))))167
(assert-true (string-contains? out "INTEGRITY"))168
(assert-true (string-contains? out "t-A8FD")))))))170
;; Named defect 3 (hard-coded needle): `{id:t-bbbb}` -- no space after the171
;; colon -- is valid to the parser and was invisible to the withdrawn172
;; scanner. Buried, it must be reported.173
(test "a buried no-space id is reported"174
(call-with-temp-directory175
(lambda (dir)176
(let ((store (make-folio-store dir)))177
(write-inbox! store "# Inbox\nlost prose capture. {id:t-bbbb}\n")178
(let ((out ((make-tool-inbox store) (dict))))179
(assert-true (string-contains? out "INTEGRITY"))180
(assert-true (string-contains? out "t-bbbb"))))))))182
(test-group "tool-call markup is refused at the door"184
;; Measured live 2026-08-06: a malformed client tool call leaked185
;; `...</text>` and `<parameter name="priority">high` INTO the stored186
;; text, filing the capture under a phantom id with the real id buried187
;; (t-c52ae8; again minutes later as t-4ec418). folio cannot fix the188
;; client; it can refuse to store the wreckage.189
(test "inbox-add with leaked markup stores NOTHING and says why"190
(call-with-temp-directory191
(lambda (dir)192
(let ((store (make-folio-store dir)))193
(inbox-ensure! store)194
(let* ((before (inbox-content store))195
(out ((make-tool-inbox-add store)196
(dict text: "web-client-update returns #t for ... </text>\n<parameter name=\"priority\">high"))))197
(assert-true (string-contains? out "REFUSING"))198
(assert-true (string-contains? out "tool-call markup"))199
(assert-equal before (inbox-content store)))))))201
(test "task-add with leaked markup stores NOTHING"202
(call-with-temp-directory203
(lambda (dir)204
(let* ((store (make-folio-store dir))205
(file (project-create! store "Target" "Goal" #f))206
(out ((make-tool-task-add store)207
(dict text: "broken </parameter> capture"208
project: "target"))))209
(assert-true (string-contains? out "REFUSING"))210
(assert-equal 0 (length (folio-project-tasks (read-project file))))))))212
(test "task-update with leaked markup leaves the task unchanged"213
(call-with-temp-directory214
(lambda (dir)215
(let ((store (make-folio-store dir)))216
(inbox-add! store "clean item" '((id . "t-cc01")))217
(let ((out ((make-tool-task-update store)218
(dict id: "t-cc01"219
text: "mangled <parameter name=\"project\">enclave"))))220
(assert-true (string-contains? out "REFUSING"))221
(assert-equal "clean item"222
(folio-task-text (car (read-inbox store)))))))))224
;; positive control: the refusal is NARROW -- ordinary angle brackets,225
;; XML-ish prose and html tags are all legitimate capture content.226
(test "ordinary markup-ish text is still accepted"227
(call-with-temp-directory228
(lambda (dir)229
(let* ((store (make-folio-store dir))230
(out ((make-tool-inbox-add store)231
(dict text: "fix <div class=\"x\"> rendering and the <text> element handling"))))232
(assert-true (string-contains? out "Added to inbox"))233
(assert-equal 1 (length (read-inbox store))))))))235
(test-group "extent table: rewrite and removal shapes"237
;; Withdrawn defect 14: backfill DE-INDENTED a nested item on rewrite.238
(test "an indented header keeps its indentation through complete/reopen"239
(call-with-temp-directory240
(lambda (dir)241
(let ((store (make-folio-store dir)))242
(write-inbox! store243
(string-join244
(list "# Inbox"245
"grouping prose"246
" - [ ] nested child {id: t-cccc}")247
"\n"))248
(complete-task-in-store! store "t-cccc")249
(assert-true (string-contains? (inbox-content store)250
" - [x] nested child"))251
(uncomplete-task-in-store! store "t-cccc")252
(assert-true (string-contains? (inbox-content store)253
" - [ ] nested child"))))))255
;; Withdrawn defect 3: task-update on multi-line text truncated it to the256
;; first line. New text (of any line count) must replace the whole block.257
(test "task-update with multi-line text replaces the block, loses nothing"258
(call-with-temp-directory259
(lambda (dir)260
(let ((store (make-folio-store dir)))261
(inbox-add! store "old head\nold body" '((id . "t-dddd")))262
(inbox-add! store "neighbour" '((id . "t-eeee")))263
(update-task-in-store! store "t-dddd"264
(dict id: "t-dddd"265
text: "new head\nnew body line 1\n\nnew body line 2"))266
(let ((tasks (read-inbox store)))267
(assert-equal 2 (length tasks))268
(assert-equal "new head\nnew body line 1\n\nnew body line 2"269
(folio-task-text (car tasks)))270
(assert-equal "neighbour" (folio-task-text (cadr tasks))))))))272
;; Withdrawn defect 7/#neighbour class: removing one item must not disturb273
;; the body of the item next to it.274
(test "removal takes exactly one block; the neighbour's body is intact"275
(call-with-temp-directory276
(lambda (dir)277
(let ((store (make-folio-store dir)))278
(inbox-add! store "first item\nfirst body" '((id . "t-f001")))279
(inbox-add! store "second item\nsecond body" '((id . "t-f002")))280
(assert-true (inbox-remove-by-id! store "t-f001"))281
(let ((tasks (read-inbox store)))282
(assert-equal 1 (length tasks))283
(assert-equal "second item\nsecond body"284
(folio-task-text (car tasks))))285
(assert-false (string-contains? (inbox-content store) "first body"))))))287
;; Withdrawn defect 5: a move whose id matched only the `## Log` archive288
;; copy ate the narrative around it. An archived copy is a small indented289
;; block; moving it takes that block and nothing else.290
(test "moving a log-archived id leaves the surrounding narrative alone"291
(call-with-temp-directory292
(lambda (dir)293
(let* ((store (make-folio-store dir))294
(src (project-create! store "Source" "Goal" #f))295
(dst (project-create! store "Target" "Goal" #f)))296
;; construct the post-clean shape by hand297
(write-file-string src298
(string-join299
(list "---"300
"status: active"301
"---"302
""303
"# Source"304
""305
"## Tasks"306
""307
"## Log"308
""309
"### 2026-08-01"310
""311
"Completed:"312
" - [x] archived thing {id: t-f003, completed: 2026-08-01}"313
"Narrative before."314
"Narrative after.")315
"\n"))316
(move-task-in-store! store "t-f003" "target")317
(let ((content (read-file-string src)))318
(assert-true (string-contains? content "Narrative before."))319
(assert-true (string-contains? content "Narrative after."))320
(assert-false (string-contains? content "t-f003"))))))))