Skip to content

Deliverable Generator — Tool Specification

Overview

This is the technical specification for the tool that turns deliverable templates into finished documents. If you're implementing a generator or want to understand exactly how the declarative pipeline executes, this page is the reference. The reference implementation is la-generate, in the Linked.Archi toolbox.

The generator reads an arch:DeliverableTemplate, asks the model questions each arch:TemplateSection names (arch:sectionAsks) or runs its inline query (arch:sectionQuery), and renders the results through a Handlebars layout into Markdown or HTML (PDF through a Markdown converter).

The pipeline is declarative: the template says what each section answers and asks, the question sets say how to ask it, and a layout says how to show it. The tool itself contains no domain-specific logic.

Architecture

flowchart TD
    Data["<b>Data</b><br/>model, its metamodel, question sets<br/>(files or a SPARQL endpoint)"]
    Tpl["<b>Deliverable template</b><br/>sections, concerns, questions,<br/>parameters"]
    Layout["<b>Layout</b><br/>(Handlebars)"]

    Tpl -->|"sections, sectionAsks,<br/>templateParameter"| Gen
    Data -->|"questions asked<br/>with VALUES pre-bound"| Gen
    Layout --> Gen

    Gen["<b>Deliverable Generator</b><br/>1. Load the template<br/>2. Resolve its parameters<br/>3. Ask each section's questions<br/>4. Run section queries<br/>5. Assemble the context<br/>6. Render and write"]

    Gen --> Output["<b>Output</b><br/>architecture-definition-document.md"]

CLI Interface

# An ArchiMate 4.0 Architecture Definition Document from a converted model
la-generate \
  --template-file archimate4-deliverable-templates.ttl \
  --template-id ArchitectureDefinitionDocument \
  --data my-model.trig \
  --data core-onto.ttl --data core-questions.ttl --data core-viewpoints.ttl \
  --data archimate4-onto.ttl --data archimate4-viewpoints.ttl --data archimate4-questions.ttl \
  --output architecture-definition-document.md

# A Change Impact Assessment for one element, between two plateaus
la-generate \
  --template-file archimate4-deliverable-templates.ttl \
  --template-id ChangeImpactAssessment \
  --data ... \
  --param focus=https://example.org/model/crm \
  --param from=https://example.org/model/baseline --param to=https://example.org/model/target \
  --output crm-impact.md

# From a SPARQL endpoint holding the model and its metamodel
la-generate \
  --template https://meta.linked.archi/archimate3/deliverable-templates#ArchitectureDefinitionDocument \
  --template-file archimate3.2-deliverable-templates.ttl \
  --endpoint http://localhost:7200/repositories/my-architecture \
  --output architecture-definition-document.md

# Override metadata
la-generate ... --meta '{"author":"Architecture Team","date":"2026-04-24","status":"Draft","version":"1.0"}'

Each document's Turtle is published at its namespace path followed by /ontology.ttl — https://meta.linked.archi/archimate4/questions/ontology.ttl for the ArchiMate 4.0 question set. The generator fetches nothing: it reads the files it is given and names a missing one when a question needs it.

Processing Pipeline

Step 1: Load the Template

From the template file, the data or the endpoint, the tool reads:

  • skos:prefLabel and skos:definition — the document's title and description;
  • arch:templateParameter — what the document is produced for;
  • arch:templateHasSection — its sections, each with arch:sectionOrder, skos:prefLabel, skos:definition, arch:sectionViewpoint, arch:sectionAddressesConcern, arch:sectionResultKey, and arch:sectionAsks (read in list order) or arch:sectionQuery.

Sections are sorted by arch:sectionOrder ascending.

Step 2: Resolve the Parameters

Each arch:templateParameter binds the variable named by its sh:path's local name (arch:focus binds ?focus). Values are given as IRIs (--param focus=<IRI>) and must occur in the data. A required parameter without a value stops the run — except ?model, which is the data's only arch:Model when there is exactly one.

Step 3: Ask the Questions

The data is read as one graph: the model, its metamodel and the question sets together, each triple once, whatever named graph a TriG or N-Quads file puts it in — the reading the questions are written for. For each question a section asks, in order:

  1. Find it in the loaded question sets. One that is not there is reported as not asked, naming the set to load.
  2. Bind the parameters it declares: ?viewpoint with the section's arch:sectionViewpoint, every other one with the template's value for it. A required parameter left without a value makes the question not asked; it is never run unbound.
  3. Probe for what it reads beyond the model when it declares arch:ModelAndMetamodelDataset: each ontology it names in arch:requiresMetamodel (ASK { <ontology> a owl:Ontology }), the viewpoint catalogue when it reads arch:includesConcept, the relationship classes when it reads arch:unqualifiedForm. Without them such a question would not fail — it would answer wrongly — so a failed probe makes it not asked, naming the Turtle to load.
  4. Run it: the PREFIX lines its sh:prefixes declare, its sh:select with VALUES ?var { <iri> } inserted at the start of the outermost WHERE group, and LIMIT one more than its arch:defaultLimit, so a cut-off answer is reported as such.

