# `Artefact`
[🔗](https://github.com/diffo-dev/artefactory/blob/v0.1.5/lib/artefact.ex#L4)

A knowledge graph fragment — a small, self-contained piece of knowledge
expressed as a property graph.

The canonical form is the `%Artefact{}` struct. Arrows JSON and Cypher are
derived representations: JSON for interchange and visual editing, Cypher
for persistence.

## Operations

  * `new/1` — build an artefact, inline (`:nodes` + `:relationships`) or
    from a pre-built `%Artefact.Graph{}`.
  * `compose/3` — concatenate two artefacts; nodes remain disjoint.
  * `combine/3` — pipeline-friendly union; bindings auto-found via shared
    uuid; delegates to `harmonise/4`.
  * `harmonise/4` — union via declared bindings; lower uuid wins identity,
    labels are unioned, left wins on property conflict.
  * `graft/3` — pipeline-friendly extension; integrates inline `args`
    (same shape as `new`'s inline form, but every node MUST carry `:uuid`)
    into an existing artefact.

Every operation records its lineage in the result's `metadata.provenance`,
validates its inputs, and validates the produced artefact before returning
— so corruption fails at the call site rather than five steps downstream.

## Validation

  * `is_artefact?/1` — true when the value is an `%Artefact{}` struct.
  * `is_valid?/1` — true when the artefact passes every structural rule.
  * `validate/1` — returns `:ok` or `{:error, reasons}` (a list of strings).
  * `validate!/1` — returns `:ok` or raises `ArgumentError` with the
    collected reasons.

An artefact is *valid* when its uuid is a UUIDv7, every node has a
UUIDv7 uuid, every node's labels is a list of strings, every node's
properties is a map, every relationship's `from_id` and `to_id`
reference an extant node, every relationship type is a non-empty
string, and node uuids, node ids and relationship ids are unique
within the graph.

## Exporting

  * `Artefact.Arrows` — round-trip with [arrows.app](https://arrows.app)
    via `from_json/2`, `from_json!/2`, `to_json/1`.
  * `Artefact.Cypher` — derive Cypher (CREATE or MERGE) for Neo4j
    persistence, with parameterised variants for driver use.

# `t`

```elixir
@type t() :: %Artefact{
  base_label: String.t() | nil,
  description: String.t() | nil,
  graph: Artefact.Graph.t(),
  id: String.t(),
  metadata: map(),
  style: atom() | nil,
  title: String.t() | nil,
  uuid: String.t()
}
```

# `combine`
*macro* 

Combine `other` into `heart` using bindings auto-found between them.

Designed for pipelines — `heart` flows through the pipe as the first argument,
so a chain of combines accumulates a single heart from many others:

    me_knowing
    |> Artefact.combine(me_valuing)
    |> Artefact.combine(me_being)
    |> Artefact.combine(me_doing, title: "MeMind", description: "Mind of Me.")

Bindings are found via `Artefact.Binding.find/2` — every node sharing a uuid
between `heart` and `other` becomes a binding. Raises `MatchError` if no
bindings are found.

Internally delegates to `harmonise/4`, so `:title` and `:base_label` overrides
in `opts` are honoured. `:description` is also accepted and applied to the
result.

Records `:harmonised` provenance with the calling module.

# `compose`
*macro* 

Compose two artefacts into one. Graphs are concatenated without merging.
Nodes remain disjoint; label-based relationships are implicit.

`base_label` defaults to the portmanteau of both artefacts' base_labels.
Override with `base_label:` or `title:` in opts.

Records `:composed` provenance with the calling module and the metadata
of both source artefacts.

# `graft`
*macro* 

Graft `args` onto `left`, integrating new nodes and relationships
declared inline (same shape as `Artefact.new` accepts) without creating
a second artefact.

Designed for pipelines after a series of `combine`s — `args` flows in as
the second argument, with the result's `:title` and `:description` named
in `opts`:

    our_shells_artefact
    |> Artefact.combine(our_manifesto_artefact)
    |> Artefact.graft(args, title: "Our Shells and Manifesto",
         description: "Our Shells and Manifesto shape our Association Knowing.")

## args

A keyword list with `:nodes` and `:relationships`, identical in shape to
what `Artefact.new` accepts inline — except that **every node entry must
carry an explicit `:uuid`**. There is no auto-find: the uuid is the
binding.

Each args node either:

  * **Binds** to an existing left node (uuid present in `left.graph.nodes`).
    Labels are unioned, properties merged with **left winning** on key
    conflicts. Position is untouched.

  * **Adds** a new node (uuid not in left). Receives a fresh sequential id
    continuing left's offset.

Args relationships use args-local atom keys, just like `Artefact.new`.
Every key referenced by a relationship must be declared in `args.nodes`.

## opts

Honours `:title` and `:description` only — both name the result. If
omitted, `left`'s title and description carry forward. `:base_label` is
**not** honoured; the result keeps `left.base_label`.

## Raises

  * `ArgumentError` — any args node missing `:uuid`
  * `ArgumentError` — duplicate keys in `args.nodes`
  * `ArgumentError` — a relationship references a key not in `args.nodes`

## Provenance

Records `:grafted` with the calling module, a summary of `left`, and
`right: %{title: <opts.title>, description: <opts.description>}` — the
result's name as provided.

# `harmonise`
*macro* 

Harmonise two artefacts using declared bindings.

Bound nodes are merged: lower uuid wins for identity and properties,
labels are unioned. All relationships are preserved and remapped.
Returns a new artefact with a portmanteau base_label unless overridden.

Records `:harmonised` provenance with the calling module and the metadata
of both source artefacts.

# `is_artefact?`

Returns `true` when `value` is an `%Artefact{}` struct.

# `is_valid?`

Returns `true` when `value` is a valid artefact (see module docs).

# `new`
*macro* 

Create a new Artefact. Defaults `base_label` and `title` to the short name
of the calling module. Override with `title:` or `base_label:` in attrs.

Optional `description:` is a longer human-readable note about the artefact —
surfaced as Mermaid `accDescr` and in the `ArtefactKino` inspector. Defaults
to `nil`; pass it explicitly when you have something to say.

Records `:struct` provenance with the calling module.

# `validate`

Validate an artefact. Returns `:ok` or `{:error, reasons}` where reasons
is a list of human-readable strings describing each rule violation.

# `validate!`

Validate an artefact. Returns `:ok` or raises `ArgumentError` with the
collected reasons.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
