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:prefLabelandskos:definition— the document's title and description;arch:templateParameter— what the document is produced for;arch:templateHasSection— its sections, each witharch:sectionOrder,skos:prefLabel,skos:definition,arch:sectionViewpoint,arch:sectionAddressesConcern,arch:sectionResultKey, andarch:sectionAsks(read in list order) orarch: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:
- Find it in the loaded question sets. One that is not there is reported as not asked, naming the set to load.
- Bind the parameters it declares:
?viewpointwith the section'sarch: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. - Probe for what it reads beyond the model when it declares
arch:ModelAndMetamodelDataset: each ontology it names inarch:requiresMetamodel(ASK { <ontology> a owl:Ontology }), the viewpoint catalogue when it readsarch:includesConcept, the relationship classes when it readsarch: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. - Run it: the PREFIX lines its
sh:prefixesdeclare, itssh:selectwithVALUES ?var { <iri> }inserted at the start of the outermost WHERE group, andLIMITone more than itsarch: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:templateHasFormatand generate every supported format in one run. - Watch mode: regenerate when the knowledge graph changes.