Step 4: Run Section Queries

A section with arch:sectionQuery runs it against the data. $name placeholders are substituted from --params (an IRI as <iri>, anything else as a string literal), and $viewpoint with the section's viewpoint.

Step 5: Assemble the Context

{
  "meta": {
    "title": "Change Impact Assessment (ArchiMate 4.0)",
    "date": "2026-04-24", "status": "Draft", "version": "1.0",
    "description": "Assesses a proposed change to one element: ...",
    "model": { "iri": "...", "label": "Claims handling", "curie": ":model" },
    "parameters": [ { "name": "focus", "variable": "focus", "iri": "...", "label": "CRM", "curie": ":crm" } ]
  },
  "sections": [
    {
      "order": 1, "title": "1. What Depends on the Element", "description": "...", "resultKey": "impact",
      "asksQuestions": true,
      "viewpoint": { "iri": "...", "label": "Layered Viewpoint", "curie": "am4vp:LayeredViewpoint" },
      "concerns": [ { "iri": "...", "label": "Impact of change" } ], "concernText": "Dependencies, Impact of change",
      "views": [ { "label": "...", "elements": [ ... ], "elementCount": 3, "relationshipCount": 1, "image": null } ],
      "questions": [
        {
          "iri": "https://meta.linked.archi/core-questions#neighbours", "label": "What is this element directly related to?",
          "description": "...", "answers": "One row per relationship: ...", "doesNotProve": "...",
          "isFinding": false, "severity": null, "concernText": "Dependencies",
          "status": "answered", "answered": true, "noneFound": false, "empty": false, "notAsked": false, "reason": null,
          "columns": ["direction", "relationship", "relationshipType", "other", "otherLabel"],
          "rows": [ ["incoming", ":r15", "am4:Aggregation", ":plateauA", "Baseline"] ],
          "displayColumns": ["Direction", "Relationship", "Relationship type", "Other"],
          "displayRows": [ ["incoming", "Aggregation", "Aggregation", "Baseline"] ],
          "rowCount": 1, "limit": 500, "truncated": false, "repeated": false
        }
      ]
    }
  ],
  "impact": { "questions": [ ... ], "neighbours": { ... } }
}

Each section of a template is an entry of sections; in a template whose sections ask questions, one that asks none and has no query still carries its viewpoint, concerns and views. A question's status is answered, none-found (a finding with no rows), empty (no rows, not a finding) or not-asked (with its reason). rows are as the query returned them, IRIs compacted; displayRows are as a reader sees them — an IRI by its name, a label column folded into its IRI's column, a relationship without a name by its type. A section is also available under its arch:sectionResultKey, with each question under its own arch:resultKey. A section query's rows are an array of objects under the section's result key, one key per projected variable.

Step 6: Render and Write

A document whose sections ask questions is rendered by the tool's own layout for Markdown and HTML, unless a layout is given (--hbs, or a file in --templates-dir named after the template's arch:templateResource, or after its label when it has none). A document of section queries is rendered by its layout, or a plain table per section when none is found. The output is written to the given path; for PDF, the Markdown is piped through a converter such as Pandoc or WeasyPrint.

Dependencies

  • Runtime: Java 25 (la-generate)
  • SPARQL: RDF4J, in memory for files; any SPARQL 1.1 endpoint (Oxigraph, Fuseki, GraphDB) with --endpoint
  • RDF: Turtle, TriG, N-Quads and N-Triples, RDF 1.2 triple terms included
  • Templating: Handlebars
  • PDF (optional): Pandoc or WeasyPrint

Error Handling

  • A question that cannot be asked is not an error: the document says why, and the run succeeds.
  • A question that fails to run is an error: the document reports it, and the run exits with status 1.
  • A required template parameter without a value, a value that is not an IRI or does not occur in the data, a missing data file, or a template that is not found or is ambiguous stops the run with status 1 and a message saying which.
  • A section query that returns no rows renders the note "No elements found for this section."; a failing section query is reported with the section's label and the section rendered empty.
  • If the SPARQL endpoint is unreachable, the tool exits with a clear error message.

Future Extensions

  • Diagrams: render each view of a section's viewpoint from its diagram geometry; today a view is listed with the elements it shows, and its image embedded where the model links one (schema:image).
  • CONSTRUCT queries: graph-shaped section results, such as nested elements with their relationships.
  • Multi-format: read arch:templateHasFormat and generate every supported format in one run.
  • Watch mode: regenerate when the knowledge graph changes.