@x12i/lattix
v0.2.0
Published
KnowX living knowledge graph Web Component (static Explorer + live memorix-service)
Downloads
62
Maintainers
Readme
@x12i/lattix
KnowX-native living-knowledge graph. A single interactive graph plus a smart sidebar for consuming and managing a KnowX knowledge graph — search, inspect evidence, and review pending claims.
It is a framework-neutral Web Component. It does not read fixture files,
hard-code demo records, or depend on a particular frontend framework. Object
model, evidence, and relationships are fetched through Memorix — either
static (@x12i/static-memorix Explorer API) or live (memorix-service
/api/data, /api/metadata, /api/relationships) — and rendered as
memorix-record/2 + KnowX content, per
Memorix Format for Dummies
and
Building KnowX as a Native Memorix Content Type.
The object model
Every subject on the graph — an employee, a work item, a claim between the
two — is a memorix-record/2 envelope. Business subjects (employees,
work-items) carry a snapshots instance (the product view) and a knowx
instance (the epistemic NODE facet: provenance + status). Claims between
subjects (assertions) are first-class subjects of their own, carrying a
knowx EDGE facet, a workflow instance (assignment/review state), and a
confirmations instance (the human accept/reject decision). Accepted claims
materialize as lightweight four-field relationship links for fast graph
traversal — see @x12i/memorix-format's v2 module and
src/api/knowledge-repository.ts for the read/write contracts this
component implements.
Package contents
src/
api/
knowledge-api.ts Backend port (static Explorer vs live memorix-service)
explorer-api-client.ts Typed Explorer API HTTP client
explorer-knowledge-api.ts Static adapter
memorix-service-client.ts Live memorix-service HTTP client
knowledge-repository.ts KnowledgeGraphRepository: compose, search, review
component/
lattix.ts <lattix-explorer> Web Component
styles.ts Shadow-DOM styles
renderers/
graph-renderers.ts The one graph layout (focus + depth-1 neighborhood)
types.ts KnowX .data shapes, RelationshipLink, ComposedSubject
mocks/
metadata/ Object types, list/write descriptors
data/ Fieldstone ops fixtures (employees, work-items, assertions, relationships)
examples/ Runnable Vite example
index.html Built-package HTMLData flow
<lattix-explorer mode="static|live">
│
▼
KnowledgeGraphRepository
│
├─ static → ExplorerKnowledgeApi → /api/explorer/* → @x12i/static-memorix (:5100)
└─ live → MemorixServiceClient → /api/data|metadata|relationships → memorix-service (:5000)mode="static" (default) uses the Explorer mock. mode="live" talks to a
running memorix-service with org-id / agent-ids scope headers.
Run the complete example
1. Install dependencies
npm install2. Start the static Memorix API
npm run dev (step 3) already starts this for you via dev:mock. To run it
standalone instead:
@x12i/static-memorix@^3.5.1 is a devDependency of this package (installed by
step 1). From this package directory, point it at Lattix's fixtures:
MOCKS_DIR="$PWD/mocks" static-memorix --port 5100Port 5100 is the static mock band (5000–5099 is reserved for live
memorix-service). It speaks content types natively (knowx, workflow,
confirmations, …) and enforces the write contract's revision preconditions —
see mocks/README.md for the fixture domain and
@x12i/static-memorix's own README for the general API contract.
After npm run build, index.html is also a direct replacement for the
original all-in-one page. It imports dist/index.js and contains no embedded
knowledge records. Serve the package directory through any static HTTP server;
do not open it with file://, because the browser must call the API.
3. Start the example UI
In another terminal:
npm run devThe example uses the included Vite proxy, so the browser calls /api on the
same origin and Vite forwards requests to port 5100 (the port npm run
dev:mock listens on).
Use the component
npm install @x12i/lattix<!-- static (default) — @x12i/static-memorix Explorer API -->
<lattix-explorer
mode="static"
api-base-url="http://localhost:5100"
initial-record-id="wi-100"
></lattix-explorer>
<!-- live — memorix-service -->
<lattix-explorer
mode="live"
api-base-url="http://localhost:5000"
org-id="g12opsdemo"
agent-ids="ops"
initial-record-id="wi-100"
></lattix-explorer>
<script type="module">
import "@x12i/lattix";
</script>For a same-origin static deployment, omit api-base-url or set it to an empty
string (Vite proxy / reverse proxy). Live mode always needs org-id and
agent-ids.
Attributes
| Attribute | Default | Meaning |
| --- | --- | --- |
| mode | static | static → Explorer/static-memorix · live → memorix-service |
| api-base-url | http://localhost:5100 (static) / http://localhost:5000 (live) | API origin; empty means same origin |
| org-id | — | Live only → x-memorix-org-id |
| agent-ids | — | Live only → x-memorix-agent-ids (comma-separated) |
| initial-record-id | first KnowX search result | recordId the graph opens focused on |
| poll-interval | 0 | Refresh interval in milliseconds; 0 disables polling |
| edit-mode | review | read-only (browse only, Review/Propose never render, even for pending claims) · review (today's Accept/Reject/Propose) · manage (same as review, plus unlocks the editableFields metadata hook for a future inline-editing UI) |
Public methods
const explorer = document.querySelector("lattix-explorer");
await explorer.refresh();
await explorer.selectSubject("wi-101");The narrate hook — explaining a connection in plain English
Opening a claim's Evidence tab shows a Story section. Lattix will not call an LLM itself — it's a portable, embeddable package, and it must not ship a vendor API key to every consumer. Instead it exposes an extension point:
const explorer = document.querySelector("lattix-explorer");
explorer.narrate = async ({ focus, other, link, assertion }) => {
// focus/other are the two connected ComposedSubjects, link is the
// GraphLink (direction + epistemic status), assertion is the claim's own
// ComposedSubject (knowx + workflow + confirmations) when resolvable.
// Call whatever LLM/proxy your host application controls -- the key
// stays server-side, on your infrastructure, never in this bundle.
const response = await fetch("/my-app/narrate-connection", {
method: "POST",
body: JSON.stringify({ focus, other, link, assertion })
});
return response.text();
};Leave narrate unset and the Story section instead shows a clearly labeled,
honest structured summary composed from the same evidence (not claiming to
be AI-generated) — useful out of the box, and a legible fallback if your
narrator throws.
Events
| Event | Detail |
| --- | --- |
| lattix-ready | { apiBaseUrl } |
| lattix-error | { error } |
| lattix-focus-change | { recordId, objectType } |
| lattix-selection-change | { recordId, objectType, selection: ComposedSubject \| RelationshipLink \| null } |
| lattix-confirm-request | { recordId, decision } — fired before a review write |
| lattix-confirmed | { recordId, decision, subject: ComposedSubject } — fired after a successful review write |
| lattix-proposed | { recordId, fromRecordId, toRecordId, subject: ComposedSubject } — fired after successfully proposing a new claim |
| lattix-share-request | { recordId, objectType } |
Accept/Reject in the Review tab, and "+ Propose new claim" in the
Connections tab, perform real writes against the configured Memorix backend
(KnowledgeGraphRepository.confirmAssertion / .proposeAssertion) — that is
this component's actual "manage the graph" surface. Sharing a view stays an
emitted intent event for the host application to fulfill, per the original
design philosophy: the component does not decide how sharing works.
API mapping
Static (mode="static") — Explorer / @x12i/static-memorix
| UI requirement | Explorer resource |
| --- | --- |
| Compose a subject's snapshot + knowx + workflow + confirmations | GET /api/explorer/records/full?entityName=…&recordId=… |
| Resolve a bare recordId to its objectType | GET /api/explorer/records/content?contentType=knowx&recordId=… |
| Search / browse KnowX subjects | GET /api/explorer/lists/{…}-knowx/records |
| Assertions pending review | list filter data.epistemic.confirmation:eq:pending |
| Relationship neighborhood | relationships-default list |
| Accept/reject a claim | POST /api/explorer/records/write (+ relationships upsert on accept) |
| Propose a new claim | POST /api/explorer/records/write × 2 (absent precondition) |
Live (mode="live") — memorix-service
| UI requirement | Service resource |
| --- | --- |
| Scope | headers x-memorix-org-id + x-memorix-agent-ids (always win over custom headers) |
| Object-type / knowx config | GET /api/metadata/effective → effective[] entries with kind: "object-types"; each definition must carry a Lattix knowx block |
| Compose | POST /api/data/compose { recordId, explicit: true } |
| List knowx / pending | GET /api/data/content?objectType&contentType=knowx&filter=… |
| Resolve knowx | GET /api/data/abstract?abstractType=KnowXEntity&id=… (projection → concrete content get) |
| Writes | POST /api/data/content with required precondition (no silent absent default on patch/upsert) |
| Relationship neighborhood | GET /api/relationships/query |
| Materialize on accept | POST /api/relationships/discover { definitionId, confirm: true } — definition-wide, not a single-link upsert; never PUT /api/relationships/links |
Live agent packs must declare the same knowx participation block Lattix reads on
static fixtures (at minimum role: "node"|"edge"). Stock platform packs that only
expose KnowXEntity as an abstract, without per-type knowx blocks, will load
zero graph subjects until those definitions are extended.
src/api/knowledge-repository.ts is the read/write contract; adapters live in
explorer-knowledge-api.ts and memorix-service-client.ts. The low-level
Explorer client remains exported at @x12i/lattix/client.
Fixture model
See mocks/README.md for the full fixture domain
(Fieldstone ops: employees, work-items, assertions, relationships) and how it
maps onto memorix-record/2 + KnowX. In short:
employees/work-items—snapshots(product view) +knowx(NODE facet: provenance + epistemic status).assertions—knowx(EDGE facet:refs.from/refs.to, provenance, epistemic status),workflow(assignment + review state),confirmations(the accept/reject decision, once reviewed).relationships— the four-field navigation links materialized once a claim's epistemic status is accepted asfact.
The graph is metadata-driven, not hardcoded
Lattix does not know the names "employees", "work-items", or "assertions" —
those are this package's demo domain, not something baked into the
component. Whether an object type shows up on the graph at all, whether it
behaves as a node or an edge (a claim/relationship type), which list
serves its knowx-tier content, which write descriptors handle its writes,
and which .data fields manage edit mode may touch, are all read from
that object type's own metadata document at runtime
(KnowledgeGraphRepository.loadMetadata(), src/api/knowledge-repository.ts).
Object types already carry an arbitrary metadata bucket
({name, summary?, catalogRelations?, [key: string]: unknown}), so this is
a convention layered on an existing platform primitive — no changes to
memorix-metadata/memorix-metadata-runtime were needed to build it.
To add a new object type to the graph, give its metadata document a knowx
block:
{
"name": "vendors",
"knowx": {
"role": "node",
"knowxListId": "vendors-knowx",
"editableFields": ["tier"]
}
}{
"name": "disputes",
"knowx": {
"role": "edge",
"knowxListId": "disputes-knowx",
"knowxWriteDescriptorId": "disputes-knowx-write",
"workflowWriteDescriptorId": "disputes-workflow-write",
"confirmationsWriteDescriptorId": "disputes-confirmations-write"
}
}Then add the matching list descriptor(s), write descriptor(s), and fixture
data under mocks/, and restart the static API — it loads fixtures once at
startup. No lattix source change, and no renderer change: the single graph
layout, the sidebar, and the review/propose flow all work off role, not a
name. role: "edge" is what makes an object type eligible for Review and
for proposeAssertion's edgeObjectType (it's picked automatically when
exactly one edge type is configured; pass it explicitly once you have more
than one).
If you want knowx.role to become a standard, documented, first-class part
of the Memorix object-type schema — not just a convention this package
happens to read — that's a real feature request against
memorix-metadata/memorix-metadata-runtime, not a lattix change.
Live packs: the same knowx block must appear on object-type definitions
returned by GET /api/metadata/effective (kind: "object-types"). Without it,
lattix reports that object types loaded but none participate in the graph.
Build and verify
npm run typecheck
npm test
npm run pack:checknpm test builds the package and runs node --test against the real
@x12i/static-memorix server (via MOCKS_DIR): it validates every fixture
against the real @x12i/memorix-format validator, exercises the Explorer API
client contract, and drives the full Web Component end to end — including
searching, focusing a subject, and running the Accept/Reject review flow
against real revision-safe writes.
