annotating Python objects with property values passes a simple experiment test
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Thu, 8 Jan 2026 15:37:58 +0000 (15:37 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Thu, 8 Jan 2026 15:37:58 +0000 (15:37 +0000)
24 files changed:
README.md [deleted file]
README.org [new file with mode: 0644]
developer/__init__.py [new file with mode: 0644]
developer/authored/Epimetheus/ObjectRegistry.py [new file with mode: 0644]
developer/authored/Epimetheus/ProcessLocalId.py [new file with mode: 0644]
developer/authored/Epimetheus/Property.py [new file with mode: 0644]
developer/authored/Epimetheus/PropertyManager.py [new file with mode: 0644]
developer/authored/Epimetheus/PropertyStore.py [new file with mode: 0644]
developer/authored/Epimetheus/SemanticSets.py [new file with mode: 0644]
developer/authored/Epimetheus/Syntax.py [new file with mode: 0644]
developer/authored/Epimetheus/__init__.py [new file with mode: 0644]
developer/authored/Identity.py [deleted file]
developer/authored/ObjectRegistry.py [deleted file]
developer/authored/ProcessLocalId.py [deleted file]
developer/authored/Property.py [deleted file]
developer/authored/PropertyManager.py [deleted file]
developer/authored/PropertyStore.py [deleted file]
developer/authored/SemanticSets.py [deleted file]
developer/authored/Syntax.py [deleted file]
developer/authored/__init__.py [deleted file]
developer/experiment/test_Epimetheus.py [new file with mode: 0755]
developer/tool/env
nohup.out [deleted file]
temp.sh [deleted file]

diff --git a/README.md b/README.md
deleted file mode 100644 (file)
index 7104a94..0000000
--- a/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Epimetheus
-
diff --git a/README.org b/README.org
new file mode 100644 (file)
index 0000000..07975ca
--- /dev/null
@@ -0,0 +1,28 @@
+* Epimetheus
+/The God of Afterthought/
+
+Epimetheus is a Python library for **Higher Order Analysis** and **Semantic Annotation**.
+
+Unlike Prometheus (Forethought), who defines what an object /is/ before it is born (classic class instantiation), Epimetheus allows you to attach meaning, identity, and semantic types to objects /after/ they exist.
+
+It is a mechanism for "Late Binding" of ontology.
+
+** Concept
+Most type systems are embedded in the object's definition (the Class). Epimetheus treats the object as a point in space and allows an external observer (the library) to weave a "constellation" of properties and relationships around it.
+
+This enables:
+- **Semantic Typing:** Tagging objects with properties ($color$, $spin$, $is_valid$) without altering the object's code.
+- **Higher Order Analysis:** defining relationships between objects (constellations) rather than just properties of objects.
+- **Non-Invasive Instrumentation:** Annotating third-party objects or C-extensions that cannot be subclassed.
+
+** Etymology
+Named after the Greek Titan *Epimetheus* (Afterthought). While his brother Prometheus is associated with architectural planning, Epimetheus is the patron of patching, extending, and adapting the world as it is found.
+
+** Status
+- **Author:** Thomas Walker Lynch
+- **Version:** Pre-Alpha
+
+#+BEGIN_QUOTE
+"Zero length is a second order concept."
+-- T.W. Lynch, *TTCA*
+#+END_QUOTE
diff --git a/developer/__init__.py b/developer/__init__.py
new file mode 100644 (file)
index 0000000..45fa30d
--- /dev/null
@@ -0,0 +1,2 @@
+# developer/authored/__init__.py
+from .PropertyManager import PropertyManager
diff --git a/developer/authored/Epimetheus/ObjectRegistry.py b/developer/authored/Epimetheus/ObjectRegistry.py
new file mode 100644 (file)
index 0000000..9d30fd9
--- /dev/null
@@ -0,0 +1,113 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
+
+"""
+ObjectRegistry
+
+Maps Python runtime objects to ProcessLocalId.
+
+Strategies:
+  1. Weak Identity (Preferred): For objects that support weakrefs (instances ,classes).
+     - ID lifetime is bound to object lifetime.
+     - Auto-cleaned on GC.
+  
+  2. Value Identity (Fallback): For immutable primitives (int ,str ,tuple).
+     - ID is bound to the *value* (hash/equality).
+     - Stored strongly (since values like 42 or "red" are conceptually eternal).
+"""
+
+from __future__ import annotations
+
+import weakref
+from typing import Any ,Callable ,Dict ,Optional
+
+from .ProcessLocalId import ProcessLocalIdGenerator ,ProcessLocalId
+
+
+class ObjectRegistry:
+  def __init__(self ,id_gen: ProcessLocalIdGenerator):
+    self._id_gen = id_gen
+    
+    # Strategy 1: Entities (Weakref-able)
+    self._obj_to_id_wkd: "weakref.WeakKeyDictionary[Any ,ProcessLocalId]" = weakref.WeakKeyDictionary()
+    self._id_to_obj_ref: Dict[ProcessLocalId ,weakref.ref] = {}
+    
+    # Strategy 2: Values (Hashable ,not weakref-able)
+    self._value_to_id: Dict[Any ,ProcessLocalId] = {}
+    
+    self._finalizers: Dict[ProcessLocalId ,Callable[[ProcessLocalId] ,None]] = {}
+    self._global_finalizer: Optional[Callable[[ProcessLocalId] ,None]] = None
+
+  def register_finalizer(self ,fn: Callable[[ProcessLocalId] ,None]):
+    """
+    Registers a finalizer callback invoked when any registered *weak-refable* object is GC'd.
+    """
+    self._global_finalizer = fn
+
+  def _on_collect(self ,obj_id: ProcessLocalId):
+    # Only called for Strategy 1 objects
+    ref = self._id_to_obj_ref.pop(obj_id ,None)
+    if(ref is not None):
+      # The WeakKeyDictionary auto-cleans the forward mapping ,
+      # but we double check or clean any edge cases if needed.
+      pass 
+      
+    fn = self._global_finalizer
+    if(fn is not None):
+      fn(obj_id)
+
+  def get_id(self ,obj: Any) -> ProcessLocalId:
+    """
+    Returns the ProcessLocalId for `obj` ,registering it if needed.
+    """
+    # 1. Try WeakRef Strategy (Entities)
+    try:
+      existing = self._obj_to_id_wkd.get(obj)
+      if(existing is not None):
+        return existing
+    except TypeError:
+      # obj is not weakref-able (e.g. int ,str ,tuple ,or list).
+      # Fall through to Strategy 2.
+      pass
+    else:
+      # It IS weakref-able ,but wasn't in the dictionary. Register it.
+      obj_id = self._id_gen.next_id()
+      self._obj_to_id_wkd[obj] = obj_id
+      # Create reverse lookup with callback
+      self._id_to_obj_ref[obj_id] = weakref.ref(obj ,lambda _ref ,oid=obj_id: self._on_collect(oid))
+      return obj_id
+
+    # 2. Try Value Strategy (Primitives)
+    # Note: Mutable non-weakrefables (like standard lists) will fail here because they are unhashable.
+    try:
+      existing = self._value_to_id.get(obj)
+      if(existing is not None):
+        return existing
+      
+      # Register new value
+      obj_id = self._id_gen.next_id()
+      self._value_to_id[obj] = obj_id
+      return obj_id
+    except TypeError:
+      # It is neither weakref-able NOR hashable (e.g. standard list ,dict).
+      raise TypeError(
+        f"ObjectRegistry: cannot track object of type {type(obj)!r}. "
+        "It is neither weakref-able (Entity) nor hashable (Value)."
+      )
+
+  def try_get_object(self ,obj_id: ProcessLocalId) -> Optional[Any]:
+    """
+    Best-effort: returns the live object/value.
+    """
+    # Check Entities
+    ref = self._id_to_obj_ref.get(obj_id)
+    if(ref is not None):
+      return ref()
+      
+    # Check Values
+    # For now ,we assume values identify themselves.
+    for val ,pid in self._value_to_id.items():
+      if(pid == obj_id):
+        return val
+        
+    return None
diff --git a/developer/authored/Epimetheus/ProcessLocalId.py b/developer/authored/Epimetheus/ProcessLocalId.py
new file mode 100644 (file)
index 0000000..b57c47c
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
+
+
+"""
+ProcessLocalId
+
+A process-local identifier used as an internal key.
+
+Design constraint:
+  - NOT intended to be serialized or persisted.
+  - `repr()` intentionally does not reveal the numeric token, to discourage logging/persistence.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+
+@dataclass(frozen=True ,slots=True)
+class ProcessLocalId:
+  _n: int
+
+  def __repr__(self) -> str:
+    return "<ProcessLocalId>"
+
+  def __str__(self) -> str:
+    return "<ProcessLocalId>"
+
+  def as_int_UNSAFE(self) -> int:
+    """
+    Returns the raw integer token.
+
+    UNSAFE because:
+      - tokens are process-local
+      - do not write these into files/databases/logs as stable identifiers
+    """
+    return self._n
+
+
+class ProcessLocalIdGenerator:
+  """
+  Monotonic generator; ids are never recycled.
+  """
+  def __init__(self ,start: int = 1):
+    if start < 1: raise ValueError("start must be >= 1")
+    self._next_n: int = start
+
+  def next_id(self) -> ProcessLocalId:
+    n = self._next_n
+    self._next_n += 1
+    return ProcessLocalId(n)
diff --git a/developer/authored/Epimetheus/Property.py b/developer/authored/Epimetheus/Property.py
new file mode 100644 (file)
index 0000000..5b33226
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
+
+
+"""
+Property
+
+A Property is itself an entity (it has an Identity id) so that:
+  - properties can have properties
+  - properties can be members of semantic sets
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Optional ,Tuple
+
+from .ProcessLocalId import ProcessLocalId
+
+
+@dataclass(frozen=True ,slots=True)
+class Property:
+  id: ProcessLocalId
+  name_path: Tuple[str ,...]
+  doc: str = ""
+
+  def __repr__(self) -> str:
+    # name_path is safe to reveal; id token is not.
+    return f"<Property {'.'.join(self.name_path)!r}>"
diff --git a/developer/authored/Epimetheus/PropertyManager.py b/developer/authored/Epimetheus/PropertyManager.py
new file mode 100644 (file)
index 0000000..36e2b9e
--- /dev/null
@@ -0,0 +1,155 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
+
+
+"""
+PropertyManager
+
+Core RT property system.
+
+Key decisions vs the earlier `property_manager.py`:
+  - do NOT key by `object_path()` strings (avoids collisions) fileciteturn3file4
+  - runtime objects are keyed by weak identity (ProcessLocalId assigned by ObjectRegistry)
+  - properties are first-class entities (Property has an id), so properties can have properties
+
+This remains process-local and in-memory.
+"""
+
+from __future__ import annotations
+
+from typing import Any ,Dict ,Iterable ,List ,Optional ,Tuple ,Union
+
+from .ProcessLocalId import ProcessLocalIdGenerator ,ProcessLocalId
+from .ObjectRegistry import ObjectRegistry
+from .PropertyStore import PropertyStore
+from .Property import Property
+from .SemanticSets import SemanticSet ,SemanticSetStore
+from .Syntax import SyntaxInstance
+
+
+NamePathLike = Union[str ,List[str] ,Tuple[str ,...]]
+
+
+class PropertyManager:
+  def __init__(self):
+    self._id_gen = ProcessLocalIdGenerator()
+    self._obj_reg = ObjectRegistry(self._id_gen)
+    self._store = PropertyStore()
+    self._sets = SemanticSetStore()
+
+    # Declare-by-name registry
+    self._name_path_to_property: Dict[Tuple[str ,...],Property] = {}
+    self._property_id_to_property: Dict[ProcessLocalId ,Property] = {}
+
+    self._name_path_to_set: Dict[Tuple[str ,...],SemanticSet] = {}
+    self._set_id_to_set: Dict[ProcessLocalId ,SemanticSet] = {}
+
+    # Optional syntax instances (if user chooses to model them)
+    self._syntax_id_to_instance: Dict[ProcessLocalId ,SyntaxInstance] = {}
+
+    # Finalization cleanup
+    self._obj_reg.register_finalizer(self._on_subject_finalized)
+
+  def _on_subject_finalized(self ,subject_id: ProcessLocalId):
+    self._store.remove_subject(subject_id)
+    self._sets.remove_subject(subject_id)
+
+  def _normalize_name_path(self ,name_path: NamePathLike) -> Tuple[str ,...]:
+    if isinstance(name_path ,tuple): return name_path
+    if isinstance(name_path ,list): return tuple(name_path)
+    if isinstance(name_path ,str): return tuple(name_path.split("."))
+    raise TypeError("name_path must be str ,list[str] ,or tuple[str ,...]")
+
+  # -------------------------
+  # Identity acquisition
+  # -------------------------
+  def id_of_py_object(self ,obj: Any) -> ProcessLocalId:
+    return self._obj_reg.get_id(obj)
+
+  def create_syntax_identity(self ,syntax: SyntaxInstance) -> ProcessLocalId:
+    sid = self._id_gen.next_id()
+    self._syntax_id_to_instance[sid] = syntax
+    return sid
+
+  def try_get_syntax(self ,syntax_id: ProcessLocalId) -> Optional[SyntaxInstance]:
+    return self._syntax_id_to_instance.get(syntax_id)
+
+  # -------------------------
+  # Property declaration
+  # -------------------------
+  def declare_property(self ,name_path: NamePathLike ,doc: str = "") -> ProcessLocalId:
+    np = self._normalize_name_path(name_path)
+    existing = self._name_path_to_property.get(np)
+    if existing is not None: return existing.id
+    pid = self._id_gen.next_id()
+    p = Property(pid ,np ,doc)
+    self._name_path_to_property[np] = p
+    self._property_id_to_property[pid] = p
+    return pid
+
+  def property_id(self ,name_path: NamePathLike) -> ProcessLocalId:
+    np = self._normalize_name_path(name_path)
+    p = self._name_path_to_property.get(np)
+    if p is None: raise KeyError(f"Property not declared: {np!r}")
+    return p.id
+
+  def try_get_property(self ,prop_id: ProcessLocalId) -> Optional[Property]:
+    return self._property_id_to_property.get(prop_id)
+
+  # -------------------------
+  # Semantic sets
+  # -------------------------
+  def declare_set(self ,name_path: NamePathLike ,doc: str = "") -> ProcessLocalId:
+    np = self._normalize_name_path(name_path)
+    existing = self._name_path_to_set.get(np)
+    if existing is not None: return existing.id
+    sid = self._id_gen.next_id()
+    s = SemanticSet(sid ,np ,doc)
+    self._name_path_to_set[np] = s
+    self._set_id_to_set[sid] = s
+    return sid
+
+  def add_to_set(self ,subject: Any ,set_id: ProcessLocalId):
+    subject_id = self._coerce_subject_id(subject)
+    self._sets.add_member(set_id ,subject_id)
+
+  def is_in_set(self ,subject: Any ,set_id: ProcessLocalId) -> bool:
+    subject_id = self._coerce_subject_id(subject)
+    return self._sets.has_member(set_id ,subject_id)
+
+  def members(self ,set_id: ProcessLocalId) -> List[ProcessLocalId]:
+    return list(self._sets.members(set_id))
+
+  # -------------------------
+  # Set/get properties
+  # -------------------------
+  def set(self ,subject: Any ,prop: Union[ProcessLocalId ,NamePathLike] ,value: Any):
+    subject_id = self._coerce_subject_id(subject)
+    prop_id = self._coerce_property_id(prop)
+    self._store.set(subject_id ,prop_id ,value)
+
+  def get(self ,subject: Any ,prop: Union[ProcessLocalId ,NamePathLike] ,default: Any = None) -> Any:
+    subject_id = self._coerce_subject_id(subject)
+    prop_id = self._coerce_property_id(prop)
+    return self._store.get(subject_id ,prop_id ,default)
+
+  def has(self ,subject: Any ,prop: Union[ProcessLocalId ,NamePathLike]) -> bool:
+    subject_id = self._coerce_subject_id(subject)
+    prop_id = self._coerce_property_id(prop)
+    return self._store.has(subject_id ,prop_id)
+
+  def subjects_with(self ,prop: Union[ProcessLocalId ,NamePathLike]) -> List[ProcessLocalId]:
+    prop_id = self._coerce_property_id(prop)
+    return list(self._store.subjects_with(prop_id))
+
+  # -------------------------
+  # Coercions
+  # -------------------------
+  def _coerce_subject_id(self ,subject: Any) -> ProcessLocalId:
+    if isinstance(subject ,ProcessLocalId): return subject
+    # For Python runtime objects, we require weakref-able instances.
+    return self._obj_reg.get_id(subject)
+
+  def _coerce_property_id(self ,prop: Union[ProcessLocalId ,NamePathLike]) -> ProcessLocalId:
+    if isinstance(prop ,ProcessLocalId): return prop
+    return self.property_id(prop)
diff --git a/developer/authored/Epimetheus/PropertyStore.py b/developer/authored/Epimetheus/PropertyStore.py
new file mode 100644 (file)
index 0000000..bc8f3f3
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
+
+
+"""
+PropertyStore
+
+Stores property values and maintains reverse lookups.
+
+This is intentionally process-local and in-memory.
+"""
+
+from __future__ import annotations
+
+from typing import Any ,Dict ,Optional ,Set ,Tuple
+
+from .ProcessLocalId import ProcessLocalId
+
+
+class PropertyStore:
+  def __init__(self):
+    # (subject_id ,property_id) -> value
+    self._values: Dict[Tuple[ProcessLocalId ,ProcessLocalId] ,Any] = {}
+
+    # subject_id -> set(property_id)
+    self._subject_to_props: Dict[ProcessLocalId ,Set[ProcessLocalId]] = {}
+
+    # property_id -> set(subject_id)
+    self._prop_to_subjects: Dict[ProcessLocalId ,Set[ProcessLocalId]] = {}
+
+  def set(self ,subject_id: ProcessLocalId ,prop_id: ProcessLocalId ,value: Any):
+    key = (subject_id ,prop_id)
+    self._values[key] = value
+    self._subject_to_props.setdefault(subject_id ,set()).add(prop_id)
+    self._prop_to_subjects.setdefault(prop_id ,set()).add(subject_id)
+
+  def get(self ,subject_id: ProcessLocalId ,prop_id: ProcessLocalId ,default: Any = None) -> Any:
+    return self._values.get((subject_id ,prop_id) ,default)
+
+  def has(self ,subject_id: ProcessLocalId ,prop_id: ProcessLocalId) -> bool:
+    return (subject_id ,prop_id) in self._values
+
+  def subjects_with(self ,prop_id: ProcessLocalId) -> Set[ProcessLocalId]:
+    return set(self._prop_to_subjects.get(prop_id ,set()))
+
+  def props_of(self ,subject_id: ProcessLocalId) -> Set[ProcessLocalId]:
+    return set(self._subject_to_props.get(subject_id ,set()))
+
+  def remove_subject(self ,subject_id: ProcessLocalId):
+    """
+    Remove all stored properties for a subject (used on finalization).
+    """
+    prop_ids = self._subject_to_props.pop(subject_id ,set())
+    for prop_id in prop_ids:
+      self._values.pop((subject_id ,prop_id) ,None)
+      s = self._prop_to_subjects.get(prop_id)
+      if s is not None:
+        s.discard(subject_id)
+        if not s: self._prop_to_subjects.pop(prop_id ,None)
diff --git a/developer/authored/Epimetheus/SemanticSets.py b/developer/authored/Epimetheus/SemanticSets.py
new file mode 100644 (file)
index 0000000..f0fdf48
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
+
+
+"""
+SemanticSets
+
+Membership sets over identities. Used for semantic typing.
+
+Design:
+  - set_id identifies the set
+  - members are subject ids
+  - reverse index for cleanup
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Dict ,Optional ,Set
+
+from .ProcessLocalId import ProcessLocalId
+
+
+@dataclass(frozen=True ,slots=True)
+class SemanticSet:
+  id: ProcessLocalId
+  name_path: tuple[str ,...]
+  doc: str = ""
+
+  def __repr__(self) -> str:
+    return f"<SemanticSet {'.'.join(self.name_path)!r}>"
+
+
+class SemanticSetStore:
+  def __init__(self):
+    self._members: Dict[ProcessLocalId ,Set[ProcessLocalId]] = {}
+    self._subject_to_sets: Dict[ProcessLocalId ,Set[ProcessLocalId]] = {}
+
+  def add_member(self ,set_id: ProcessLocalId ,subject_id: ProcessLocalId):
+    self._members.setdefault(set_id ,set()).add(subject_id)
+    self._subject_to_sets.setdefault(subject_id ,set()).add(set_id)
+
+  def has_member(self ,set_id: ProcessLocalId ,subject_id: ProcessLocalId) -> bool:
+    return subject_id in self._members.get(set_id ,set())
+
+  def members(self ,set_id: ProcessLocalId) -> Set[ProcessLocalId]:
+    return set(self._members.get(set_id ,set()))
+
+  def remove_subject(self ,subject_id: ProcessLocalId):
+    set_ids = self._subject_to_sets.pop(subject_id ,set())
+    for set_id in set_ids:
+      m = self._members.get(set_id)
+      if m is not None:
+        m.discard(subject_id)
+        if not m: self._members.pop(set_id ,None)
diff --git a/developer/authored/Epimetheus/Syntax.py b/developer/authored/Epimetheus/Syntax.py
new file mode 100644 (file)
index 0000000..4fcfa7c
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
+
+
+"""
+Syntax
+
+RT syntax identity instances.
+
+We treat "syntax" as AST-level objects:
+  - kind: official-ish AST node kind name (e.g., "ast.FunctionDef")
+  - location: file + span
+  - scope: enclosing syntax identity id (optional)
+  - parts: mapping of part-name to literal or referenced syntax identity id(s)
+
+This module does NOT traverse Python programs. It only defines the data model.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Any ,Dict ,Optional ,Tuple ,Union ,List
+
+from .ProcessLocalId import ProcessLocalId
+
+
+@dataclass(frozen=True ,slots=True)
+class SourceSpan:
+  file_path: str
+  lineno: int
+  col: int
+  end_lineno: int
+  end_col: int
+
+
+SyntaxPartValue = Union[
+  None
+  ,bool
+  ,int
+  ,float
+  ,str
+  ,ProcessLocalId
+  ,List["SyntaxPartValue"]
+  ,Dict[str ,"SyntaxPartValue"]
+]
+
+
+@dataclass(frozen=True ,slots=True)
+class SyntaxInstance:
+  """
+  A single syntax node instance.
+
+  NOTE: many syntax nodes have no identifier-name. Name-like things (identifiers)
+  appear as child nodes or literals inside `parts`.
+  """
+  kind: str
+  span: SourceSpan
+  scope_id: Optional[ProcessLocalId] = None
+  parts: Dict[str ,SyntaxPartValue] = None
diff --git a/developer/authored/Epimetheus/__init__.py b/developer/authored/Epimetheus/__init__.py
new file mode 100644 (file)
index 0000000..ce046ae
--- /dev/null
@@ -0,0 +1 @@
+from .PropertyManager import PropertyManager
diff --git a/developer/authored/Identity.py b/developer/authored/Identity.py
deleted file mode 100644 (file)
index 236ef8a..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-
-"""
-Identity
-
-An abstract identity used as the subject key for property attachment.
-
-Kinds (strings) determine storage and resolution behavior.
-"""
-
-from __future__ import annotations
-
-from dataclasses import dataclass
-from typing import Any ,Optional ,Tuple
-
-from .ProcessLocalId import ProcessLocalId
-
-
-IDENTITY_KIND_PY_OBJECT = "py_object"
-IDENTITY_KIND_SYNTAX = "syntax"
-IDENTITY_KIND_PROPERTY = "property"
-IDENTITY_KIND_SET = "semantic_set"
-
-
-@dataclass(frozen=True ,slots=True)
-class Identity:
-  """
-  `id` is always a ProcessLocalId.
-
-  `kind` partitions lookup behavior.
-
-  `payload` is kind-specific metadata (kept small; do not put giant graphs here).
-  """
-  id: ProcessLocalId
-  kind: str
-  payload: Any = None
-
-  def __repr__(self) -> str:
-    # Do not reveal id token.
-    return f"<Identity kind={self.kind!r}>"
diff --git a/developer/authored/ObjectRegistry.py b/developer/authored/ObjectRegistry.py
deleted file mode 100644 (file)
index b3e4cab..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-
-"""
-ObjectRegistry
-
-Maps Python runtime objects to ProcessLocalId using weak identity.
-
-Constraints:
-  - Only weakref-able Python objects can be registered.
-  - This is intentional: RT properties attach to identity-bearing runtime instances,
-    not to value-like primitives (ints/strings/lists/dicts).
-"""
-
-from __future__ import annotations
-
-import weakref
-from typing import Any ,Callable ,Dict ,Optional
-
-from .ProcessLocalId import ProcessLocalIdGenerator ,ProcessLocalId
-
-
-class ObjectRegistry:
-  def __init__(self ,id_gen: ProcessLocalIdGenerator):
-    self._id_gen = id_gen
-    self._obj_to_id_wkd: "weakref.WeakKeyDictionary[Any ,ProcessLocalId]" = weakref.WeakKeyDictionary()
-    self._id_to_obj_ref: Dict[ProcessLocalId ,weakref.ref] = {}
-    self._finalizers: Dict[ProcessLocalId ,Callable[[ProcessLocalId],None]] = {}
-
-  def register_finalizer(self ,fn: Callable[[ProcessLocalId],None]):
-    """
-    Registers a finalizer callback invoked when any registered object is GC'd.
-    """
-    self._global_finalizer = fn
-
-  def _on_collect(self ,obj_id: ProcessLocalId):
-    self._obj_to_id_wkd.pop(self._id_to_obj_ref[obj_id]() ,None)
-    self._id_to_obj_ref.pop(obj_id ,None)
-    fn = getattr(self ,"_global_finalizer" ,None)
-    if fn is not None: fn(obj_id)
-
-  def get_id(self ,obj: Any) -> ProcessLocalId:
-    """
-    Returns the ProcessLocalId for `obj`, registering it if needed.
-
-    Raises TypeError if `obj` is not weakref-able.
-    """
-    try:
-      existing = self._obj_to_id_wkd.get(obj)
-    except TypeError:
-      raise TypeError("ObjectRegistry: object is not weakref-able; RT properties do not attach to value-like primitives.")
-    if existing is not None: return existing
-
-    obj_id = self._id_gen.next_id()
-    try:
-      self._obj_to_id_wkd[obj] = obj_id
-    except TypeError:
-      raise TypeError("ObjectRegistry: object is not weakref-able; RT properties do not attach to value-like primitives.")
-    self._id_to_obj_ref[obj_id] = weakref.ref(obj ,lambda _ref ,oid=obj_id: self._on_collect(oid))
-    return obj_id
-
-  def try_get_object(self ,obj_id: ProcessLocalId) -> Optional[Any]:
-    """
-    Best-effort: returns the live object, or None if it has been collected or never registered.
-    """
-    ref = self._id_to_obj_ref.get(obj_id)
-    if ref is None: return None
-    return ref()
diff --git a/developer/authored/ProcessLocalId.py b/developer/authored/ProcessLocalId.py
deleted file mode 100644 (file)
index b57c47c..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-
-"""
-ProcessLocalId
-
-A process-local identifier used as an internal key.
-
-Design constraint:
-  - NOT intended to be serialized or persisted.
-  - `repr()` intentionally does not reveal the numeric token, to discourage logging/persistence.
-"""
-
-from __future__ import annotations
-
-from dataclasses import dataclass
-
-
-@dataclass(frozen=True ,slots=True)
-class ProcessLocalId:
-  _n: int
-
-  def __repr__(self) -> str:
-    return "<ProcessLocalId>"
-
-  def __str__(self) -> str:
-    return "<ProcessLocalId>"
-
-  def as_int_UNSAFE(self) -> int:
-    """
-    Returns the raw integer token.
-
-    UNSAFE because:
-      - tokens are process-local
-      - do not write these into files/databases/logs as stable identifiers
-    """
-    return self._n
-
-
-class ProcessLocalIdGenerator:
-  """
-  Monotonic generator; ids are never recycled.
-  """
-  def __init__(self ,start: int = 1):
-    if start < 1: raise ValueError("start must be >= 1")
-    self._next_n: int = start
-
-  def next_id(self) -> ProcessLocalId:
-    n = self._next_n
-    self._next_n += 1
-    return ProcessLocalId(n)
diff --git a/developer/authored/Property.py b/developer/authored/Property.py
deleted file mode 100644 (file)
index 5b33226..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-
-"""
-Property
-
-A Property is itself an entity (it has an Identity id) so that:
-  - properties can have properties
-  - properties can be members of semantic sets
-"""
-
-from __future__ import annotations
-
-from dataclasses import dataclass
-from typing import Optional ,Tuple
-
-from .ProcessLocalId import ProcessLocalId
-
-
-@dataclass(frozen=True ,slots=True)
-class Property:
-  id: ProcessLocalId
-  name_path: Tuple[str ,...]
-  doc: str = ""
-
-  def __repr__(self) -> str:
-    # name_path is safe to reveal; id token is not.
-    return f"<Property {'.'.join(self.name_path)!r}>"
diff --git a/developer/authored/PropertyManager.py b/developer/authored/PropertyManager.py
deleted file mode 100644 (file)
index 36e2b9e..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-
-"""
-PropertyManager
-
-Core RT property system.
-
-Key decisions vs the earlier `property_manager.py`:
-  - do NOT key by `object_path()` strings (avoids collisions) fileciteturn3file4
-  - runtime objects are keyed by weak identity (ProcessLocalId assigned by ObjectRegistry)
-  - properties are first-class entities (Property has an id), so properties can have properties
-
-This remains process-local and in-memory.
-"""
-
-from __future__ import annotations
-
-from typing import Any ,Dict ,Iterable ,List ,Optional ,Tuple ,Union
-
-from .ProcessLocalId import ProcessLocalIdGenerator ,ProcessLocalId
-from .ObjectRegistry import ObjectRegistry
-from .PropertyStore import PropertyStore
-from .Property import Property
-from .SemanticSets import SemanticSet ,SemanticSetStore
-from .Syntax import SyntaxInstance
-
-
-NamePathLike = Union[str ,List[str] ,Tuple[str ,...]]
-
-
-class PropertyManager:
-  def __init__(self):
-    self._id_gen = ProcessLocalIdGenerator()
-    self._obj_reg = ObjectRegistry(self._id_gen)
-    self._store = PropertyStore()
-    self._sets = SemanticSetStore()
-
-    # Declare-by-name registry
-    self._name_path_to_property: Dict[Tuple[str ,...],Property] = {}
-    self._property_id_to_property: Dict[ProcessLocalId ,Property] = {}
-
-    self._name_path_to_set: Dict[Tuple[str ,...],SemanticSet] = {}
-    self._set_id_to_set: Dict[ProcessLocalId ,SemanticSet] = {}
-
-    # Optional syntax instances (if user chooses to model them)
-    self._syntax_id_to_instance: Dict[ProcessLocalId ,SyntaxInstance] = {}
-
-    # Finalization cleanup
-    self._obj_reg.register_finalizer(self._on_subject_finalized)
-
-  def _on_subject_finalized(self ,subject_id: ProcessLocalId):
-    self._store.remove_subject(subject_id)
-    self._sets.remove_subject(subject_id)
-
-  def _normalize_name_path(self ,name_path: NamePathLike) -> Tuple[str ,...]:
-    if isinstance(name_path ,tuple): return name_path
-    if isinstance(name_path ,list): return tuple(name_path)
-    if isinstance(name_path ,str): return tuple(name_path.split("."))
-    raise TypeError("name_path must be str ,list[str] ,or tuple[str ,...]")
-
-  # -------------------------
-  # Identity acquisition
-  # -------------------------
-  def id_of_py_object(self ,obj: Any) -> ProcessLocalId:
-    return self._obj_reg.get_id(obj)
-
-  def create_syntax_identity(self ,syntax: SyntaxInstance) -> ProcessLocalId:
-    sid = self._id_gen.next_id()
-    self._syntax_id_to_instance[sid] = syntax
-    return sid
-
-  def try_get_syntax(self ,syntax_id: ProcessLocalId) -> Optional[SyntaxInstance]:
-    return self._syntax_id_to_instance.get(syntax_id)
-
-  # -------------------------
-  # Property declaration
-  # -------------------------
-  def declare_property(self ,name_path: NamePathLike ,doc: str = "") -> ProcessLocalId:
-    np = self._normalize_name_path(name_path)
-    existing = self._name_path_to_property.get(np)
-    if existing is not None: return existing.id
-    pid = self._id_gen.next_id()
-    p = Property(pid ,np ,doc)
-    self._name_path_to_property[np] = p
-    self._property_id_to_property[pid] = p
-    return pid
-
-  def property_id(self ,name_path: NamePathLike) -> ProcessLocalId:
-    np = self._normalize_name_path(name_path)
-    p = self._name_path_to_property.get(np)
-    if p is None: raise KeyError(f"Property not declared: {np!r}")
-    return p.id
-
-  def try_get_property(self ,prop_id: ProcessLocalId) -> Optional[Property]:
-    return self._property_id_to_property.get(prop_id)
-
-  # -------------------------
-  # Semantic sets
-  # -------------------------
-  def declare_set(self ,name_path: NamePathLike ,doc: str = "") -> ProcessLocalId:
-    np = self._normalize_name_path(name_path)
-    existing = self._name_path_to_set.get(np)
-    if existing is not None: return existing.id
-    sid = self._id_gen.next_id()
-    s = SemanticSet(sid ,np ,doc)
-    self._name_path_to_set[np] = s
-    self._set_id_to_set[sid] = s
-    return sid
-
-  def add_to_set(self ,subject: Any ,set_id: ProcessLocalId):
-    subject_id = self._coerce_subject_id(subject)
-    self._sets.add_member(set_id ,subject_id)
-
-  def is_in_set(self ,subject: Any ,set_id: ProcessLocalId) -> bool:
-    subject_id = self._coerce_subject_id(subject)
-    return self._sets.has_member(set_id ,subject_id)
-
-  def members(self ,set_id: ProcessLocalId) -> List[ProcessLocalId]:
-    return list(self._sets.members(set_id))
-
-  # -------------------------
-  # Set/get properties
-  # -------------------------
-  def set(self ,subject: Any ,prop: Union[ProcessLocalId ,NamePathLike] ,value: Any):
-    subject_id = self._coerce_subject_id(subject)
-    prop_id = self._coerce_property_id(prop)
-    self._store.set(subject_id ,prop_id ,value)
-
-  def get(self ,subject: Any ,prop: Union[ProcessLocalId ,NamePathLike] ,default: Any = None) -> Any:
-    subject_id = self._coerce_subject_id(subject)
-    prop_id = self._coerce_property_id(prop)
-    return self._store.get(subject_id ,prop_id ,default)
-
-  def has(self ,subject: Any ,prop: Union[ProcessLocalId ,NamePathLike]) -> bool:
-    subject_id = self._coerce_subject_id(subject)
-    prop_id = self._coerce_property_id(prop)
-    return self._store.has(subject_id ,prop_id)
-
-  def subjects_with(self ,prop: Union[ProcessLocalId ,NamePathLike]) -> List[ProcessLocalId]:
-    prop_id = self._coerce_property_id(prop)
-    return list(self._store.subjects_with(prop_id))
-
-  # -------------------------
-  # Coercions
-  # -------------------------
-  def _coerce_subject_id(self ,subject: Any) -> ProcessLocalId:
-    if isinstance(subject ,ProcessLocalId): return subject
-    # For Python runtime objects, we require weakref-able instances.
-    return self._obj_reg.get_id(subject)
-
-  def _coerce_property_id(self ,prop: Union[ProcessLocalId ,NamePathLike]) -> ProcessLocalId:
-    if isinstance(prop ,ProcessLocalId): return prop
-    return self.property_id(prop)
diff --git a/developer/authored/PropertyStore.py b/developer/authored/PropertyStore.py
deleted file mode 100644 (file)
index bc8f3f3..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-
-"""
-PropertyStore
-
-Stores property values and maintains reverse lookups.
-
-This is intentionally process-local and in-memory.
-"""
-
-from __future__ import annotations
-
-from typing import Any ,Dict ,Optional ,Set ,Tuple
-
-from .ProcessLocalId import ProcessLocalId
-
-
-class PropertyStore:
-  def __init__(self):
-    # (subject_id ,property_id) -> value
-    self._values: Dict[Tuple[ProcessLocalId ,ProcessLocalId] ,Any] = {}
-
-    # subject_id -> set(property_id)
-    self._subject_to_props: Dict[ProcessLocalId ,Set[ProcessLocalId]] = {}
-
-    # property_id -> set(subject_id)
-    self._prop_to_subjects: Dict[ProcessLocalId ,Set[ProcessLocalId]] = {}
-
-  def set(self ,subject_id: ProcessLocalId ,prop_id: ProcessLocalId ,value: Any):
-    key = (subject_id ,prop_id)
-    self._values[key] = value
-    self._subject_to_props.setdefault(subject_id ,set()).add(prop_id)
-    self._prop_to_subjects.setdefault(prop_id ,set()).add(subject_id)
-
-  def get(self ,subject_id: ProcessLocalId ,prop_id: ProcessLocalId ,default: Any = None) -> Any:
-    return self._values.get((subject_id ,prop_id) ,default)
-
-  def has(self ,subject_id: ProcessLocalId ,prop_id: ProcessLocalId) -> bool:
-    return (subject_id ,prop_id) in self._values
-
-  def subjects_with(self ,prop_id: ProcessLocalId) -> Set[ProcessLocalId]:
-    return set(self._prop_to_subjects.get(prop_id ,set()))
-
-  def props_of(self ,subject_id: ProcessLocalId) -> Set[ProcessLocalId]:
-    return set(self._subject_to_props.get(subject_id ,set()))
-
-  def remove_subject(self ,subject_id: ProcessLocalId):
-    """
-    Remove all stored properties for a subject (used on finalization).
-    """
-    prop_ids = self._subject_to_props.pop(subject_id ,set())
-    for prop_id in prop_ids:
-      self._values.pop((subject_id ,prop_id) ,None)
-      s = self._prop_to_subjects.get(prop_id)
-      if s is not None:
-        s.discard(subject_id)
-        if not s: self._prop_to_subjects.pop(prop_id ,None)
diff --git a/developer/authored/SemanticSets.py b/developer/authored/SemanticSets.py
deleted file mode 100644 (file)
index f0fdf48..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-
-"""
-SemanticSets
-
-Membership sets over identities. Used for semantic typing.
-
-Design:
-  - set_id identifies the set
-  - members are subject ids
-  - reverse index for cleanup
-"""
-
-from __future__ import annotations
-
-from dataclasses import dataclass
-from typing import Dict ,Optional ,Set
-
-from .ProcessLocalId import ProcessLocalId
-
-
-@dataclass(frozen=True ,slots=True)
-class SemanticSet:
-  id: ProcessLocalId
-  name_path: tuple[str ,...]
-  doc: str = ""
-
-  def __repr__(self) -> str:
-    return f"<SemanticSet {'.'.join(self.name_path)!r}>"
-
-
-class SemanticSetStore:
-  def __init__(self):
-    self._members: Dict[ProcessLocalId ,Set[ProcessLocalId]] = {}
-    self._subject_to_sets: Dict[ProcessLocalId ,Set[ProcessLocalId]] = {}
-
-  def add_member(self ,set_id: ProcessLocalId ,subject_id: ProcessLocalId):
-    self._members.setdefault(set_id ,set()).add(subject_id)
-    self._subject_to_sets.setdefault(subject_id ,set()).add(set_id)
-
-  def has_member(self ,set_id: ProcessLocalId ,subject_id: ProcessLocalId) -> bool:
-    return subject_id in self._members.get(set_id ,set())
-
-  def members(self ,set_id: ProcessLocalId) -> Set[ProcessLocalId]:
-    return set(self._members.get(set_id ,set()))
-
-  def remove_subject(self ,subject_id: ProcessLocalId):
-    set_ids = self._subject_to_sets.pop(subject_id ,set())
-    for set_id in set_ids:
-      m = self._members.get(set_id)
-      if m is not None:
-        m.discard(subject_id)
-        if not m: self._members.pop(set_id ,None)
diff --git a/developer/authored/Syntax.py b/developer/authored/Syntax.py
deleted file mode 100644 (file)
index 4fcfa7c..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-
-"""
-Syntax
-
-RT syntax identity instances.
-
-We treat "syntax" as AST-level objects:
-  - kind: official-ish AST node kind name (e.g., "ast.FunctionDef")
-  - location: file + span
-  - scope: enclosing syntax identity id (optional)
-  - parts: mapping of part-name to literal or referenced syntax identity id(s)
-
-This module does NOT traverse Python programs. It only defines the data model.
-"""
-
-from __future__ import annotations
-
-from dataclasses import dataclass
-from typing import Any ,Dict ,Optional ,Tuple ,Union ,List
-
-from .ProcessLocalId import ProcessLocalId
-
-
-@dataclass(frozen=True ,slots=True)
-class SourceSpan:
-  file_path: str
-  lineno: int
-  col: int
-  end_lineno: int
-  end_col: int
-
-
-SyntaxPartValue = Union[
-  None
-  ,bool
-  ,int
-  ,float
-  ,str
-  ,ProcessLocalId
-  ,List["SyntaxPartValue"]
-  ,Dict[str ,"SyntaxPartValue"]
-]
-
-
-@dataclass(frozen=True ,slots=True)
-class SyntaxInstance:
-  """
-  A single syntax node instance.
-
-  NOTE: many syntax nodes have no identifier-name. Name-like things (identifiers)
-  appear as child nodes or literals inside `parts`.
-  """
-  kind: str
-  span: SourceSpan
-  scope_id: Optional[ProcessLocalId] = None
-  parts: Dict[str ,SyntaxPartValue] = None
diff --git a/developer/authored/__init__.py b/developer/authored/__init__.py
deleted file mode 100644 (file)
index 2b53823..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
-
-"""
-rt_property_manager
-
-Process-local property attachment with:
-  - weak identity for Python runtime instances
-  - explicit identities for syntax instances and properties
-
-Notes:
-  - ProcessLocalId values are not meant to be serialized or persisted.
-"""
-
-from .PropertyManager import PropertyManager
diff --git a/developer/experiment/test_Epimetheus.py b/developer/experiment/test_Epimetheus.py
new file mode 100755 (executable)
index 0000000..7024e9d
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*-
+
+import sys
+# CHANGED: Import directly from the package 'Epimetheus'
+from Epimetheus import PropertyManager
+
+def test_everything() -> int:
+  """
+  The Work Function.
+  Returns 0 on success ,non-zero on failure.
+  """
+  try:
+    pm = PropertyManager()
+    
+    # 1. Declare Properties (The Schema)
+    prop_color = pm.declare_property("color")
+    prop_meaning = pm.declare_property("meaning")
+
+    # 2. Annotate an Entity (Instance)
+    class Robot: pass
+    r1 = Robot()
+    
+    pm.set(r1 ,"color" ,"silver")
+    print(f"Robot Color: {pm.get(r1 ,'color')}") 
+    # Expect: silver
+
+    # 3. Annotate an Entity (Class)
+    pm.set(Robot ,"meaning" ,"A machine")
+    print(f"Robot Class Meaning: {pm.get(Robot ,'meaning')}")
+    # Expect: A machine
+
+    # 4. Annotate a Value (Primitive)
+    pm.set(42 ,"meaning" ,"The Answer")
+    print(f"42 Meaning: {pm.get(42 ,'meaning')}")
+    # Expect: The Answer
+
+    # Annotating a Tuple
+    my_key = (1 ,2)
+    pm.set(my_key ,"color" ,"invisible")
+    print(f"Tuple Color: {pm.get((1 ,2) ,'color')}")
+    # Expect: invisible
+
+    print("\n--- Success ---")
+    return 0
+
+  except Exception as e:
+    print(f"Test Failed: {e}")
+    return 1
+
+
+def CLI(args_seq) -> int:
+  """
+  The CLI Entry Point.
+  """
+  return test_everything()
+
+
+if(__name__ == "__main__"):
+  sys.exit(CLI(sys.argv))
index 0b993ad..610170c 100644 (file)
@@ -1,3 +1,6 @@
 #!/usr/bin/env bash
 script_afp=$(realpath "${BASH_SOURCE[0]}")
 
+export PYTHONPATH="$REPO_HOME/developer/authored:$PYTHONPATH"
+
+
diff --git a/nohup.out b/nohup.out
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/temp.sh b/temp.sh
deleted file mode 100644 (file)
index c166d29..0000000
--- a/temp.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-2025-11-25 09:33:05 Z [subu:developer] Thomas_developer@StanleyPark
-§/home/Thomas/subu_data/developer/subu_data/Harmony§
-> find . -type l -exec ls -l {} \;
-lrwxrwxrwx 1 Thomas_developer Thomas_developer 35 Nov 25 09:08 ./tool/sync -> ../tool_shared/authored/sync/CLI.py
-lrwxrwxrwx 1 Thomas_developer Thomas_developer 3 May 19  2025 ./shared/third_party/Python/lib64 -> lib
-lrwxrwxrwx 1 Thomas_developer Thomas_developer 16 May 19  2025 ./shared/third_party/Python/bin/python3 -> /usr/bin/python3
-lrwxrwxrwx 1 Thomas_developer Thomas_developer 7 May 19  2025 ./shared/third_party/Python/bin/python -> python3
-lrwxrwxrwx 1 Thomas_developer Thomas_developer 7 May 19  2025 ./shared/third_party/Python/bin/python3.11 -> python3
-lrwxrwxrwx 1 Thomas_developer Thomas_developer 15 Nov 24 15:19 ./shared/authored/git-empty-dir/source_sync -> ../source_sync/
-lrwxrwxrwx 1 Thomas_developer Thomas_developer 25 Nov 24 15:21 ./shared/authored/git-empty-dir/Harmony.py -> ../source_sync/Harmony.py
-lrwxrwxrwx 1 Thomas_developer Thomas_developer 37 Nov 24 15:22 ./shared/authored/git-empty-dir/load_command_module.py -> ../source_sync/load_command_module.py