Index: README.md
===================================================================
--- README.md	(revision 37b5d111a63a7caf8477361289173c4798e334cb)
+++ README.md	(revision c98728bb39751828e4df0f0c00cf0f33d210b95d)
@@ -151,6 +151,4 @@
   "flexo_id": "AF-Q250101-9A4C2D@003S",
   "signature": "1E3A9F2A8B7C4D11",
-  "created_at": "2025-10-20T11:08:26Z",
-  "updated_at": "2025-10-20T11:12:44Z"
 }
 #+END_SRC
Index: flexoentity/flexo_entity.py
===================================================================
--- flexoentity/flexo_entity.py	(revision 37b5d111a63a7caf8477361289173c4798e334cb)
+++ flexoentity/flexo_entity.py	(revision c98728bb39751828e4df0f0c00cf0f33d210b95d)
@@ -7,5 +7,5 @@
 from enum import Enum, auto
 from dataclasses import dataclass, field
-from datetime import datetime, UTC
+from datetime import datetime
 from typing import Optional
 from abc import ABC, abstractmethod
@@ -248,5 +248,4 @@
     if target_state == EntityState.OBSOLETE:
         self.state = target_state
-        self.updated_at = datetime.now(UTC)
         return
 
@@ -257,5 +256,4 @@
 
     self.state = target_state
-    self.updated_at = datetime.now(UTC)
 
     # ───────────────────────────────────────────────────────────────
@@ -274,5 +272,4 @@
         """Increment version number on the ID."""
         self.flexo_id = FlexOID.next_version(self.flexo_id)
-        self.updated_at = datetime.now(UTC)
 
     def lineage(self, repo):
@@ -302,5 +299,4 @@
             self.flexo_id = new_fid
             self.state = EntityState.APPROVED
-            self.updated_at = datetime.now(UTC)
             return self
         raise ValueError("Only drafts can be approved")
@@ -310,6 +306,30 @@
 
     def publish(self):
-        if self.state == EntityState.APPROVED_AND_SIGNED:
-            self._transition(EntityState.PUBLISHED)
+        """
+    Move     from APPROVED or APPROVED_AND_SIGNED to PUBLISHED.
+        
+        Uses allowed_transitions() to verify legality,
+        then performs a version bump and lineage update.
+        """
+        allowed = self.allowed_transitions()
+        if "PUBLISHED" not in allowed:
+            raise ValueError(
+                f"Illegal state transition: {self.state.name} → PUBLISHED. "
+                f"Allowed: {', '.join(allowed) or 'none'}"
+            )
+
+        new_version = self.flexo_id.version + 1
+        new_fid = FlexOID.safe_generate(
+            self.domain_code(),
+            self.etype.short(),
+            EntityState.PUBLISHED.short(),
+            self.text_seed,
+            version=new_version
+        )
+
+        self.previous_id = self.flexo_id
+        self.flexo_id = new_fid
+        self.state = EntityState.PUBLISHED
+        return self
 
     def obsolete(self):
@@ -327,5 +347,4 @@
         )
         self.state = EntityState.DRAFT
-        self.updated_at = datetime.now(UTC)
 
     # ───────────────────────────────────────────────────────────────
@@ -347,5 +366,5 @@
                 return ["APPROVED"]
             case EntityState.APPROVED:
-                return ["APPROVED_AND_SIGNED", "OBSOLETE"]
+                return ["APPROVED_AND_SIGNED", "PUBLISHED", "OBSOLETE"]
             case EntityState.APPROVED_AND_SIGNED:
                 return ["PUBLISHED", "OBSOLETE"]
