Skip to content

Decisions ​

Architecture decision records: each one states the context, the decision, the alternatives and the consequences, and is not rewritten once accepted. A later decision supersedes an earlier one instead.

RecordDecisionStatus
ADR-0001Named-graph partitioning: one graph per model, one for the seamsAccepted
ADR-0002IRI identity and migration: native element IDs as IRI fragmentsAccepted
ADR-0003Payload resolution: how native BPMN and DMN files are found and servedAccepted
ADR-0004View response contract: the JSON the viewer's /view endpoint returnsAccepted
ADR-0005Navigation resolution: which edges a click follows, and how farAccepted
ADR-0006Index-payload drift lint: keeping the graph and the native files in stepAccepted
ADR-0007Execution strategy: interpret where an engine exists, generate where none doesAccepted
ADR-0008Node-link renderer: elkjs layout with hand-rolled SVG cards, shared by C4 and shape graphsAccepted
ADR-0009Human tasks: a BPMN user task points at the form it is completed on (seam:completedOn)Accepted
ADR-0010Channel-started processes: a message start event that seam:subscribesTo a channel starts its process on each publishAccepted
ADR-0011Storage resolution: a shape is seam:storedIn a store whose deploy:storeKind the runtime supports, reached through a deploy:connectsTo bindingAccepted
ADR-0012Operation implementation: an operation seam:implementedBy an LLM completion, a Python handler in the handlers package, or an HTTP endpointAccepted
ADR-0013Error shapes and boundaries: an operation's hydra:possibleStatus carries a comm:errorCode and a problem+json error shape, and a lifted BPMN error boundary event seam:catches itAccepted
ADR-0014Scenarios and derived tests: verify: scenarios, decision examples and shape examples in the seams graph, and ODRL policies, become generated pytest files under generated/testsAccepted
ADR-0015Environments: development and production are arch:Environments, an environment-scoped deploy:override replaces the shared fact of the same predicate, and the compose generator writes one file per environmentAccepted
ADR-0016Generator protocol: a generator is a seams_gen.protocol.Generator (name, version, preconditions, extract, render, sources) registered in the seams.generators entry-point group; seams generate checks its precondition shapes strictly and seams.lock records its versionAccepted
ADR-0017Write path and editor: a patch edits the source TriG text at the triple level so comments and layout survive, the write path is a new seams_edit package outside the read-only seams library, and editing is a viewer mode that is off by defaultAccepted
ADR-0018Domain profiles: a profiles/<name>/profile.ttl package attached by seam:conformsTo lists the classes the editor offers (seam:offersClass) with SKOS or RDFS labels, the generators it previews (seam:usesGenerator) and shapes merged into health (seam:profileShapes); out-of-profile classes are flagged, never refusedAccepted
ADR-0019Generation trace: seams generate writes a line-level <output>.trace.json sidecar beside each output (template origins by sentinel render, model IRIs through finalize), records it in seams.lock, and derives the lock's sources from itAccepted
ADR-0020LLM drafts: seams propose sends a versioned, data-quoted prompt to a provider command, caches the response by context hash, and writes the handlers module only when its acceptance tests pass in a scratch copyAccepted
ADR-0021Vocabulary versions: each vocabulary carries an owl:versionIRI (MAJOR.MINOR), a model owl:imports the versions it targets in the manifest graph, seams.load loads same, refuses newer and migrates older models in memory with one SPARQL Update file per major step, seams migrate persists through seams_edit, and a deprecated term gets an sh:Warning shapeAccepted
ADR-0022Ontology notation: a seam:Ontology model graph per model is also an owl:Ontology with one owl:versionIRI; its owl:imports name version IRIs resolved offline against files vendored under ontology/vendor/ (gistCore 14.0.0), an unresolved or mismatched import is a ResolutionError(ontology, owl:imports), and the viewer draws it with the shape-graph card rendererAccepted
ADR-0023Shapes target classes: a shape names its ontology class with sh:targetClass, sh:name is a field's wire name, a field whose sh:class is a gist:Category subclass (or whose sh:in lists IRIs) offers its members' labels in lifecycle order while the wire value stays the label, an sh:node with one free property and fixed sh:hasValues flattens into one field, and seam moves to 1.2Accepted
ADR-0024JSON-LD context: the jsonld generator writes one context per shape with a sh:targetClass (<shape>.jsonld), mapping each Field.name to its sh:path typed by sh:datatype, a category field to a scoped @vocab term whose labels map to member IRIs (the wire value stays the label), flattened and reference fields to null; the generated API serves it at GET /contexts/<shape>.jsonld and links it with a rel="http://www.w3.org/ns/json-ld#context" headerAccepted
ADR-0025SemPKM as a store: deploy:SemPKM is a third deploy:StoreKind for domain data only; writes validate against the seam shapes then go to POST /api/commands with one service token, nested values become objects joined by an edge, SemPKM mints the IRIs, reads are SPARQL scoped to the shape's class, gist 14.0.0 is checked offline, and embedded SQLite stays the defaultAccepted
ADR-0026The Mental Model: the mentalmodel generator writes a SemPKM package (manifest.yaml, ontology/, shapes/, views/ JSON-LD) for the one ontology model; modelId from the ontology's IRI, version from owl:versionInfo, name from rdfs:label; package nodes in urn:sempkm:model:<modelId>:, domain IRIs kept as objects; the target-classed shapes as SHACL, one table view per IFML list or details view; gist 14.0.0 stated by owl:imports; a missing fact is a GeneratorErrorAccepted
ADR-0027SemPKM webhooks as a channel: a channel with comm:transport comm:SemPKMWebhook is inbound, received by POST at its comm:path on the one container that exposes it; SemPKM's unsigned object.changed delivery is a hint, so the runtime reads the event back with its service token, reads the affected objects of the channel's seam:acceptsShape class through SemPKMStore, and starts each subscribed process once per event and object with the object's IRI as correlation key; the runtime's own writes cannot start it, a load-time ruleAccepted
ADR-0028Copilot suggestions: seams suggest asks ADR-0020's provider command, not SemPKM's copilot, for a JSON suggestion from the versioned suggest-v1.txt; the first kind, condition-to-dmn, moves an inline gateway condition into a DMN decision with a business-rule task before the gateway; deterministic code builds the seams_edit patch and payload text, a scratch copy must pass the shapes, the decision checks and generation, and a passing suggestion waits under .seams/suggestions/<id>/ until a person runs --accept, which refuses a changed model; never in CIAccepted
ADR-0029LLM-drafted generators: seams draft-generator NAME --reference FILE asks ADR-0020's provider command, from the versioned draft-generator-v1.txt holding the reference and the model's names as quoted data, for one JSON object with the extract step, the template and the golden; the draft is assembled into seams new-generator's scaffold and kept only when the scaffold's four tests pass in a scratch copy, the template restates no model label and holds no logic; accepted answers are cached under .seams/generator-drafts/; nothing is written on failure; never in CIAccepted
ADR-0030Domains and bounded contexts: an arch:Domain arch:contains systems and contexts and carries one arch:classification (core, supporting or generic); an arch:BoundedContext is an arch: element, not a seam:Model, that seam:owns exactly one seam:Ontology and the model graphs in its language; one context per TriG file is a convention; a shared ontology has one owning context and others reach it by owl:imports of its version IRI (amending ADR-0022); L1 draws domains and contexts; context maps are B2'sAccepted
ADR-0031Lifecycle on the class: seam:lifecycle names an owl:Class (preferred) or a sh:NodeShape (kept); a shape's effective lifecycle is its own, else its sh:targetClass's, and more than one is a violation; a state:Transition is a domain event named by its IRI or state:trigger and may seam:publishesTo a comm:Channel; runtime emission is a follow-up; seam 1.4, no migrationAccepted
ADR-0032Context-map relationships: an arch:ContextRelationship in the architecture model joins one arch:upstream and one arch:downstream arch:BoundedContext with one arch:relationshipType (customer-supplier, conformist, anti-corruption layer, published language, shared kernel), at most one per pair; upstream is the side whose change breaks the other; a cross-context seam:reads/seam:writes is untranslated unless made by a translating operation or the relationship is conformist or a shared kernel; arch 1.2, no migrationAccepted
ADR-0033The DDD profile: seams check resolves a model's seam:conformsTo profile and validates the model with the profile's shapes, failing on sh:Violation (amending ADR-0018; seams generate still ignores profiles); the DDD rules (governed writes, fired transitions, translated cross-context access, one writing context per shape, one lifecycle, key and store per aggregate) are SHACL and sh:sparql in examples/expenses/chapter-11/profiles/ddd/, pinned to the gate queries they restate by a parity test; a type pill is a labelled sh:Info profile shape's conforming focus nodes, with no rdf:type triple; resolve_profile moves to the seams libraryAccepted
ADR-0034Context Mapper interchange: seams cml export writes each arch:BoundedContext as a CML BoundedContext named by its IRI local name and each arch:ContextRelationship as a named ContextMap line (customer-supplier [U,S]->[D,C], conformist [U]->[D,CF], anti-corruption layer [U]->[D,ACL], published language [U,PL]->[D], shared kernel [SK]<->[SK]), sorted; seams cml import adds the relationships it can map through seams_edit, never removes a fact, flags OHS alone, partnership, unknown contexts, unused roles and all other CML content on stderr instead of refusing them, and exits 1 only on a parse errorAccepted
ADR-0035Aggregate transaction boundary: seam:aggregateMember in the seams graph names the sh:NodeShapes an aggregate (the class or shape seam:lifecycle names, ADR-0033) commits together, listed and never derived; the seam shapes check it, the DDD profile requires its members in one store; Store.transaction() makes entity writes all-or-nothing on SQLite and PostgreSQL, SplitStore delegates to its relational store; Runtime.write_aggregate and the worker validate every member first and commit them in one transaction, raising AggregateRolledBack naming the aggregate and the failing shape, with one seams.aggregate.commit span; Runtime.load refuses an aggregate with more than one member in a SemPKM store or members in two stores with ResolutionError(aggregate, seam:aggregateMember); seam 1.5, no migrationAccepted
ADR-0036Cross-aggregate invariants: an attached profile's sh:Warning shapes with a sh:sparql constraint are instance-data rules (amending ADR-0033); Runtime.data_health() lifts every stored entity to RDF with contracts.to_rdf through its shape's sh:paths only (no rdf:type, no seam: term), validates it with those rules and returns sorted findings with a data health <profile> N warning(s) log line and a seams.data_health span; a payment names its expense through a :PaymentShape property whose sh:name is the correlation key label, filled by the runtime from the instance's key; warnings never refuse a write, and violations on instance data are out of scopeAccepted
ADR-0037Timers and scheduling: BPMN timer events are lifted as proc:TimerStartEvent (a timer-started process, no route) and proc:TimerCatchEvent (intermediate catch, or a boundary on a user task such as the overdue-approval reminder), with the expression kept in the XML and checked at load; only quoted ISO 8601 timeDate, timeDuration and timeCycle literals with a fixed offset and an explicit cycle start are accepted, anything else is ResolutionError naming the expression element; seam:schedules in the seams graph names the one container that runs a process's timer starts, which must also seam:executes it, and the compose generator adds no service; each firing runs once through a firing row or a lease in the shared store, with due times computed from the expression in UTC; timer fire log lines and seams.timer.claim / seams.timer.fire spans; seam 1.6 and proc 1.1, no migrationAccepted
ADR-0038Shared stream channel and replicas: the live-update bus follows the store kind, LISTEN/NOTIFY on one channel per runtime schema with a {channel, scope, frame} payload on PostgreSQL (a frame over 8000 bytes notified by its channel_frames id) and in-process on SQLite; new subscribers replay a backlog from channel_frames bounded per (channel, scope); Runtime.instances is a cache and every resume path reloads from the store, task locks stay per process and the user_tasks row is the cross-replica claim; the compose replica cap is lifted, more than one replica on a SQLite store is a GeneratorError naming the container and deploy:replicas; the proxy reaches replicas by compose service DNSAccepted

Released under the Apache-2.0 License.