AtlatestRepositoryfolio
1
;;; The F1a pressure manuscript, built against real authored source.2
;;;3
;;; F1a freezes the MODEL and deliberately ships no reader, so this fixture is4
;;; how the freeze gets exercised against real bytes rather than against5
;;; synthetic locations. `folio-pressure-project.txt` is authored `#lang6
;;; folio/doc` source; every node below is constructed with a location computed7
;;; from that file, so `(source-slice-at (folio-node-location node))` returns8
;;; the exact authored text the node came from. A location that does not9
;;; correspond to real source cannot pass.10
;;;11
;;; NEEDLES MUST BE UNIQUE, AND THAT IS ENFORCED. `located` refuses a needle12
;;; occurring zero times OR more than once. The failure this prevents is quiet:13
;;; an ambiguous needle silently resolves to the FIRST match, so the fixture14
;;; would build, the suite would pass, and a node's recorded span would point at15
;;; the wrong region of the document. A test whose fixture is subtly wrong is16
;;; worse than no test, so ambiguity is an error here, not a preference.17
;;;18
;;; NOTE ON SCOPE, CORRECTED AFTER REVIEW: an earlier version of this comment19
;;; claimed the manuscript deliberately carries the vocabulary the leakage arm20
;;; forbids, and thereby proves that arm is correctly scoped. That was simply21
;;; false -- the manuscript contains none of `sqlite|fts5|json-rpc|mcp|slate`.22
;;; The arm's scoping is real (the rest of `src/folio/` is a working MCP server23
;;; and is full of those words), but this file is not what demonstrates it, and24
;;; a comment asserting evidence that does not exist is worse than no comment.25
;;; LOADED, NOT IMPORTED, and that is forced rather than stylistic. Measured:26
;;; under `sigil test` this package builds a native test harness, and a27
;;; `(fixtures …)` library on `-L test` does not resolve from inside it -- the28
;;; whole test file fails to LOAD, taking every assertion with it. No existing29
;;; folio test defines a helper library, so the pattern had no prior art here.30
;;; `(load …)` does work under that harness (probed, with a deliberate failing31
;;; assertion to confirm the runner still reports failures through it), so both32
;;; the model suite and the publication probe pull this file in with `load` and33
;;; supply the imports themselves. Paths are relative to the repository root.34
(define fixture-source "test/fixtures/folio-pressure-project.txt")35
(define fixture-manuscript (read-file-string fixture-source))37
(define (starts-at? needle at)38
(let ((n (string-length needle)))39
(and (<= (+ at n) (string-length fixture-manuscript))40
(string=? needle (substring fixture-manuscript at (+ at n))))))41
(define (count-occurrences needle)42
(let loop ((at 0) (seen 0))43
(if (> (+ at (string-length needle)) (string-length fixture-manuscript)) seen44
(loop (+ at 1) (if (starts-at? needle at) (+ seen 1) seen)))))45
(define (find-only needle)46
(let ((total (count-occurrences needle)))47
(if (= total 0) (error "fixture needle not found" needle)48
(if (> total 1) (error "fixture needle is not unique" needle total)49
(let loop ((at 0))50
(if (starts-at? needle at) at (loop (+ at 1))))))))52
;; Byte position and byte span, scalar line and column -- the same shape53
;; every other srcloc in this estate uses. The manuscript carries Greek,54
;; Japanese and an astral-plane emoji precisely so these two measures55
;; disagree and a byte/scalar confusion cannot pass unnoticed.56
(define (utf8-width c)57
(let ((n (char->integer c)))58
(cond ((<= n #x7f) 1) ((<= n #x7ff) 2) ((<= n #xffff) 3) (else 4))))59
(define (byte-count text end)60
(let loop ((i 0) (n 0)) (if (= i end) n (loop (+ i 1) (+ n (utf8-width (string-ref text i)))))))61
(define (byte->char-index byte-position)62
(let loop ((i 0) (bytes 0))63
(cond ((= bytes byte-position) i)64
((or (> bytes byte-position) (= i (string-length fixture-manuscript)))65
(error "location is not on a UTF-8 scalar boundary" byte-position))66
(else (loop (+ i 1) (+ bytes (utf8-width (string-ref fixture-manuscript i))))))))67
(define (source-slice-at loc)68
(let ((start (byte->char-index (srcloc-position loc)))69
(end (byte->char-index (+ (srcloc-position loc) (srcloc-span loc)))))70
(substring fixture-manuscript start end)))71
(define (line-column at)72
(let loop ((i 0) (line 1) (column 1))73
(if (= i at) (cons line column)74
(if (char=? (string-ref fixture-manuscript i) #\newline)75
(loop (+ i 1) (+ line 1) 1)76
(loop (+ i 1) line (+ column 1))))))77
(define (locate-text needle)78
(let* ((at (find-only needle)) (lc (line-column at)))79
(make-srcloc fixture-source (car lc) (cdr lc)80
(byte-count fixture-manuscript at)81
(byte-count needle (string-length needle)))))83
;; Every located node is recorded with the needle it was built from, so the84
;; gate can assert the slice for EVERY node rather than for a sample.85
(define fixture-location-expectations '())86
(define (located needle make)87
(let ((node (make (locate-text needle))))88
(set! fixture-location-expectations89
(cons (list node needle) fixture-location-expectations))90
node))91
(define (txt s) (located s (lambda (loc) (make-text s loc))))92
(define (para needle children) (located needle (lambda (loc) (make-paragraph children loc))))93
(define (cell needle) (located needle (lambda (loc) (make-table-cell (list (para needle (list (txt needle)))) loc))))94
(define (bullet needle) (located needle (lambda (loc) (make-list-item (list (para needle (list (txt needle)))) loc))))96
;; ---------------------------------------------------------------97
;; The project root98
;; ---------------------------------------------------------------99
(define intro-paragraph100
(para "Turn Folio from a Markdown-convention server into a local-first semantic\nknowledge workspace whose canonical source is @code{(folio doc model)}"101
(list (txt "Turn Folio from a Markdown-convention server into a local-first semantic\nknowledge workspace whose canonical source is ")102
(located "(folio doc model)" (lambda (loc) (make-code "(folio doc model)" loc))))))104
(define procedure-edge105
(located "@related[document: \"procedures/plan-driven-execution-loop\" kind: follows-procedure]"106
(lambda (loc) (make-related "procedures/plan-driven-execution-loop" #f 'follows-procedure107
(list (txt "the plan-driven execution loop")) loc))))108
(define workflow-edge109
(located "@related[document: \"topics/folio-architecture\" node: \"t-arch01\" kind: governed-by-workflow]"110
(lambda (loc) (make-related "topics/folio-architecture" "t-arch01" 'governed-by-workflow #f loc))))112
(define source-term113
(located "@term[\"source-of-truth\" name: \"source of truth\"]"114
(lambda (loc)115
(make-term "source-of-truth" "source of truth" '()116
(list (para "@emph{Authored source files} are authoritative, and every derived table is\ndisposable: deleting the index and rebuilding it must produce the same graph."117
(list (located "Authored source files"118
(lambda (e) (make-emphasis (list (txt "Authored source files")) e)))119
(txt " are authoritative, and every derived table is\ndisposable: deleting the index and rebuilding it must produce the same graph."))))120
loc))))122
;; ---- phase 1 ---------------------------------------------------123
(define manifest-task124
(located "@task[\"t-f0a1\" status: done priority: high tags: (list \"inventory\") due: \"2026-08-09\"]"125
(lambda (loc) (make-task "t-f0a1" "Produce the canonical corpus manifest"126
'done 'high (list "inventory") "2026-08-09" '() loc))))127
(define review-update128
;; A REAL id, not #f. With the corpus's only update carrying #f, every129
;; assertion about update identity was value-blind: a `update-id` accessor130
;; replaced by a constant #f satisfied all of them. Review demonstrated131
;; exactly that with an owned sabotage rather than asserting it.132
(located "@update[\"u-f0review\" timestamp: \"2026-08-10T09:14:00Z\" author: \"fable\" commit: \"3a7a0eb\"]"133
(lambda (loc)134
(make-update "u-f0review" "2026-08-10T09:14:00Z" "fable" "3a7a0eb"135
(list (para "Independent re-execution in a detached worktree reproduced every recorded count."136
(list (txt "Independent re-execution in a detached worktree reproduced every recorded count."))))137
loc))))138
(define freeze-estate-phase139
(located "@phase[\"p-freeze-estate\" title: \"Freeze the current estate and safety baseline\" status: done order: 1]"140
(lambda (loc) (make-phase "p-freeze-estate" "Freeze the current estate and safety baseline"141
'done 1 (list manifest-task review-update) loc))))143
;; ---- phase 2 ---------------------------------------------------144
(define blocks-edge145
(located "@related[node: \"t-f1a02\" kind: blocks]"146
(lambda (loc) (make-related #f "t-f1a02" 'blocks #f loc))))147
(define depends-edge148
(located "@related[document: \"topics/sigil-doc-passive-parse-api\" kind: depends-on]"149
(lambda (loc) (make-related "topics/sigil-doc-passive-parse-api" #f 'depends-on #f loc))))150
(define supports-edge151
(located "@related[document: \"topics/folio-sigil-doc-implementation-plan\" node: \"d-one\" kind: supports]"152
(lambda (loc) (make-related "topics/folio-sigil-doc-implementation-plan" "d-one" 'supports #f loc))))153
;; Direction matters and was backwards before review: the ACCEPTED decision154
;; supersedes the superseded one, not the other way round.155
(define supersedes-edge156
(located "@related[node: \"d-flat-ids\" kind: supersedes]"157
(lambda (loc) (make-related #f "d-flat-ids" 'supersedes #f loc))))159
(define model-task160
(located "@task[\"t-f1a01\" status: active priority: high tags: (list \"model\" \"freeze\")]"161
(lambda (loc) (make-task "t-f1a01" "Immutable records for the whole vocabulary"162
'active 'high (list "model" "freeze") #f (list blocks-edge) loc))))163
(define reader-task164
(located "@task[\"t-f1a02\" status: ready priority: medium tags: (list \"reader\")]"165
(lambda (loc) (make-task "t-f1a02" "Build the profile reader and prove delegation parity"166
'ready 'medium (list "reader") #f '() loc))))167
(define inspector-task168
(located "@task[\"t-f1a03\" status: blocked priority: low]"169
(lambda (loc) (make-task "t-f1a03" "Wire the passive inspector into the index"170
'blocked 'low '() #f (list depends-edge) loc))))171
(define inbox-task172
(located "@task[\"t-f1a04\" status: inbox]"173
(lambda (loc) (make-task "t-f1a04" "Decide whether a phase may carry a due date"174
'inbox #f '() #f '() loc))))175
(define cancelled-task176
(located "@task[\"t-f1a05\" status: cancelled]"177
(lambda (loc) (make-task "t-f1a05" "Fork the prose grammar for folio"178
'cancelled #f '() #f '() loc))))180
(define compact-question181
(located "@question[\"q-compact-address\" title: \"Should a compact address spelling exist?\" status: answered]"182
(lambda (loc)183
(make-question "q-compact-address" "Should a compact address spelling exist?" 'answered184
(list (para "Answered no for the first edition: only a form that can be semantically reasoned\nabout is spelled at all, so the explicit two-part address is the only one."185
(list (txt "Answered no for the first edition: only a form that can be semantically reasoned\nabout is spelled at all, so the explicit two-part address is the only one."))))186
loc))))187
;; The open question is not decoration: the frozen transition relation has no188
;; edge out of `done`, and the shipping product exposes a reopen operation.189
;; The manuscript records that as an OPEN question rather than resolving it.190
(define reopen-question191
(located "@question[\"q-reopen\" title: \"May a done work item return to ready?\" status: open]"192
(lambda (loc)193
(make-question "q-reopen" "May a done work item return to ready?" 'open194
(list (para "The frozen relation @code{task-transition-allowed?} draws no edge out of a\nfinished item, while the shipping product still offers to reopen one."195
(list (txt "The frozen relation ")196
(located "task-transition-allowed?" (lambda (c) (make-code "task-transition-allowed?" c)))197
(txt " draws no edge out of a\nfinished item, while the shipping product still offers to reopen one."))))198
loc))))200
(define profile-decision201
(located "@decision[\"d-profile\" title: \"folio/doc is a prelude and profile, not a new language\" status: accepted]"202
(lambda (loc)203
(make-decision "d-profile" "folio/doc is a prelude and profile, not a new language" 'accepted204
(list (para "Reusing one surface grammar is what makes a single toolchain pay off in five\ndifferent places at once."205
(list (txt "Reusing one surface grammar is what makes a single toolchain pay off in five\ndifferent places at once.")))206
supports-edge)207
loc))))208
(define superseded-decision209
(located "@decision[\"d-flat-ids\" title: \"Loosen the node id grammar to admit slashes\" status: superseded]"210
(lambda (loc)211
(make-decision "d-flat-ids" "Loosen the node id grammar to admit slashes" 'superseded212
'() loc))))213
;; `rejected` and `proposed` exist so that every one of the eighteen frozen214
;; status values is actually constructed by the manuscript. Review found215
;; four that were never built, rendered or asserted -- a vocabulary property216
;; asserted from the members someone happened to think of.217
(define rejected-decision218
(located "@decision[\"d-compact-address\" title: \"Add a compact doc#node address spelling\" status: rejected]"219
(lambda (loc)220
(make-decision "d-compact-address" "Add a compact doc#node address spelling" 'rejected221
(list (para "Only a form that can be semantically reasoned about should be spelled at all."222
(list (txt "Only a form that can be semantically reasoned about should be spelled at all."))))223
loc))))224
(define proposed-decision225
(located "@decision[\"d-reopen-policy\" title: \"Allow a done work item to return to ready\" status: proposed]"226
(lambda (loc)227
(make-decision "d-reopen-policy" "Allow a done work item to return to ready" 'proposed228
(list (para "Today the transition relation admits no edge out of a finished item, while the\nshipping product still offers to reopen one, so one of the two has to move."229
(list (txt "Today the transition relation admits no edge out of a finished item, while the\nshipping product still offers to reopen one, so one of the two has to move."))))230
loc))))231
(define accepted-decision232
(located "@decision[\"d-two-grammars\" title: \"Node ids stay flat; document addresses are a second grammar\" status: accepted]"233
(lambda (loc)234
(make-decision "d-two-grammars" "Node ids stay flat; document addresses are a second grammar" 'accepted235
(list supersedes-edge compact-question reopen-question) loc))))237
(define freeze-model-phase238
(located "@phase[\"p-freeze-model\" title: \"Freeze the folio/doc semantic model\" status: active order: 2]"239
(lambda (loc)240
(make-phase "p-freeze-model" "Freeze the folio/doc semantic model" 'active 2241
(list (para "The vocabulary frozen in this phase is deliberately small, because a universal\nontology is not a first-edition deliverable."242
(list (txt "The vocabulary frozen in this phase is deliberately small, because a universal\nontology is not a first-edition deliverable.")))243
model-task reader-task inspector-task inbox-task cancelled-task244
profile-decision superseded-decision rejected-decision245
proposed-decision accepted-decision)246
loc))))248
;; ---- phase 3 ---------------------------------------------------249
(define trust-table250
(located "@table[columns: (list left left) caption: \"Trust levels\"]"251
(lambda (loc)252
(make-table (list (txt "Trust levels")) (list 'left 'left)253
(list (located "@row[header: #t]@cell{trust level}@cell{what it may do}"254
(lambda (r) (make-table-row #t (list (cell "trust level") (cell "what it may do")) r)))255
(located "@row@cell{passive tier}@cell{parse syntax and inspect declared fields}"256
(lambda (r) (make-table-row #f (list (cell "passive tier") (cell "parse syntax and inspect declared fields")) r)))257
(located "@row@cell{trusted tier}@cell{evaluate author forms after an explicit grant}"258
(lambda (r) (make-table-row #f (list (cell "trusted tier") (cell "evaluate author forms after an explicit grant")) r))))259
loc))))260
(define bounded-task261
(located "@task[\"t-f2a01\" status: ready tags: (list \"passivity\")]"262
(lambda (loc) (make-task "t-f2a01" "Parse untrusted sources in a bounded worker"263
'ready #f (list "passivity") #f '() loc))))264
(define deferred-question265
(located "@question[\"q-subject-index\" title: \"How should a subject index beyond glossary terms work?\" status: deferred]"266
(lambda (loc) (make-question "q-subject-index"267
"How should a subject index beyond glossary terms work?" 'deferred '() loc))))268
(define inspect-phase269
(located "@phase[\"p-passive-inspect\" title: \"Passive inspection and dual-format discovery\" status: planned order: 3]"270
(lambda (loc) (make-phase "p-passive-inspect" "Passive inspection and dual-format discovery"271
'planned 3 (list trust-table bounded-task deferred-question) loc))))272
;; The fourth container status. Also the only phase with no children, which273
;; exercises the empty-container path through the lowering.274
(define paused-phase275
(located "@phase[\"p-publication\" title: \"Publish a knowledgebase without a Folio runtime\" status: paused order: 4]"276
(lambda (loc) (make-phase "p-publication" "Publish a knowledgebase without a Folio runtime"277
'paused 4 '() loc))))279
(define first-edition-milestone280
(located "@milestone[\"m-first-edition\" title: \"First edition of the semantic workspace\" status: active]"281
(lambda (loc) (make-milestone "m-first-edition" "First edition of the semantic workspace" 'active282
(list freeze-estate-phase freeze-model-phase inspect-phase paused-phase) loc))))284
;; ---- ordinary Sigil Doc content --------------------------------285
;;286
;; NOTE FOR THE REVIEW: these sections are SIBLINGS of the folio nodes, not287
;; parents of them, and that is forced rather than chosen. `make-section`288
;; validates its children with sigil-doc's own `block-node?`, which is a289
;; closed enumeration of core kinds, so a `@task` can never be a child of a290
;; `@section`. The freeze therefore cannot express "a task underneath an291
;; ordinary heading" -- which is how folio notes are written today. This is292
;; recorded as a finding in the session log; it is a sigil-doc-side293
;; extensibility gap of the same family as B2, not something F1a can fix.294
(define reading-section295
(located "@section[\"reading\"]{Where the rules actually live}"296
(lambda (loc)297
(make-section "reading" (list (txt "Where the rules actually live"))298
(list299
(para "Rules are referenced rather than reproduced, as in @ref[\"reading\"]{this very section},\nand the published plan lives at @url[\"https://codeberg.org/sigil/folio\"]{the folio repository}"300
(list (txt "Rules are referenced rather than reproduced, as in ")301
(located "this very section"302
(lambda (r) (make-reference "reading" (list (txt "this very section")) r)))303
(txt ",\nand the published plan lives at ")304
;; The node this whole phase waited for SD-P2 to land.305
(located "@url[\"https://codeberg.org/sigil/folio\"]{the folio repository}"306
(lambda (u) (make-url-link "https://codeberg.org/sigil/folio"307
(list (txt "the folio repository")) u)))))308
(located "@list[ordered: #t start: 1]"309
(lambda (l) (make-item-list #t 1310
(list (bullet "Source fidelity: untouched bytes stay exact.")311
(bullet "Identity: a move never changes a stable local id.")312
(bullet "Honesty about derived state: rebuilding yields the same answers."))313
l)))314
(located "(folio-update-field! coordinator id: \"t-f1a01\" field: 'status value: 'done)"315
(lambda (c) (make-code-block "sigil"316
"(folio-update-field! coordinator id: \"t-f1a01\" field: 'status value: 'done)" c)))317
;; A declared `@term` obliges the publication to carry exactly one318
;; `@glossary` insertion point; `build-publication-index` refuses319
;; otherwise. Found by running the lowering, not by reading.320
(located "@(glossary)" (lambda (g) (make-glossary g))))321
loc))))323
(define unicode-section324
(located "@section[\"unicode\"]{Locations that are not ASCII}"325
(lambda (loc)326
(make-section "unicode" (list (txt "Locations that are not ASCII"))327
(list (para "Several scripts at once, so that byte spans and scalar columns disagree:\nκαλημέρα κόσμε, 日本語のテキスト, and a rocket 🚀 far past the basic plane."328
(list (txt "Several scripts at once, so that byte spans and scalar columns disagree:\nκαλημέρα κόσμε, 日本語のテキスト, and a rocket 🚀 far past the basic plane."))))329
loc))))331
;; A work item whose own title is non-ASCII, so a byte/scalar confusion332
;; shows up on a FOLIO node and not only on core prose.333
(define unicode-task334
(located "@task[\"t-unicode\" status: ready]"335
(lambda (loc) (make-task "t-unicode" "Prove καλημέρα spans survive the round trip"336
'ready #f '() #f '() loc))))338
(define ready-query339
(located "@query[id: \"q-ready-work\" title: \"Everything ready and unblocked\" spec: (and (kind task) (status ready))]"340
(lambda (loc) (make-query "q-ready-work" "Everything ready and unblocked"341
'(and (kind task) (status ready)) loc))))342
(define taxonomy-transclusion343
(located "@transclude[document: \"topics/instrument-failure-taxonomy\" node: \"s-shape-15\"]"344
(lambda (loc) (make-transclude "topics/instrument-failure-taxonomy" "s-shape-15" loc))))346
(define pressure-project347
(located "@project[\"projects/folio-sigil-doc\" title: \"Folio as a semantic Sigil Doc workspace\" status: active tags: (list \"folio\" \"sigil-doc\" \"knowledge-graph\")]"348
(lambda (loc)349
(make-project "projects/folio-sigil-doc" "Folio as a semantic Sigil Doc workspace" 'active350
(list "folio" "sigil-doc" "knowledge-graph")351
(list intro-paragraph procedure-edge workflow-edge source-term352
first-edition-milestone reading-section unicode-section353
unicode-task ready-query taxonomy-transclusion)354
loc))))356
;; ---------------------------------------------------------------357
;; The note root -- note metadata, and the same-document transclusion358
;; ---------------------------------------------------------------359
(define mentions-edge360
(located "@related[document: \"projects/folio-sigil-doc\" node: \"t-f1a01\" kind: mentions]"361
(lambda (loc) (make-related "projects/folio-sigil-doc" "t-f1a01" 'mentions362
(list (txt "the freeze task")) loc))))363
(define note-task364
(located "@task[\"t-note01\" status: ready priority: low]"365
(lambda (loc) (make-task "t-note01" "Record the reopen gap for the mutation phase"366
'ready 'low '() #f '() loc))))367
;; `document: #f` means "this document" -- the two-part address with its368
;; first part omitted, never a compact one-string spelling.369
(define local-transclusion370
(located "@transclude[node: \"t-f1a01\"]"371
(lambda (loc) (make-transclude #f "t-f1a01" loc))))373
(define pressure-note374
(located "@note[\"topics/folio-doc-freeze-notes\" title: \"Notes taken while freezing the model\" tags: (list \"folio\" \"freeze\" \"0.1\")]"375
(lambda (loc)376
(make-note "topics/folio-doc-freeze-notes" "Notes taken while freezing the model"377
(list "folio" "freeze" "0.1")378
(list (para "Metadata on a note is ordinary declared data: an address for identity, a title\nfor the outline, and tags for filtering, none of it computed by author code."379
(list (txt "Metadata on a note is ordinary declared data: an address for identity, a title\nfor the outline, and tags for filtering, none of it computed by author code.")))380
mentions-edge note-task local-transclusion)381
loc))))383
(set! fixture-location-expectations (reverse fixture-location-expectations))