-- design settled with gritzko, 2026-07-30
Today's 'W' blob is rewritten whole on every fold (dog/WEAVE.h:100) because DELETION MUTATES stored tokens: a later commit sets rm and pushes into 'M', so earlier bytes cannot be final. Make deletion its own index entry and the BODY becomes strictly append-only — a commit's text bytes are final forever, the small parent-sorted INDEX merges per commit, and the merge-time renumbering that DIS-081 exists to fix never happens. The shape: a BODY of text pieces, an INDEX of tok32 pairs carrying the CT parent/child relation, and a per-commit table of bounds. 'X'/'K'/'I'/'M'/'C'/'A' collapse into three streams.
The current six-column layout pays for in-place tombstones.
WEAVENext re-emits the entire weave per fold, so writes are O(history); WEAVEMerge then permutes commits[] and remaps every ins/rms.'A' is a u64 anchor hash per token — on word tokens the anchor column can outweigh the text it anchors.'M' needs the tok32 custom bit as a multi-remover escape, spending the one spare bit in the token.An append-only format where a commit's bytes are final once written.
'C', 'I' and 'A'.Weaves are derived data; git is the oplog.
MAX_SOURCE_SIZE (4 MB) stay blobs, untokenised — big generated files are not diffed and do not enter a weave.MAX_SOURCE_MARKED_UP; the exposure is churn over history, not file size.Three streams replace the six columns; TLV tags below are PROPOSALS, pin them before coding. The BODY is append-only, the INDEX is SORTED — not the same property.
'B': u8, the text of every token ever inserted, live and dead, in append order; a commit appends only the tokens it inserts. Strictly append-only.'E': tokv32 entries — a declared PAIR of tok32 (parent, child), 64 bits — sorted by parent offset then by sibling order.tokv32 is a real ABC record type with fixed bit layout, so the index is a tokv32cs/tokv32b and sorting/search go through the typed slice API, not pointer work.WEAVENext's whole-weave re-emit.'C': per commit, the record (id, body range [start,end), L, K, P, ignore-set); append order is build order.'G': pool of ignored-commit-INDEX lists referenced by 'C' records, so a commit record stays fixed-size; consecutive indices may compress to ranges — compression, not correctness.The child tok32's TAG field carries the entry TYPE; a syntax tag means an ordinary token, a reserved letter means tomb or terminator.
parent = body offset of the left-neighbour token (the CT parent), child = this token, tag = syntax tag, offset = its first body byte.parent = body offset of the target token, child tag = the tomb letter, child offset = the DELETING commit's INDEX in 'C'.parent = the chain's last token, child offset = termpos, the chain's end.[start,end).'M' count+N escape and the custom bit both disappear.Document order is a preorder walk of the CT; visibility is a bound plus an exclusion.
'E' because it is parent-sorted, so the walk is a scan plus a binary search per fork, never a hash lookup.u precedes v iff idx(u) < K_v and u not in ignore(v) — the sibling compare is two lookups, no topo rank is ever recomputed.c: offset < L_c and the inserting commit not in c's ignore-set; a TOMB is visible iff its deleting commit's index is under K_c and not ignored.weavescope bitmap is not needed at all.L_c is the body length at c, K_c the commit-index cut; P_c is the MATERIALIZED length at c — an allocation size for the render and an integrity check on the walk.'C' range table (empty pure-delete ranges never cover an offset); why: needs no per-token inserter at all.K and ignore-set.ignore(c) = [0, K_c) \ closure(parents(c)), closure supplied by the caller (graf owns the DAG, the shape WEAVEScope takes today); two-parent intuition: a commit hides only when NEITHER parent saw it.Settled in conversation; the rest is Blockers.
custom (no multi-remover escape) plus side (a tomb entry IS the removal) — 3 bits, enough for the diff class/color set.tokv32 (a tok32 pair, 64-bit fixed layout) so the index gets the typed slice/buffer family rather than ad-hoc pair handling.Spec first, then reader, then writer, then callers, then cutover. Nothing lands before the parity harness exists.
'W' tags — 'V' outer, 'B'/'E'/'C'/'G' subs, tags 'A'/'B'/'E'/'T'WEAVEAlive on the fixture(L, ignore-set, tombs); byte-parity against WEAVEProduce per revwhy: output on the fixturecfstat.bsearch == 0 assertedCFOLDEmitDiff/CFOLDEmitFull over (from,to) COMMIT INDICES on a multi-scope walk; test/CFOLD05 asserts BYTE-PARITY with the WEAVE02 hunk expectationsCFOLDProduce at the merge commitCFOLDBIG, test/CFOLD05)weave.hpp -> cfold.hpp, 11 _cfold_* leaves; cont.cpp PROTO["CFOLD"]; test/weave.js rewritten; 104/105 ctest + weave.js OKCFOLDIterNext/cfoldtok/CFOLDIterMem — one DFS step per call, state in a caller-owned zeroed u32 area; produce/emit/walk are loops over it; yields BODY offsets so CFOLDBlame composes; a first-cut CFOLDTokens array dump was built and REMOVED_cfold_step + _cfold_itermem, JS rewind(rev)/next()/tok{text,tag,off,end,alive} — the _weave_step pattern, so why.js keeps its loop shape; both wts identical, dog 73/73test/bass.js (JABCbass, JAB-008 canary) ported to the CFOLD family; probe re-sized 1500 -> 1200 folds per its own sizing rule (be/shared/weave.js (2026-07-31, //DIS-082-be): ONE weave per file — build() -> {weave, rev, ids}, commits record only on change/delete/join, carries are free; fold/merge take ancestors; foldWt(w, rev, ids, bytes, ext); weave3 folds base once; JOIN_ID exportedbe/views/why/why.js onto blame: origin = blame(tok.off) per cursor token; ?a..b deletes surface by from/to diff (wash = range head, see Blockers)patch/expected/classify onto the one-weave join (ctx.w is the only stable ref — fold/merge return fresh containers); diff.js+links.js onto rev-hashlet emits; parity: 27/27 patch, 15/15 status, 17/17 non-skip diff, why suite greengit.pack namespace drift in the fresh jab binary (shared/wire.js:269 still calls the old function form) — seed-path only, zero weave involvement, set flaps with scratch reusemergedLive onto the C emit — now cursor+blame based in JS, still a mirror of the conflict rule; the C span API (freed class bits) subsumes itbro.js consumes the class bits instead of re-deriving them from hunk adjacencywmerge_commits, the ra/rb remap, and the old columns; re-scope DIS-081 as subsumed — old columns/remap went with WEAVE.c 2026-07-31; DIS-081 re-scope still openThe jab binary embeds dog and exposes it to JS via jab/weave.hpp (JSC leaves, installed by cont.cpp); JS-side callers live in be/. The old surface is 12 _weave_* leaves; the CFOLD surface is smaller because scope bitmaps, the identity hash and the step cursor all dissolve into commit indices and body offsets.
dog/ to it — the compile break in weave.hpp IS the checklist; (3) rewrite the binding; (4) port be/ callers one verb at a time, parity-checked: cat, diff, why, patch, get, bro; (5) rerun the be test suite + a pty-click pager check.cfold.hpp (replaces weave.hpp): container holds one 'V' blob; hashlet convention unchanged (id = hi64 of the sha1, 16-hex strings).
_cfold_next(dest, base, len, blob, ext, hash, ancestors[]) — fold now takes the ANCESTOR hashlet array (the closure graf already computes for _weave_scope); pairwise _weave_merge(a,b) becomes _cfold_merge(dest, base, len, hash, ancestors[]) — one weave, no weave-pair plumbing._cfold_count, _cfold_commits, _cfold_alive; _cfold_produce(rev) takes a commit INDEX (or hashlet), not a bitmap._cfold_emitdiff/_cfold_emitfull with from/to as commit hashlets resolved via CFOLDFindCommit — scope-bitmap args gone._weave_scope (visibility is stored), _weave_idhash (identity = body offset)._cfold_step(blob, len, rev, cursor) with the DFS state in a JS-owned Uint32Array (_cfold_itermem sizes it) — tokens yield BODY offsets, _cfold_blame(off) -> commit hashlet names the inserter; flat array dumps rejected._weave_step can go: the C merged-live render (bytes + conflict SPANS per Dirty's shared-anchor/interior-insertion rule) that retires JS mergedLive — this is where the freed tok32 class bits (DIS-080 blue/orange/yellow) surface.be/shared/weave.js build + graf.js): today replays a file's DAG as one weave PER NODE with pairwise merges; with CFOLD it is ONE weave per file, folded once per commit in topo order with the closure as ancestors — the per-node weave array, the merge cascade and the 16 MB-per-node buffers collapse into one growing buffer.abc.ram("WEAVE" / .scope(): weave.js, graf.js, classify.js, expected.js, ingest.js, idxmaint.js, submount.js, patch.js, diff.js, bro.js, why.js, test/diff/links/links.js, plus jab's own test/weave.js.mergedLive/_weave_step is the long pole (needs the new C span API); everything else is arg reshaping. The MAX_SOURCE_MARKED_UP fixed-buffer policy carries over unchanged — the body cap (16 MiB) equals it by design.cfoldtok is {off,end,tag,alive} and _cfold_blame names inserters only, so why:'s ?a..b surfaced-delete wash approximates with the range HEAD; exact colouring needs a tomb accessor (e.g. the iterator yielding the tomb's commit index, or the freed class bits).Suggested commit (//DIS-082 wt, staged): DIS-082: CFOLD replaces WEAVE (append-only weave + emit)
delete file# row-unstage (ULOG-004 shape) is not implemented, needs a hand fix or the ulog edit verbcfold.hpp, cont.cpp, INDEX.md, test/weave.js, dog/CFOLD.{c,h}; suggested commit: DIS-082: jab binding on CFOLD (_cfold_* leaves)weave.hpp could not be delete-staged (same ULOG-004 gap); its dog/ CFOLD copy is a STAND-IN for the real subrepo advance once dog landsshared/{weave,expected,classify}.js, verbs/patch/patch.js, views/{diff/diff,why/why}.js, test/diff/links/links.js; suggested commit: DIS-082: be JS on CFOLD (one weave per file)