Repository: INCATools/kgcl-rdflib (language: python)

Issue:
Fix schema for edge changes to clarify how to uniquely identify an edge
The node change hierarchy makes use of an `about` field to indicate which node is being modified. Nodes have primary keys so it's easy to do things like:

```
c = NewSynonym(id='chg12345', about='ANAT:HindLimb', new_value='hindlimb')
```

It looks like I attempted to do something similar for edges, using an `about` field that points to an edge. However, edges don't have singular primary keys and we want to be able to refer to edges by SPO triple.

so the current model does allow this:

```
c = PredicateChange(id='chg12345', about=Edge(subject=FOO1:, predicate=..., object=..) new_value='hindlimb')
```

This has a natural YAML, JSON, JSON-LD, RDF serialization. However, due to the nesting it does NOT have a natural CSV serialization, and a core use case is being able to specify a set of changes in a spreadsheet

## option 1: denormalize the core model

for edges, rather than `about` we would have 3 fields `about_{s,p,o}`

The first disadvantage is denormalization

The second is that it assumes every edge (OWL axiom) is uniquely identified by a SPO triple. But in fact that is neither true of OWL nor of the more generalized use cases.

I have quite a few ontologies where I have >1 axiom with the same SPO. This is useful for a number of reasons such as provenenace. Axiom 1 may have evidence E1, contributor C1, publication P1, and Axiom 2 may have evidence E2, contributor C2, publication P2. If these all get conflated to the same edge then we mix E1 with P2 and so on.

See the discussion here for further context on how we deal with this in robot: https://github.com/ontodev/robot/issues/214

## option 2: denormalize/flatten at time of mapping to CSV

here we would keep the normalized/nested datamodel, but when translating to and from CSVs we would flatten things such that the outer field is concatenated with the inner field

e.g.

```yaml
id: chg123
description: I am changing predicate because blah blah
about:
  subject: FOO:1
  predicate: P:1
  object: FOO:2
...
```

==>

```yaml
d: chg123
description: I am changing predicate because blah blah
about_subject: FOO:1
about_predicate: P:1
about_object: FOO:2
```

(as a generalized algorithm this only works when the containing slot is singlevalued)

now, technically the SPO may not be unique here. We can imagine two modes:

 1. unambiguous: if your about selector does not uniquely identify a single edge, fail
 2. global: the change is applied to all edges that match

This has some nice properties. E.g. most graphs have maximum one edge between them, so P doesn't necessarily do much work as disambiguator, so we could say

 * "change predicate to subClassOf in edge between CNS and nervous system"


Discussion / hints:

Fix patch:
diff --git a/Makefile b/Makefile
index cdf0a60a..15e70104 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ TGTS = graphql jsonschema ldcontext docs  owl csv graphql python shex
 #GEN_OPTS = --no-mergeimports
 GEN_OPTS = 
 
-all: gen stage
+all: gen stage deploy-python
 gen: $(patsubst %,gen-%,$(TGTS))
 clean:
 	rm -rf target/
@@ -120,6 +120,7 @@ target/linkml/%.yaml: $(SCHEMA_DIR)/%.yaml tdir-limkml
 docserve:
 	mkdocs serve
 
