FBL — Format Binding Language
Contents of FBL 0.15
Specification, version 0.15 (Draft)
| Date | 2026-10-31 |
| Document schema | fbl.schema.json (JSON Schema, draft 2020-12), $defs/Document |
| Also in the schema | $defs/Registration (the parsed .adp registration), $defs/Fixture (round-trip fixtures) |
| Serves | every kind of tool: DISL through persistence.binding, DESL through persistence.bindings |
| Expression language | CEL — Common Expression Language (https://cel.dev) |
| Media type (provisional) | application/vnd.fbl.document+json |
| File extension | .fbl |
| Licence | Apache License 2.0 (Apache-2.0) |
This is a draft, version 0.15. It is complete enough to implement a conforming validator of FBL documents and a host that reads and writes bodies through declared bindings, but individual constructs may still change before version 1.0. FBL 0.2 (2026-10-05, approved by the product owner as part of the DISL 0.3 additions) gives the model changes a persistence plugin plans their fields: an add with a position, a move and a retype (section 11.2), which declared bindings follow too (section 6.4); and it lets a DISL specification map a binding's own type and attribute names onto its metamodel (DISL §11.2, persistence.typeMap; section 5.1). Every valid 0.1 document is a valid 0.2 document with the same meaning, except that a plugin that does not declare move is sent a reorder or re-parent as a remove and an add. FBL 0.3 (2026-10-09, for the Knowledge designer, spec 013) changes no behaviour: it names the tool type's specification wherever 0.2 said DISL, since a DESL specification uses bindings too (DESL section 5), and pairs each construct it relies on with its DISL and its DESL section (section 1.3); it lets a specification name one binding per format family, the body choosing among them by the files each claims (sections 1.2, 8.1); it spells a reference to a stored id as by: "id" (sections 5.2, 5.7); it lets a fixture's edit be a move or an add with a position, as section 11.2 already defines them (section 15.3); and it says how a container written on one line is created and removed around its entry (section 6.2). Every valid 0.2 document is a valid 0.3 document with the same meaning. FBL 0.4 (2026-10-09, for the Agent activity diagram, spec 014, by the ruling of 2026-10-09) lets a binding keep its entries and values in a nested mapping that the body may not have yet: an insert whose container is placed at: "end" creates every missing level of its selector (section 6.2), an attribute reached through child is written into a mapping it creates the same way in yaml and json (section 5.2), and remove-empty-levels removes the levels an entry's removal leaves empty (section 6.2). Every valid 0.3 document is a valid 0.4 document with the same meaning. FBL 0.5 (2026-10-10, spec 015, for the tools whose YAML files people write by hand) says what a slot reads from a scalar: it defines a scalar's written text (section 4.1.5), lets an attribute binding read it with read and makes it what a text attribute, an id and a reference read (sections 5.2, 5.3), presents it to CEL as text and style beside entry (section 2.4), lets a binding keep an entry readable when one value does not convert, with fallback, and read a quoted number, with quoted (sections 5.2, 7.4), and lets a fixture state the attribute values a reading yields and an add name the slot it fills (section 15.3). Every valid 0.4 document is a valid 0.5 document; section 5.2 lists the two places where 0.5 gives a meaning that 0.4 left open. FBL 0.6 (2026-10-12, spec 026, by the maintainer's ruling of 2026-10-12 on row B-09 of spec 025's behaviour-change register) makes an attribute binding's empty rule decide a new entry's key as it decides a set (sections 5.2, 6.2): 0.5 said that a key a new entry has no value for is left out, and left open whether an empty value counts as no value on an insert; 0.6 says it does not, so an attribute an add gives its empty value is written with keep, left out with remove and refuses the add with refuse (section 6.4), with a set's defaults, while an attribute the add does not give is still left out. Every valid 0.5 document is a valid 0.6 document. FBL 0.7 (2026-10-14, spec 028, for the Hierarchical Requirement Designer) adds a sixth family, bullets, which reads the list items under named headings of a Markdown file as a tree (section 4.8): a binding's sections name the headings, a rule's within the sections its items stand in, the slot {continuation: true} reads the lines indented under an item (section 5.2), and a move to another depth re-indents the entry it moves (sections 5.5, 6.3), and an insert's emit can write a literal bracket, as a Markdown task list needs (section 6.2), a fixture's read may state the parent of an element (section 15.3), an insert may place a line right after the header, and a case-insensitive rule's map ignores case (sections 5.2, 6.2). It lets a binding declare further blocks of the registration, registration.blocks, in the grammar layout: and identities: already have, each with the order its entries keep, whether they are keyed by ids and whether its writes are steps of the history (sections 8.1, 8.4), so a tool type can keep beside the body what the body's readers do not need. A word slot of a many attribute holds every word of its group that matches, which lets a line carry a list of tags (sections 4.6, 6.3). It also makes a host compare the body with the bytes an edit was planned against before applying it, as 0.6 required before an undo or a redo (section 7.2). Every valid 0.6 document is a valid 0.7 document with the same meaning. FBL 0.8 (2026-10-22, spec 033, for the functional decomposition graph, by the maintainer's ruling of 2026-10-22) lets an insert of the yaml and json families write a constant: a key and a scalar value every new entry of the rule is written with, that no attribute supplies, such as the value of the key whose value chose the rule (section 6.2). A constant only writes: when alone decides which rule an entry is read by, no edit but the add writes it, and a binding whose rule also writes the constant's key is refused when it is loaded (section 14.1). Every valid 0.7 document is a valid 0.8 document with the same meaning. FBL 0.9 (2026-10-25, spec 035, story 7, for the ADP tool language) adds a seventh family, grammar, for a format no other family reads (section 4.9): the binding carries an LALR(1) grammar in ATD grammar notation, inline or in a file beside the document, which must build without a conflict or the binding is refused when it is loaded (section 14.1); its rules select the nodes of the tree the grammar reads by their names, a slot {node} reads the text of a node, with match when only part of it is the value (section 5.2), a node's splice is its byte range, text no rule maps is kept, new entries are written from emit and removed with their lines (sections 6.2, 6.3), and with recover a body that does not parse is read in part, the text that does not being kept and reported at its line (sections 4.9, 7.4). No host is required to support the family in 0.9; one that does passes its round-trip fixtures byte for byte (section 15.1). Every valid 0.8 document is a valid 0.9 document with the same meaning. FBL 0.10 (2026-10-29, spec 066, for the Databricks conversion) lets one binding serve readings whose tool types have different metamodels: a rule's origins names the readings it is read for, so that an entry becomes at most one element per reading (section 5.1); a registration's resource takes a capture per origin with byOrigin, or none, and a header naming a resource the body does not hold opens the reading empty with fbl.missing-resource (sections 7.4, 8.2); and one open body keeps one model per origin it is read for, every reading of it editable and sharing its history (sections 9.1, 9.2). It states how the yaml family writes into a flow sequence: an empty one is opened into a block sequence, a one-line one takes a new item inside it and gives one up with one separator, and one written over several lines, like a flow mapping, takes no insert (sections 4.3, 6.2). It lets a binding give the sentence of each refusal it causes, with refusals (sections 5.2, 6.2, 6.4); writes an attribute reached through child into the mapping an add gives the new entry (section 6.2); reads a secondary slot when the attribute's own is absent and removes it when the attribute is written (section 5.2); and offers the template placeholder {key:lower} (section 13). A fixture can state that its input is a template, with template, and the bytes of the files beside its input that a step writes, with expectFiles, so that a fixture of a registration states the edits of the body it opens (section 15.3). Every valid 0.9 document is a valid 0.10 document with the same meaning. FBL 0.11 (2026-10-29, spec 066, for the C4 conversions) extends the grammar family so that the Structurizr DSL is bound in it (structurizr.fbl, with structurizr.lark). A comment is owned by the statement it stands on or before and kept by every splice, moved and removed with it (section 4.9); recover reads inside blocks too, with nested naming a block's opening and closing tokens and unclosed a token that may run to the end of the body, so that a statement that does not parse is skipped to the next one in its block, and an unclosed block, an unmatched closing token and an unclosed comment are each read as far as they go and reported (sections 4.9, 7.4); a slot's escape reads and writes a quoted value's escapes (sections 5.2, 6.3); an attribute's absent and positional parts are written with create's after and pad, and a many attribute's items are read from several nodes, gather reading a read-only value from two places (sections 5.2, 6.2); a rule's opens writes the block a first child needs around it (section 4.9); a block rule gives the views of a grammar binding, with at, view, origins and insert, so that a view block can be added and a selectedView rule reads a statement of the selected view only, the registration's view header choosing among the reading's views by their origin (sections 5.1, 9.3); and the legacy layout matches its keys ignoring case, writes the shortest number and prunes what is stale, reporting a key written twice as fbl.duplicate-key (sections 7.4, 8.7). A host that reads or writes Structurizr DSL through its declared binding supports the family (section 15.1). Every valid 0.10 document is a valid 0.11 document with the same meaning. FBL 0.12 (2026-10-29, spec 066, for the Wardley map conversion) extends the grammar family so that the Wardley map's .owm is bound in it (wardley-map.fbl, with wardley-map.lark), so that both formats section 15.1 requires a host of the family to read have a declared binding. A block whose opening brace stands on the line after its statement is read as the block of that statement (section 4.9); the segments that follow a statement's fixed parts are read in any order, each by its node, a single-valued one set alone and a many one written as a set of its items (section 4.9); a {node} slot's items reads a list of structured values, each item set, added and removed with its separator (sections 5.2, 6.3); an id kept in the sidecar has a kind, the key space it shares with the rules of that kind, and an ordinal, which gives each later holder of one natural key an entry and an id of its own; an entry moves with its key when the values it is computed from change, in the same edit; and an id first stored for an element is written with the next write of the body and is no step of the history (sections 5.3, 8.6). registration.legacyIdentities is read as the JSON array of {id, kind, key} the Wardley map's tool writes, its entries of a kind no rule declares kept and reported (section 8.7). Every valid 0.11 document is a valid 0.12 document with the same meaning, except that FBL 0.9 described the legacy identities file as a JSON object from natural key to id: no binding, example or fixture used that form and no tool wrote it, and 0.12 does not read it. FBL 0.13 (2026-10-30, spec 066, for gap 16 of the C4 conversions, by the maintainer's ruling of 2026-10-30) lets a {node} slot of a many attribute read several items from one node: with split, the slot's value is cut at every occurrence of that text, each piece without the white space around it is an item with a span of its own, and an empty piece is no item, so that Structurizr's tags "Extra, More" reads the two tags Extra and More (sections 4.9, 5.2). A set writes the difference into the node: a changed item by a replace-value of its own span, a new one after the last item with the slot's separator, a removed one with one separator, and a node left with no item as a removed node of the slot, so that every byte of an item the set keeps, and between two items it keeps, stays (section 6.3). structurizr.fbl splits the tags at their commas. Every valid 0.12 document is a valid 0.13 document with the same meaning. FBL 0.14 (2026-10-30, spec 066, for the Databricks conversion's adds of a resource) lets an insert of the yaml and json families add a member to a mapping: a rule whose at ends in a {capture} segment, such as /resources/jobs/{job}, writes a new entry under the key the add gives the attribute bound to that capture, at the place its place gives among the container's members, its value the entry's keys, constants and skeleton one step deeper, and refuses an add whose key is missing, empty or already in the mapping, with the sentences refusals.key and refusals.duplicate give (section 6.2). databricks.fbl adds a bundle's jobs and pipelines so. Every valid 0.13 document is a valid 0.14 document with the same meaning; no earlier document declared such an insert, which 0.13 left undefined. FBL 0.15 (2026-10-31, spec 067, for Turtle) lets the grammar family read a triple: a slot's selector reaches up and across the tree with .. and +, an attribute binding's or names further slots, the first present being read, a slot with expand reads a term through the body's prefixes and writes it back through them, one with ordinal reads a node's number in one of the body's ordinals, stable across readings of the same bytes, one with name reads a node's name, a slot's escape takes the other sequences of a format's strings with sequences, unicode, writes and requote, and body.order can read the elements in the order their entries end (sections 4.9, 5.2, 6.3). w3c-turtle-grammar.fbl binds Turtle's triples so. Every valid 0.14 document is a valid 0.15 document with the same meaning; none could give these properties, and .. and + were names no grammar can give a node. Sections and paragraphs marked (informative) explain intent and give guidance; everything else is normative.
FBL relies on constructs that the specification language of the tool type's kind defines: id strategies and ephemeral ids, findings and their source locations, derived elements, the editing transaction and the tool type's origin. Where this document names one of them, the specification language is the definition; FBL defines none of its own. Where this document says the tool type's specification, it means the DISL specification of a diagram or the DESL specification of a designer whose persistence names the binding. Section 1.3 lists, for each construct, the section of DISL and of DESL that defines it; a citation of DISL in this document is read, for a designer, as a citation of the DESL section that table pairs it with.
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY and OPTIONAL are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in bold capitals.