@@ -373,10 +397,8 @@ defmodule ArtefactKino do
})
.then(() => {
if (!window.vis) return;
- const labelHues = buildLabelHues(data.nodes);
-
const nodes = new vis.DataSet(data.nodes.map(n => {
- const { bg, border } = nodeColour(n.labels, labelHues);
+ const { bg, border } = nodeColour(n.labels);
return {
...n,
shape: "ellipse",
diff --git a/artefact_kino/mix.exs b/artefact_kino/mix.exs
index fa2d876..af21264 100644
--- a/artefact_kino/mix.exs
+++ b/artefact_kino/mix.exs
@@ -5,7 +5,7 @@ defmodule ArtefactKino.MixProject do
@moduledoc false
use Mix.Project
- @version "0.2.0"
+ @version "0.3.0"
@github_url "https://github.com/diffo-dev/artefactory"
def project do
@@ -42,13 +42,13 @@ defmodule ArtefactKino.MixProject do
defp artefact_dep do
cond do
System.get_env("HEX_PUBLISH") == "1" ->
- {:artefact, "~> 0.2.0"}
+ {:artefact, "~> 0.3.0"}
File.exists?(Path.join(__DIR__, "../artefact/mix.exs")) ->
{:artefact, path: "../artefact"}
true ->
- {:artefact, "~> 0.2.0"}
+ {:artefact, "~> 0.3.0"}
end
end
diff --git a/artefact_kino/mix.lock b/artefact_kino/mix.lock
index 34d8e38..f8392ae 100644
--- a/artefact_kino/mix.lock
+++ b/artefact_kino/mix.lock
@@ -7,5 +7,6 @@
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
+ "splode": {:hex, :splode, "0.3.1", "9843c54f84f71b7833fec3f0be06c3cfb5be6b35960ee195ea4fad84b1c25030", [:mix], [], "hexpm", "8f2309b6ec2ecbb01435656429ed1d9ed04ba28797a3280c3b0d1217018ecfbd"},
"table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"},
}
diff --git a/artefactory_neo4j/CHANGELOG.md b/artefactory_neo4j/CHANGELOG.md
index b1e6958..1d08a1b 100644
--- a/artefactory_neo4j/CHANGELOG.md
+++ b/artefactory_neo4j/CHANGELOG.md
@@ -1,5 +1,22 @@
+
+
# Changelog
+## 0.3.0 — 2026-05-13
+
+### Tooling
+
+- Igniter task `mix artefactory_neo4j.install` — preferred installation method; configures Bolty in `runtime.exs`, adds `Bolty` to the supervision tree, and installs the `artefact` dependency automatically.
+- `usage-rules.md` — consumer-facing AI agent guidance, compatible with the `usage_rules` hex package ecosystem.
+- `usage_rules` dev dependency added.
+
+### Dependencies
+
+- Bumps `artefact` requirement to `~> 0.3.0`.
+
## 0.1.0 — 2026-04-21
Initial release.
diff --git a/artefactory_neo4j/README.md b/artefactory_neo4j/README.md
index ffb71ab..8b0c32c 100644
--- a/artefactory_neo4j/README.md
+++ b/artefactory_neo4j/README.md
@@ -17,10 +17,20 @@ Named databases on Neo4j Community Edition require **DozerDB** — a free plugin
## Installation
+The preferred way to install ArtefactoryNeo4j is via Igniter:
+
+```bash
+mix igniter.install artefactory_neo4j
+```
+
+This automatically configures Bolty in `runtime.exs`, adds `Bolty` to the supervision tree, and installs the `artefact` dependency.
+
+Or add the dependency manually:
+
```elixir
def deps do
[
- {:artefactory_neo4j, "~> 0.1"}
+ {:artefactory_neo4j, "~> 0.3"}
]
end
```
diff --git a/artefactory_neo4j/lib/mix/tasks/artefactory_neo4j.install.ex b/artefactory_neo4j/lib/mix/tasks/artefactory_neo4j.install.ex
new file mode 100644
index 0000000..a9ee268
--- /dev/null
+++ b/artefactory_neo4j/lib/mix/tasks/artefactory_neo4j.install.ex
@@ -0,0 +1,90 @@
+# SPDX-FileCopyrightText: 2026 artefactory contributors
+# SPDX-License-Identifier: MIT
+
+defmodule Mix.Tasks.ArtefactoryNeo4j.Install.Docs do
+ @moduledoc false
+
+ def short_doc, do: "Installs ArtefactoryNeo4j"
+ def example, do: "mix igniter.install artefactory_neo4j"
+
+ def long_doc do
+ """
+ #{short_doc()}
+
+ ## Example
+
+ ```bash
+ #{example()}
+ ```
+ """
+ end
+end
+
+if Code.ensure_loaded?(Igniter) do
+ defmodule Mix.Tasks.ArtefactoryNeo4j.Install do
+ @shortdoc "#{__MODULE__.Docs.short_doc()}"
+ @moduledoc __MODULE__.Docs.long_doc()
+
+ use Igniter.Mix.Task
+
+ @impl Igniter.Mix.Task
+ def info(_argv, _composing_task) do
+ %Igniter.Mix.Task.Info{
+ group: :artefactory_neo4j,
+ installs: [{:artefact, "~> 0.2"}],
+ example: __MODULE__.Docs.example()
+ }
+ end
+
+ @impl Igniter.Mix.Task
+ def igniter(igniter) do
+ igniter
+ |> Igniter.Project.Formatter.import_dep(:artefactory_neo4j)
+ |> Igniter.Project.Config.configure(
+ "runtime.exs",
+ :bolty,
+ [Bolt, :uri],
+ "bolt://localhost:7687"
+ )
+ |> Igniter.Project.Config.configure(
+ "runtime.exs",
+ :bolty,
+ [Bolt, :auth],
+ username: "neo4j",
+ password: "password"
+ )
+ |> Igniter.Project.Config.configure(
+ "runtime.exs",
+ :bolty,
+ [Bolt, :pool_size],
+ 10
+ )
+ |> Igniter.Project.Config.configure(
+ "runtime.exs",
+ :bolty,
+ [Bolt, :name],
+ Bolt
+ )
+ |> Igniter.Project.Application.add_new_child(
+ {Bolty, {:code, quote(do: Application.get_env(:bolty, Bolt))}}
+ )
+ end
+ end
+else
+ defmodule Mix.Tasks.ArtefactoryNeo4j.Install do
+ @shortdoc "#{__MODULE__.Docs.short_doc()} | Install `igniter` to use"
+ @moduledoc __MODULE__.Docs.long_doc()
+
+ use Mix.Task
+
+ def run(_argv) do
+ Mix.shell().error("""
+ The task 'artefactory_neo4j.install' requires igniter. Please install igniter and try again.
+
+ For more information, see: https://hexdocs.pm/igniter/readme.html#installation
+ """)
+
+ exit({:shutdown, 1})
+ end
+ end
+end
diff --git a/artefactory_neo4j/mix.exs b/artefactory_neo4j/mix.exs
index c77467e..4e8613c 100644
--- a/artefactory_neo4j/mix.exs
+++ b/artefactory_neo4j/mix.exs
@@ -5,7 +5,7 @@ defmodule ArtefactoryNeo4j.MixProject do
@moduledoc false
use Mix.Project
- @version "0.1.0"
+ @version "0.3.0"
@github_url "https://github.com/diffo-dev/artefactory"
def project do
@@ -30,8 +30,10 @@ defmodule ArtefactoryNeo4j.MixProject do
defp deps do
[
- {:artefact, "~> 0.1"},
+ {:artefact, "~> 0.3.0"},
{:bolty, "~> 0.0.9"},
+ {:igniter, ">= 0.6.29 and < 1.0.0-0", optional: true},
+ {:usage_rules, "~> 1.0", only: :dev, runtime: false},
{:ex_doc, "~> 0.37", only: [:dev, :test], runtime: false}
]
end
@@ -39,7 +41,7 @@ defmodule ArtefactoryNeo4j.MixProject do
defp package do
[
licenses: ["MIT"],
- files: ~w(lib .formatter.exs mix.exs README* CHANGELOG* LICENSES),
+ files: ~w(lib .formatter.exs mix.exs README* CHANGELOG* LICENSES usage-rules.md),
links: %{"GitHub" => @github_url}
]
end
diff --git a/artefactory_neo4j/mix.lock b/artefactory_neo4j/mix.lock
index 6a21235..7236db8 100644
--- a/artefactory_neo4j/mix.lock
+++ b/artefactory_neo4j/mix.lock
@@ -3,11 +3,27 @@
"bolty": {:hex, :bolty, "0.0.9", "c8026ce9804347f71e23b3a0cbc01b918ef94b61e159b5ba7fb48527878033ad", [:mix], [{:db_connection, "~> 2.7.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 6.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "fc20c42550c0fce370276b4ef119e92792761b2fea1aef9cccf8de946bc39d35"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
+ "ex_ast": {:hex, :ex_ast, "0.11.2", "8a0330e7b2bc664b52d7b60b8d5faa2628cd6e02fe80f89e726ff20328411dbf", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "df343260bd443306ca5da232615f1534a4aa740960055a4699e493fad8b4bd66"},
"ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"},
+ "finch": {:hex, :finch, "0.22.0", "5c48fa6f9706a78eb9036cacb67b8b996b4e66d111c543f4c29bb0f879a6806b", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b94e83c47780fc6813f746a1f1a34ee65cda42da4c5ea26a68f0acc4498e23dc"},
+ "glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"},
+ "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
+ "igniter": {:hex, :igniter, "0.8.0", "c7cab589440e5f20ff68e00f60eb094378114dab3105c0784ce8140f8dfdd2c0", [:mix], [{:ex_ast, "~> 0.5", [hex: :ex_ast, repo: "hexpm", optional: false]}, {:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "fcd99096fde4797f7b48bebddcfc58785569acd696346a3eb385bf813f47a7cc"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"},
+ "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
+ "mint": {:hex, :mint, "1.8.0", "b964eaf4416f2dee2ba88968d52239fca5621b0402b9c95f55a08eb9d74803e9", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "f3c572c11355eccf00f22275e9b42463bc17bd28db13be1e28f8e0bb4adbc849"},
+ "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
+ "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
+ "owl": {:hex, :owl, "0.13.0", "26010e066d5992774268f3163506972ddac0a7e77bfe57fa42a250f24d6b876e", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "59bf9d11ce37a4db98f57cb68fbfd61593bf419ec4ed302852b6683d3d2f7475"},
+ "req": {:hex, :req, "0.5.17", "0096ddd5b0ed6f576a03dde4b158a0c727215b15d2795e59e0916c6971066ede", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0b8bc6ffdfebbc07968e59d3ff96d52f2202d0536f10fef4dc11dc02a2a43e39"},
+ "rewrite": {:hex, :rewrite, "1.3.0", "67448ba7975690b35ba7e7f35717efcce317dbd5963cb0577aa7325c1923121a", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "d111ac7ff3a58a802ef4f193bbd1831e00a9c57b33276e5068e8390a212714a5"},
+ "sourceror": {:hex, :sourceror, "1.12.0", "da354c5f35aad3cc1132f5d5b0d8437d865e2661c263260480bab51b5eedb437", [:mix], [], "hexpm", "755703683bd014ebcd5de9acc24b68fb874a660a568d1d63f8f98cd8a6ef9cd0"},
+ "spitfire": {:hex, :spitfire, "0.3.11", "79dfcb033762470de472c1c26ea2b4e3aca74700c685dbffd9a13466272c323d", [:mix], [], "hexpm", "eb6e2dadf63214e8bfe65ca9788cef2b03b01027365d78d3c0e3d9ebd3d5b7b4"},
"telemetry": {:hex, :telemetry, "1.4.1", "ab6de178e2b29b58e8256b92b382ea3f590a47152ca3651ea857a6cae05ac423", [:rebar3], [], "hexpm", "2172e05a27531d3d31dd9782841065c50dd5c3c7699d95266b2edd54c2dafa1c"},
+ "text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
+ "usage_rules": {:hex, :usage_rules, "1.2.6", "a7b3f8d6e5d265701139d5714749c37c54bb82230a4c51ec54a12a1e4769b9d1", [:mix], [{:igniter, ">= 0.6.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "608411b9876a16a9d62a427dbaf42faf458e4cd0a508b3bd7e5ee71502073582"},
}
diff --git a/artefactory_neo4j/usage-rules.md b/artefactory_neo4j/usage-rules.md
new file mode 100644
index 0000000..7b289b1
--- /dev/null
+++ b/artefactory_neo4j/usage-rules.md
@@ -0,0 +1,109 @@
+
+
+# Rules for working with ArtefactoryNeo4j
+
+## Installation
+
+The preferred way to add ArtefactoryNeo4j to a project is via Igniter:
+
+```bash
+mix igniter.install artefactory_neo4j
+```
+
+This automatically: configures Bolty connection details in `runtime.exs`, adds `Bolty` to the supervision tree, and installs the `artefact` dependency. If your project does not use Igniter, do these steps manually and run `mix deps.get`.
+
+## What ArtefactoryNeo4j is
+
+ArtefactoryNeo4j persists `%Artefact{}` structs to a Neo4j-compatible graph database via the Bolt protocol (Bolty driver). It is a persistence boundary for knowledge graph fragments — not an ORM, not a data layer, not a query builder. You bring the artefact; it handles the Cypher.
+
+Multi-database support (one named database per entity — Me, Mob, a You) requires DozerDB or Neo4j Enterprise. It is not available on Neo4j Community Edition.
+
+## Connection model
+
+ArtefactoryNeo4j uses **direct Bolty connections**, not a named supervised pool. There is no global `Bolt` process, no `Repo` module, no application-level config key to set.
+
+```elixir
+{:ok, conn} = ArtefactoryNeo4j.connect(
+ uri: "bolt://localhost:7688",
+ auth: [username: "neo4j", password: "password"]
+)
+```
+
+This is different from `ash_neo4j`, which starts a named `Bolt` process in the supervision tree and reads from `config :bolty, Bolt, ...`. Do not carry that pattern here.
+
+## The `db:` option is required on every query
+
+Every `write/3` and `fetch/3` call requires a `db:` option that names the target database. There is no default database.
+
+```elixir
+:ok = ArtefactoryNeo4j.write(conn, artefact, db: "matt_artefactory")
+{:ok, rows} = ArtefactoryNeo4j.fetch(conn, uuid, db: "matt_artefactory")
+```
+
+Omitting `db:` raises `KeyError` — it is a required key, not optional.
+
+## Database naming convention
+
+Database names follow Elixir convention in code — `snake_case` atom or string. ArtefactoryNeo4j converts them to Neo4j `kebab-case` automatically:
+
+```elixir
+# These are equivalent
+ArtefactoryNeo4j.write(conn, artefact, db: :matt_artefactory)
+ArtefactoryNeo4j.write(conn, artefact, db: "matt_artefactory")
+# Both write to Neo4j database "matt-artefactory"
+```
+
+Do not pass a `kebab-case` string directly — it will be double-converted.
+
+## Property naming convention
+
+Node property keys are converted at the Bolt boundary automatically:
+
+- Elixir `snake_case` → Neo4j `camelCase` on write
+- Neo4j `camelCase` → Elixir `snake_case` on read
+
+```elixir
+# In Elixir: %{"first_name" => "Matt"}
+# In Neo4j: {firstName: "Matt"}
+```
+
+Do not manually convert property keys before passing them to ArtefactoryNeo4j, and do not expect `camelCase` keys in results.
+
+## write/3 uses MERGE — it is idempotent
+
+`write/3` generates parameterised `MERGE` Cypher, not `CREATE`. Calling it twice with the same artefact will update in place rather than create duplicate nodes. This is intentional — artefacts are knowledge fragments, and re-writing the same knowledge should be safe.
+
+## Connection pooling (DBConnection)
+
+Bolty is built on [DBConnection](https://hexdocs.pm/db_connection), which provides connection pooling. The `connect/1` call starts a pool — `pool_size:` controls how many concurrent Bolt connections it maintains. Tune this based on load in production.
+
+```elixir
+{:ok, conn} = ArtefactoryNeo4j.connect(
+ uri: "bolt://localhost:7688",
+ auth: [username: "neo4j", password: "password"],
+ pool_size: 10
+)
+```
+
+ArtefactoryNeo4j does not currently expose transactions. If you need transactional writes, use `Bolty.transaction/4` directly on the connection returned by `connect/1` — see the Bolty documentation.
+
+## Database lifecycle (DozerDB)
+
+Named databases are a DozerDB / Neo4j Enterprise feature. Each entity in the diffo model has its own named database:
+
+```elixir
+:ok = ArtefactoryNeo4j.create_database(conn, "matt_artefactory")
+:ok = ArtefactoryNeo4j.write(conn, artefact, db: "matt_artefactory")
+
+# Lifecycle
+:ok = ArtefactoryNeo4j.stop_database(conn, "matt_artefactory")
+:ok = ArtefactoryNeo4j.start_database(conn, "matt_artefactory")
+:ok = ArtefactoryNeo4j.drop_database(conn, "matt_artefactory")
+```
+
+All lifecycle operations route through the `system` database internally — you do not need to manage that yourself.
+
+`create_database/2` uses `IF NOT EXISTS` — safe to call repeatedly.