+# remember to run this to update the datamodel in kgcl/
 deploy-python: stage-python
 	cp python/* kgcl/model/
 
diff --git a/examples/de-novo.yaml b/examples/de-novo.yaml
index 0dc3d864..c8fe7ac6 100644
--- a/examples/de-novo.yaml
+++ b/examples/de-novo.yaml
@@ -8,9 +8,10 @@ change_set:
 - {type: ClassCreation, node_id: ANAT:Limb, name: "limb"}  ## TODO: decide whether to use node_id or about
 - {type: PlaceUnder, object: ANAT:BodyPart}   ## subject is filled from about of last change
 - {type: ClassCreation, node_id: ANAT:Forelimb, name: "forelimb", superclass: ANAT:Limb} 
-- {type: NewSynonym, about: ANAT:Forelimb, new_value: "upper limb"}
+- {type: NewSynonym, about_node: ANAT:Forelimb, new_value: "upper limb"}
 - {type: ClassCreation, node_id: ANAT:Hindlimb, name: "hindlimb", superclass: ANAT:Limb}
-- {type: NewSynonym, about: ANAT:Hindlimb, new_value: "lower limb"}
+- {type: NewSynonym, about_node: ANAT:Hindlimb, new_value: "lower limb"}
 #- {type: ClassCreation, node_id: ANAT:Foot, name: "foot", superclass: ANAT:BodyPart, relationship: {predicate: ANAT:part-of, filler: ANAT:Hindlimb}}
 #- {type: ClassCreation, node_id: ANAT:Hand, name: "hand", superclass: ANAT:BodyPart, relationship: {predicate: ANAT:part-of, filler: ANAT:Hindlimb}, was_generated_by: uuid:e887d3aac33f} ## deliberate mistake
-#- {type: NodeMove, about: ANAT:Hand, old_predicate: ANAT:part-of, new_predicate: ANAT:Hindlimb, new_parent: ANAT:Forelimb}
+#- {type: NodeMove, about_node: ANAT:Hand, old_predicate: ANAT:part-of, new_predicate: ANAT:Hindlimb, new_parent: ANAT:Forelimb}
+#- {type: PredicateChange, about_edge: {subject: ANAT:Hindlimb, object: ANAT:Limb}, new_value: "subClassOf"}
diff --git a/graphql/kgcl.graphql b/graphql/kgcl.graphql
index decf94c2..ab810ab1 100644
--- a/graphql/kgcl.graphql
+++ b/graphql/kgcl.graphql
@@ -21,7 +21,7 @@ type AddNodeToSubset implements AddToSubset
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
     inSubset: OntologySubset
   }
 
@@ -39,7 +39,7 @@ type Agent
 
 interface AllowsAutomaticReplacementOfEdges
   {
-    about: String
+    about: OntologyElement
     hasUndo: Obsoletion
   }
 
@@ -85,7 +85,7 @@ type ClassCreation
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
     nodeId: Node
     name: String
     owlType: OwlTypeEnum
@@ -142,7 +142,6 @@ type DatatypeChange
     changeDate: String
     contributor: String
     hasUndo: Change
-    about: String
     oldValue: String
     newValue: String
   }
@@ -171,7 +170,7 @@ interface EdgeChange
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
   }
 
@@ -187,7 +186,7 @@ type EdgeCreation implements Creation
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
     predicate: Node
     object: Node
@@ -207,7 +206,7 @@ type EdgeDeletion implements Deletion
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
     predicate: Node
     object: Node
@@ -227,7 +226,7 @@ type EdgeLogicalInterpretationChange
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
   }
 
@@ -243,12 +242,13 @@ type EdgeObsoletion implements Obsoletion
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
     predicate: Node
     object: Node
     annotationSet: Annotation
     changeDescription: String
+    about: OntologyElement
   }
 
 type EdgeRewiring
@@ -263,7 +263,7 @@ type EdgeRewiring
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
   }
 
@@ -285,7 +285,6 @@ type LogicalAxiomChange
     changeDate: String
     contributor: String
     hasUndo: Change
-    about: String
     oldValue: String
     newValue: String
   }
@@ -321,7 +320,7 @@ type NameBecomesSynonym
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
     change1: NodeRename
     change2: NewSynonym
     changeDescription: String
@@ -338,7 +337,7 @@ type NewSynonym
     contributor: String
     hasUndo: Change
     oldValue: String
-    about: String
+    aboutNode: Node
     newValue: String
   }
 
@@ -353,7 +352,7 @@ type NewTextDefinition
     contributor: String
     hasUndo: Change
     oldValue: String
-    about: String
+    aboutNode: Node
     newValue: String
   }
 
@@ -377,7 +376,7 @@ type NodeAnnotationChange
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
   }
 
 type NodeAnnotationReplacement
@@ -392,7 +391,7 @@ type NodeAnnotationReplacement
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
   }
 
 interface NodeChange
@@ -407,7 +406,7 @@ interface NodeChange
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
   }
 
 type NodeCreation implements Creation
@@ -422,7 +421,7 @@ type NodeCreation implements Creation
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
     nodeId: Node
     name: String
     owlType: OwlTypeEnum
@@ -442,7 +441,7 @@ type NodeDeepening
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
     changeDescription: String
   }
@@ -459,7 +458,7 @@ type NodeDeletion implements Deletion
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
     changeDescription: String
   }
 
@@ -477,8 +476,9 @@ type NodeDirectMerge implements AllowsAutomaticReplacementOfEdges
     newValue: String
     hasNondirectReplacement: [Node]
     associatedChangeSet: [Change]
+    about: OntologyElement
     hasDirectReplacement: Node!
-    about: String
+    aboutNode: Node
     changeDescription: String
   }
 
@@ -494,7 +494,7 @@ type NodeMove
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
     changeDescription: String
   }
@@ -511,11 +511,12 @@ type NodeObsoletion implements Obsoletion
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
     hasDirectReplacement: Node
     hasNondirectReplacement: [Node]
     changeDescription: String
     associatedChangeSet: [Change]
+    about: OntologyElement
   }
 
 type NodeObsoletionWithDirectReplacement implements AllowsAutomaticReplacementOfEdges
@@ -530,9 +531,10 @@ type NodeObsoletionWithDirectReplacement implements AllowsAutomaticReplacementOf
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
     hasNondirectReplacement: [Node]
     associatedChangeSet: [Change]
+    about: OntologyElement
     hasDirectReplacement: Node!
     changeDescription: String
   }
@@ -549,9 +551,10 @@ type NodeObsoletionWithNoDirectReplacement
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
     hasDirectReplacement: Node
     associatedChangeSet: [Change]
+    about: OntologyElement
     hasNondirectReplacement: [Node]!
     changeDescription: String
   }
@@ -566,7 +569,7 @@ type NodeRename
     changeDate: String
     contributor: String
     hasUndo: Change
-    about: String
+    aboutNode: Node
     oldValue: String
     newValue: String
     hasTextualDiff: TextualDiff
@@ -585,7 +588,7 @@ type NodeShallowing
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
     changeDescription: String
   }
@@ -602,7 +605,7 @@ type NodeSynonymChange
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
   }
 
 interface NodeTextDefinitionChange
@@ -617,7 +620,7 @@ interface NodeTextDefinitionChange
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
   }
 
 type NodeUnobsoletion implements Unobsoletion
@@ -632,7 +635,7 @@ type NodeUnobsoletion implements Unobsoletion
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutNode: Node
     changeDescription: String
     replacedBy: Node
     consider: Node
@@ -640,7 +643,7 @@ type NodeUnobsoletion implements Unobsoletion
 
 interface Obsoletion
   {
-    about: String
+    about: OntologyElement
     hasUndo: Obsoletion
   }
 
@@ -664,7 +667,7 @@ type PlaceUnder
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
     predicate: Node
     object: Node
@@ -684,7 +687,7 @@ type PredicateChange
     hasUndo: Change
     oldValue: String
     newValue: String
-    about: String
+    aboutEdge: Edge
     subject: Node
     changeDescription: String
   }
@@ -712,7 +715,7 @@ type RemoveSynonym
     contributor: String
     hasUndo: Change
     newValue: String
-    about: String
+    aboutNode: Node
     oldValue: String
   }
 
@@ -727,7 +730,7 @@ type RemoveTextDefinition
     contributor: String
     hasUndo: Change
     newValue: String
-    about: String
+    aboutNode: Node
     oldValue: String
   }
 
@@ -744,7 +747,7 @@ type RemovedNodeFromSubset implements RemoveFromSubset
     oldValue: String
     newValue: String
     changeDescription: String
-    about: String
+    aboutNode: Node
     subset: String
     inSubset: OntologySubset
   }
@@ -765,7 +768,6 @@ interface SimpleChange
     changeDate: String
     contributor: String
     hasUndo: Change
-    about: String
     oldValue: String
     newValue: String
   }
@@ -785,7 +787,7 @@ type SynonymPredicateChange
     changeDate: String
     contributor: String
     hasUndo: Change
-    about: String
+    aboutNode: Node
     oldValue: String
     newValue: String
     hasTextualDiff: TextualDiff
@@ -801,7 +803,7 @@ type SynonymReplacement
     changeDate: String
     contributor: String
     hasUndo: Change
-    about: String
+    aboutNode: Node
     oldValue: String
     newValue: String
     hasTextualDiff: TextualDiff
@@ -817,7 +819,7 @@ type TextDefinitionReplacement
     changeDate: String
     contributor: String
     hasUndo: Change
-    about: String
+    aboutNode: Node
     oldValue: String
     newValue: String
     hasTextualDiff: TextualDiff
diff --git a/jsonschema/kgcl.schema.json b/jsonschema/kgcl.schema.json
index bc9dfdd2..b2707351 100644
--- a/jsonschema/kgcl.schema.json
+++ b/jsonschema/kgcl.schema.json
@@ -38,7 +38,7 @@
          "additionalProperties": false,
          "description": "Places a node inside a subset, by annotating that node",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -149,7 +149,7 @@
          "additionalProperties": false,
          "description": "A node creation where the owl type is 'class'",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "annotation_set": {
@@ -271,10 +271,6 @@
          "additionalProperties": false,
          "description": "",
          "properties": {
-            "about": {
-               "description": "The 'focus' entity on which the change operates",
-               "type": "string"
-            },
             "change_date": {
                "type": "string"
             },
@@ -340,8 +336,8 @@
          "additionalProperties": false,
          "description": "An edge change in which a de-novo edge is created. The edge is potentially annotated in the same action.",
          "properties": {
-            "about": {
-               "type": "string"
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "annotation_set": {
                "$ref": "#/definitions/Annotation"
@@ -402,8 +398,8 @@
          "additionalProperties": false,
          "description": "An edge change in which an edge is removed. All edge annotations/properies are removed in the same action.",
          "properties": {
-            "about": {
-               "type": "string"
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "annotation_set": {
                "$ref": "#/definitions/Annotation"
@@ -464,8 +460,8 @@
          "additionalProperties": false,
          "description": "An edge change where the subjet, object, and predicate are unchanged, but the logical interpretation changes",
          "properties": {
-            "about": {
-               "type": "string"
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "change_date": {
                "type": "string"
@@ -515,7 +511,11 @@
          "description": "An edge change in which an edge is obsoleted.",
          "properties": {
             "about": {
-               "type": "string"
+               "$ref": "#/definitions/OntologyElement",
+               "description": "The element that is obsoleted by this change."
+            },
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "annotation_set": {
                "$ref": "#/definitions/Annotation"
@@ -576,8 +576,8 @@
          "additionalProperties": false,
          "description": "An edge change where one node is replaced with another, as in the case of obsoletion with replacement",
          "properties": {
-            "about": {
-               "type": "string"
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "change_date": {
                "type": "string"
@@ -649,10 +649,6 @@
          "additionalProperties": false,
          "description": "A simple change where a logical axiom is changed, where the logical axiom cannot be represented as an edge",
          "properties": {
-            "about": {
-               "description": "The 'focus' entity on which the change operates",
-               "type": "string"
-            },
             "change_date": {
                "type": "string"
             },
@@ -757,7 +753,7 @@
          "additionalProperties": false,
          "description": "A node synonym where the name NAME of an node NODE moves to a synonym, and NODE receives a new name. This change consists of compose of (1) a node rename where NAME is replaced by a different name (2) a new synonym",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_1": {
@@ -813,7 +809,7 @@
          "additionalProperties": false,
          "description": "A node synonym change where a de-novo synonym is created",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -860,7 +856,7 @@
          "additionalProperties": false,
          "description": "A node change where a de-novo text definition is created",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -930,7 +926,7 @@
          "additionalProperties": false,
          "description": "A node change where the change alters node properties/annotations. TODO",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -977,7 +973,7 @@
          "additionalProperties": false,
          "description": "A node annotation change where the change replaces a particular property value. TODO",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -1024,7 +1020,7 @@
          "additionalProperties": false,
          "description": "a node change in which a new node is created",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "annotation_set": {
@@ -1087,8 +1083,8 @@
          "additionalProperties": false,
          "description": "A node move in which a node where the destination is a proper descendant of the original location. Note that here descendant applied not just to subclass, but edges of any predicate in the relational graph",
          "properties": {
-            "about": {
-               "type": "string"
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "change_date": {
                "type": "string"
@@ -1140,7 +1136,7 @@
          "additionalProperties": false,
          "description": "Deletion of a node from the graph. Note it is recommended nodes are obsoleted and never merged, but this operation exists to represent deletions in ontologies, accidental or otherwise",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -1191,6 +1187,10 @@
          "description": "An obsoletion change in which all metadata (including name/label) from the source node is deleted and added to the target node, and edges can automatically be rewired to point to the target node",
          "properties": {
             "about": {
+               "$ref": "#/definitions/OntologyElement",
+               "description": "The element that is obsoleted by this change."
+            },
+            "about_node": {
                "type": "string"
             },
             "associated_change_set": {
@@ -1258,8 +1258,8 @@
          "additionalProperties": false,
          "description": "A node move is a combination of deleting a parent edge and adding a parent edge, where the predicate is preserved and the object/parent node changes",
          "properties": {
-            "about": {
-               "type": "string"
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "change_date": {
                "type": "string"
@@ -1312,6 +1312,10 @@
          "description": "Obsoletion of a node deprecates usage of that node, but does not delete it.",
          "properties": {
             "about": {
+               "$ref": "#/definitions/OntologyElement",
+               "description": "The element that is obsoleted by this change."
+            },
+            "about_node": {
                "type": "string"
             },
             "associated_change_set": {
@@ -1380,6 +1384,10 @@
          "description": "An obsoletion change in which information from the obsoleted node is selectively copied to a single target, and edges can automatically be rewired to point to the target node",
          "properties": {
             "about": {
+               "$ref": "#/definitions/OntologyElement",
+               "description": "The element that is obsoleted by this change."
+            },
+            "about_node": {
                "type": "string"
             },
             "associated_change_set": {
@@ -1448,6 +1456,10 @@
          "description": "An obsoletion change in which there is no direct replacement",
          "properties": {
             "about": {
+               "$ref": "#/definitions/OntologyElement",
+               "description": "The element that is obsoleted by this change."
+            },
+            "about_node": {
                "type": "string"
             },
             "associated_change_set": {
@@ -1515,7 +1527,7 @@
          "additionalProperties": false,
          "description": "A node change where the name (aka rdfs:label) of the node changes",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -1567,8 +1579,8 @@
          "additionalProperties": false,
          "description": "The opposite of node deepening",
          "properties": {
-            "about": {
-               "type": "string"
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "change_date": {
                "type": "string"
@@ -1620,7 +1632,7 @@
          "additionalProperties": false,
          "description": "",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -1667,7 +1679,7 @@
          "additionalProperties": false,
          "description": "unobsoletion of a node deprecates usage of that node. Rarely applied.",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -1739,8 +1751,8 @@
          "additionalProperties": false,
          "description": "An edge creation where the predicate is owl:subClassOf",
          "properties": {
-            "about": {
-               "type": "string"
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "annotation_set": {
                "$ref": "#/definitions/Annotation"
@@ -1801,8 +1813,8 @@
          "additionalProperties": false,
          "description": "An edge change where the predicate (relationship type) is modified.",
          "properties": {
-            "about": {
-               "type": "string"
+            "about_edge": {
+               "$ref": "#/definitions/Edge"
             },
             "change_date": {
                "type": "string"
@@ -1869,7 +1881,7 @@
          "additionalProperties": false,
          "description": "A node synonym change where a synonym is deleted",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -1916,7 +1928,7 @@
          "additionalProperties": false,
          "description": "A node change where a text definition is deleted",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -1963,7 +1975,7 @@
          "additionalProperties": false,
          "description": "Removes a node from a subset, by removing an annotation",
          "properties": {
-            "about": {
+            "about_node": {
                "description": "The node that is removed from the subset",
                "type": "string"
             },
@@ -2044,7 +2056,7 @@
          "additionalProperties": false,
          "description": "A node synonym change where the predicate of a synonym is changed. Background: synonyms can be represented by a variety of predicates. For example, many OBO ontologies make use of predicates such as oio:hasExactSynonym, oio:hasRelatedSynonym, etc",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -2095,7 +2107,7 @@
          "additionalProperties": false,
          "description": "A node synonym change where the text of a synonym is changed",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
@@ -2146,7 +2158,7 @@
          "additionalProperties": false,
          "description": "A node change where a text definition is modified",
          "properties": {
-            "about": {
+            "about_node": {
                "type": "string"
             },
             "change_date": {
diff --git a/kgcl/model/kgcl.py b/kgcl/model/kgcl.py
index a5bba54a..fec75fcd 100644
--- a/kgcl/model/kgcl.py
+++ b/kgcl/model/kgcl.py
@@ -1,11 +1,12 @@
 # Auto generated from kgcl.yaml by pythongen.py version: 0.9.0
-# Generation date: 2021-05-15 16:30
+# Generation date: 2021-07-16 20:38
 # Schema: kgcl
 #
 # id: https://w3id.org/kgcl
 # description: A data model for describing change operations at a high level on an ontology or ontology-like
-#              artefact, such as a Knowledge Graph. See
-#              [https://github.com/cmungall/knowledge-graph-change-language](https://github.com/cmungall/knowledge-graph-change-language)
+#              artefact, such as a Knowledge Graph. * [Browse
+#              Schema](https://cmungall.github.io/knowledge-graph-change-language/) *
+#              [GitHub](https://github.com/cmungall/knowledge-graph-change-language)
 # license: https://creativecommons.org/publicdomain/zero/1.0/
 
 import dataclasses
@@ -24,7 +25,7 @@
 from linkml_runtime.utils.enumerations import EnumDefinitionImpl
 from rdflib import Namespace, URIRef
 from linkml_runtime.utils.curienamespace import CurieNamespace
-from . ontology_model import Annotation, NodeId, OntologySubset, OwlTypeEnum, PropertyValue
+from . ontology_model import Annotation, Edge, NodeId, OntologyElement, OntologySubset, OwlTypeEnum, PropertyValue
 from . prov import Activity, ActivityId
 from linkml_runtime.linkml_model.types import Integer, String, Uriorcurie
 from linkml_runtime.utils.metamodelcore import URIorCURIE
@@ -246,7 +247,7 @@ class Change(YAMLRoot):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, ChangeId):
             self.id = ChangeId(self.id)
 
@@ -287,14 +288,10 @@ class SimpleChange(Change):
     class_model_uri: ClassVar[URIRef] = KGCL.SimpleChange
 
     id: Union[str, SimpleChangeId] = None
-    about: Optional[str] = None
     old_value: Optional[str] = None
     new_value: Optional[str] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
-
         if self.old_value is not None and not isinstance(self.old_value, str):
             self.old_value = str(self.old_value)
 
@@ -344,7 +341,7 @@ class MultiNodeObsoletion(ComplexChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, MultiNodeObsoletionId):
             self.id = MultiNodeObsoletionId(self.id)
 
@@ -376,7 +373,7 @@ class Transaction(Change):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, TransactionId):
             self.id = TransactionId(self.id)
 
@@ -409,7 +406,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
             self.count = int(self.count)
 
         if not isinstance(self.property_value_set, list):
-            self.property_value_set = [self.property_value_set]
+            self.property_value_set = [self.property_value_set] if self.property_value_set is not None else []
         self.property_value_set = [v if isinstance(v, PropertyValue) else PropertyValue(**v) for v in self.property_value_set]
 
         super().__post_init__(**kwargs)
@@ -439,12 +436,12 @@ class Obsoletion(ChangeMixin):
     class_name: ClassVar[str] = "obsoletion"
     class_model_uri: ClassVar[URIRef] = KGCL.Obsoletion
 
-    about: Optional[str] = None
+    about: Optional[Union[dict, OntologyElement]] = None
     has_undo: Optional[Union[dict, "Obsoletion"]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about is not None and not isinstance(self.about, OntologyElement):
+            self.about = OntologyElement()
 
         if self.has_undo is not None and not isinstance(self.has_undo, Obsoletion):
             self.has_undo = Obsoletion(**self.has_undo)
@@ -599,12 +596,12 @@ class EdgeChange(SimpleChange):
     class_model_uri: ClassVar[URIRef] = KGCL.EdgeChange
 
     id: Union[str, EdgeChangeId] = None
-    about: Optional[str] = None
+    about_edge: Optional[Union[dict, Edge]] = None
     subject: Optional[Union[str, NodeId]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about_edge is not None and not isinstance(self.about_edge, Edge):
+            self.about_edge = Edge(**self.about_edge)
 
         if self.subject is not None and not isinstance(self.subject, NodeId):
             self.subject = NodeId(self.subject)
@@ -633,7 +630,7 @@ class EdgeCreation(EdgeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, EdgeCreationId):
             self.id = EdgeCreationId(self.id)
 
@@ -671,7 +668,7 @@ class PlaceUnder(EdgeCreation):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, PlaceUnderId):
             self.id = PlaceUnderId(self.id)
 
@@ -699,7 +696,7 @@ class EdgeDeletion(EdgeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, EdgeDeletionId):
             self.id = EdgeDeletionId(self.id)
 
@@ -739,10 +736,11 @@ class EdgeObsoletion(EdgeChange):
     object: Optional[Union[str, NodeId]] = None
     annotation_set: Optional[Union[dict, Annotation]] = None
     change_description: Optional[str] = None
+    about: Optional[Union[dict, OntologyElement]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, EdgeObsoletionId):
             self.id = EdgeObsoletionId(self.id)
 
@@ -761,6 +759,9 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self.change_description is not None and not isinstance(self.change_description, str):
             self.change_description = str(self.change_description)
 
+        if self.about is not None and not isinstance(self.about, OntologyElement):
+            self.about = OntologyElement()
+
         super().__post_init__(**kwargs)
 
 
@@ -780,7 +781,7 @@ class EdgeRewiring(EdgeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, EdgeRewiringId):
             self.id = EdgeRewiringId(self.id)
 
@@ -805,7 +806,7 @@ class NodeMove(EdgeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeMoveId):
             self.id = NodeMoveId(self.id)
 
@@ -833,7 +834,7 @@ class NodeDeepening(NodeMove):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeDeepeningId):
             self.id = NodeDeepeningId(self.id)
 
@@ -860,7 +861,7 @@ class NodeShallowing(NodeMove):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeShallowingId):
             self.id = NodeShallowingId(self.id)
 
@@ -887,7 +888,7 @@ class PredicateChange(EdgeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, PredicateChangeId):
             self.id = PredicateChangeId(self.id)
 
@@ -913,7 +914,7 @@ class EdgeLogicalInterpretationChange(EdgeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, EdgeLogicalInterpretationChangeId):
             self.id = EdgeLogicalInterpretationChangeId(self.id)
 
@@ -936,7 +937,7 @@ class LogicalAxiomChange(SimpleChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, LogicalAxiomChangeId):
             self.id = LogicalAxiomChangeId(self.id)
 
@@ -956,11 +957,11 @@ class NodeChange(SimpleChange):
     class_model_uri: ClassVar[URIRef] = KGCL.NodeChange
 
     id: Union[str, NodeChangeId] = None
-    about: Optional[str] = None
+    about_node: Optional[Union[str, NodeId]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about_node is not None and not isinstance(self.about_node, NodeId):
+            self.about_node = NodeId(self.about_node)
 
         super().__post_init__(**kwargs)
 
@@ -985,7 +986,7 @@ class NodeRename(NodeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeRenameId):
             self.id = NodeRenameId(self.id)
 
@@ -1020,7 +1021,7 @@ class NodeAnnotationChange(NodeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeAnnotationChangeId):
             self.id = NodeAnnotationChangeId(self.id)
 
@@ -1043,7 +1044,7 @@ class NodeAnnotationReplacement(NodeAnnotationChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeAnnotationReplacementId):
             self.id = NodeAnnotationReplacementId(self.id)
 
@@ -1063,7 +1064,7 @@ class NodeSynonymChange(NodeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeSynonymChangeId):
             self.id = NodeSynonymChangeId(self.id)
 
@@ -1087,7 +1088,7 @@ class NewSynonym(NodeSynonymChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NewSynonymId):
             self.id = NewSynonymId(self.id)
 
@@ -1117,7 +1118,7 @@ class NameBecomesSynonym(NodeSynonymChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NameBecomesSynonymId):
             self.id = NameBecomesSynonymId(self.id)
 
@@ -1150,7 +1151,7 @@ class RemoveSynonym(NodeSynonymChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, RemoveSynonymId):
             self.id = RemoveSynonymId(self.id)
 
@@ -1179,7 +1180,7 @@ class SynonymReplacement(NodeSynonymChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, SynonymReplacementId):
             self.id = SynonymReplacementId(self.id)
 
@@ -1216,7 +1217,7 @@ class SynonymPredicateChange(NodeSynonymChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, SynonymPredicateChangeId):
             self.id = SynonymPredicateChangeId(self.id)
 
@@ -1263,7 +1264,7 @@ class NewTextDefinition(NodeTextDefinitionChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NewTextDefinitionId):
             self.id = NewTextDefinitionId(self.id)
 
@@ -1290,7 +1291,7 @@ class RemoveTextDefinition(NodeTextDefinitionChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, RemoveTextDefinitionId):
             self.id = RemoveTextDefinitionId(self.id)
 
@@ -1319,7 +1320,7 @@ class TextDefinitionReplacement(NodeTextDefinitionChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, TextDefinitionReplacementId):
             self.id = TextDefinitionReplacementId(self.id)
 
@@ -1348,7 +1349,7 @@ class DatatypeChange(SimpleChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, DatatypeChangeId):
             self.id = DatatypeChangeId(self.id)
 
@@ -1372,7 +1373,7 @@ class AddNodeToSubset(NodeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, AddNodeToSubsetId):
             self.id = AddNodeToSubsetId(self.id)
 
@@ -1396,21 +1397,21 @@ class RemovedNodeFromSubset(NodeChange):
 
     id: Union[str, RemovedNodeFromSubsetId] = None
     change_description: Optional[str] = None
-    about: Optional[str] = None
+    about_node: Optional[Union[str, NodeId]] = None
     subset: Optional[str] = None
     in_subset: Optional[Union[dict, OntologySubset]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, RemovedNodeFromSubsetId):
             self.id = RemovedNodeFromSubsetId(self.id)
 
         if self.change_description is not None and not isinstance(self.change_description, str):
             self.change_description = str(self.change_description)
 
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about_node is not None and not isinstance(self.about_node, NodeId):
+            self.about_node = NodeId(self.about_node)
 
         if self.subset is not None and not isinstance(self.subset, str):
             self.subset = str(self.subset)
@@ -1438,10 +1439,11 @@ class NodeObsoletion(NodeChange):
     has_nondirect_replacement: Optional[Union[Union[str, NodeId], List[Union[str, NodeId]]]] = empty_list()
     change_description: Optional[str] = None
     associated_change_set: Optional[Union[Dict[Union[str, ChangeId], Union[dict, Change]], List[Union[dict, Change]]]] = empty_dict()
+    about: Optional[Union[dict, OntologyElement]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeObsoletionId):
             self.id = NodeObsoletionId(self.id)
 
@@ -1449,7 +1451,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
             self.has_direct_replacement = NodeId(self.has_direct_replacement)
 
         if not isinstance(self.has_nondirect_replacement, list):
-            self.has_nondirect_replacement = [self.has_nondirect_replacement]
+            self.has_nondirect_replacement = [self.has_nondirect_replacement] if self.has_nondirect_replacement is not None else []
         self.has_nondirect_replacement = [v if isinstance(v, NodeId) else NodeId(v) for v in self.has_nondirect_replacement]
 
         if self.change_description is not None and not isinstance(self.change_description, str):
@@ -1457,6 +1459,9 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
 
         self._normalize_inlined_as_list(slot_name="associated_change_set", slot_type=Change, key_name="id", keyed=True)
 
+        if self.about is not None and not isinstance(self.about, OntologyElement):
+            self.about = OntologyElement()
+
         super().__post_init__(**kwargs)
 
 
@@ -1479,7 +1484,7 @@ class NodeUnobsoletion(NodeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeUnobsoletionId):
             self.id = NodeUnobsoletionId(self.id)
 
@@ -1516,7 +1521,7 @@ class NodeCreation(NodeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeCreationId):
             self.id = NodeCreationId(self.id)
 
@@ -1556,7 +1561,7 @@ class ClassCreation(NodeCreation):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, ClassCreationId):
             self.id = ClassCreationId(self.id)
 
@@ -1587,7 +1592,7 @@ class NodeDeletion(NodeChange):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeDeletionId):
             self.id = NodeDeletionId(self.id)
 
@@ -1612,22 +1617,22 @@ class NodeDirectMerge(NodeObsoletion):
 
     id: Union[str, NodeDirectMergeId] = None
     has_direct_replacement: Union[str, NodeId] = None
-    about: Optional[str] = None
+    about_node: Optional[Union[str, NodeId]] = None
     change_description: Optional[str] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeDirectMergeId):
             self.id = NodeDirectMergeId(self.id)
 
         if self._is_empty(self.has_direct_replacement):
-            raise ValueError("has_direct_replacement must be supplied")
+            self.MissingRequiredField("has_direct_replacement")
         if not isinstance(self.has_direct_replacement, NodeId):
             self.has_direct_replacement = NodeId(self.has_direct_replacement)
 
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about_node is not None and not isinstance(self.about_node, NodeId):
+            self.about_node = NodeId(self.about_node)
 
         if self.change_description is not None and not isinstance(self.change_description, str):
             self.change_description = str(self.change_description)
@@ -1654,12 +1659,12 @@ class NodeObsoletionWithDirectReplacement(NodeObsoletion):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeObsoletionWithDirectReplacementId):
             self.id = NodeObsoletionWithDirectReplacementId(self.id)
 
         if self._is_empty(self.has_direct_replacement):
-            raise ValueError("has_direct_replacement must be supplied")
+            self.MissingRequiredField("has_direct_replacement")
         if not isinstance(self.has_direct_replacement, NodeId):
             self.has_direct_replacement = NodeId(self.has_direct_replacement)
 
@@ -1687,14 +1692,14 @@ class NodeObsoletionWithNoDirectReplacement(NodeObsoletion):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeObsoletionWithNoDirectReplacementId):
             self.id = NodeObsoletionWithNoDirectReplacementId(self.id)
 
         if self._is_empty(self.has_nondirect_replacement):
-            raise ValueError("has_nondirect_replacement must be supplied")
+            self.MissingRequiredField("has_nondirect_replacement")
         if not isinstance(self.has_nondirect_replacement, list):
-            self.has_nondirect_replacement = [self.has_nondirect_replacement]
+            self.has_nondirect_replacement = [self.has_nondirect_replacement] if self.has_nondirect_replacement is not None else []
         self.has_nondirect_replacement = [v if isinstance(v, NodeId) else NodeId(v) for v in self.has_nondirect_replacement]
 
         if self.change_description is not None and not isinstance(self.change_description, str):
@@ -1819,7 +1824,13 @@ class slots:
                    model_uri=KGCL.superclass, domain=None, range=Optional[Union[str, NodeId]])
 
 slots.about = Slot(uri=KGCL.about, name="about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.about, domain=None, range=Optional[str])
+                   model_uri=KGCL.about, domain=None, range=Optional[Union[dict, OntologyElement]])
+
+slots.about_node = Slot(uri=KGCL.about_node, name="about node", curie=KGCL.curie('about_node'),
+                   model_uri=KGCL.about_node, domain=None, range=Optional[Union[str, NodeId]])
+
+slots.about_edge = Slot(uri=KGCL.about_edge, name="about edge", curie=KGCL.curie('about_edge'),
+                   model_uri=KGCL.about_edge, domain=None, range=Optional[Union[dict, Edge]])
 
 slots.target = Slot(uri=KGCL.target, name="target", curie=KGCL.curie('target'),
                    model_uri=KGCL.target, domain=None, range=Optional[str])
@@ -1933,7 +1944,7 @@ class slots:
                    model_uri=KGCL.change_set_summary_statistic_property_value_set, domain=ChangeSetSummaryStatistic, range=Optional[Union[Union[dict, PropertyValue], List[Union[dict, PropertyValue]]]])
 
 slots.obsoletion_about = Slot(uri=KGCL.about, name="obsoletion_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.obsoletion_about, domain=None, range=Optional[str])
+                   model_uri=KGCL.obsoletion_about, domain=None, range=Optional[Union[dict, OntologyElement]])
 
 slots.obsoletion_has_undo = Slot(uri=KGCL.has_undo, name="obsoletion_has undo", curie=KGCL.curie('has_undo'),
                    model_uri=KGCL.obsoletion_has_undo, domain=None, range=Optional[Union[dict, "Obsoletion"]])
@@ -1953,9 +1964,6 @@ class slots:
 slots.remove_from_subset_has_undo = Slot(uri=KGCL.has_undo, name="remove from subset_has undo", curie=KGCL.curie('has_undo'),
                    model_uri=KGCL.remove_from_subset_has_undo, domain=None, range=Optional[Union[dict, AddToSubset]])
 
-slots.edge_change_about = Slot(uri=KGCL.about, name="edge change_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.edge_change_about, domain=EdgeChange, range=Optional[str])
-
 slots.edge_change_subject = Slot(uri=KGCL.subject, name="edge change_subject", curie=KGCL.curie('subject'),
                    model_uri=KGCL.edge_change_subject, domain=EdgeChange, range=Optional[Union[str, NodeId]])
 
@@ -1980,9 +1988,6 @@ class slots:
 slots.predicate_change_change_description = Slot(uri=KGCL.change_description, name="predicate change_change description", curie=KGCL.curie('change_description'),
                    model_uri=KGCL.predicate_change_change_description, domain=PredicateChange, range=Optional[str])
 
-slots.node_change_about = Slot(uri=KGCL.about, name="node change_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.node_change_about, domain=NodeChange, range=Optional[str])
-
 slots.node_rename_old_value = Slot(uri=KGCL.old_value, name="node rename_old value", curie=KGCL.curie('old_value'),
                    model_uri=KGCL.node_rename_old_value, domain=NodeRename, range=Optional[str])
 
@@ -2004,8 +2009,8 @@ class slots:
 slots.removed_node_from_subset_change_description = Slot(uri=KGCL.change_description, name="removed node from subset_change description", curie=KGCL.curie('change_description'),
                    model_uri=KGCL.removed_node_from_subset_change_description, domain=RemovedNodeFromSubset, range=Optional[str])
 
-slots.removed_node_from_subset_about = Slot(uri=KGCL.about, name="removed node from subset_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.removed_node_from_subset_about, domain=RemovedNodeFromSubset, range=Optional[str])
+slots.removed_node_from_subset_about_node = Slot(uri=KGCL.about_node, name="removed node from subset_about node", curie=KGCL.curie('about_node'),
+                   model_uri=KGCL.removed_node_from_subset_about_node, domain=RemovedNodeFromSubset, range=Optional[Union[str, NodeId]])
 
 slots.removed_node_from_subset_subset = Slot(uri=KGCL.subset, name="removed node from subset_subset", curie=KGCL.curie('subset'),
                    model_uri=KGCL.removed_node_from_subset_subset, domain=RemovedNodeFromSubset, range=Optional[str])
@@ -2037,8 +2042,8 @@ class slots:
 slots.node_direct_merge_has_direct_replacement = Slot(uri=KGCL.has_direct_replacement, name="node direct merge_has direct replacement", curie=KGCL.curie('has_direct_replacement'),
                    model_uri=KGCL.node_direct_merge_has_direct_replacement, domain=NodeDirectMerge, range=Union[str, NodeId])
 
-slots.node_direct_merge_about = Slot(uri=KGCL.about, name="node direct merge_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.node_direct_merge_about, domain=NodeDirectMerge, range=Optional[str])
+slots.node_direct_merge_about_node = Slot(uri=KGCL.about_node, name="node direct merge_about node", curie=KGCL.curie('about_node'),
+                   model_uri=KGCL.node_direct_merge_about_node, domain=NodeDirectMerge, range=Optional[Union[str, NodeId]])
 
 slots.node_direct_merge_change_description = Slot(uri=KGCL.change_description, name="node direct merge_change description", curie=KGCL.curie('change_description'),
                    model_uri=KGCL.node_direct_merge_change_description, domain=NodeDirectMerge, range=Optional[str])
diff --git a/kgcl/model/ontology_model.py b/kgcl/model/ontology_model.py
index 0e81d8eb..1fa5f6b2 100644
--- a/kgcl/model/ontology_model.py
+++ b/kgcl/model/ontology_model.py
@@ -1,5 +1,5 @@
 # Auto generated from ontology_model.yaml by pythongen.py version: 0.9.0
-# Generation date: 2021-05-15 17:00
+# Generation date: 2021-06-25 07:49
 # Schema: kgcl-ontology-model
 #
 # id: https://w3id.org/kgcl/ontology
@@ -140,7 +140,7 @@ class Node(OntologyElement):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, NodeId):
             self.id = NodeId(self.id)
 
@@ -172,7 +172,7 @@ class ClassNode(Node):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, ClassNodeId):
             self.id = ClassNodeId(self.id)
 
@@ -195,7 +195,7 @@ class InstanceNode(Node):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, InstanceNodeId):
             self.id = InstanceNodeId(self.id)
 
@@ -269,9 +269,12 @@ class OwlTypeEnum(EnumDefinitionImpl):
 
 class SynonymScopeEnum(EnumDefinitionImpl):
 
-    related = PermissibleValue(text="related")
-    broad = PermissibleValue(text="broad")
-    narrow = PermissibleValue(text="narrow")
+    related = PermissibleValue(text="related",
+                                     meaning=OIO.hasNarrowSynonym)
+    broad = PermissibleValue(text="broad",
+                                 meaning=OIO.hasBroadSynonym)
+    narrow = PermissibleValue(text="narrow",
+                                   meaning=OIO.hasNarrowSynonym)
     exact = PermissibleValue(text="exact",
                                  meaning=OIO.hasExactSynonym)
 
diff --git a/kgcl/model/prov.py b/kgcl/model/prov.py
index 9cf6d34f..74018c27 100644
--- a/kgcl/model/prov.py
+++ b/kgcl/model/prov.py
@@ -1,5 +1,5 @@
 # Auto generated from prov.yaml by pythongen.py version: 0.9.0
-# Generation date: 2021-05-15 16:35
+# Generation date: 2021-07-16 19:00
 # Schema: KGCL-PROV
 #
 # id: https://w3id.org/kgcl/prov
@@ -68,7 +68,7 @@ class Activity(YAMLRoot):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, ActivityId):
             self.id = ActivityId(self.id)
 
@@ -111,7 +111,7 @@ class Agent(YAMLRoot):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, AgentId):
             self.id = AgentId(self.id)
 
diff --git a/ldcontext/kgcl.context.jsonld b/ldcontext/kgcl.context.jsonld
index 93996c7a..a1aae40f 100644
--- a/ldcontext/kgcl.context.jsonld
+++ b/ldcontext/kgcl.context.jsonld
@@ -1,5 +1,5 @@
 {
-   "_comments": "Auto generated from kgcl.yaml by jsonldcontextgen.py version: 0.1.1\n    Generation date: 2021-06-25 07:49\n    Schema: kgcl\n    \n    id: https://w3id.org/kgcl\n    description: A data model for describing change operations at a high level on an ontology or ontology-like artefact, such as a Knowledge Graph.\n\n* [Browse Schema](https://cmungall.github.io/knowledge-graph-change-language/)\n* [GitHub](https://github.com/cmungall/knowledge-graph-change-language)\n    license: https://creativecommons.org/publicdomain/zero/1.0/\n    ",
+   "_comments": "Auto generated from kgcl.yaml by jsonldcontextgen.py version: 0.1.1\n    Generation date: 2021-07-16 20:38\n    Schema: kgcl\n    \n    id: https://w3id.org/kgcl\n    description: A data model for describing change operations at a high level on an ontology or ontology-like artefact, such as a Knowledge Graph.\n\n* [Browse Schema](https://cmungall.github.io/knowledge-graph-change-language/)\n* [GitHub](https://github.com/cmungall/knowledge-graph-change-language)\n    license: https://creativecommons.org/publicdomain/zero/1.0/\n    ",
    "@context": {
       "IAO": {
          "@id": "http://purl.obolibrary.org/obo/IAO_",
@@ -16,6 +16,15 @@
       "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
       "skos": "http://www.w3.org/2004/02/skos/core#",
       "@vocab": "http://w3id.org/kgcl/",
+      "about": {
+         "@type": "@id"
+      },
+      "about_edge": {
+         "@type": "@id"
+      },
+      "about_node": {
+         "@type": "@id"
+      },
       "acted_on_behalf_of": {
          "@type": "@id",
          "@id": "prov:actedOnBehalfOf"
diff --git a/owl/kgcl.owl.ttl b/owl/kgcl.owl.ttl
index 70c7a50d..109cab90 100644
--- a/owl/kgcl.owl.ttl
+++ b/owl/kgcl.owl.ttl
@@ -23,10 +23,6 @@
         linkml:ClassDefinition ;
     rdfs:label "configuration" ;
     rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty linkml:main_synonym_predicate ],
-        [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
             owl:onProperty linkml:definition_predicate ],
@@ -37,7 +33,7 @@
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
-            owl:onProperty linkml:synonym_predicates ],
+            owl:onProperty linkml:obsoletion_policy ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
@@ -49,11 +45,15 @@
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
-            owl:onProperty linkml:obsoletion_policy ],
+            owl:onProperty linkml:synonym_predicates ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass linkml:String ;
+            owl:onProperty linkml:obsoletion_workflow ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
-            owl:onProperty linkml:obsoletion_workflow ] ;
+            owl:onProperty linkml:main_synonym_predicate ] ;
     skos:definition "The meaning of operations can be configured" .
 
 <http://w3id.org/kgcl/DatatypeChange> a owl:Class,
@@ -180,33 +180,6 @@
     skos:definition "A node that is a class" ;
     skos:exactMatch owl:Class .
 
-<http://w3id.org/kgcl/om/Edge> a owl:Class,
-        linkml:ClassDefinition ;
-    rdfs:label "edge" ;
-    rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/om/object> ],
-        [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/om/predicate> ],
-        [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
-            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
-        [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/om/subject> ],
-        <http://w3id.org/kgcl/om/OntologyElement> ;
-    skos:altLabel "axiom",
-        "relationship",
-        "triple" ;
-    skos:definition "A relationship between two nodes. We assume owlstar or similar for existential restrictions" ;
-    skos:exactMatch rdf:Statement,
-        owl:Axiom .
-
 <http://w3id.org/kgcl/om/InstanceNode> a owl:Class,
         linkml:ClassDefinition ;
     rdfs:label "instance node" ;
@@ -233,11 +206,11 @@
 
 * [Browse Schema](https://cmungall.github.io/knowledge-graph-change-language/)
 * [GitHub](https://github.com/cmungall/knowledge-graph-change-language)""" ;
-    linkml:generation_date "2021-06-25 07:49" ;
+    linkml:generation_date "2021-07-16 20:38" ;
     linkml:metamodel_version "1.7.0" ;
     linkml:source_file "kgcl.yaml" ;
-    linkml:source_file_date "Thu Jun 24 12:21:39 2021" ;
-    linkml:source_file_size 24849 .
+    linkml:source_file_date "Fri Jul 16 20:38:20 2021" ;
+    linkml:source_file_size 25096 .
 
 linkml:Boolean a owl:Class,
         linkml:TypeDefinition ;
@@ -469,13 +442,6 @@ linkml:creation_has_undo a owl:ObjectProperty,
     rdfs:range <http://w3id.org/kgcl/Deletion> ;
     rdfs:subPropertyOf <http://w3id.org/kgcl/has_undo> .
 
-linkml:edge_change_about a owl:ObjectProperty,
-        linkml:SlotDefinition ;
-    rdfs:label "edge change_about" ;
-    rdfs:domain <http://w3id.org/kgcl/EdgeChange> ;
-    rdfs:range linkml:String ;
-    rdfs:subPropertyOf <http://w3id.org/kgcl/about> .
-
 linkml:edge_change_subject a owl:ObjectProperty,
         linkml:SlotDefinition ;
     rdfs:label "edge change_subject" ;
@@ -565,12 +531,12 @@ linkml:node_deletion_change_description a owl:ObjectProperty,
     rdfs:range linkml:String ;
     rdfs:subPropertyOf <http://w3id.org/kgcl/change_description> .
 
-linkml:node_direct_merge_about a owl:ObjectProperty,
+linkml:node_direct_merge_about_node a owl:ObjectProperty,
         linkml:SlotDefinition ;
-    rdfs:label "node direct merge_about" ;
+    rdfs:label "node direct merge_about node" ;
     rdfs:domain <http://w3id.org/kgcl/NodeDirectMerge> ;
-    rdfs:range linkml:String ;
-    rdfs:subPropertyOf linkml:node_change_about .
+    rdfs:range <http://w3id.org/kgcl/om/Node> ;
+    rdfs:subPropertyOf <http://w3id.org/kgcl/about_node> .
 
 linkml:node_direct_merge_change_description a owl:ObjectProperty,
         linkml:SlotDefinition ;
@@ -675,7 +641,7 @@ linkml:obsoletion_about a owl:ObjectProperty,
         linkml:SlotDefinition ;
     rdfs:label "obsoletion_about" ;
     rdfs:domain <http://w3id.org/kgcl/Obsoletion> ;
-    rdfs:range linkml:String ;
+    rdfs:range <http://w3id.org/kgcl/om/OntologyElement> ;
     rdfs:subPropertyOf <http://w3id.org/kgcl/about> ;
     skos:definition "The element that is obsoleted by this change." .
 
@@ -708,12 +674,12 @@ linkml:remove_from_subset_in_subset a owl:ObjectProperty,
     rdfs:subPropertyOf <http://w3id.org/kgcl/in_subset> ;
     skos:definition "subset that the element is being removed from" .
 
-linkml:removed_node_from_subset_about a owl:ObjectProperty,
+linkml:removed_node_from_subset_about_node a owl:ObjectProperty,
         linkml:SlotDefinition ;
-    rdfs:label "removed node from subset_about" ;
+    rdfs:label "removed node from subset_about node" ;
     rdfs:domain <http://w3id.org/kgcl/RemovedNodeFromSubset> ;
-    rdfs:range linkml:String ;
-    rdfs:subPropertyOf linkml:node_change_about ;
+    rdfs:range <http://w3id.org/kgcl/om/Node> ;
+    rdfs:subPropertyOf <http://w3id.org/kgcl/about_node> ;
     skos:definition "The node that is removed from the subset" .
 
 linkml:removed_node_from_subset_change_description a owl:ObjectProperty,
@@ -766,24 +732,24 @@ linkml:unobsoletion_has_undo a owl:ObjectProperty,
     rdfs:label "edge deletion" ;
     rdfs:subClassOf [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/om/predicate> ],
-        [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/om/subject> ],
+            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
+            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
             owl:onProperty <http://w3id.org/kgcl/change_description> ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/om/subject> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://w3id.org/kgcl/om/Node> ;
             owl:onProperty <http://w3id.org/kgcl/om/object> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
-            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/om/predicate> ],
         <http://w3id.org/kgcl/Deletion>,
         <http://w3id.org/kgcl/EdgeChange> ;
     skos:altLabel "relationship deletion" ;
@@ -796,23 +762,27 @@ linkml:unobsoletion_has_undo a owl:ObjectProperty,
     rdfs:subClassOf [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/om/subject> ],
+            owl:onProperty <http://w3id.org/kgcl/om/predicate> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/om/predicate> ],
+            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
+            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/change_description> ],
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/om/subject> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://w3id.org/kgcl/om/Node> ;
             owl:onProperty <http://w3id.org/kgcl/om/object> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
-            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
+            owl:onClass <http://w3id.org/kgcl/om/OntologyElement> ;
+            owl:onProperty <http://w3id.org/kgcl/about> ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass linkml:String ;
+            owl:onProperty <http://w3id.org/kgcl/change_description> ],
         <http://w3id.org/kgcl/EdgeChange>,
         <http://w3id.org/kgcl/Obsoletion> ;
     skos:altLabel "relationship obsoletion" ;
@@ -869,6 +839,12 @@ linkml:unobsoletion_has_undo a owl:ObjectProperty,
     skos:definition "An edge change where the predicate (relationship type) is modified." ;
     linkml:examples "Example(value='changing a is-a b to a part-of b', description=None)" .
 
+<http://w3id.org/kgcl/about_edge> a owl:ObjectProperty,
+        linkml:SlotDefinition ;
+    rdfs:label "about edge" ;
+    rdfs:range <http://w3id.org/kgcl/om/Edge> ;
+    rdfs:subPropertyOf <http://w3id.org/kgcl/about> .
+
 <http://w3id.org/kgcl/contributor> a owl:ObjectProperty,
         linkml:SlotDefinition ;
     rdfs:label "contributor" ;
@@ -965,13 +941,13 @@ linkml:node_creation_change_description a owl:ObjectProperty,
     rdfs:label "edge creation" ;
     rdfs:seeAlso "http://wiki.geneontology.org/index.php/Guidelines_for_creating_relationships_between_terms" ;
     rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
-            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
-        [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://w3id.org/kgcl/om/Node> ;
             owl:onProperty <http://w3id.org/kgcl/om/object> ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
+            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
@@ -995,24 +971,24 @@ linkml:node_creation_change_description a owl:ObjectProperty,
     rdfs:seeAlso "http://wiki.geneontology.org/index.php/Guidelines_for_creating_a_GO_term" ;
     rdfs:subClassOf [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/node_id> ],
+            owl:onClass linkml:String ;
+            owl:onProperty <http://w3id.org/kgcl/om/name> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
-            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
+            owl:onClass linkml:String ;
+            owl:onProperty <http://w3id.org/kgcl/change_description> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://UNKNOWN.org/owl_type_enum> ;
             owl:onProperty <http://w3id.org/kgcl/om/owl_type> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/om/name> ],
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/node_id> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/change_description> ],
+            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
+            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
         <http://w3id.org/kgcl/Creation>,
         <http://w3id.org/kgcl/NodeChange> ;
     skos:definition "a node change in which a new node is created" .
@@ -1023,13 +999,13 @@ linkml:node_creation_change_description a owl:ObjectProperty,
     rdfs:seeAlso "http://wiki.geneontology.org/index.php/Merging_Ontology_Terms",
         "http://wiki.geneontology.org/index.php/Principles_for_merging_terms" ;
     rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/change_description> ],
-        [ a owl:Restriction ;
             owl:onClass <http://w3id.org/kgcl/om/Node> ;
             owl:onProperty <http://w3id.org/kgcl/has_direct_replacement> ;
             owl:qualifiedCardinality 1 ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass linkml:String ;
+            owl:onProperty <http://w3id.org/kgcl/change_description> ],
         <http://w3id.org/kgcl/AllowsAutomaticReplacementOfEdges>,
         <http://w3id.org/kgcl/NodeObsoletion> ;
     skos:definition "An obsoletion change in which information from the obsoleted node is selectively copied to a single target, and edges can automatically be rewired to point to the target node" .
@@ -1037,16 +1013,16 @@ linkml:node_creation_change_description a owl:ObjectProperty,
 <http://w3id.org/kgcl/NodeObsoletionWithNoDirectReplacement> a owl:Class,
         linkml:ClassDefinition ;
     rdfs:label "node obsoletion with no direct replacement" ;
-    rdfs:subClassOf [ a owl:Class ;
+    rdfs:subClassOf [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass linkml:String ;
+            owl:onProperty <http://w3id.org/kgcl/change_description> ],
+        [ a owl:Class ;
             owl:intersectionOf ( [ a owl:Restriction ;
                         owl:allValuesFrom <http://w3id.org/kgcl/om/Node> ;
                         owl:onProperty <http://w3id.org/kgcl/has_nondirect_replacement> ] [ a owl:Restriction ;
                         owl:onProperty <http://w3id.org/kgcl/has_nondirect_replacement> ;
                         owl:someValuesFrom <http://w3id.org/kgcl/om/Node> ] ) ],
-        [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/change_description> ],
         <http://w3id.org/kgcl/NodeObsoletion> ;
     skos:definition "An obsoletion change in which there is no direct replacement" .
 
@@ -1083,6 +1059,33 @@ linkml:node_creation_change_description a owl:ObjectProperty,
     rdfs:label "creator" ;
     rdfs:range linkml:String .
 
+<http://w3id.org/kgcl/om/Edge> a owl:Class,
+        linkml:ClassDefinition ;
+    rdfs:label "edge" ;
+    rdfs:subClassOf [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
+            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/om/predicate> ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/om/object> ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/om/subject> ],
+        <http://w3id.org/kgcl/om/OntologyElement> ;
+    skos:altLabel "axiom",
+        "relationship",
+        "triple" ;
+    skos:definition "A relationship between two nodes. We assume owlstar or similar for existential restrictions" ;
+    skos:exactMatch rdf:Statement,
+        owl:Axiom .
+
 <http://w3id.org/kgcl/om/name> a owl:ObjectProperty,
         linkml:SlotDefinition ;
     rdfs:label "name" ;
@@ -1153,13 +1156,6 @@ linkml:count a owl:ObjectProperty,
     rdfs:label "count" ;
     rdfs:range linkml:Integer .
 
-linkml:node_change_about a owl:ObjectProperty,
-        linkml:SlotDefinition ;
-    rdfs:label "node change_about" ;
-    rdfs:domain <http://w3id.org/kgcl/NodeChange> ;
-    rdfs:range linkml:String ;
-    rdfs:subPropertyOf <http://w3id.org/kgcl/about> .
-
 linkml:node_move_change_description a owl:ObjectProperty,
         linkml:SlotDefinition ;
     rdfs:label "node move_change description" ;
@@ -1216,15 +1212,15 @@ linkml:subset a owl:ObjectProperty,
     rdfs:label "multi node obsoletion" ;
     rdfs:seeAlso "https://github.com/cmungall/obo-scripts/blob/master/obo-obsoletify.pl" ;
     rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/change_description> ],
+            owl:allValuesFrom <http://w3id.org/kgcl/NodeObsoletion> ;
+            owl:onProperty <http://w3id.org/kgcl/change_set> ],
         [ a owl:Restriction ;
             owl:allValuesFrom <http://w3id.org/kgcl/Change> ;
             owl:onProperty linkml:associated_change_set ],
         [ a owl:Restriction ;
-            owl:allValuesFrom <http://w3id.org/kgcl/NodeObsoletion> ;
-            owl:onProperty <http://w3id.org/kgcl/change_set> ],
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass linkml:String ;
+            owl:onProperty <http://w3id.org/kgcl/change_description> ],
         <http://w3id.org/kgcl/ComplexChange> ;
     skos:altLabel "multi class obsoletion",
         "multi concept obsoletion",
@@ -1237,16 +1233,16 @@ linkml:subset a owl:ObjectProperty,
     rdfs:label "name becomes synonym" ;
     rdfs:subClassOf [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/NodeRename> ;
-            owl:onProperty linkml:change_1 ],
+            owl:onClass linkml:String ;
+            owl:onProperty <http://w3id.org/kgcl/change_description> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://w3id.org/kgcl/NewSynonym> ;
             owl:onProperty linkml:change_2 ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/change_description> ],
+            owl:onClass <http://w3id.org/kgcl/NodeRename> ;
+            owl:onProperty linkml:change_1 ],
         <http://w3id.org/kgcl/NodeSynonymChange> ;
     skos:definition "A node synonym where the name NAME of an node NODE moves to a synonym, and NODE receives a new name. This change consists of compose of (1) a node rename where NAME is replaced by a different name (2) a new synonym" .
 
@@ -1262,17 +1258,17 @@ linkml:subset a owl:ObjectProperty,
     rdfs:seeAlso "http://wiki.geneontology.org/index.php/Merging_Ontology_Terms",
         "http://wiki.geneontology.org/index.php/Principles_for_merging_terms" ;
     rdfs:subClassOf [ a owl:Restriction ;
-            owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/has_direct_replacement> ;
-            owl:qualifiedCardinality 1 ],
-        [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/about> ],
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/about_node> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
             owl:onProperty <http://w3id.org/kgcl/change_description> ],
+        [ a owl:Restriction ;
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/has_direct_replacement> ;
+            owl:qualifiedCardinality 1 ],
         <http://w3id.org/kgcl/AllowsAutomaticReplacementOfEdges>,
         <http://w3id.org/kgcl/NodeObsoletion> ;
     skos:definition "An obsoletion change in which all metadata (including name/label) from the source node is deleted and added to the target node, and edges can automatically be rewired to point to the target node" ;
@@ -1323,13 +1319,13 @@ linkml:subset a owl:ObjectProperty,
         linkml:ClassDefinition ;
     rdfs:label "remove from subset" ;
     rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/AddToSubset> ;
-            owl:onProperty <http://w3id.org/kgcl/has_undo> ],
-        [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://w3id.org/kgcl/om/OntologySubset> ;
             owl:onProperty <http://w3id.org/kgcl/in_subset> ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/AddToSubset> ;
+            owl:onProperty <http://w3id.org/kgcl/has_undo> ],
         <http://w3id.org/kgcl/SubsetMembershipChange>,
         linkml:mixin ;
     skos:definition "removing an element from a subset" .
@@ -1339,16 +1335,16 @@ linkml:subset a owl:ObjectProperty,
     rdfs:label "removed node from subset" ;
     rdfs:subClassOf [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/about> ],
+            owl:onClass <http://w3id.org/kgcl/om/OntologySubset> ;
+            owl:onProperty <http://w3id.org/kgcl/in_subset> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
             owl:onProperty <http://w3id.org/kgcl/change_description> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/OntologySubset> ;
-            owl:onProperty <http://w3id.org/kgcl/in_subset> ],
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/about_node> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
@@ -1409,10 +1405,6 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
         linkml:ClassDefinition ;
     rdfs:label "simple change" ;
     rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/about> ],
-        [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
             owl:onProperty <http://w3id.org/kgcl/new_value> ],
@@ -1485,6 +1477,12 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
     rdfs:label "textual diff" ;
     skos:definition "A summarizing of a change on a piece of text. This could be rendered in a number of different ways" .
 
+<http://w3id.org/kgcl/about_node> a owl:ObjectProperty,
+        linkml:SlotDefinition ;
+    rdfs:label "about node" ;
+    rdfs:range <http://w3id.org/kgcl/om/Node> ;
+    rdfs:subPropertyOf <http://w3id.org/kgcl/about> .
+
 <http://w3id.org/kgcl/has_direct_replacement> a owl:ObjectProperty,
         linkml:SlotDefinition ;
     rdfs:label "has direct replacement" ;
@@ -1493,20 +1491,10 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
     skos:note "if a node obsoletion C C about N1, and Chas direct replacement N2, then N1 IAO:0100001 N2",
         "if a node obsoletion C, C about N1, and C has direct replacement N2, and there exists an edge with an edge property referencing N1, it is possible to replace with N2" .
 
-<http://w3id.org/kgcl/om/OntologyElement> a owl:Class,
-        linkml:ClassDefinition ;
-    rdfs:label "ontology element" ;
-    skos:altLabel "entity" ;
-    skos:definition "Any component of an ontology or knowledge graph" .
-
 <http://w3id.org/kgcl/NodeRename> a owl:Class,
         linkml:ClassDefinition ;
     rdfs:label "node rename" ;
     rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/new_value> ],
-        [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://w3id.org/kgcl/TextualDiff> ;
             owl:onProperty <http://w3id.org/kgcl/has_textual_diff> ],
@@ -1518,10 +1506,20 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
             owl:onProperty <http://w3id.org/kgcl/old_value> ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass linkml:String ;
+            owl:onProperty <http://w3id.org/kgcl/new_value> ],
         <http://w3id.org/kgcl/NodeChange> ;
     skos:definition "A node change where the name (aka rdfs:label) of the node changes" ;
     linkml:examples "Example(value=\"rename UBERON:0002398 from 'manus' to 'hand'\", description=\"replacing the rdfs:label of 'manus' on an uberon class with the rdfs:label 'hand'\")" .
 
+<http://w3id.org/kgcl/about> a owl:ObjectProperty,
+        linkml:SlotDefinition ;
+    rdfs:label "about" ;
+    rdfs:range <http://w3id.org/kgcl/om/OntologyElement> ;
+    skos:definition "The 'focus' entity on which the change operates" .
+
 <http://w3id.org/kgcl/om/subject> a owl:ObjectProperty,
         linkml:SlotDefinition ;
     rdfs:label "subject" ;
@@ -1531,17 +1529,17 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
         linkml:ClassDefinition ;
     rdfs:label "agent" ;
     rdfs:subClassOf [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://www.w3.org/ns/prov#Agent> ;
+            owl:onProperty <http://www.w3.org/ns/prov#acted_on_behalf_of> ],
+        [ a owl:Restriction ;
             owl:onClass linkml:String ;
             owl:onProperty <https://w3id.org/kgcl/basics/id> ;
             owl:qualifiedCardinality 1 ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://www.w3.org/ns/prov#Activity> ;
-            owl:onProperty <http://www.w3.org/ns/prov#was_informed_by> ],
-        [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://www.w3.org/ns/prov#Agent> ;
-            owl:onProperty <http://www.w3.org/ns/prov#acted_on_behalf_of> ] ;
+            owl:onProperty <http://www.w3.org/ns/prov#was_informed_by> ] ;
     skos:definition "a provence-generating agent" ;
     skos:exactMatch <http://www.w3.org/ns/prov#Agent> .
 
@@ -1556,6 +1554,22 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
     rdfs:label "annotation set" ;
     rdfs:range <http://w3id.org/kgcl/om/Annotation> .
 
+<http://w3id.org/kgcl/EdgeChange> a owl:Class,
+        linkml:ClassDefinition ;
+    rdfs:label "edge change" ;
+    rdfs:subClassOf [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Edge> ;
+            owl:onProperty <http://w3id.org/kgcl/about_edge> ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/om/subject> ],
+        <http://w3id.org/kgcl/SimpleChange> ;
+    skos:altLabel "axiom change",
+        "triple change" ;
+    skos:definition "A change in which the element that is the focus of the change is an edge." .
+
 <http://w3id.org/kgcl/NodeObsoletion> a owl:Class,
         linkml:ClassDefinition ;
     rdfs:label "node obsoletion" ;
@@ -1571,6 +1585,10 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
         [ a owl:Restriction ;
             owl:allValuesFrom <http://w3id.org/kgcl/Change> ;
             owl:onProperty linkml:associated_change_set ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/OntologyElement> ;
+            owl:onProperty <http://w3id.org/kgcl/about> ],
         [ a owl:Restriction ;
             owl:allValuesFrom <http://w3id.org/kgcl/om/Node> ;
             owl:onProperty <http://w3id.org/kgcl/has_nondirect_replacement> ],
@@ -1604,22 +1622,6 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
     rdfs:label "ontology subset" ;
     rdfs:subClassOf <http://w3id.org/kgcl/om/OntologyElement> .
 
-<http://w3id.org/kgcl/EdgeChange> a owl:Class,
-        linkml:ClassDefinition ;
-    rdfs:label "edge change" ;
-    rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Node> ;
-            owl:onProperty <http://w3id.org/kgcl/om/subject> ],
-        [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/about> ],
-        <http://w3id.org/kgcl/SimpleChange> ;
-    skos:altLabel "axiom change",
-        "triple change" ;
-    skos:definition "A change in which the element that is the focus of the change is an edge." .
-
 <http://w3id.org/kgcl/Obsoletion> a owl:Class,
         linkml:ClassDefinition ;
     rdfs:label "obsoletion" ;
@@ -1627,7 +1629,7 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
         "https://mondo.readthedocs.io/en/latest/editors-guide/merging-and-obsoleting/" ;
     rdfs:subClassOf [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
+            owl:onClass <http://w3id.org/kgcl/om/OntologyElement> ;
             owl:onProperty <http://w3id.org/kgcl/about> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
@@ -1640,12 +1642,6 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
     skos:definition "Obsoletion of an element deprecates usage of that element, but does not delete that element." ;
     skos:note "In OWL, deprecation is handled by the deprecation axiom. In OBO, we use this, but place additional requirements and expectations on obsolete elements" .
 
-<http://w3id.org/kgcl/about> a owl:ObjectProperty,
-        linkml:SlotDefinition ;
-    rdfs:label "about" ;
-    rdfs:range linkml:String ;
-    skos:definition "The 'focus' entity on which the change operates" .
-
 <http://w3id.org/kgcl/has_undo> a owl:ObjectProperty,
         linkml:SlotDefinition ;
     rdfs:label "has undo" ;
@@ -1653,17 +1649,29 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
     rdfs:range <http://w3id.org/kgcl/Change> ;
     skos:definition "A change that reverses this change" .
 
+<http://w3id.org/kgcl/NodeChange> a owl:Class,
+        linkml:ClassDefinition ;
+    rdfs:label "node change" ;
+    rdfs:subClassOf [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Node> ;
+            owl:onProperty <http://w3id.org/kgcl/about_node> ],
+        <http://w3id.org/kgcl/SimpleChange> ;
+    skos:definition "A simple change where the change is about a node" .
+
+<http://w3id.org/kgcl/om/OntologyElement> a owl:Class,
+        linkml:ClassDefinition ;
+    rdfs:label "ontology element" ;
+    skos:altLabel "entity" ;
+    skos:definition "Any component of an ontology or knowledge graph" .
+
 <http://www.w3.org/ns/prov#Activity> a owl:Class,
         linkml:ClassDefinition ;
     rdfs:label "activity" ;
     rdfs:subClassOf [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://www.w3.org/ns/prov#ended_at_time> ],
-        [ a owl:Restriction ;
-            owl:onClass linkml:String ;
-            owl:onProperty <https://w3id.org/kgcl/basics/id> ;
-            owl:qualifiedCardinality 1 ],
+            owl:onClass <http://www.w3.org/ns/prov#Agent> ;
+            owl:onProperty <http://www.w3.org/ns/prov#was_associated_with> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://www.w3.org/ns/prov#Activity> ;
@@ -1671,32 +1679,26 @@ linkml:node_obsoletion_change_description a owl:ObjectProperty,
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
-            owl:onProperty <https://w3id.org/kgcl/basics/description> ],
+            owl:onProperty <http://www.w3.org/ns/prov#ended_at_time> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://www.w3.org/ns/prov#Agent> ;
-            owl:onProperty <http://www.w3.org/ns/prov#was_associated_with> ],
+            owl:onClass linkml:String ;
+            owl:onProperty <http://www.w3.org/ns/prov#started_at_time> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
-            owl:onProperty <http://www.w3.org/ns/prov#used> ],
+            owl:onProperty <https://w3id.org/kgcl/basics/description> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
-            owl:onProperty <http://www.w3.org/ns/prov#started_at_time> ] ;
+            owl:onProperty <http://www.w3.org/ns/prov#used> ],
+        [ a owl:Restriction ;
+            owl:onClass linkml:String ;
+            owl:onProperty <https://w3id.org/kgcl/basics/id> ;
+            owl:qualifiedCardinality 1 ] ;
     skos:definition "a provence-generating activity" ;
     skos:exactMatch <http://www.w3.org/ns/prov#Activity> .
 
-<http://w3id.org/kgcl/NodeChange> a owl:Class,
-        linkml:ClassDefinition ;
-    rdfs:label "node change" ;
-    rdfs:subClassOf [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/about> ],
-        <http://w3id.org/kgcl/SimpleChange> ;
-    skos:definition "A simple change where the change is about a node" .
-
 linkml:topValue a owl:DatatypeProperty ;
     rdfs:label "value" .
 
@@ -1709,16 +1711,16 @@ linkml:TypeDefinition a owl:Class ;
     rdfs:label "change" ;
     rdfs:subClassOf [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
-            owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/change_date> ],
+            owl:onClass <http://www.w3.org/ns/prov#Activity> ;
+            owl:onProperty <http://www.w3.org/ns/prov#was_generated_by> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
             owl:onProperty <http://w3id.org/kgcl/creator> ],
         [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
-            owl:onProperty <https://w3id.org/kgcl/basics/id> ;
-            owl:qualifiedCardinality 1 ],
+            owl:onProperty <http://w3id.org/kgcl/pull_request> ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
@@ -1726,7 +1728,11 @@ linkml:TypeDefinition a owl:Class ;
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/pull_request> ],
+            owl:onProperty <http://w3id.org/kgcl/contributor> ],
+        [ a owl:Restriction ;
+            owl:onClass linkml:String ;
+            owl:onProperty <https://w3id.org/kgcl/basics/id> ;
+            owl:qualifiedCardinality 1 ],
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://w3id.org/kgcl/Change> ;
@@ -1734,11 +1740,7 @@ linkml:TypeDefinition a owl:Class ;
         [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
-            owl:onProperty <http://w3id.org/kgcl/contributor> ],
-        [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://www.w3.org/ns/prov#Activity> ;
-            owl:onProperty <http://www.w3.org/ns/prov#was_generated_by> ] ;
+            owl:onProperty <http://w3id.org/kgcl/change_date> ] ;
     skos:definition "Any change perform on an ontology or knowledge graph" .
 
 <http://w3id.org/kgcl/change_description> a owl:ObjectProperty,
@@ -1758,14 +1760,6 @@ linkml:TypeDefinition a owl:Class ;
         linkml:ClassDefinition ;
     rdfs:label "node" ;
     rdfs:subClassOf [ a owl:Restriction ;
-            owl:onClass linkml:String ;
-            owl:onProperty <https://w3id.org/kgcl/basics/id> ;
-            owl:qualifiedCardinality 1 ],
-        [ a owl:Restriction ;
-            owl:maxQualifiedCardinality 1 ;
-            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
-            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
-        [ a owl:Restriction ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass linkml:String ;
             owl:onProperty <http://w3id.org/kgcl/om/name> ],
@@ -1773,6 +1767,14 @@ linkml:TypeDefinition a owl:Class ;
             owl:maxQualifiedCardinality 1 ;
             owl:onClass <http://UNKNOWN.org/owl_type_enum> ;
             owl:onProperty <http://w3id.org/kgcl/om/owl_type> ],
+        [ a owl:Restriction ;
+            owl:onClass linkml:String ;
+            owl:onProperty <https://w3id.org/kgcl/basics/id> ;
+            owl:qualifiedCardinality 1 ],
+        [ a owl:Restriction ;
+            owl:maxQualifiedCardinality 1 ;
+            owl:onClass <http://w3id.org/kgcl/om/Annotation> ;
+            owl:onProperty <http://w3id.org/kgcl/om/annotation_set> ],
         <http://w3id.org/kgcl/om/OntologyElement> ;
     skos:altLabel "entity",
         "term" ;
@@ -1782,10 +1784,6 @@ linkml:ClassDefinition a owl:Class ;
     rdfs:label "class_definition" ;
     skos:definition "the definition of a class or interface" .
 
-linkml:SlotDefinition a owl:Class ;
-    rdfs:label "slot_definition" ;
-    skos:definition "the definition of a property or a slot" .
-
 linkml:String a owl:Class,
         linkml:TypeDefinition ;
     rdfs:label "string" ;
@@ -1795,5 +1793,9 @@ linkml:String a owl:Class,
             owl:qualifiedCardinality 1 ] ;
     skos:definition "A character string" .
 
+linkml:SlotDefinition a owl:Class ;
+    rdfs:label "slot_definition" ;
+    skos:definition "the definition of a property or a slot" .
+
 
 
diff --git a/python/kgcl.py b/python/kgcl.py
index d8dea693..fec75fcd 100644
--- a/python/kgcl.py
+++ b/python/kgcl.py
@@ -1,5 +1,5 @@
 # Auto generated from kgcl.yaml by pythongen.py version: 0.9.0
-# Generation date: 2021-06-25 07:49
+# Generation date: 2021-07-16 20:38
 # Schema: kgcl
 #
 # id: https://w3id.org/kgcl
@@ -25,7 +25,7 @@
 from linkml_runtime.utils.enumerations import EnumDefinitionImpl
 from rdflib import Namespace, URIRef
 from linkml_runtime.utils.curienamespace import CurieNamespace
-from . ontology_model import Annotation, NodeId, OntologySubset, OwlTypeEnum, PropertyValue
+from . ontology_model import Annotation, Edge, NodeId, OntologyElement, OntologySubset, OwlTypeEnum, PropertyValue
 from . prov import Activity, ActivityId
 from linkml_runtime.linkml_model.types import Integer, String, Uriorcurie
 from linkml_runtime.utils.metamodelcore import URIorCURIE
@@ -288,14 +288,10 @@ class SimpleChange(Change):
     class_model_uri: ClassVar[URIRef] = KGCL.SimpleChange
 
     id: Union[str, SimpleChangeId] = None
-    about: Optional[str] = None
     old_value: Optional[str] = None
     new_value: Optional[str] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
-
         if self.old_value is not None and not isinstance(self.old_value, str):
             self.old_value = str(self.old_value)
 
@@ -440,12 +436,12 @@ class Obsoletion(ChangeMixin):
     class_name: ClassVar[str] = "obsoletion"
     class_model_uri: ClassVar[URIRef] = KGCL.Obsoletion
 
-    about: Optional[str] = None
+    about: Optional[Union[dict, OntologyElement]] = None
     has_undo: Optional[Union[dict, "Obsoletion"]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about is not None and not isinstance(self.about, OntologyElement):
+            self.about = OntologyElement()
 
         if self.has_undo is not None and not isinstance(self.has_undo, Obsoletion):
             self.has_undo = Obsoletion(**self.has_undo)
@@ -600,12 +596,12 @@ class EdgeChange(SimpleChange):
     class_model_uri: ClassVar[URIRef] = KGCL.EdgeChange
 
     id: Union[str, EdgeChangeId] = None
-    about: Optional[str] = None
+    about_edge: Optional[Union[dict, Edge]] = None
     subject: Optional[Union[str, NodeId]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about_edge is not None and not isinstance(self.about_edge, Edge):
+            self.about_edge = Edge(**self.about_edge)
 
         if self.subject is not None and not isinstance(self.subject, NodeId):
             self.subject = NodeId(self.subject)
@@ -740,6 +736,7 @@ class EdgeObsoletion(EdgeChange):
     object: Optional[Union[str, NodeId]] = None
     annotation_set: Optional[Union[dict, Annotation]] = None
     change_description: Optional[str] = None
+    about: Optional[Union[dict, OntologyElement]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
@@ -762,6 +759,9 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self.change_description is not None and not isinstance(self.change_description, str):
             self.change_description = str(self.change_description)
 
+        if self.about is not None and not isinstance(self.about, OntologyElement):
+            self.about = OntologyElement()
+
         super().__post_init__(**kwargs)
 
 
@@ -957,11 +957,11 @@ class NodeChange(SimpleChange):
     class_model_uri: ClassVar[URIRef] = KGCL.NodeChange
 
     id: Union[str, NodeChangeId] = None
-    about: Optional[str] = None
+    about_node: Optional[Union[str, NodeId]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about_node is not None and not isinstance(self.about_node, NodeId):
+            self.about_node = NodeId(self.about_node)
 
         super().__post_init__(**kwargs)
 
@@ -1397,7 +1397,7 @@ class RemovedNodeFromSubset(NodeChange):
 
     id: Union[str, RemovedNodeFromSubsetId] = None
     change_description: Optional[str] = None
-    about: Optional[str] = None
+    about_node: Optional[Union[str, NodeId]] = None
     subset: Optional[str] = None
     in_subset: Optional[Union[dict, OntologySubset]] = None
 
@@ -1410,8 +1410,8 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self.change_description is not None and not isinstance(self.change_description, str):
             self.change_description = str(self.change_description)
 
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about_node is not None and not isinstance(self.about_node, NodeId):
+            self.about_node = NodeId(self.about_node)
 
         if self.subset is not None and not isinstance(self.subset, str):
             self.subset = str(self.subset)
@@ -1439,6 +1439,7 @@ class NodeObsoletion(NodeChange):
     has_nondirect_replacement: Optional[Union[Union[str, NodeId], List[Union[str, NodeId]]]] = empty_list()
     change_description: Optional[str] = None
     associated_change_set: Optional[Union[Dict[Union[str, ChangeId], Union[dict, Change]], List[Union[dict, Change]]]] = empty_dict()
+    about: Optional[Union[dict, OntologyElement]] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
@@ -1458,6 +1459,9 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
 
         self._normalize_inlined_as_list(slot_name="associated_change_set", slot_type=Change, key_name="id", keyed=True)
 
+        if self.about is not None and not isinstance(self.about, OntologyElement):
+            self.about = OntologyElement()
+
         super().__post_init__(**kwargs)
 
 
@@ -1613,7 +1617,7 @@ class NodeDirectMerge(NodeObsoletion):
 
     id: Union[str, NodeDirectMergeId] = None
     has_direct_replacement: Union[str, NodeId] = None
-    about: Optional[str] = None
+    about_node: Optional[Union[str, NodeId]] = None
     change_description: Optional[str] = None
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
@@ -1627,8 +1631,8 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if not isinstance(self.has_direct_replacement, NodeId):
             self.has_direct_replacement = NodeId(self.has_direct_replacement)
 
-        if self.about is not None and not isinstance(self.about, str):
-            self.about = str(self.about)
+        if self.about_node is not None and not isinstance(self.about_node, NodeId):
+            self.about_node = NodeId(self.about_node)
 
         if self.change_description is not None and not isinstance(self.change_description, str):
             self.change_description = str(self.change_description)
@@ -1820,7 +1824,13 @@ class slots:
                    model_uri=KGCL.superclass, domain=None, range=Optional[Union[str, NodeId]])
 
 slots.about = Slot(uri=KGCL.about, name="about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.about, domain=None, range=Optional[str])
+                   model_uri=KGCL.about, domain=None, range=Optional[Union[dict, OntologyElement]])
+
+slots.about_node = Slot(uri=KGCL.about_node, name="about node", curie=KGCL.curie('about_node'),
+                   model_uri=KGCL.about_node, domain=None, range=Optional[Union[str, NodeId]])
+
+slots.about_edge = Slot(uri=KGCL.about_edge, name="about edge", curie=KGCL.curie('about_edge'),
+                   model_uri=KGCL.about_edge, domain=None, range=Optional[Union[dict, Edge]])
 
 slots.target = Slot(uri=KGCL.target, name="target", curie=KGCL.curie('target'),
                    model_uri=KGCL.target, domain=None, range=Optional[str])
@@ -1934,7 +1944,7 @@ class slots:
                    model_uri=KGCL.change_set_summary_statistic_property_value_set, domain=ChangeSetSummaryStatistic, range=Optional[Union[Union[dict, PropertyValue], List[Union[dict, PropertyValue]]]])
 
 slots.obsoletion_about = Slot(uri=KGCL.about, name="obsoletion_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.obsoletion_about, domain=None, range=Optional[str])
+                   model_uri=KGCL.obsoletion_about, domain=None, range=Optional[Union[dict, OntologyElement]])
 
 slots.obsoletion_has_undo = Slot(uri=KGCL.has_undo, name="obsoletion_has undo", curie=KGCL.curie('has_undo'),
                    model_uri=KGCL.obsoletion_has_undo, domain=None, range=Optional[Union[dict, "Obsoletion"]])
@@ -1954,9 +1964,6 @@ class slots:
 slots.remove_from_subset_has_undo = Slot(uri=KGCL.has_undo, name="remove from subset_has undo", curie=KGCL.curie('has_undo'),
                    model_uri=KGCL.remove_from_subset_has_undo, domain=None, range=Optional[Union[dict, AddToSubset]])
 
-slots.edge_change_about = Slot(uri=KGCL.about, name="edge change_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.edge_change_about, domain=EdgeChange, range=Optional[str])
-
 slots.edge_change_subject = Slot(uri=KGCL.subject, name="edge change_subject", curie=KGCL.curie('subject'),
                    model_uri=KGCL.edge_change_subject, domain=EdgeChange, range=Optional[Union[str, NodeId]])
 
@@ -1981,9 +1988,6 @@ class slots:
 slots.predicate_change_change_description = Slot(uri=KGCL.change_description, name="predicate change_change description", curie=KGCL.curie('change_description'),
                    model_uri=KGCL.predicate_change_change_description, domain=PredicateChange, range=Optional[str])
 
-slots.node_change_about = Slot(uri=KGCL.about, name="node change_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.node_change_about, domain=NodeChange, range=Optional[str])
-
 slots.node_rename_old_value = Slot(uri=KGCL.old_value, name="node rename_old value", curie=KGCL.curie('old_value'),
                    model_uri=KGCL.node_rename_old_value, domain=NodeRename, range=Optional[str])
 
@@ -2005,8 +2009,8 @@ class slots:
 slots.removed_node_from_subset_change_description = Slot(uri=KGCL.change_description, name="removed node from subset_change description", curie=KGCL.curie('change_description'),
                    model_uri=KGCL.removed_node_from_subset_change_description, domain=RemovedNodeFromSubset, range=Optional[str])
 
-slots.removed_node_from_subset_about = Slot(uri=KGCL.about, name="removed node from subset_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.removed_node_from_subset_about, domain=RemovedNodeFromSubset, range=Optional[str])
+slots.removed_node_from_subset_about_node = Slot(uri=KGCL.about_node, name="removed node from subset_about node", curie=KGCL.curie('about_node'),
+                   model_uri=KGCL.removed_node_from_subset_about_node, domain=RemovedNodeFromSubset, range=Optional[Union[str, NodeId]])
 
 slots.removed_node_from_subset_subset = Slot(uri=KGCL.subset, name="removed node from subset_subset", curie=KGCL.curie('subset'),
                    model_uri=KGCL.removed_node_from_subset_subset, domain=RemovedNodeFromSubset, range=Optional[str])
@@ -2038,8 +2042,8 @@ class slots:
 slots.node_direct_merge_has_direct_replacement = Slot(uri=KGCL.has_direct_replacement, name="node direct merge_has direct replacement", curie=KGCL.curie('has_direct_replacement'),
                    model_uri=KGCL.node_direct_merge_has_direct_replacement, domain=NodeDirectMerge, range=Union[str, NodeId])
 
-slots.node_direct_merge_about = Slot(uri=KGCL.about, name="node direct merge_about", curie=KGCL.curie('about'),
-                   model_uri=KGCL.node_direct_merge_about, domain=NodeDirectMerge, range=Optional[str])
+slots.node_direct_merge_about_node = Slot(uri=KGCL.about_node, name="node direct merge_about node", curie=KGCL.curie('about_node'),
+                   model_uri=KGCL.node_direct_merge_about_node, domain=NodeDirectMerge, range=Optional[Union[str, NodeId]])
 
 slots.node_direct_merge_change_description = Slot(uri=KGCL.change_description, name="node direct merge_change description", curie=KGCL.curie('change_description'),
                    model_uri=KGCL.node_direct_merge_change_description, domain=NodeDirectMerge, range=Optional[str])
diff --git a/python/prov.py b/python/prov.py
index 9cf6d34f..74018c27 100644
--- a/python/prov.py
+++ b/python/prov.py
@@ -1,5 +1,5 @@
 # Auto generated from prov.yaml by pythongen.py version: 0.9.0
-# Generation date: 2021-05-15 16:35
+# Generation date: 2021-07-16 19:00
 # Schema: KGCL-PROV
 #
 # id: https://w3id.org/kgcl/prov
@@ -68,7 +68,7 @@ class Activity(YAMLRoot):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, ActivityId):
             self.id = ActivityId(self.id)
 
@@ -111,7 +111,7 @@ class Agent(YAMLRoot):
 
     def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
         if self._is_empty(self.id):
-            raise ValueError("id must be supplied")
+            self.MissingRequiredField("id")
         if not isinstance(self.id, AgentId):
             self.id = AgentId(self.id)
 
diff --git a/requirements.txt b/requirements.txt
index a2bf26df..d8b12ea5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,2 @@
-linkml >= 1.0.0
+linkml >= 1.0.1
 mkdocs
diff --git a/shex/kgcl.shex b/shex/kgcl.shex
index 18d11ae9..ddc2fbf2 100644
--- a/shex/kgcl.shex
+++ b/shex/kgcl.shex
@@ -219,7 +219,7 @@ om:Edge CLOSED {
 <EdgeChange_struct> {
     (  $<EdgeChange_tes> (  &<SimpleChange_tes> ;
           rdf:type [ <SimpleChange> ] ? ;
-          <about> @linkml:String ? ;
+          <about_edge> @om:Edge ? ;
           <subject> @om:Node ?
        ) ;
        rdf:type [ <EdgeChange> ]
@@ -275,7 +275,8 @@ om:Edge CLOSED {
           om:predicate @om:Node ? ;
           om:object @om:Node ? ;
           om:annotation_set @om:Annotation ? ;
-          <change_description> @linkml:String ?
+          <change_description> @linkml:String ? ;
+          <about> @om:OntologyElement ?
        ) ;
        rdf:type [ <EdgeObsoletion> ]
     )
@@ -390,7 +391,7 @@ om:Node  (
 <NodeChange_struct> {
     (  $<NodeChange_tes> (  &<SimpleChange_tes> ;
           rdf:type [ <SimpleChange> ] ? ;
-          <about> @linkml:String ?
+          <about_node> @om:Node ?
        ) ;
        rdf:type [ <NodeChange> ]
     )
@@ -439,7 +440,7 @@ om:Node  (
           &<AllowsAutomaticReplacementOfEdges_tes> ;
           rdf:type [ <AllowsAutomaticReplacementOfEdges> ] ? ;
           <has_direct_replacement> @om:Node ;
-          <about> @linkml:String ? ;
+          <about_node> @om:Node ? ;
           <change_description> @linkml:String ?
        ) ;
        rdf:type [ <NodeDirectMerge> ]
@@ -466,7 +467,8 @@ om:Node  (
              <has_direct_replacement> @om:Node ? ;
              <has_nondirect_replacement> @om:Node * ;
              <change_description> @linkml:String ? ;
-             <associated_change_set> @<Change> *
+             <associated_change_set> @<Change> * ;
+             <about> @om:OntologyElement ?
           ) ;
           rdf:type [ <NodeObsoletion> ]
        )
@@ -555,7 +557,7 @@ om:Node  (
     {
        (  $<Obsoletion_tes> (  &<ChangeMixin_tes> ;
              rdf:type [ <ChangeMixin> ] ? ;
-             <about> @linkml:String ? ;
+             <about> @om:OntologyElement ? ;
              <has_undo> @<Obsoletion> ?
           ) ;
           rdf:type [ <Obsoletion> ] ?
@@ -640,7 +642,7 @@ om:PropertyValue  (
           &<RemoveFromSubset_tes> ;
           rdf:type [ <RemoveFromSubset> ] ? ;
           <change_description> @linkml:String ? ;
-          <about> @linkml:String ? ;
+          <about_node> @om:Node ? ;
           <subset> @linkml:String ? ;
           <in_subset> @om:OntologySubset ?
        ) ;
@@ -663,7 +665,6 @@ om:PropertyValue  (
 <SimpleChange_struct> {
     (  $<SimpleChange_tes> (  &<Change_tes> ;
           rdf:type [ <Change> ] ? ;
-          <about> @linkml:String ? ;
           <old_value> @linkml:String ? ;
           <new_value> @linkml:String ?
        ) ;
diff --git a/src/schema/kgcl.yaml b/src/schema/kgcl.yaml
index d9fd5f1c..0b49619d 100644
--- a/src/schema/kgcl.yaml
+++ b/src/schema/kgcl.yaml
@@ -72,7 +72,6 @@ classes:
     description: >-
       A change that is about a single ontology element
     slots:
-      - about
       - old value
       - new value
 
@@ -233,12 +232,14 @@ classes:
     is_a: simple change
     description: >-
       A change in which the element that is the focus of the change is an edge.
+    slots:
+      - about edge
     slot_usage:
-      about:
-        domain: edge
       subject:
         comments:
           - if subject is empty, use the about field of the last node change
+    todos:
+      - decide on flat serialization, see https://github.com/cmungall/knowledge-graph-change-language/issues/7
 
   edge creation:
     aliases:
@@ -320,7 +321,7 @@ classes:
       - value: changing a is-a b to a is-a c
     slot_usage:
       change description:
-        string_serialization: "move {about} from {old value} to {new value}"
+        string_serialization: "move {about edge} from {old value} to {new value}"
 
   node deepening:
     is_a: node move
@@ -331,7 +332,7 @@ classes:
       - value: changing a is-a b to a is-a c, where c is a subclass of b
     slot_usage:
       change description:
-        string_serialization: "deepened {about} from {old value} to {new value}"
+        string_serialization: "deepened {about edge} from {old value} to {new value}"
 
   node shallowing:
     is_a: node move
@@ -371,10 +372,7 @@ classes:
     description: >-
       A simple change where the change is about a node
     slots:
-      - about
-    slot_usage:
-      about:
-        domain: node
+      - about node
 
   node rename:
     is_a: node change
@@ -513,7 +511,7 @@ classes:
     slot_usage:
       change description:
         string_serialization: "removing {about} from {subset}"
-      about:
+      about node:
         description: >-
           The node that is removed from the subset
       subset:
@@ -624,7 +622,7 @@ classes:
     slot_usage:
       has direct replacement:
         required: true
-      about:
+      about node:
         aliases:
           - alt_id
       change description:
@@ -718,6 +716,17 @@ slots:
     description: >-
       The 'focus' entity on which the change operates
     multivalued: false
+    range: ontology element
+
+  about node:
+    is_a: about
+    multivalued: false
+    range: node
+
+  about edge:
+    is_a: about
+    multivalued: false
+    range: edge
 
   target:
     description: >-


Test patch:
diff --git a/tests/test_change_model.py b/tests/test_change_model.py
index e3ee2343..9392ae51 100644
--- a/tests/test_change_model.py
+++ b/tests/test_change_model.py
@@ -3,6 +3,7 @@
 import os
 from kgcl.utils import from_yaml, to_rdf, to_json
 from kgcl.model.kgcl import *
+from kgcl.model.ontology_model import Edge, Node, NodeId
 from linkml_runtime.dumpers import JSONDumper
 
 cwd = os.path.abspath(os.path.dirname(__file__))
@@ -12,7 +13,17 @@
 class ChangeModelTestSuite(unittest.TestCase):
 
     def test_create(self):
-        c = NewSynonym(id='chg12345', about='ANAT:HindLimb', new_value='hindlimb')
+        c = NewSynonym(id='chg12345',
+                       about_node = 'ANAT:HindLimb',
+                       new_value='hindlimb')
         print(c)
         d = JSONDumper()
         print(d.dumps(c))
+
+    def test_predicate_change(self):
+        c = PredicateChange(id='chg4',
+                            about_edge=Edge(subject='ANAT:Hand', object='ANAT:Forelimb'),
+                            new_value='BFO:000005')
+        print(c)
+        d = JSONDumper()
+        print(d.dumps(c))
\ No newline at end of file