Skip to content

8. The registration

Contents of FBL 0.8

8.1 The line form

A registration is a small text file, *.adp, that names what a document is and keeps the view data a user placed. It is UTF-8 lines:

databricks/job
body: resources/nightly_ingest.yml
resource: nightly_ingest
layout:
  task:ingest: 0 0
  task:quality_gate: 260 0
  1. Line 1 is the origin of the tool type (language.origin, DISL §3.2), after a byte-order mark if there is one. A host finds the tool type's specification with that origin, and through its persistence the binding: the one a DISL specification's persistence.binding names, or, of the bindings a DESL specification's persistence.bindings names, the one whose claims take the body (section 12.1). A body two of them claim is refused with a reason, and a validator reports a specification whose bindings claim one extension twice.
  2. Headers follow, one per line, key: value (the first : separates them; blank lines are skipped). FBL defines body, view and resource; a binding declares others in registration.headers (the SKOS reading's language). A header neither defines is kept and reported as fbl.unknown-header. The header region ends at the first line that is not a header.
  3. Blocks follow the headers: layout: (section 8.3), identities: (section 8.6) and (FBL 0.7) the blocks the binding declares, each a line holding only the block's name and colon, followed by its entries, each indented by two spaces (any whitespace is read), <key>: <value>, where the key is everything before the last : of the line, so keys may contain : . A block's name is a Name (section 2.2).

(FBL 0.7) A binding declares further blocks in registration.blocks, as it declares further headers in registration.headers, each by its name, with these properties:

Property Values Meaning
order "ordinal" (default), "kept" ordinal: entries are written in the ordinal order of their keys, as layout: is. kept: an entry stands where the edit put it (section 8.4).
keys "names" (default), "ids" ids: an entry's key is an element's id, and the block is pruned and keeps no ephemeral id as section 8.5 says of layout.
history true (default), false false: writes to the block are not steps of the history (section 7.1).
doc Doc What the block holds.

A value of a declared block is one line of text, not empty, and holds no : , since the key is everything before the last one. What a value means is the tool type's specification's; FBL reads and writes it as text. A block neither FBL nor the binding declares is kept as it is written, and reported as fbl.unknown-block.

Anything after the blocks that is neither a block nor an entry is kept as unbound content. $defs/Registration in fbl.schema.json describes the parsed form; the examples in registrations/ validate against it.

8.2 Finding the body

body is a path relative to the registration's folder, /-separated. Without it, the body is the sibling file with the registration's base name and one of the binding's extensions (the first that exists, in the order of claims.extensions), or, for a folder binding, the folder the registration is in. A body that does not exist opens the document empty with fbl.missing-body, and nothing is written until the user points the registration at a file. A host MUST NOT follow a body outside the workspace without the user's consent (section 16).

view names which view of the body the document shows (section 9.3). resource selects one resource of a body that holds several: its value binds the selector capture the binding's registration.resource.capture names (a Databricks bundle file's job key); without the header, the first such entry in document order is used.

8.3 The layout block

layout: holds the positions the user placed, and only those: one entry per placed element, <id>: <x> <y>, the top-left corner of the element in the reading's canvas units. An element without an entry is placed by layout. Entries are:

  • written in the ordinal order of their ids (byte order of the UTF-8 encoding);
  • with numbers in {decimals: 3} form (section 6.3), so 120.5 and 640;
  • with the registration's own line ending by the rules of section 6.3.

Placing an element for the first time inserts its entry (insert-entry), creating layout: after the headers first when it is missing (ensure-container); moving it replaces its numbers (replace-value); undoing the first placement removes the entry, and the layout: line with the last entry (remove-container). Other view data (sizes, waypoints, label offsets) is not stored in FBL 0.1. (FBL 0.7) What else a tool type keeps beside the body, in a block of its own, a binding declares in registration.blocks (section 8.1).

8.4 Writing the registration

A registration is written with the splice rules of section 6, so a user's hand edits, comments below the blocks and unknown headers survive. View changes write only the registration; model changes write only the body, except where one edit concerns both (a rename of a stored id moves its layout entry in the same edit, with splices in both files, undone together). A host creates a registration beside a body the first time the user places an element, when the binding's registration.createOnFirstPlacement is true, and refuses the placement with a reason otherwise; a registration created this way is <origin> then body: <name> when the body's base name differs from the registration's, with the body's dominant line ending. (FBL 0.7) createOnFirstPlacement also creates it the first time the user changes a setting that a declared block keeps. Creating the file is not part of the edit: it is written with that text first, outside the history, and the edit is planned against it, so an undo leaves the file in place.

(FBL 0.7) Declared blocks are written by the same splices. A change to a setting that a declared block keeps writes the registration, as part of the edit of the transaction that makes it, so a change that concerns the body too is one edit with splices in both files, undone together.

  • The order of blocks. layout:, identities:, then the declared blocks in the order the binding declares them. A missing block is created (ensure-container) right after the last line of the nearest block before it in that order that exists, or after the headers when none does, and its line goes with its last entry (remove-container). An undeclared block stays where it is.
  • Entries. An entry is written <key>: <value>, indented by two spaces, with the registration's line ending. A new entry (insert-entry) goes where the ordinal order of the keys puts it in an ordinal block; in a kept block it goes at the position the change gives, else after the last entry. The place is found among the entries that remain once stale entries and, for a move, the moved entry are taken out. A changed value is a replace-value of the value, a renamed key a replace-value of the key, and a removed entry a remove-entry of its line. A move in a kept block is a remove-entry and an insert-entry of the entry's own bytes.
  • Refusals. An edit that would write an empty value, or a value that holds : or a line break, is refused (section 6.4), and so is a key that is empty, holds a line break, or is the key of another entry of the block.

8.5 Stale and ephemeral ids

A layout entry whose id is not in the model is stale: it is not applied to any other element, it is reported as fbl.stale-view-data, and it is removed at the next write of the registration, as part of that edit. A host MUST NOT store a position for an element whose id DISL marks ephemeral; dragging such an element moves it for the current session only, and the host tells the user why the position will not be kept. A layout entry found for an ephemeral id is ignored and reported by DISL's std.ephemeralViewData (DISL §11.5.3). (FBL 0.7) All of this holds for a declared block with keys: "ids" as for layout:: an entry whose key is no id of the model is stale, reported as fbl.stale-view-data and removed at the next write of the registration, and an ephemeral id is never stored as a key. The removal of a stale entry from a block with history: false is no step of the history either: the undo of the edit it was written with does not put the entry back.

8.6 Identities

identities: holds ids for bindings whose rules use id.sidecar (section 5.3): one entry per element, <natural key>: <id>, where the natural key is the value of the rule's sidecar.key expression and the id is made by DISL's strategy the first time the element is read without one. Entries are written, ordered and pruned like layout entries. Two elements with the same natural key share nothing: the second is reported as DISL's std.duplicateId and gets an id that is not stored.

8.7 Legacy sidecars

Two sidecar files that hosts wrote before FBL are read, so existing documents keep their view data:

  • registration.legacyLayout, such as {base}.layout.json for the C4 types: a JSON object keyed by view key, then by element id, each {"x": …, "y": …}. Positions of the registration's view are read from it when the registration has no layout: block, and written back to it, by json splices, as long as it exists; view keys are matched ignoring case.
  • registration.legacyIdentities, such as {base}.identities.json for the Wardley map: a JSON object mapping a natural key to an id, read when the registration has no identities: block and written back to it while it exists.

A host MUST NOT create either file for a new document.

Referenced from