Linked.Archi Agent Skills (APM)¶
Full documentation
This page summarizes the package. Everything — the six skills in detail, all 39 templates, the nine
analysis patterns, a worked conversational walkthrough, the CLI and the exit codes — is on its own
documentation site: linked-archi.github.io/linked-archi-apm.
Source: linked-archi/linked-archi-apm.
linked-archi-apm is six
Agent Skills that let an AI agent answer questions
about an enterprise architecture from the RDF graph the converters produce, and attach the
query, the dataset identity and the profile version to every answer it gives.
There is no application. The artifact is the context and the query templates, so a coding agent in an ordinary IDE can work as an architecture analyst against converter output — nothing is generated, bundled or copied at install time.
The problem it exists for¶
An architecture question answered from a model file is unfalsifiable: the reader cannot tell a correct answer from a plausible one. Two failure modes look identical to a caller, and both are silent:
- An empty result reads as absence. "No capability is unrealised" and "no capability is modelled" are different statements, and a query returning zero rows produces both.
- A wrong vocabulary returns nothing, quietly. Scope a query to a named graph the dataset does not have, and it succeeds with no rows.
Generic text-to-SPARQL hits both, for structural reasons no ontology dump conveys. Relationships are
resources rather than triples — an arch:QualifiedRelationship with arch:source and arch:target, with
the direct predicate shortcut opt-in at conversion time. Everything sits in named graphs, so a query without
a GRAPH clause matches a default graph that converter TriG leaves empty. Ownership, lifecycle and
cross-source identity are notation-specific or authored rather than derivable. And some IRIs denote a record
about a thing — a LeanIX fact sheet is a document about an application, not the application. Each produces
a query that runs and misleads.
Every design decision in the package follows from refusing to let those look like answers.
The six skills¶
Ownership is strict, and no skill imports another's Python package — cross-skill calls go through versioned JSON subprocess contracts.
| Skill | Owns | Never does |
|---|---|---|
linked-archi-source |
Acquiring RDF from bounded HTTPS, a pinned Git revision, or a read-only GitLab MCP handoff. Digest, size, redirect and parse verification before anything is promoted into an immutable local cache. | Executes no SPARQL. Has no exit code 1 — acquisition either verifies or refuses. |
linked-archi-connect |
Dataset discovery, attachment and identity. A local pyoxigraph store or a read-only SPARQL endpoint, reporting how many quads loaded and whether named graphs survived. |
Chooses no dataset for you. |
linked-archi-profile |
The graph profile: namespaces, roles, graph layout, capabilities, notations, membership mode and limits. verify probes the dataset and reports drift. |
Never applies a recommendation. |
linked-archi-query |
The 39-template catalogue, rendering, read-only enforcement, execution and the result envelope. | Invents no IRI from a label. |
linked-archi-validate |
SHACL in-process through pyshacl, plus reading a report somebody else produced. Target-class coverage and vacuity beside the verdict. |
Needs no JVM, converter or network. Executes no SPARQL. |
linked-archi-analyse |
Routing a question to an analysis pattern, planning the ordered steps, bundling the envelopes. | Executes nothing. |
Install¶
The package is an APM bundle, and the committed tree is the artifact. Requires Python 3.11+.
apm install linked-archi/linked-archi-apm#v0.6.0
apm install . # from a local clone
apm install . --target claude,codex,kiro # or -t all, when there is no signal to auto-detect
apm targets # what auto-detection resolves to, before committing to it
Without APM, copy the skill directories into whatever the client reads — ~/.claude/skills/,
~/.kiro/skills/, and so on.
| Dependency | Needed for | Without it |
|---|---|---|
PyYAML |
reading profiles | no profile loads |
pyoxigraph |
local file execution | endpoints still work; local files do not |
pyshacl (pulls rdflib) |
la-validate run |
la-validate doctor reports it missing and SHACL is skipped |
git |
Git-revision acquisition | HTTPS and MCP acquisition still work |
No triplestore, JVM or MCP server is required, and local querying plus exact source-cache reuse work
offline. Every skill has a doctor that reports where it is, which companions it resolved and what is
missing — check that before blaming the data.
From a question to a cited answer¶
Four steps of setup, then questions. Orientation is per session, not per question.
la-connect datasets # lists candidates, selects none
la-connect connect --data graph.trig # repeat --data to merge into one store
la-profile recommend --data graph.trig
la-profile verify --profile linked-archi-default --data graph.trig
la-query query run core/inventory-summary --data graph.trig
la-query query run core/resolve-element --data graph.trig --set TERM="order service"
la-connect datasets deliberately chooses nothing. Answering from a file nobody picked is the one failure
that cannot be detected afterwards: the result looks sound and cites the wrong architecture. For the same
reason the bundled fixtures are excluded from discovery unless --include-fixtures is passed.
--data and --endpoint together are refused, because two datasets would make the recorded identity wrong.
The template catalogue¶
Thirty-nine tested templates. Each declares what it answers, what it does not prove, what it needs from the profile, and which templates to use instead when it is refused. Thirty-three are cross-notation; six are notation-specific, gated on the vocabulary IRI they are written against rather than on a notation label.
| Stage | Templates | Purpose |
|---|---|---|
| Orientation | 4 | Establish what is actually loaded, so a later empty result can be told from a partial export |
| Resolution | 5 | Turn a name somebody typed into an IRI — never construct one from a label |
| Discovery | 3 | Find which predicates and relationship types this dataset actually uses |
| Analysis | 10 | What depends on what, what realises what, what crosses a layer |
| Views | 6 | What is drawn, where, and what differs between two diagrams |
| Enrichment | 3 | Attributes only some notations carry, such as lifecycle and ownership |
| Quality | 8 | Whether the model can be trusted to answer at all: gaps, orphans, provenance, identity, conformance |
The stages are an order rather than a menu: orient so a later absence can be trusted, resolve the names the question used, run the analysis template, then cite where the answer came from. Full catalogue on the template catalogue page.
Analysis patterns¶
Nine patterns, each a method for a kind of architecture question: which templates to run, in what order, what gates them, and — the part that matters most — when to stop.
| Pattern | Answers |
|---|---|
impact-and-dependency |
What breaks if this is retired, replaced or migrated |
traceability |
Which capability a system supports, and what realises what across layers |
coverage-and-gaps |
What is missing, unowned or uncovered |
model-contents |
What a given model actually contains |
model-quality |
Whether the model can be trusted to answer at all |
governance-and-decisions |
Which decisions, principles, policies and exceptions are recorded |
lifecycle-and-portfolio |
Duplication, overlap, lifecycle and rationalisation candidates |
cross-notation |
Whether two tools describe the same thing |
views-and-documentation |
What is drawn, and what changed between two diagrams |
A question with no pattern match is a real answer, not a failure: it does not look like anything the package has a method for. Every pattern also carries explicit stop rules — reachability established is not criticality established, a label match is not an identity, a difference between two diagrams is not yet a difference in the architecture. Depth on the analysis patterns page.
Three properties worth knowing¶
A refusal is an answer. Every template declares the profile roles, graph roles, capabilities and notation vocabulary it needs. When the dataset cannot support one, it is refused with a reason and a named alternative, on exit code 1 — routing, not a crash:
Template 'core/dependents-direct' cannot run against profile 'linked-archi-default':
- capability 'direct_rel_triples' is False but this template needs True
Try instead: core/dependents-qualified, core/neighbours-qualified
This is a refusal, not an empty result: running it anyway would return no rows and
read as 'nothing exists'.
Exit codes carry that meaning throughout: 1 is usually a finding to read and act on, not something to
retry. A SHACL run that selected no focus node exits 2 rather than reporting the pass a namespace mismatch
would otherwise produce.
Vocabulary is data. A profile binds semantic roles to concrete terms, prefixes, graph layout and taxonomy
terms — so the same template runs against a differently-flagged converter run, or a custom ontology, with no
code change. Seven profiles ship: three for converter output shapes (default, --emit-direct-rel-triples,
and a merged store with an authored reconciliation graph) and four worked examples including a flattened
Turtle store and a custom cloud-platform metamodel. la-profile derive drafts one from a converter
type-mapping file or a published arch:Metamodel manifest, and
la-profile verify reports where a claim is false about the dataset. Until a profile has been verified,
every result from it carries a caveat saying so.
Read-only is enforced, not requested. Update, insert, delete and load are rejected before any backend is
touched, by one policy implementation that linked-archi-connect delegates to — including queries arriving
through analyse's plans and connect's machine contract. Every result then carries a citation line:
core/models | query 4ae81572f06e | dataset base.trig | profile linked-archi-default v2 | 2026-09-16T23:24:55.552005+00:00 | 2 row(s)
query 4ae81572f06e is a SHA-256 over the query with whitespace collapsed, so re-indenting a template does
not change it but changing an IRI, a limit or a graph does. Two answers citing the same id ran the same
question.
Where it sits in the toolchain¶
The skills read what the converters write. They assume the RDF contract documented there — qualified relationships, named graphs per model, asserted model membership, a provenance graph — and the profile mechanism exists so that assumption is checkable rather than hoped for. See the flow end to end for how the two halves meet.