Skip to content

8. The registration

Contents of FBL 0.15

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 names for the reading (a Databricks bundle file's job key), so that every selector segment of that capture matches only that key: capture, the same capture for every reading, or (FBL 0.10) the capture byOrigin gives the reading's origin, so that one binding selects a job for the job reading and a pipeline for the pipeline reading. Without the header, the first such entry in document order is used, (FBL 0.10) for each origin by its own capture. A reading whose origin byOrigin maps to null, or does not list, selects no resource and sees every one, as without resource; a resource header in its registration is kept and selects nothing. (FBL 0.10) A header naming a resource the body does not hold opens the reading empty, with the finding fbl.missing-resource (section 7.4): the body is not unreadable, and its other readings are read as before.

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. (FBL 0.12) When the rule declares sidecar.kind, its entries' keys are <kind>:<natural key>, so that the elements of two rules with one natural key, a component and a note of one text, keep entries of their own; rules that declare one kind, a Wardley map's components, anchors and submaps, share its key space. Entries are written, ordered and pruned like layout entries.

Two elements of one kind (or, without kind, of the binding) with the same natural key: without ordinal, the second is reported as DISL's std.duplicateId and gets an id that is not stored. (FBL 0.12) With ordinal, a text holding {n} once, the holders are counted per kind in document order, and each later holder's natural key is followed by ordinal with {n} replaced by its ordinal in decimal, 2 for the second: the Wardley map's "\u001f{n}\t" keys the second Tea as Tea, U+001F, 2 and a tab, the form its tool writes. Each holder so has an entry and an id of its own, read again for it when the body is opened again; a later holder's entry is first stored with the next write of the body.

(FBL 0.12) When an attribute that a natural key is computed from changes, by a set or by a reference rewritten in the same edit (section 5.7), the element's entry moves to its new key in the same edit, by a replace-value of its key, and an undo moves it back. An id first stored for an element, one read without an entry, is written with the next write of the body and is no step of the history, as a history: false block's writes are not (section 7.1); an entry whose key no longer names an element is pruned by the write that finds it so, as section 8.5 prunes a layout entry.

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 (section 6), as long as it exists. (FBL 0.11) View keys and element keys are matched with a view's key and an element's id ignoring the case of ASCII letters. When two keys of one view match one id, the first in the file applies; the others are neither applied nor written, and each is reported as fbl.duplicate-key. A number is written as section 6.3 writes it with number: "shortest". A move is a replace-value of x and of y; a first placement an insert-entry of the element's member, laid out as the member before it in its view is (on one line, or one key per line), and an ensure-container of the view's object, at the end of the root object, when the file has none for the view; undoing it removes them. An entry of the view being written whose key names no element of the body is stale: it is reported as fbl.stale-view-data and removed at that write, in the same edit, as section 8.5 removes a layout entry; entries of the other views of the file are not touched.
  • registration.legacyIdentities, such as {base}.identities.json for the Wardley map: (FBL 0.12) a JSON array of entries {"id": …, "kind": …, "key": …}, in which kind is a rule's sidecar.kind and key its natural key as section 8.6 makes it, ordinal included. An entry is matched to the element of that kind with that key. The file is read when the registration has no identities: block, and written back to it while it exists, by json splices (section 6): a new entry is an insert-entry after the array's last item, laid out as that item is; a moved key a replace-value of key; a pruned entry a remove-entry with one separator. An entry whose kind no rule of the binding declares is kept as written, never pruned, and reported as fbl.stale-view-data. FBL 0.9 described the file as a JSON object from natural key to id; no tool wrote that form, and it is not read.

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

Referenced from