Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4 unterscheidet [x] fertig von [^] in Produktion (D30): - #ed.docs.picker: Breadcrumb picker in the app header (S) %% Werkbaum › name, see D81 - #col.live.upload: Share: put a local plan on the server (S) %% a button in the editor title bar, see D81
1099 lines
49 KiB
Plaintext
1099 lines
49 KiB
Plaintext
%% Werkbaum, planned with Werkbaum — what exists today and where it could go.
|
||
%% Distilled from docs/ROADMAP.md, docs/TASKS.md and docs/DECISIONS.md.
|
||
%% Every node carries an id; the block behind --- explains it. Hover a node.
|
||
|
||
[~] #wb: Werkbaum (XXL) https://github.com/mhoennig/werkbaum
|
||
- [^] #not: Notation (XL)
|
||
- [^] #not.line: Line format (M)
|
||
- [^] #not.status: Status box, eight states (S)
|
||
- [^] #not.size: T-shirt size and the "decompose from M" rule (S)
|
||
- [^] #not.people: People tags, bare URLs, %% comments (XS)
|
||
- [^] #not.cont: A trailing backslash continues the line (XS) %% space before it is required
|
||
- [^] #not.gates: And/or decomposition (S)
|
||
- [^] #not.opt: Optional nodes — neither required nor an alternative (S)
|
||
- [^] #not.id: Node IDs, (#auth:) before the title (S) %% often the ticket number, see SPEC §11
|
||
+ [?] #not.tag: Free tags, &tag (M) %% only together with a consumer, see D34
|
||
- [?] #not.tag.lens: A lens: highlight every node tagged &x (S)
|
||
- [^] #not.dep: Dependencies across the tree, (:#auth,#api) (M)
|
||
- [^] #not.dep.status: They constrain the status, not when work may start (S)
|
||
- [^] #not.dep.eff: Effective status: intrinsic, plus what it depends on (S)
|
||
- [^] #not.dep.cycle: Cycles are legal — they mean "finished together" (XS)
|
||
- [^] #not.dep.warn: An id with no node behind it is a warning (XS)
|
||
- [^] #not.xor: XOR, = — exactly one alternative, not at least one (S)
|
||
- [^] #not.desc: Node descriptions, shown as a tooltip (M)
|
||
- [^] #not.desc.short: A short text right at the node (S) %% a " line below the node
|
||
- [^] #not.desc.long: A long block at the end, addressed by its id (S) %% behind a --- separator
|
||
- [^] #not.sizes: Size conflict — sub-packages must fit the given size (S) %% ranges, XXL open-ended, see D62
|
||
- [-] #not.store: A separate storage format for the structure (L) %% the text is the format
|
||
- [ ] #not.ticket: Ticket references (#123,#US-123) (S) %% Taiga writes #US-123 for a user story
|
||
- [^] #not.llms: llms.md, the notation explained to AI agents (S)
|
||
- [^] #not.llms.index: llms.txt, the signpost that points at it (XS) %% the convention wants an index, not the content
|
||
- [?] #not.benefit: Benefit per node, not only cost (M)
|
||
| [?] #not.benefit.sigil: Another sigil next to the size (S)
|
||
| [?] #not.benefit.points: Story points behind the T-shirt sizes (M)
|
||
- [~] #ed: Editor (XXL) https://werkbaum.javagil.de
|
||
- [^] #ed.live: Live diagram from text (L)
|
||
- [^] #ed.parser: Parser (S)
|
||
- [^] #ed.render: Renderer, three layout modes (L)
|
||
- [^] #ed.render.horiz: Horizontal fan (S)
|
||
- [^] #ed.render.vert: Vertical, transposed (S)
|
||
- [^] #ed.render.comp: Compact (S)
|
||
- [^] #ed.render.wrap: Long titles wrap into balanced lines (S) %% max ~40 chars, see D64
|
||
- [^] #ed.deplink: Cross links for dependencies (M)
|
||
- [^] #ed.deplink.calm: Visually secondary — the tree carries the message (S)
|
||
- [^] #ed.deplink.focus: Highlight what a selected node needs and feeds (S)
|
||
- [^] #ed.deplink.fold: A hidden endpoint falls to its visible ancestor (S) %% source and target alike, see D75
|
||
- [^] #ed.deplink.toggle: A switch hides the cross links (XS)
|
||
- [^] #ed.path: Cheapest path (XL)
|
||
- [^] #ed.path.cost: Cost model, a size prices its subtree (S)
|
||
- [^] #ed.path.est: Missing size estimated from the sub-packages (S) %% see D66
|
||
- [^] #ed.path.front: Only the open front: what is done costs nothing (S) %% see D46
|
||
- [^] #ed.path.started: Started work is on the path, extras included (S) %% see D61
|
||
- [^] #ed.path.line: Metro-map line through the leaves (S)
|
||
- [^] #ed.path.step: Step from station to station, one button (S) %% see D47
|
||
- [^] #ed.path.load: Warn when one person is the bottleneck (S) %% see D71
|
||
- [^] #ed.closure: Count the whole dependency closure (M)
|
||
- [^] #ed.closure.once: Shared dependencies counted only once (S)
|
||
- [^] #ed.closure.nonlocal: The cheapest alternative is no longer the local one (M) %% see D34, D42
|
||
| [^] #ed.closure.exact: Search exactly, the trees are small enough (M)
|
||
- [^] #ed.closure.enum: Enumerate the coupled any-of choices (S)
|
||
- [^] #ed.closure.union: Cost is the size of the union (S)
|
||
| [^] #ed.closure.greedy: Stay greedy, but say so (XS) %% built as the named fallback
|
||
- [^] #ed.export: Export (M)
|
||
- [^] #ed.export.file: SVG and PNG download (S)
|
||
- [^] #ed.export.clip: PNG to the clipboard (S)
|
||
+ [^] #ed.export.print: Print stylesheet (XS)
|
||
- [^] #ed.i18n: Nine interface languages (M)
|
||
- [^] #ed.i18n.texts: Translations (S)
|
||
- [^] #ed.i18n.auto: Default taken from the browser (XS)
|
||
- [^] #ed.a11y: Accessibility: aria labels, focus order (S)
|
||
- [^] #ed.mobile: Works on a phone (S)
|
||
- [^] #ed.mobile.tip: A tap on a node opens its tooltip as a window (XS) %% no pointer, no tooltip
|
||
- [^] #ed.tipwindow: The same window at the pointer and on keyboard focus (XS) %% a title has no paragraphs and never shows on focus
|
||
- [^] #ed.docs: Several documents in one editor (M)
|
||
- [^] #ed.docs.picker: Breadcrumb picker in the app header (S) %% Werkbaum › name, see D81
|
||
- [^] #ed.docs.url: Load a document from ?sourceUrl= (S)
|
||
- [^] #ed.docs.restore: Restore a shipped document from the menu (XS)
|
||
- [^] #ed.jump: Jump between diagram and text (S)
|
||
- [^] #ed.jump.dep: Ctrl+click follows a dependency to its id (XS)
|
||
- [^] #ed.lineno: Line numbers in the text editor (XS) %% the warnings name them
|
||
- [^] #ed.shortid: Type #.kc under #prod-stage, get #prod-stage.kc (XS) %% an input aid, not notation
|
||
- [^] #ed.depcomplete: Typing :# suggests the known ids (S) %% arrows choose, Enter inserts
|
||
- [^] #ed.showids: A # button shows the ids before the titles (XS) %% written as in the text, with a colon
|
||
- [^] #ed.lineno.tip: The warning is the tooltip of its number (XS) %% no hunting in the list
|
||
- [^] #ed.fold: Collapsible subtrees (L)
|
||
- [^] #ed.fold.marks: Initial state from the text: > collapses, < re-opens (S)
|
||
- [^] #ed.fold.click: Fold and unfold in the diagram (S)
|
||
- [^] #ed.fold.write: Folding writes the mark back into the text (S) %% one undo step
|
||
- [^] #ed.fold.preset: The fold button cycles through four presets (S) %% M-and-smaller, off-path, all, none — see D44/D75
|
||
- [^] #ed.snaps: Earlier states of a document, every ten minutes (S) %% only when something changed
|
||
- [^] #ed.snaps.manual: Save a state by hand, before a larger change (XS) %% ten minutes is the wrong beat for that moment
|
||
- [^] #ed.files: Open and save .werkbaum files (S)
|
||
- [^] #ed.files.inplace: Saving writes back into the opened file (S) %% File System Access, Chromium
|
||
- [^] #ed.files.ctrls: Ctrl+S saves the document as a file (XS) %% in place with a remembered handle
|
||
+ [^] #ed.fresh: Show what is new since your last visit (S)
|
||
- [^] #ed.fresh.news: A star in the header, with the last few days (S) %% see D58
|
||
+ [?] #ed.percolor: A pastel colour per person (S)
|
||
- [?] #ed.dates: Dates and milestones (M)
|
||
| [?] #ed.dates.attr: An attribute in the line (S)
|
||
| [?] #ed.dates.view: A separate timeline view (L)
|
||
- [^] #bld: Build and delivery (XL)
|
||
- [^] #bld.bundle: One self-contained file, bundled by Vite (S)
|
||
- [^] #bld.tests: Unit tests (S)
|
||
- [^] #bld.pages: Published on every push (S)
|
||
- [^] #bld.update: Says when a new build is out (S) %% compares against the running one, see D45
|
||
- [^] #bld.pwa: Installable as a progressive web app (M)
|
||
- [^] #bld.pwa.manifest: A manifest with name, icons and standalone display (XS)
|
||
- [^] #bld.pwa.sw: A service worker that serves the app offline (S) %% network-first, cache as fallback
|
||
- [^] #bld.pwa.update: The reload notice stays truthful under the worker (S) %% see D45/D73
|
||
- [^] #bld.pwa.files: Double-click a .werkbaum file, save without any dialog (S) :#ed.files.inplace %% file_handlers + launchQueue
|
||
- [^] #bld.backend: Ship the backend as a service of its own (L)
|
||
- [^] #bld.backend.jdk: Its own JDK 21 in the server's home (S) %% only 17 is installed there
|
||
- [^] #bld.backend.unit: A systemd user unit that survives the session (S)
|
||
- [^] #bld.backend.proxy: Apache passes /api/ on, in .htaccess (XS) %% measured: the P flag is allowed
|
||
- [^] #bld.backend.remote: One command for everything on the server (S) %% remote <target> <action>
|
||
- [^] #bld.backend.backup: Save the database, and prove it can be read back (XS)
|
||
- [-] #bld.ghpages: Deploy through a gh-pages branch (S) %% GitHub Actions instead
|
||
- [-] #bld.gfonts: Fonts from Google (XS) %% self-hosted, no third-party request
|
||
- [ ] #be: Backend (XL)
|
||
- [/] #be.scaffold: Kotlin and Spring Boot scaffold (M)
|
||
- [^] #be.scaffold.gradle: Gradle Kotlin DSL, JDK 21 (S)
|
||
- [^] #be.scaffold.info: An endpoint that says name and version (XS)
|
||
- [ ] #be.scaffold.ci: Continuous integration for the backend (S)
|
||
- [^] #be.docs: Documents on the server (L) :#be.scaffold
|
||
- [^] #be.docs.model: Text plus metadata, no structure format (M)
|
||
- [^] #be.docs.rest: REST endpoints (S)
|
||
- [^] #be.docs.db: Persistence (S)
|
||
- [^] #be.docs.history: History in two layers, and restore (S)
|
||
- [^] #be.docs.share: Share by link (S)
|
||
- [^] #be.docs.list: Master password for the document list (S)
|
||
- [?] #be.auth: Accounts and permissions (L) :#be.docs
|
||
| [?] #be.auth.token: Single user, one token (S)
|
||
| [?] #be.auth.oidc: Log in with OIDC (L)
|
||
- [~] #col: Collaborating on a Diagram (XXL)
|
||
" Reading together is solved, changing together too — typing together at the
|
||
" same instant is the expensive one.
|
||
- [^] #col.point: A shared pointer: !!! focuses a node for everyone (XS)
|
||
- [-] #col.pad: Borrow an Etherpad — it merges, we render (L) %% built, then removed, see D78
|
||
- [ ] #col.git: Git as the shared store (L)
|
||
| [ ] #col.git.pr: A file in a repository, changed by pull request (S) %% works today, no code
|
||
| [?] #col.git.auto: The backend commits every change (L) :#be.scaffold
|
||
- [?] #col.git.hist: History and restore (M)
|
||
- [?] #col.git.diff: Diff between two versions (S)
|
||
- [?] #col.git.blame: Who changed this line (S)
|
||
- [?] #col.git.branch: A branch per scenario (S)
|
||
- [/] #col.live: Live editing, several people at once (XL) :#be.docs
|
||
- [^] #col.live.http: Transport over plain HTTP (M) :#be.docs %% no websocket needed
|
||
- [^] #col.live.patch: Submit a change as a line diff (S)
|
||
- [^] #col.live.feed: Long polling holds the other direction open (S)
|
||
- [^] #col.live.merge: Merging simultaneous edits (L) %% the actual work
|
||
| [^] #col.live.diff: Line diffs against a base version; the server rebases (L)
|
||
| [?] #col.live.crdt: Text CRDT — the plan is plain text, so it fits (L)
|
||
| [?] #col.live.ot: Operational transformation (XL)
|
||
- [^] #col.live.ask: On a real overlap, ask whose version wins (S)
|
||
- [^] #col.live.upload: Share: put a local plan on the server (S) %% a button in the editor title bar, see D81
|
||
- [?] #col.live.cursors: Cursors and who else is here (S)
|
||
- [-] #col.live.ws: Transport over a websocket (S) %% plain HTTP turned out to be enough
|
||
- [-] #col.live.own: A merge algorithm of our own (XL) %% solved problem, do not reinvent
|
||
- [?] #mmd: Mermaid plugin (XL)
|
||
- [!] #mmd.layout: A layout engine of its own (XL) %% measure, place, route — the real work
|
||
- [ ] #mmd.measure: Measure node sizes (M)
|
||
- [ ] #mmd.measure.text: Text metrics without a browser (S)
|
||
- [ ] #mmd.measure.badge: Badges and tags inside the box (S)
|
||
- [ ] #mmd.place: Place nodes and route connectors (L)
|
||
- [ ] #mmd.place.and: All-of fan (M)
|
||
- [ ] #mmd.place.horiz: Horizontal (S)
|
||
- [ ] #mmd.place.vert: Transposed (S)
|
||
- [ ] #mmd.place.or: Any-of rail (S)
|
||
- [?] #mmd.reg: Registration with Mermaid (M)
|
||
| [?] #mmd.reg.ext: External diagram, self-hosted pages only (M)
|
||
| [?] #mmd.reg.pr: Pull request into Mermaid itself (L)
|
||
- [?] > #idea: IntelliJ IDEA plugin (XL)
|
||
- [?] #idea.view: Show the diagram in the IDE (L)
|
||
| [?] #idea.view.mmd: Through Mermaid, once Werkbaum is part of Mermaid itself (M)
|
||
- [?] #idea.view.fence: Fenced werkbaum blocks render in Markdown (S)
|
||
- [-] #idea.view.selfmmd: A self-hosted Mermaid diagram type (S) %% the IDE brings its own Mermaid
|
||
| [?] #idea.view.jcef: Our own preview: the bundled single file in a JCEF window (M)
|
||
- [?] #idea.view.push: Hand the editor text in (S)
|
||
- [?] #idea.view.back: Get the clicked line back to the caret (S)
|
||
+ [?] #idea.edit: A real editor, not just a preview (XL)
|
||
- [?] #idea.lang: Language support for .werkbaum (L)
|
||
- [?] #idea.lang.psi: Lexer and parser to a PSI tree (M)
|
||
- [?] #idea.lang.color: Syntax highlighting (S)
|
||
- [?] #idea.lang.inspect: Warnings as inspections (S) %% mixed gate, unknown status
|
||
- [?] #idea.lang.complete: Completion for status, size and tags (S)
|
||
- [?] #idea.lang.struct: Structure view and folding (S)
|
||
- [?] #idea.build: Kotlin with the Gradle IntelliJ plugin (S)
|
||
- [!] < #idea.drift: A second parser that must not drift from the SPEC (M) %% exactly what D14 warns about
|
||
| [?] #idea.drift.fixtures: Both parsers test against the same fixtures (S)
|
||
| [?] #idea.drift.js: Run the one JS parser inside the IDE (M)
|
||
- [?] #trk: Tracker integration (XL) %% exactly one of these, hence =
|
||
= [?] #trk.taiga: Taiga (L) https://taiga.io
|
||
- [ ] #trk.resolve: Resolve "#123" over the REST API (M)
|
||
- [ ] #trk.resolve.read: Read title, link and status (S)
|
||
- [ ] #trk.resolve.map: Map the workflow onto the states (S)
|
||
- [?] #trk.write: Write the status back (M) :#trk.resolve
|
||
- [-] #trk.plugin: A plugin inside Taiga itself (L) %% legacy frontend, self-hosted only
|
||
= [?] #trk.tenzu: Tenzu, the successor (XXL) https://tenzu.net
|
||
= [ ] #trk.none: No tracker, plain text is enough (XS)
|
||
- [?] #ai: AI integration (XL)
|
||
- [?] #ai.dialog: Edit the plan in a dialogue with the AI (L)
|
||
- [?] #ai.key: Bring your own API key, no server in between (M)
|
||
- [?] #ai.ground: llms.md grounds the model in the notation (S)
|
||
|
||
---
|
||
#wb
|
||
The whole tool, planned with itself: a textual notation for work breakdown
|
||
structures, a browser editor that draws it, and the integrations that could
|
||
grow around both. What you are reading is that plan, written in its own
|
||
notation.
|
||
|
||
#not
|
||
The notation is the contract between every part of Werkbaum, and it is
|
||
plain text — so a plan can be reviewed, diffed and blamed like code.
|
||
docs/SPEC.md is normative: changes are written there before any code moves.
|
||
|
||
#not.line
|
||
One line is one node: indentation, gate character, status box, fold mark,
|
||
label, size, URL, tags, comment. The order in which those are extracted is
|
||
fixed, which is what keeps them from colliding with each other.
|
||
|
||
#not.cont
|
||
A long line may be spread over several text lines: ending one with a space
|
||
and a backslash makes the next line part of it, so no new node begins. The
|
||
space is required, or a label like C:\temp\ would silently swallow the node
|
||
below it.
|
||
|
||
#not.status
|
||
Eight states from vague idea to live in production, written as an extended
|
||
Markdown task box. The colours follow risk rather than progress: money spent
|
||
with the outcome still open is the loud one.
|
||
|
||
#not.size
|
||
T-shirt sizes in parentheses, plus the rule that anything M or larger ought
|
||
to be broken down further. A node that breaks the rule grows a dashed
|
||
placeholder child — shown, not just reported. The last free-standing token
|
||
of the line is the size, so parentheses in the title stay text.
|
||
|
||
#not.people
|
||
Three conveniences that need no syntax of their own: @name for who is on it,
|
||
a bare https URL that makes the whole node clickable, and %% for a comment.
|
||
|
||
#not.gates
|
||
The heart of the notation: a dash means every sibling is required, a pipe
|
||
means at least one alternative gets chosen. That single distinction is what
|
||
turns a breakdown into something a cheapest path can be searched through.
|
||
|
||
#not.opt
|
||
A plus node hangs off the same and-group but is dispensable. Without it,
|
||
every nice-to-have is priced into the cheapest path and the estimate comes
|
||
out systematically too high.
|
||
|
||
#not.id
|
||
An id names a node across the whole document — the address that dependencies
|
||
and description blocks point at. It goes before the title with a colon,
|
||
which is also where a ticket number naturally sits.
|
||
|
||
#not.tag
|
||
A tag names a set of nodes across the tree, whereas the tree itself is
|
||
exactly one decomposition. Reserved but deliberately unbuilt: with nothing
|
||
to evaluate them, tags are only comments with extra syntax.
|
||
|
||
#not.tag.lens
|
||
The consumer that would justify tags: pick one and let everything else
|
||
recede. The mechanics already exist — the cheapest path dims unneeded nodes
|
||
in exactly that way.
|
||
|
||
#not.dep
|
||
A node may need nodes outside its own subtree. That is the one thing a pure
|
||
tree cannot express, and everything below this node follows from it.
|
||
|
||
#not.dep.status
|
||
A dependency says nothing about order or start dates; it says the node
|
||
cannot be further along than what it needs. That is what separates Werkbaum
|
||
from a PERT chart.
|
||
|
||
#not.dep.eff
|
||
What stands in the box is the node's own progress; effectively it is the
|
||
minimum over everything it depends on. Computed, never written — the text
|
||
stays the single source of truth.
|
||
|
||
#not.dep.cycle
|
||
Two nodes that need each other are not an error but a statement: these
|
||
finish together. A tool that forbids cycles forces you to lie about them.
|
||
|
||
#not.dep.warn
|
||
A dependency pointing at nothing gets a warning naming its line, and the
|
||
plan still renders. Fault tolerance throughout: one bad line never swallows
|
||
the rest of the document.
|
||
|
||
#not.xor
|
||
Where a pipe means at least one, an equals sign means exactly one. Nothing
|
||
changes for the cost calculation, but a second realized alternative becomes
|
||
a rule that can be broken — and is then reported.
|
||
|
||
#not.desc
|
||
Explaining text for a node, shown in its tooltip. The work was never the
|
||
display: indentation already means hierarchy, so a description needed a
|
||
notation of its own.
|
||
|
||
#not.desc.short
|
||
A line beginning with a quote mark belongs to the node above it. Convenient
|
||
for one remark, tedious for a paragraph.
|
||
|
||
#not.desc.long
|
||
Behind a --- separator an id opens a block, and the indented lines under it
|
||
are its text. There is no closing fence, so there is none to forget — this
|
||
section is its own example.
|
||
|
||
#not.sizes
|
||
Every size is read as a range whose lower bounds double, XXL open-ended. A
|
||
warning appears only when even the most optimistic reading of the children
|
||
exceeds the most generous reading of the parent — nothing is corrected
|
||
automatically, the badge just turns amber.
|
||
|
||
#not.store
|
||
Rejected on purpose: the text is the format. A second representation of the
|
||
structure would need syncing, and the first disagreement between the two
|
||
would have no arbiter.
|
||
|
||
#not.ticket
|
||
A reference into a tracker. Which token counts as a ticket comes from the
|
||
connected tracker's own pattern rather than from guessing at its shape,
|
||
which is why this waits for the tracker work.
|
||
|
||
#not.llms
|
||
An English short form of the specification, served at the site root, so an
|
||
AI agent can read and write Werkbaum without working through the German
|
||
SPEC. It is not normative: the SPEC changes first and this follows.
|
||
|
||
#not.llms.index
|
||
The llms.txt convention asks for a short index that links to the detailed
|
||
files, not for the detailed file itself. This is that index, and it sits at
|
||
the one address an agent tries on its own.
|
||
|
||
#not.benefit
|
||
Sizes say what something costs; nothing yet says what it is worth. With both
|
||
you could sort by value for money instead of only minimizing cost.
|
||
|
||
#not.benefit.sigil
|
||
A separate character next to the size, in the spirit of everything else in
|
||
the line. Needs a collision-free sigil, and parentheses, @, # and %% are
|
||
taken.
|
||
|
||
#not.benefit.points
|
||
Put numbers behind the T-shirt sizes and both benefit and effort become
|
||
addable. That buys the effort rollup as well, at the price of a mapping
|
||
convention the ordinal sizes deliberately avoid today.
|
||
|
||
#ed
|
||
The browser editor: text on one side, a live diagram on the other, no server
|
||
involved. One self-contained HTML file that runs from a web server and from
|
||
a file:// URL alike.
|
||
|
||
#ed.live
|
||
Every keystroke re-parses the text and redraws the tree. That immediacy is
|
||
the point — a notation is only pleasant to write if you can see what it
|
||
means while writing it.
|
||
|
||
#ed.parser
|
||
Around a hundred lines, no DOM, tested against the canonical example in the
|
||
SPEC. Headless on purpose: an export or a plugin has to be able to use it
|
||
without a browser around it.
|
||
|
||
#ed.render
|
||
Three arrangements of the same tree. The mode changes the layout only, never
|
||
what is drawn — the gates stay coded by line style in all of them.
|
||
|
||
#ed.render.horiz
|
||
The classic organigram fan: children side by side, alternatives stacked
|
||
below a dashed rail. Reads best on a wide screen.
|
||
|
||
#ed.render.vert
|
||
The same fan turned by ninety degrees, children to the right. Fits beside
|
||
the editor instead of above it.
|
||
|
||
#ed.render.comp
|
||
Everything runs downward, so the tree stays narrow. Here the line style
|
||
alone carries the gate: solid for and, dashed for or.
|
||
|
||
#ed.render.wrap
|
||
A single long title used to cost a whole column of width. Titles beyond
|
||
forty characters now break into evenly balanced lines, set by the renderer
|
||
so the box shrinks to the longest line; connectors anchor on the first line.
|
||
|
||
#ed.deplink
|
||
Dependencies drawn as curved dotted edges on their own layer behind the
|
||
nodes — the first line type that does not follow the decomposition.
|
||
|
||
#ed.deplink.calm
|
||
Thin, pale, dotted and curved: four ways of differing from the tree lines at
|
||
once. The tree keeps the main statement; these are footnotes to it.
|
||
|
||
#ed.deplink.focus
|
||
The selected node — keyboard focus in the diagram, otherwise the node of the
|
||
caret line — pulls its incoming and outgoing edges forward in ink.
|
||
|
||
#ed.deplink.fold
|
||
When an endpoint sits in a folded subtree, the edge ends at the nearest
|
||
visible ancestor instead of vanishing — the collapsed node stands in for
|
||
its subtree here too, for source and target alike. Edges whose two ends
|
||
fall into the same visible node disappear, duplicates merge into one.
|
||
|
||
#ed.deplink.toggle
|
||
A toggle next to the cheapest-path button hides the cross links entirely;
|
||
export and print follow it like the other view filters.
|
||
|
||
#ed.path
|
||
Highlight the cheapest way through the tree: with and, everything; with or,
|
||
the cheapest alternative. This is the feature the whole notation exists for.
|
||
|
||
#ed.path.cost
|
||
The cost of a node is its stated (or estimated) T-shirt size — the size
|
||
prices the whole subtree, sub-packages are not added on top. Whether they
|
||
fit is the size conflict check's job.
|
||
|
||
#ed.path.est
|
||
A node without a size is estimated from its sub-packages: at least the
|
||
largest child size, one step more when three or more children carry that
|
||
size. Only a leaf without children still falls back to M.
|
||
|
||
#ed.path.front
|
||
Done costs nothing any more, so the path prices what is left rather than
|
||
what the plan would have cost from scratch. An alternative already built
|
||
therefore wins its group even against a cheaper untouched one.
|
||
|
||
#ed.path.started
|
||
An extra (`+`) joins the path while it is being worked on, and a started
|
||
alternative wins its group. Optionality says the plan can do without it;
|
||
the status says work has begun, and the front is made of exactly that.
|
||
|
||
#ed.path.line
|
||
A dashed curve threads through the open leaves of the path, with a pale
|
||
station dot at each. Borrowed from metro maps: the dot is large and faint so
|
||
the label underneath stays readable.
|
||
|
||
#ed.path.step
|
||
One button walks the stations in order, starting with the one to tackle
|
||
next. Seeing the front was solved; walking it across a very wide tree was
|
||
not.
|
||
|
||
#ed.path.load
|
||
Warns when one @name carries more than half of the open work on the
|
||
cheapest path while at least two people are on it: the next increment
|
||
would serialize through that person. Untagged nodes inherit the nearest
|
||
tagged ancestor, and the person's tag pills turn amber on open path nodes.
|
||
|
||
#ed.closure
|
||
The cheapest path pulls every dependency of a needed node into the needed
|
||
set, and shared targets count only once. That is why an alternative can win
|
||
although it looks more expensive locally: its dependencies are already paid
|
||
for elsewhere.
|
||
|
||
#ed.closure.once
|
||
Two alternatives that need the same thing pay for it together, not twice.
|
||
That is the whole reason the calculation works on a set rather than on a
|
||
subtree.
|
||
|
||
#ed.closure.nonlocal
|
||
Once dependencies count, the cheapest alternative in a group depends on what
|
||
the rest of the plan buys anyway. The choice stops being decidable locally —
|
||
the only place in Werkbaum with real algorithmics in it.
|
||
|
||
#ed.closure.exact
|
||
Enumerate the choices instead of guessing: a plan has dozens of nodes, not
|
||
millions. Only the groups that actually couple take part, so a plan without
|
||
dependencies costs exactly what it did before.
|
||
|
||
#ed.closure.enum
|
||
Coupled means: dependencies inside the subtree, or something elsewhere
|
||
pointing at it. Every other group keeps choosing locally.
|
||
|
||
#ed.closure.union
|
||
Cost is the size of the union of everything needed, so shared targets are
|
||
counted once by construction rather than by a special rule.
|
||
|
||
#ed.closure.greedy
|
||
Above a search limit the calculation falls back on the local choice and says
|
||
so, in a warning. Being approximately right is fine; pretending to be exact
|
||
is not.
|
||
|
||
#ed.export
|
||
The diagram redrawn from the live geometry into a standalone SVG, shapes and
|
||
text only. Nothing external is referenced, so the file works anywhere.
|
||
|
||
#ed.export.file
|
||
SVG for scaling, PNG for the programs that cannot read SVG. The file route
|
||
is the reliable one wherever the image clipboard is not.
|
||
|
||
#ed.export.clip
|
||
PNG straight into the clipboard, for pasting into a chat or a slide. Falls
|
||
back to the SVG source where the image clipboard is missing entirely.
|
||
|
||
#ed.export.print
|
||
Printing reduces the page to the diagram. Status colours are printed on
|
||
purpose, and nodes do not break across the page edge.
|
||
|
||
#ed.i18n
|
||
The interface speaks nine languages. German is the source language: a new
|
||
text is written there first and then translated into the rest.
|
||
|
||
#ed.i18n.texts
|
||
All interface strings in one object in the source — no loading, no
|
||
framework. The plan text itself is never translated; it is user data.
|
||
|
||
#ed.i18n.auto
|
||
Without a stored choice the first matching browser language wins, with
|
||
German as the fallback. A deliberate choice overrides it for good.
|
||
|
||
#ed.a11y
|
||
Everything the diagram codes visually is spelled out per node in an
|
||
aria-label, while the decorative badges are hidden from screen readers so
|
||
nothing is read out twice. Focus order follows reading order.
|
||
|
||
#ed.mobile
|
||
Below 640 px exactly one area is visible, diagram or text, and a button
|
||
switches between them. Any split would leave both halves too small to use.
|
||
|
||
#ed.mobile.tip
|
||
A tooltip needs a pointer, so without one the description could not be seen
|
||
at all. A tap opens the same content as a window instead; on a linked node
|
||
the link becomes a button inside it, because one tap cannot do both.
|
||
|
||
#ed.tipwindow
|
||
A native title has no paragraphs, cannot draw a line, and never appears on
|
||
keyboard focus. The window built for touch can do all three, so it now shows
|
||
everywhere and the title is gone.
|
||
|
||
#ed.docs
|
||
Several notation texts side by side, kept in the browser. Each is only text
|
||
plus a name — no invented structure format, so this maps onto a server later
|
||
without changing anything.
|
||
|
||
#ed.docs.picker
|
||
The app header reads "Werkbaum > name": the chip opens the menu, grouped by
|
||
kind (shipped, yours, sources), with rename and delete on each row. Restore
|
||
lives on the editor title bar's reload button instead, acting only on the
|
||
open document. The menu scrolls as a whole instead of being cut off on
|
||
small screens, and on the phone it is reachable from both panes.
|
||
|
||
#ed.docs.url
|
||
A link can carry the address of a text file, which is then fetched on every
|
||
load. Good for a plan maintained elsewhere, in a wiki or a repository.
|
||
|
||
#ed.docs.restore
|
||
Brings a shipped document back to the state it was delivered in. Without it
|
||
an edited example would never see a newer version again.
|
||
|
||
#ed.jump
|
||
Alt+click on a node selects its line in the text; Alt+click in the text
|
||
centres the node. One gesture, both directions — and descriptions count as
|
||
part of the node they describe.
|
||
|
||
#ed.jump.dep
|
||
Ctrl+click (Cmd on macOS) on an id inside a dependency token like (:#target)
|
||
selects the line that defines the id; Ctrl+Enter at the caret does the same.
|
||
The first assignment wins for duplicate ids, an unknown id does nothing.
|
||
|
||
#ed.lineno
|
||
A narrow strip beside the text field showing the logical line numbers, the
|
||
same ones the warnings name. Measured rather than calculated: a wrapped line
|
||
keeps exactly one number.
|
||
|
||
#ed.lineno.tip
|
||
An amber number says that a line has a warning; its tooltip says which one,
|
||
word for word as it stands below the diagram. Otherwise you read the number,
|
||
then hunt for it in the list.
|
||
|
||
#ed.shortid
|
||
The dotted prefix repeats what the indentation already says. Resolved while
|
||
typing, not while parsing: the file keeps the full id, so it stays greppable
|
||
and survives re-ordering, which is what the :#... references rely on.
|
||
|
||
#ed.depcomplete
|
||
A dependency needs its target's id, and nobody remembers 180 of them. The
|
||
list at the caret offers the document's ids filtered by what is typed, with
|
||
the node title as context; an input aid like the shorthand, not notation.
|
||
|
||
#ed.showids
|
||
The id is the address of a node, but the diagram only carried it in the
|
||
tooltip. The switch writes it before the title the same way the text does,
|
||
so both can be read side by side; the graphic export follows the switch.
|
||
|
||
#ed.fold
|
||
A large plan is unreadable unless parts of it can be put away. Folding is
|
||
view only — warnings from folded subtrees are still reported, because they
|
||
are about the text.
|
||
|
||
#ed.fold.marks
|
||
A > in a line means this subtree opens collapsed, a < brings one back into
|
||
view. The author says how the document opens; the reader stays free to fold
|
||
differently afterwards.
|
||
|
||
#ed.fold.click
|
||
A small triangle before the label folds and unfolds; the arrow keys do the
|
||
same from the keyboard. A collapsed node says how many nodes it is hiding.
|
||
|
||
#ed.fold.write
|
||
Folding in the diagram writes the mark back into the text, as a single undo
|
||
step. The text stays the one truth for the fold state too, so reloading
|
||
restores exactly what you were looking at.
|
||
|
||
#ed.fold.preset
|
||
One button cycles the whole tree through four complete fold states: size M
|
||
and smaller closed, everything off the cheapest path closed, everything
|
||
closed, everything open. Icon and tooltip show the next step; a manual fold
|
||
restarts the cycle. Size rather than depth — depth only says how finely
|
||
somebody happened to write it down.
|
||
|
||
#ed.snaps
|
||
A safety net against mishaps: the text is put aside every ten minutes, but
|
||
only when it differs from the last one kept, and the last twenty are offered
|
||
beside the document picker. Loading one is a single undo step, so a wrong
|
||
pick costs nothing.
|
||
|
||
#ed.snaps.manual
|
||
Ten minutes is the wrong beat for the one moment a state is really wanted:
|
||
right before a larger change. The button promises that the current state is
|
||
kept, not that an entry was added, so it confirms even when the text is
|
||
unchanged and already at the top of the list.
|
||
|
||
#ed.files
|
||
Open a .werkbaum file and save one, from the document menu. The leading data
|
||
format was the one thing that could not leave the browser as a file, while
|
||
the diagram always could.
|
||
|
||
#ed.files.inplace
|
||
Where the File System Access API exists (Chromium), opening keeps a file
|
||
handle: saving writes back into the same file, and the same file reopens
|
||
into the same document. Firefox and Safari keep the plain download.
|
||
|
||
#ed.files.ctrls
|
||
The save gesture everyone tries first. Without it the browser offers to
|
||
save the page — the one dialog nobody wants. With a remembered handle it
|
||
writes straight back in place and flashes the document name as its answer;
|
||
otherwise it behaves like the menu entry.
|
||
|
||
#ed.fresh
|
||
For documents that come from outside, nodes that went to production since
|
||
your last visit get a yellow halo. New means live, not "line added" — a line
|
||
diff would be mostly noise.
|
||
|
||
#ed.fresh.news
|
||
A star that is always in the header opens a popup with the changes of the
|
||
last few days; every day can show its nodes in the diagram, in the very same
|
||
halo view. Notes come from docs/CHANGELOG.md, the nodes from the plan's git
|
||
history, both read at build time.
|
||
|
||
#ed.percolor
|
||
Give every @name a colour derived from the name itself, so the same person
|
||
is recognizable across the tree without looking anything up.
|
||
|
||
#ed.dates
|
||
The one dimension the notation has nothing for. Deliberately open: dates are
|
||
the point where a plan turns into a schedule, and that is a different tool.
|
||
|
||
#ed.dates.attr
|
||
A date as another token in the line, like size and tags. Cheap to build, and
|
||
the plan stays plain text.
|
||
|
||
#ed.dates.view
|
||
A second view beside the tree, on a time axis. Considerably more work, and
|
||
it would need the tree to say something about order — which dependencies
|
||
deliberately do not.
|
||
|
||
#bld
|
||
From source to a running page: one bundle, tests, and two deploy routes. The
|
||
result stays a single file without runtime dependencies.
|
||
|
||
#bld.bundle
|
||
Vite inlines every module, the stylesheet, the fonts and the favicon into
|
||
one HTML file. It runs from a web server and from a local file alike.
|
||
|
||
#bld.tests
|
||
The headless modules — parser, model, renderer — are tested against the
|
||
canonical example in the SPEC and against every edge case decided along the
|
||
way.
|
||
|
||
#bld.pages
|
||
Every push to the main branch builds and publishes the preview instance. The
|
||
stable installation is deployed deliberately, by hand.
|
||
|
||
#bld.update
|
||
The running page compares its own build with the one the server delivers and
|
||
offers a reload. It compares against itself, not against a remembered
|
||
fetch — a remembered one survives the very reload that resolves it.
|
||
|
||
#bld.pwa
|
||
Installable from the browser and startable like an app, in its own window
|
||
and with its own icon. Everything it needs was already in one file — what
|
||
was missing was mostly the declaration that says so.
|
||
|
||
#bld.pwa.manifest
|
||
Name, icons, theme colour and a standalone display mode — the declaration a
|
||
browser reads before it offers to install anything. The raster icons are
|
||
rendered from the brand mark, with a padded maskable variant.
|
||
|
||
#bld.pwa.sw
|
||
A worker lets the editor start without a network. It is deliberately dumb:
|
||
navigations go network-first, and the cache only keeps the last version it
|
||
has seen of the one self-contained file for the offline case. Texts pulled from
|
||
a URL or a server pass through untouched and stay online by their nature.
|
||
|
||
#bld.pwa.update
|
||
The feared migration turned out unnecessary: because the worker only
|
||
touches navigations and answers them network-first, the update check still
|
||
fetches the real server and a reload really loads the new build. The work
|
||
here was proving that, not moving code.
|
||
|
||
#bld.pwa.files
|
||
The installed app registers for the .werkbaum extension: double-clicking a
|
||
file in the OS opens it here, with a ready file handle from the launch
|
||
queue. Together with the persistent write permission an installed app can
|
||
hold, editing becomes open, type, save — no dialog at all. The saving
|
||
mechanics existed already; this added the manifest entry and the
|
||
launch-queue receiver. Chromium only.
|
||
|
||
#bld.backend
|
||
The editor is files, the backend is a service: a Java runtime, something that
|
||
keeps it running, and a way in from outside. Its own deploy script, because
|
||
none of that is rsync.
|
||
|
||
#bld.backend.jdk
|
||
The measured target has Java 17 while the build asks for 21. Lowering the
|
||
toolchain would be shorter and worse — development and production would run
|
||
on different versions, and the difference would only show in operation. The
|
||
archive's checksum is verified before anything is unpacked.
|
||
|
||
#bld.backend.unit
|
||
Restart after a crash and a defined state, for free. The service listens on
|
||
127.0.0.1 only; from outside there is exactly one way in.
|
||
|
||
#bld.backend.proxy
|
||
A rewrite rule with the P flag, because ProxyPass is not allowed in an
|
||
.htaccess and the vhost directory is out of reach. Measured on the target: a
|
||
request is held open for 30 seconds, which is what the change feed needs.
|
||
|
||
#bld.backend.remote
|
||
Deploying, reading the log, asking what is running and switching the service
|
||
were four different incantations, three of them typed by hand as ssh plus
|
||
systemctl. One front door names them all as target and action.
|
||
|
||
#bld.backend.backup
|
||
The database is a file in a home directory, and H2 holds it open, so a copy
|
||
taken while the service runs can be torn. Stop, fetch, start again — and read
|
||
the archive back before trusting it.
|
||
|
||
#bld.ghpages
|
||
Rejected: the official Actions route needs no extra branch and no deploy
|
||
key.
|
||
|
||
#bld.gfonts
|
||
Rejected: loading fonts from a third party sends every visitor's address
|
||
there. The two families are self-hosted and inlined into the bundle instead.
|
||
|
||
#be
|
||
Everything server-side. None of it exists yet, which makes it the largest
|
||
untouched branch of this plan — and the one most of the rest waits for.
|
||
|
||
#be.scaffold
|
||
A Spring Initializr project with Gradle Kotlin DSL on JDK 21. Everything
|
||
server-side hangs off this one node, which is what the dependencies across
|
||
the tree point at.
|
||
|
||
#be.scaffold.gradle
|
||
The build itself: Kotlin DSL, one module, the current long-term-support
|
||
Java.
|
||
|
||
#be.scaffold.info
|
||
Something to ask "are you there, and which build is it?" — open and without
|
||
side effects. Expecting a 404 from a document that does not exist would be a
|
||
poor assurance: a misconfigured proxy returns one too.
|
||
|
||
#be.scaffold.ci
|
||
A pipeline that builds and tests the backend on every push, like the editor
|
||
has. The "is it alive" half moved out into its own node and is done.
|
||
|
||
#be.docs
|
||
Documents on a server instead of in one browser. Text plus metadata —
|
||
exactly the model the editor already uses locally.
|
||
|
||
#be.docs.model
|
||
The server stores the notation text as a whole, with explicit metadata
|
||
beside it. It does not parse the notation: one grammar, in one place.
|
||
|
||
#be.docs.rest
|
||
Reading and writing documents over plain HTTP. Nothing more exotic is
|
||
needed.
|
||
|
||
#be.docs.db
|
||
Somewhere to keep them. Whether that also keeps their history is decided in
|
||
the git branch of this plan, not here.
|
||
|
||
#be.docs.history
|
||
Every change is kept as a snapshot. Two layers: milestones are the history
|
||
people read, sync versions carry the diffs of live editing and are compacted
|
||
after a while.
|
||
|
||
#be.docs.share
|
||
A link that shows a plan to someone who has no account. It is no longer the
|
||
read-only half: with live editing the same link is the way in.
|
||
|
||
#be.docs.list
|
||
Listing all documents would make every document's address findable, so that
|
||
one endpoint asks for a master password. Without a configured one the list
|
||
stays shut rather than open.
|
||
|
||
#be.auth
|
||
As soon as documents live on a server, the question is who may do what. Open
|
||
rather than decided.
|
||
|
||
#be.auth.token
|
||
The small answer: one user, one token, done in an afternoon. Enough for a
|
||
private installation.
|
||
|
||
#be.auth.oidc
|
||
The grown-up answer: log in with an existing identity provider. More work,
|
||
and it presumes there are several people to tell apart.
|
||
|
||
#col
|
||
Three steps that build on each other, and only the last needs real
|
||
machinery. Reading together is solved, changing together mostly is, and
|
||
typing together at the same instant is the expensive one.
|
||
|
||
#col.point
|
||
Nobody sees anyone else's caret, so a mark in the text is the only way to
|
||
point at a place — and it has something a caret has not: everyone sees the
|
||
same spot.
|
||
|
||
#col.pad
|
||
Removed. An Etherpad did the hard part for a while: it merged concurrent
|
||
edits and Werkbaum only rendered the result, borrowed rather than rebuilt.
|
||
The borrowing is what it cost — a rate-limited export, no writing from here,
|
||
and an author cookie that a foreign frame never gets. The backend does it
|
||
better and in our own editor, so this went out again.
|
||
|
||
#col.git
|
||
Keep the plan in a repository. Because it is text, diff, blame, review and
|
||
history come for free.
|
||
|
||
#col.git.pr
|
||
Works today without a line of code: the file lives in a repository, changes
|
||
go through a pull request, a link renders it. For a plan that changes weekly
|
||
rather than per second this is often the better answer.
|
||
|
||
#col.git.auto
|
||
The server turns every change into a commit. History, restore and branches
|
||
follow from that, with commit granularity as the open question.
|
||
|
||
#col.git.hist
|
||
Being able to look back at what the plan said, and to go back to it.
|
||
|
||
#col.git.diff
|
||
What changed between two versions of the plan.
|
||
|
||
#col.git.blame
|
||
Who moved that node to high risk, and when.
|
||
|
||
#col.git.branch
|
||
A branch per scenario: plan two futures side by side and compare their
|
||
cheapest paths.
|
||
|
||
#col.live
|
||
Everybody typing at once, cursors and all. The largest single piece of work
|
||
in this plan, and the least necessary one.
|
||
|
||
#col.live.http
|
||
Plain request and response, no websocket: a change goes up as a line diff,
|
||
and one held-open request brings the others' changes down. It is testable
|
||
with the same tools as everything else, needs no special treatment in
|
||
proxies or service workers, and at ten onlookers the efficiency difference
|
||
is beside the point.
|
||
|
||
#col.live.patch
|
||
Every change is a diff against a known version, with a checksum of that
|
||
version. A repeated submission is recognised and not applied twice — over a
|
||
mobile connection the lost answer is the normal case, not the exception.
|
||
|
||
#col.live.feed
|
||
The server holds the request open until something happens, then answers at
|
||
once. Costs one open request per onlooker and runs only in the visible tab.
|
||
|
||
#col.live.merge
|
||
What happens when two people change the same line. The actual work — and a
|
||
solved problem, which is exactly why it should not be solved again here.
|
||
|
||
#col.live.diff
|
||
The answer we took: changes are line diffs against a base version. Where two
|
||
changes do not touch the same lines the server shifts them itself, so nobody
|
||
has to retry; where they do, a human decides. No runtime dependency, and the
|
||
same model on both sides.
|
||
|
||
#col.live.ask
|
||
On a real overlap a bar asks whose version should win. Whoever loses loses
|
||
nothing: the discarded state stays in the local snapshots, and every version
|
||
stays in the server's history.
|
||
|
||
#col.live.crdt
|
||
Because the plan is plain text, a text CRDT fits it without any merge logic
|
||
of our own, and cursors come along as a by-product. The price would be the
|
||
first runtime dependency this project has ever had.
|
||
|
||
#col.live.ot
|
||
The other established answer, and the one Google Docs uses. More machinery
|
||
for the same result.
|
||
|
||
#col.live.upload
|
||
Without it the only way onto the server was a curl command. A share button
|
||
in the editor title bar; the local plan stays where it is: whoever entrusts
|
||
their only copy to a server should not lose it in the act. The picker names
|
||
the host beside a server document, so the two entries are told apart.
|
||
|
||
#col.live.cursors
|
||
Seeing where the others are. A by-product of the merge layer rather than a
|
||
feature in its own right.
|
||
|
||
#col.live.ws
|
||
A socket and a backend to terminate it — long the obvious answer, and not
|
||
needed after all: request and response carry a diff just as well, and
|
||
everything around them stays simpler.
|
||
|
||
#col.live.own
|
||
Rejected: writing a merge algorithm is a research project with a known
|
||
answer.
|
||
|
||
#mmd
|
||
Werkbaum diagrams inside Mermaid, so a fenced block renders wherever Mermaid
|
||
already runs.
|
||
|
||
#mmd.layout
|
||
Mermaid brings no layout for this shape: measuring nodes, placing them and
|
||
routing the connectors all have to be written. Today the browser does all
|
||
three for free, which is why the editor never needed any of it.
|
||
|
||
#mmd.measure
|
||
How wide is a node, given its label, its badges and its tags. Without a
|
||
browser, nothing answers that for you.
|
||
|
||
#mmd.measure.text
|
||
Text metrics without a DOM — the part that decides how good the result
|
||
looks.
|
||
|
||
#mmd.measure.badge
|
||
Size badge, tags and marks have to be placed inside the box before the box
|
||
has a size.
|
||
|
||
#mmd.place
|
||
Positions for the nodes and paths for the connectors, for every arrangement
|
||
the editor offers.
|
||
|
||
#mmd.place.and
|
||
The fan for and-groups, in both orientations.
|
||
|
||
#mmd.place.horiz
|
||
Children side by side, the classic organigram.
|
||
|
||
#mmd.place.vert
|
||
The same, turned by ninety degrees.
|
||
|
||
#mmd.place.or
|
||
The rail for alternatives, dashed, with its branches.
|
||
|
||
#mmd.reg
|
||
Making Mermaid aware of a new diagram type at all.
|
||
|
||
#mmd.reg.ext
|
||
Registering from outside works only where Mermaid is initialized by the page
|
||
itself — not on GitHub, GitLab or Notion.
|
||
|
||
#mmd.reg.pr
|
||
Getting it into Mermaid proper makes it available everywhere, at the price
|
||
of a review by somebody else's project.
|
||
|
||
#idea
|
||
Werkbaum inside the IDE, where the plan file already sits next to the code
|
||
it describes.
|
||
|
||
#idea.view
|
||
The smaller half: show the diagram for the file currently in the editor.
|
||
|
||
#idea.view.mmd
|
||
If Werkbaum were part of Mermaid, the IDE would render it already — it
|
||
brings its own Mermaid along.
|
||
|
||
#idea.view.fence
|
||
Fenced blocks in Markdown would then render on the spot, with no plugin at
|
||
all.
|
||
|
||
#idea.view.selfmmd
|
||
Rejected: the IDE uses its own Mermaid build, so a self-hosted diagram type
|
||
never reaches it.
|
||
|
||
#idea.view.jcef
|
||
Or embed the bundled single file in a browser window inside the IDE. It is
|
||
already self-contained, which is most of the work done.
|
||
|
||
#idea.view.push
|
||
Hand the editor's text into the embedded page on every change.
|
||
|
||
#idea.view.back
|
||
And get the clicked node back out, to move the caret to its line.
|
||
|
||
#idea.edit
|
||
The larger half: editing the notation with everything an IDE brings, rather
|
||
than only looking at it.
|
||
|
||
#idea.lang
|
||
Registering .werkbaum as a language. Without that there is no highlighting,
|
||
no completion and no structure view.
|
||
|
||
#idea.lang.psi
|
||
A lexer and parser producing the IDE's own syntax tree. Everything below
|
||
hangs off it.
|
||
|
||
#idea.lang.color
|
||
Gate characters, status boxes, sizes and tags in colours of their own.
|
||
|
||
#idea.lang.inspect
|
||
The warnings the editor shows, as inspections in the margin.
|
||
|
||
#idea.lang.complete
|
||
Offering the status codes, the sizes and the known tags while typing.
|
||
|
||
#idea.lang.struct
|
||
The tree as a structure view, with folding that follows it.
|
||
|
||
#idea.build
|
||
Kotlin and the Gradle IntelliJ plugin — the same stack the backend uses.
|
||
|
||
#idea.drift
|
||
A second parser is exactly what the specification warns about: two grammars
|
||
that drift apart, and a plan that renders differently depending on where you
|
||
open it.
|
||
|
||
#idea.drift.fixtures
|
||
Keep them honest by testing both against the same examples, the canonical
|
||
one included.
|
||
|
||
#idea.drift.js
|
||
Or avoid the second parser altogether by running the one JavaScript parser
|
||
inside the IDE. No drift is possible, at the cost of a runtime inside the
|
||
plugin.
|
||
|
||
#trk
|
||
Connecting nodes to a ticket tracker, so a status need not be maintained
|
||
twice. Exactly one of these gets built, which is why the group is a XOR.
|
||
|
||
#trk.taiga
|
||
The tracker actually in use here. Self-hosted, with a REST API that is
|
||
enough for this.
|
||
|
||
#trk.resolve
|
||
Turn a reference in a line into a real ticket: title, link and current
|
||
state. Read-only, and already useful on its own.
|
||
|
||
#trk.resolve.read
|
||
Fetching the ticket and showing what it says.
|
||
|
||
#trk.resolve.map
|
||
Translating the tracker's workflow onto the eight states of the notation.
|
||
Configurable, because no two projects use the same column names.
|
||
|
||
#trk.write
|
||
The other direction: change a status in the plan and the ticket follows.
|
||
Only worth doing once reading it is trustworthy.
|
||
|
||
#trk.plugin
|
||
Rejected: a plugin inside Taiga means its legacy frontend and works only in
|
||
self-hosted installations. A companion application beside it is the better
|
||
cut.
|
||
|
||
#trk.tenzu
|
||
Taiga's successor, still young. Worth watching rather than building against;
|
||
a later move should cost only the API adapter.
|
||
|
||
#trk.none
|
||
The honest alternative: a plan in a text file may simply be enough. It is
|
||
the cheapest node in this group, and the cheapest path duly picks it.
|
||
|
||
#ai
|
||
Connect an LLM or an agentic AI to the editor, so the plan can be shaped in
|
||
conversation instead of by hand alone. An idea for now: the pieces exist
|
||
(the text is the whole state, and llms.md already teaches agents to read
|
||
and write it), but nothing here is designed yet.
|
||
|
||
#ai.dialog
|
||
A chat beside the diagram: ask for a decomposition, a size estimate or a
|
||
restructuring, and the AI answers by editing the notation text — every
|
||
change lands as an undoable text edit, with the human deciding what stays.
|
||
|
||
#ai.key
|
||
The user brings their own API key and the browser talks directly to the
|
||
chosen provider — no Werkbaum server in between, following the D20/D23
|
||
line: requests only at the user's request, only to the host they configure.
|
||
|
||
#ai.ground
|
||
The system prompt is essentially llms.md (D43): the agent guide already
|
||
explains the notation for reading and writing, so the model needs no
|
||
training — just the document and the request.
|