Skip to content
🚧 Early alpha — building the foundation. See the roadmap →

Open question — variable-depth hierarchy generation from recipe primitives

Created Updated

Surfaced while making the system model’s render() diagram honest. Paraphrased:

The split() example assumes splitting always yields a clean, fixed hierarchy. But you might regex a term into a non-explicit number of parts; the regex may break or over-split; you want smart logic to control depth. The same applies to tags (which behave as poly-hierarchies — structured, nested, and a concept can sit under many at once), to headings (structured by #-depth, 1–6), and you can even emulate hierarchy with graph edges (named frontmatter links: parent/broader/partOf, each thing pointing to the next). Note content is technically a target too, but it carries no structure, so it isn’t a real primitive. Does the system account for any of this?

The user flagged it as small-but-load-bearing: “it is very important that we get this right.”

Short answer: v0.1 does fixed/known depth well; it does not do variable/ragged depth, polyhierarchical tag layout, data-driven heading depth, or graph-edge hierarchy.

MechanismHow structure/depth is set todayVariable-depth?Where
folderordered layout, one entry per level; each entry’s template is {var|filter} and produces one path segment❌ depth = number of entries, declared up frontsrc/render/mechanisms/folder.ts, src/render/template.ts
folder (wizard)deriveIdSplitTemplates() scans samples, finds delimiters present in ≥80% of values, emits one {col|split(<delim>,0)} template per delimiter (cumulative-prefix trick)❌ depth derived once for the whole import from aggregate samples — not per rowsrc/generation/generation-engine.ts (deriveIdSplitTemplates)
template filtersclosed set: lower · upper · title · slug · tagsafe · fs-safe · truncate(N) · trim · split(<delim>,<index>) · regex(<pattern>)❌ split returns one part by index; regex returns one match/first-group; neither yields a variadic list, and there’s no join-to-pathsrc/render/template.ts
tag (as layout)applyTagStub() — stub, schema-reserved for v0.2. Tags ship only via also_emit.tags = a fixed array of templates (polyhierarchy via fixed entries; nested paths via literal /)❌ no data-driven tag depth/countsrc/render/mechanisms/tag.ts
headingapplyHeading() with level_depth (1–6) fixed per entry❌ depth not data-drivensrc/render/mechanisms/heading.ts
wikilink / graph_edgesapplyWikilinkStub() — stub; graph_edges (from/via/to) schema-reserved, v0.2❌ hierarchy-via-named-edges not wiredsrc/render/mechanisms/wikilink.ts
note bodywritten via templates; carries no hierarchyn/a — not a structural primitive (user’s own read)—

The crux:

  • Depth is fixed per recipe, not per row. A uniform template set applies to every row.
  • Ragged hierarchies (row A has 3 levels, row B has 2) are handled only degenerately: a too-short value makes an empty segment → applyFolder silently skips it (if (!segment) return); a value lacking the delimiter makes split(d,0) return the whole value → can mint a folder named identically to the leaf. Rows deeper than the derived template set simply lose the extra depth.
  • There is no first-class variadic split (“break into however many parts there are and nest each”), no depth cap/overflow policy, and no smart fallback for a regex that returns nothing.

This is consistent with — but narrower than — Challenge 22, which settled the grammar shape (5 mechanisms × ordered layout × also_emit × graph_edges) but assumed known levels and did not probe per-row variable depth.

What this means for the system-model diagram

Section titled “What this means for the system-model diagram”

The render() illustration shows split(lineage, ";") producing three folders. That is a conceptual depiction of one key → nested parts; v0.1 would express it as one layout entry per level using the index-based split, with the depth known in advance. The page now carries an honest “open question” callout pointing here, so the model isn’t read as a promise the engine doesn’t yet keep.

  1. Record as open; do not fake support. The diagram stays conceptual but is explicitly flagged; the schema/engine are described accurately wherever depth is discussed.
  2. Deep-research it via Challenge 42 — the design space for variadic/ragged depth across all five mechanisms, with determinism + round-trip + commitment-#2 (closed grammar) as hard constraints.
  3. Do not pre-judge a new mechanism. This is about expressivity within the existing layout (better transforms / a layout-entry that can expand over an array / a path-valued template), not a sixth mechanism — adding one would break commitment #2.
Section titled “Prior art — the author’s own related public projects”

The depth-control problem isn’t new to this author; three public projects circle it from other angles and are worth mining before Challenge 42 runs:

  • Folder Tag Sync (docs) — bidirectional folder ↔ tag sync via regex + a typed rule model: FolderClassifier + TagVocabulary + TransferOp triples with eight library-science transfer primitives — identity · truncation · marker-only · promotion-to-root · flattening-to-leaf · post-coordination · aggregation · opaque. This is the same hierarchy-mapping problem from the opposite direction, and it already names the depth-control operations Crosswalker’s recipe grammar lacks: truncation caps depth, flattening-to-leaf collapses, promotion-to-root re-roots, post-coordination makes faceted/poly tags. Strongest precedent for the tag-polyhierarchy + ragged-depth sub-questions — and a vocabulary to reconcile against layout.
  • SEACOW(r) — knowledge-platform organization meta-framework cataloguing 100+ classification systems (PARA, Johnny Decimal, LATCH, library science) plus an explicit relation/graph layer. The classification-theory backdrop for “what shape can a hierarchy take, and when is folder vs tag vs graph the right one.”
  • Jsonaut — JSON tree → tabular flattening + transforms (search_and_flatten); the tree-transducer / ETL lineage behind Crosswalker’s import side.