SHARD-WP-0003 T3. Whole-file write-granularity anchor: an entire wiki (content + app engine) in one self-contained HTML file -> save rewrites the whole file, no per-page atomicity. Node .tid file-per-tiddler substrate is git-diffable/ fine-grained, so the same engine spans the granularity spectrum by substrate (cf. Logseq file/DB UC-62, backend-swap UC-43). Tiddler = flexible-field record (UC-34); filter expressions = native-query tier (UC-52). UC-78 (single-file container-format attach). Enriched UC-35/40/34/52/43. Marks T3 done. Feeds SHARD-WP-0002 T11/T14. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.3 KiB
TiddlyWiki — deep dive (findings)
Date: 2026-06-14 · Source: SHARD-WP-0003 T3 · Subject: TiddlyWiki / TiddlyWiki5 (TW5), Jeremy Ruston's self-contained personal wiki.
Why this dive
The synthesis matrix names whole-file write granularity as one extreme of the write-granularity spectrum, anchored by TiddlyWiki. This dive confirms the anchor and the portability extreme it implies — a wiki that is a single HTML file you can email — and finds the twist: TiddlyWiki also has a Node.js file-per-tiddler substrate, so it spans the granularity spectrum the way Logseq spans file/DB (UC-62). The question: how does shard-wiki attach a backend whose entire content is one file?
1. The single-file model
Classic TiddlyWiki ships as one .html file that contains both:
- the TiddlyWiki core (the JavaScript engine, parser, renderer, UI), and
- every tiddler (all content), serialized into the file.
Open it in a browser and the file is the running application. There is no server and no build step — the app reconstitutes itself from the file it was loaded from. This is the portability extreme: a complete, self-hosting wiki in a single, emailable, USB-stick-able artifact that runs offline anywhere.
Saving is the catch: a browser page cannot normally overwrite the file it came from, so TiddlyWiki uses "savers" — TiddlyFox/browser extension, the File System Access API, a Node.js server, TiddlySpot/put-savers, or "download a new copy." Crucially, a save rewrites the entire HTML file (core + all tiddlers re-serialized). Hence whole-file write granularity: there is no concept of writing one page in isolation in the single-file mode — every save touches the whole artifact.
2. The tiddler data model
The atomic unit is the tiddler — a named record with fields:
- Core fields:
title(the identity),text(the body),tags,created,modified,type(content type, e.g.text/vnd.tiddlywiki,text/markdown), plus arbitrary custom fields (any key→value). A tiddler is effectively a flexible flat record — closer to a typed-field record (UC-34) than to prose-with-frontmatter. - Everything is a tiddler: not just pages, but tags, macros, templates, themes, plugins, and the wiki's own configuration are all tiddlers. A plugin is a bundle of tiddlers.
- Content markup is WikiText (TW5's own), though
typecan mark a tiddler as Markdown, JSON, image, etc. Transclusion is native:{{SomeTiddler}}embeds another tiddler;{{SomeTiddler!!field}}embeds a field.
3. The dual substrate — single-file vs file-per-tiddler
TiddlyWiki on Node.js stores each tiddler as a separate .tid file on disk: a small
text file with a header of field: value lines, a blank line, then the body. The Node
server assembles these into the same wiki at serve time. This substrate is:
- git-diffable and fine-grained — one file per tiddler, line-level diffs, per-tiddler history — the opposite end of the granularity spectrum from the single HTML file.
- the natural attach surface for a versioned, multi-author TiddlyWiki.
So TiddlyWiki spans the write-granularity spectrum by substrate (single-file = whole-file write; Node = file-per-tiddler write), exactly as Logseq spans file/DB (UC-62) and as the backend-swap question (UC-43) anticipates.
4. Native query — filter expressions
TiddlyWiki's query language is filter expressions over tiddler fields, e.g.
[tag[todo]!tag[done]sort[modified]] — a compact DSL that selects/orders tiddlers by field
and tag. Lists, tables, and dynamic views are built from filters. This is a native-query
capability (UC-52 tier) — less expressive than SPARQL/datalog but real, and computed over
the tiddler store.
5. Capability profile
| Dimension (synthesis spectrum) | TiddlyWiki (single-file) | TiddlyWiki (Node .tid) |
|---|---|---|
| Attachment mode | file-store: one HTML file | file-store: dir of .tid files |
| Addressing granularity | tiddler (title) within the file |
tiddler = one file |
| Content identity | title (placement-bound) |
title ↔ filename |
| Structure | flat record store w/ arbitrary fields + tags | same |
| History | none in-file (whole-file save) | per-file git history |
| Merge model | whole-file replace (no merge) | git 3-way per tiddler |
| Native query | filter expressions | filter expressions |
| Translation | WikiText (or per-tiddler type: markdown/json/…) |
same |
| Write granularity | whole file (the anchor) | file per tiddler |
| Operational envelope | trivial — a browser; no server | a Node server |
| Access grant | file access = full access | server/file perms |
| Content opacity | transparent (parse the HTML store) | transparent text |
| Provenance | created/modified fields | git + fields |
6. INTENT mapping
Reinforcements
- Graceful degradation: a single-file TiddlyWiki is a trivial read-only / projection / backup shard — parse the tiddlers out of the HTML, project pages; no server needed. The limited-backend-still-usable principle at its simplest.
- Markdown-first but backend-neutral: tiddlers carry a
type, so Markdown tiddlers coexist with WikiText — the page model's content-type field maps directly. - Typed fields (UC-34): arbitrary tiddler fields are a flexible record model the page model already accommodates.
- Backend-swap under stable identity (UC-43): single-file ↔ Node
.tidis the same logical wiki on two substrates — the migration UC-43 anticipates, within one engine.
Divergences (boundaries / notes)
- Whole-file write granularity is a real constraint: in single-file mode shard-wiki cannot write one page atomically — an overlay applied "to one page" still rewrites the whole file (T11). This is the coarsest write tier; model it explicitly so overlays/locks account for it (a write to any page conflicts with any concurrent write).
- Identity = title, file-local; cross-shard identity (T16) layered above.
- The app is in the file: when parsing a single-file TiddlyWiki, shard-wiki must extract the tiddler store and ignore the embedded engine — i.e. treat the HTML as a container format, not as page content (don't mistake the app for content).
What to keep
- Single-file self-contained wiki as a first-class file-store shard — container-format parse, whole-file write granularity (UC-78); the portability/granularity extreme.
- Whole-file write granularity as a named tier (T11) with overlay/lock implications.
- Dual-substrate binding (single-file vs
.tiddir) as another instance of substrate-choice under one identity (UC-43/UC-62).
7. UC seed
| # | Seed | Disposition |
|---|---|---|
| UC-78 | Attach a single-file self-contained wiki (TiddlyWiki HTML) as one shard — parse tiddlers out of the container, project pages; write = rewrite the whole file (whole-file write granularity, the coarsest tier) | new |
| — | whole-file write granularity anchor + overlay/lock implications | enrich UC-35 |
| — | single HTML file as a file-store shard (container format) | enrich UC-40 |
| — | tiddler arbitrary fields = flexible record | enrich UC-34 |
| — | filter expressions as a native-query tier | enrich UC-52 |
| — | single-file ↔ Node .tid substrate swap |
enrich UC-43 |
8. Architecture notes for SHARD-WP-0002
- T11 (capability / write granularity): confirm whole-file as the coarsest named write tier (anchored by single-file TiddlyWiki), with the implication that an overlay to any page conflicts with concurrent writes (no per-page atomicity). File-per-tiddler is the fine tier on the same engine.
- T14 (attach binding): a single-file wiki binds as a container-format file-store
(parse tiddler store, ignore embedded engine); a Node TiddlyWiki binds as a dir of
.tidfiles (git-diffable). One engine, two bindings — parameterize like UC-43. - Native query: filter expressions are a low-mid native-query tier between "none" and datalog/SPARQL — delegate where present (UC-52).
9. Open questions
- In single-file mode, how does shard-wiki represent per-page overlays when writes are
whole-file — buffer overlays and re-serialize, or require the Node
.tidsubstrate for write-through and treat single-file as read/projection/backup only? - Is a single-file TiddlyWiki's embedded plugins/config ever relevant to the union, or strictly ignored as app-internals (parse only content tiddlers)?
- Does shard-wiki expose tiddler filter expressions as a delegated query, or only its own union query over projected tiddlers?
10. Sources
- TiddlyWiki.com — Tiddlers, TiddlerFields, Filters, Saving, Node.js docs
- TiddlyWiki5 GitHub (Jermolene/TiddlyWiki5) —
.tidfile format, store structure - prior:
research/260614-logseq-deep-dive/(file/DB dual substrate, UC-62)
11. Traceability
New UC UC-78 carries the marker ⊡ in the wikiengines column of
spec/UseCaseCatalog.md. Enriched: UC-35, UC-40, UC-34, UC-52, UC-43. Architecture
cross-refs: SHARD-WP-0002 T11 (whole-file tier), T14 (dual binding).