mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
docs(cookbook): add Change Management module notebook (#991)
* docs(cookbook): add Change Management module notebook Add cookbook/introduction/24_Change_Management.ipynb covering the change_management module with verified, executable examples: - ChangeLogEntry with email-validated author field - InMemoryVersionStorage save/get/list_all/exists/delete round trip - named tags (save_tag/get_tag) for release pinning - compute_checksum / verify_checksum integrity verification with tamper detection The change_management module currently has no cookbook coverage. All API calls and outputs were executed against semantica/change_management/change_log.py and version_storage.py. Signed-off-by: LeonSGP43 <LeonSGP43@users.noreply.github.com> * docs(cookbook): clarify outputs verified against repo source, not PyPI release Signed-off-by: LeonSGP43 <leonsgp43@users.noreply.github.com> * docs(cookbook): execute change management notebook in Jupyter (real kernel run, stream outputs, execution counts) Signed-off-by: LeonSGP43 <cine.dreamer.one@gmail.com> --------- Signed-off-by: LeonSGP43 <LeonSGP43@users.noreply.github.com> Signed-off-by: LeonSGP43 <leonsgp43@users.noreply.github.com> Signed-off-by: LeonSGP43 <cine.dreamer.one@gmail.com> Co-authored-by: LeonSGP43 <LeonSGP43@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8d7096ea",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"[](https://colab.research.google.com/github/semantica-agi/semantica/blob/main/cookbook/introduction/24_Change_Management.ipynb)\n",
|
||||
"\n",
|
||||
"# Change Management — Practical Guide\n",
|
||||
"\n",
|
||||
"Semantica's `change_management` module provides versioning, audit trails, and data-integrity checks for knowledge graphs and ontologies:\n",
|
||||
"\n",
|
||||
"- **`ChangeLogEntry`** — standardized change metadata (validated timestamp/author)\n",
|
||||
"- **`InMemoryVersionStorage` / `SQLiteVersionStorage`** — version snapshot storage with named tags\n",
|
||||
"- **`compute_checksum` / `verify_checksum`** — SHA-256 integrity verification\n",
|
||||
"\n",
|
||||
"This notebook runs a complete save → tag → verify → tamper-detect cycle. All outputs are real executed results verified against the repository's `semantica/change_management/` source at the time of writing (the `pip install` cell may fetch a newer release with slightly different behavior)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "7bdffec1",
|
||||
"metadata": {
|
||||
"execution": {
|
||||
"iopub.execute_input": "2026-08-26T18:46:37.171333Z",
|
||||
"iopub.status.busy": "2026-08-26T18:46:37.171183Z",
|
||||
"iopub.status.idle": "2026-08-26T18:46:39.060860Z",
|
||||
"shell.execute_reply": "2026-08-26T18:46:39.059594Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q semantica"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "169efee1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1) A `ChangeLogEntry` records *who* changed *what*, *when*\n",
|
||||
"\n",
|
||||
"`author` must be a valid email — the dataclass validates on construction (`ValidationError` otherwise), which keeps audit trails clean."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "5b17acdb",
|
||||
"metadata": {
|
||||
"execution": {
|
||||
"iopub.execute_input": "2026-08-26T18:46:39.064077Z",
|
||||
"iopub.status.busy": "2026-08-26T18:46:39.063818Z",
|
||||
"iopub.status.idle": "2026-08-26T18:46:39.321881Z",
|
||||
"shell.execute_reply": "2026-08-26T18:46:39.321036Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"ChangeLogEntry(timestamp='2026-08-15T09:00:00Z', author='demo@example.com', description='initial version', change_id=None, related_changes=[])"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from semantica.change_management import ChangeLogEntry\n",
|
||||
"\n",
|
||||
"entry = ChangeLogEntry(\n",
|
||||
" timestamp=\"2026-08-15T09:00:00Z\",\n",
|
||||
" author=\"demo@example.com\",\n",
|
||||
" description=\"initial version\",\n",
|
||||
")\n",
|
||||
"entry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "53d8df5c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2) Save a versioned snapshot\n",
|
||||
"\n",
|
||||
"A snapshot is a dict with a required `label` plus your payload. Here we attach the KG data, the change log, and a SHA-256 `checksum` computed over everything except the checksum field itself."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "fec16f24",
|
||||
"metadata": {
|
||||
"execution": {
|
||||
"iopub.execute_input": "2026-08-26T18:46:39.325528Z",
|
||||
"iopub.status.busy": "2026-08-26T18:46:39.325140Z",
|
||||
"iopub.status.idle": "2026-08-26T18:46:39.331480Z",
|
||||
"shell.execute_reply": "2026-08-26T18:46:39.330586Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"True"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from semantica.change_management import InMemoryVersionStorage, compute_checksum\n",
|
||||
"\n",
|
||||
"storage = InMemoryVersionStorage()\n",
|
||||
"\n",
|
||||
"snapshot = {\n",
|
||||
" \"label\": \"v1.0.0\",\n",
|
||||
" \"data\": {\"entities\": {\"acme\": {\"type\": \"Company\"}}},\n",
|
||||
" \"change_log\": {\n",
|
||||
" \"timestamp\": entry.timestamp,\n",
|
||||
" \"author\": entry.author,\n",
|
||||
" \"description\": entry.description,\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"snapshot[\"checksum\"] = compute_checksum({k: v for k, v in snapshot.items() if k != \"checksum\"})\n",
|
||||
"\n",
|
||||
"storage.save(snapshot)\n",
|
||||
"storage.exists(\"v1.0.0\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0f1c603b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 3) Named tags pin a version for releases\n",
|
||||
"\n",
|
||||
"`save_tag` / `get_tag` map stable names (e.g. `release`) to version labels, decoupling consumers from label churn."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "62f7643e",
|
||||
"metadata": {
|
||||
"execution": {
|
||||
"iopub.execute_input": "2026-08-26T18:46:39.335182Z",
|
||||
"iopub.status.busy": "2026-08-26T18:46:39.334886Z",
|
||||
"iopub.status.idle": "2026-08-26T18:46:39.339586Z",
|
||||
"shell.execute_reply": "2026-08-26T18:46:39.338568Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"('v1.0.0', ['v1.0.0'])"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"storage.save_tag(\"release\", \"v1.0.0\")\n",
|
||||
"\n",
|
||||
"storage.get_tag(\"release\"), [s[\"label\"] for s in storage.list_all()]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "96df12da",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 4) Verify integrity — and catch tampering\n",
|
||||
"\n",
|
||||
"`verify_checksum(snapshot)` recomputes the SHA-256 over the snapshot (minus its `checksum` field) and compares. A single mutated character in the data flips the result to `False`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "26d0de85",
|
||||
"metadata": {
|
||||
"execution": {
|
||||
"iopub.execute_input": "2026-08-26T18:46:39.342653Z",
|
||||
"iopub.status.busy": "2026-08-26T18:46:39.342466Z",
|
||||
"iopub.status.idle": "2026-08-26T18:46:39.346714Z",
|
||||
"shell.execute_reply": "2026-08-26T18:46:39.345623Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"intact: True\n",
|
||||
"tampered: False\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from semantica.change_management import verify_checksum\n",
|
||||
"\n",
|
||||
"stored = storage.get(\"v1.0.0\")\n",
|
||||
"print(\"intact:\", verify_checksum(stored))\n",
|
||||
"\n",
|
||||
"tampered = storage.get(\"v1.0.0\")\n",
|
||||
"tampered[\"data\"][\"entities\"][\"acme\"][\"note\"] = \"mutated after the fact\"\n",
|
||||
"print(\"tampered:\", verify_checksum(tampered))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "bd14c3e4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 5) Retiring a version\n",
|
||||
"\n",
|
||||
"`delete(label)` removes a snapshot; tags pointing at it are your responsibility to update."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "de9fe3e5",
|
||||
"metadata": {
|
||||
"execution": {
|
||||
"iopub.execute_input": "2026-08-26T18:46:39.349814Z",
|
||||
"iopub.status.busy": "2026-08-26T18:46:39.349513Z",
|
||||
"iopub.status.idle": "2026-08-26T18:46:39.354710Z",
|
||||
"shell.execute_reply": "2026-08-26T18:46:39.353669Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"False"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"storage.delete(\"v1.0.0\")\n",
|
||||
"storage.exists(\"v1.0.0\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ab667b32",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Summary\n",
|
||||
"\n",
|
||||
"| Task | API |\n",
|
||||
"|---|---|\n",
|
||||
"| Record audit metadata | `ChangeLogEntry(timestamp, author=email, description)` |\n",
|
||||
"| Persist a version | `InMemoryVersionStorage().save({\"label\": ..., ...})` |\n",
|
||||
"| Pin a release name | `save_tag(\"release\", \"v1.0.0\")` / `get_tag(\"release\")` |\n",
|
||||
"| Integrity check | `compute_checksum(snap)` / `verify_checksum(snap)` |\n",
|
||||
"| Persistent backend | `SQLiteVersionStorage(path)` — same interface |\n",
|
||||
"\n",
|
||||
"See also `semantica/change_management/change_management_usage.md` for the manager classes (`TemporalVersionManager`, `OntologyVersionManager`)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user