:PROPERTIES:
:ID:       gardener-skill
:CREATED:  [2026-04-13 Mon 18:50]
:END:
#+TITLE: SKILL: Autonomous Gardener (Memex Maintenance)
#+STARTUP: content
#+FILETAGS: :gardener:maintenance:memex:autonomy:

* Overview
The *Autonomous Gardener* is the metabolic immune system of the Memex. It autonomously audits the knowledge graph for structural decay—broken links, orphaned nodes, and missing metadata—ensuring that the system remains coherent and navigatable over long horizons.

* Phase A: Demand (PRD)
:PROPERTIES:
:STATUS: SIGNED
:END:

** 1. Purpose
Maintain the structural integrity and "Vibe" of the Memex through autonomous auditing and self-repair proposals.

** 2. Success Criteria
- [ ] *Link Audit:* Detect `id:` links that point to non-existent objects.
- [ ] *Orphan Detection:* Identify headlines that have zero inbound or outbound connections.
- [ ] *Reporting:* Log structural issues or propose "Flight Plans" for manual repair.

* Phase B: Blueprint (PROTOCOL)
:PROPERTIES:
:STATUS: SIGNED
:END:

** 1. Architectural Intent
The Gardener runs on a low-priority heartbeat. It performs a "Deep Audit" of the entire `*memory*` graph. Unlike the Scribe, which creates new data, the Gardener focuses on the *relationships* between existing data.

** 2. Semantic Interfaces
- Trigger: `(:sensor :heartbeat)`
- Action (Repair): `(:type :REQUEST :target :emacs :action :update-node :id "..." :attributes (...))`

* Phase D: Build (Implementation)

** Package Context
#+begin_src lisp
(in-package :opencortex)
#+end_src

** State: Maintenance Cycle
We track the last audit time to ensure the Gardener doesn't over-consume resources.

#+begin_src lisp
(defvar *gardener-last-audit* 0
  "The universal-time of the last full Memex audit.")
#+end_src

** Audit: Broken Links
Scans the content of all objects for `id:` links and verifies the targets exist.

#+begin_src lisp
(defun gardener-find-broken-links ()
  "Returns a list of broken ID links found in the Memex."
  (let ((broken nil))
    (maphash (lambda (id obj)
               (let ((content (org-object-content obj)))
                 (when content
                   (cl-ppcre:do-register-groups (target-id) ("id:([A-Za-z0-9-]+)" content)
                     (unless (lookup-object target-id)
                       (push (list :source id :broken-target target-id) broken))))))
             *memory*)
    broken))
#+end_src

** Audit: Orphaned Nodes
Identifies nodes that are not linked to and do not link to anything else.

#+begin_src lisp
(defun gardener-find-orphans ()
  "Returns a list of IDs for headlines that are structurally isolated."
  (let ((inbound (make-hash-table :test 'equal))
        (outbound (make-hash-table :test 'equal))
        (orphans nil))
    ;; 1. Map all connections
    (maphash (lambda (id obj)
               (let ((content (org-object-content obj)))
                 (when content
                   (cl-ppcre:do-register-groups (target-id) ("id:([A-Za-z0-9-]+)" content)
                     (setf (gethash id outbound) t)
                     (setf (gethash target-id inbound) t)))))
             *memory*)
    ;; 2. Identify nodes with zero connections
    (maphash (lambda (id obj)
               (declare (ignore obj))
               (unless (or (gethash id inbound) (gethash id outbound))
                 (push id orphans)))
             *memory*)
    orphans))
#+end_src

** Skill Logic: The Audit Pass
The Gardener's deterministic gate performs the actual analysis and logs the results. In future versions, it will generate probabilistic repair proposals.

#+begin_src lisp
(defun gardener-deterministic-gate (action context)
  "Main gate for the Gardener skill. Audits graph integrity."
  (declare (ignore action context))
  (let ((broken (gardener-find-broken-links))
        (orphans (gardener-find-orphans)))
    
    (when (or broken orphans)
      (harness-log "GARDENER: Audit found ~a broken links and ~a orphans." 
                   (length broken) (length orphans))
      
      (dolist (link broken)
        (harness-log "  [BROKEN LINK] Node ~a -> ~a" (getf link :source) (getf link :broken-target)))
      
      (dolist (orphan orphans)
        (harness-log "  [ORPHAN] Node ~a is isolated." orphan)))
    
    (setf *gardener-last-audit* (get-universal-time))
    ;; Return a log to stop the loop
    (list :type :LOG :payload (list :text "Gardener audit complete."))))
#+end_src

** Skill Registration
#+begin_src lisp
(defskill :skill-gardener
  :priority 40
  :trigger (lambda (ctx)
             (let* ((payload (getf ctx :payload))
                    (sensor (getf payload :sensor)))
               (and (eq sensor :heartbeat)
                    ;; Only audit once per day
                    (> (- (get-universal-time) *gardener-last-audit*) 86400))))
  :probabilistic nil
  :deterministic #'gardener-deterministic-gate)
#+end_src
