Files
passepartout/org/core-context.org
Amr Gharbeia 5a0d1b1c38 remediation: backfill v0.1.0/v0.2.0 gaps (P0+P1)
- vault: add vault-get-secret/vault-set-secret wrappers
- programming-org: implement org-modify (text search-replace) and org-ast-render (AST to Org text)
- programming-literate: implement literate-block-balance-check (paren validation) and literate-tangle-sync-check (org→lisp diff)
- system-self-improve: replace stubs with surgical text editing and error diagnosis; remove dead first defskill
- system-event-orchestrator: implement orchestrator-bootstrap (scan Org files for HOOK/CRON)
- system-archivist: implement Scribe distillation (daily logs→atomic notes) and Gardener link/orphan repair
- system-memory: implement memory-inspect with type/todo/orphan statistics
- core-skills, core-context: fix path relic (skills/ → lisp/, org/)
- docs: add Token Economics section to DESIGN_DECISIONS, remediation roadmap entries
2026-05-03 10:43:14 -04:00

13 KiB

Context API (context.lisp)

Overview: Architectural Intent

The Context API implements the Foveal-Peripheral awareness model. When the agent thinks, it doesn't dump everything it knows into the LLM's context window — that would saturate the token budget immediately. Instead, it builds a skeletal outline of the entire Memex and only shows full detail for the current focus.

This mirrors human attention: you are aware of your entire apartment (peripheral vision), but you only see the book in front of you in detail (foveal vision).

The Foveal-Peripheral Model

Three factors determine how much detail an object gets:

  1. Depth — objects within 2 levels of the root get full outline (title + ID). Deeper objects are summarized or omitted.
  2. Foveal focus — the object the user is currently interacting with gets full content rendered.
  3. Semantic similarity — objects whose vector embedding is similar to the current foveal focus get promoted from peripheral to foveal detail.

Why Not Just Dump Everything?

A naive implementation that serializes every org-object to text would produce hundreds of thousands of tokens for a typical knowledge base. The LLM would spend its attention budget on noise, not signal. The Foveal-Peripheral model preserves the signal (the current task and related information) while reducing noise (everything else).

The semantic threshold is configurable via CONTEXT_SEMANTIC_THRESHOLD env var (default 0.75). Lower values include more peripherally related content; higher values restrict to tightly related content.

Implementation

Package Context

(in-package :passepartout)

Memory Query (context-query)

Filters the Memory store by tag, TODO state, or object type. This is the primary retrieval function used by skills to find relevant information.

(defun context-query (&key tag todo-state type)
  "Filters the Memory based on tags, todo states, or types."
  (let ((results nil))
    (maphash (lambda (id obj)
               (declare (ignore id))
               (let* ((attrs (org-object-attributes obj)) (state (getf attrs :TODO-STATE)) (match t))
                 (when (and type (not (eq (org-object-type obj) type))) (setf match nil))
                 (when tag (unless (search tag (format nil "~a" (getf attrs :TAGS)) :test #'string-equal) (setf match nil)))
                 (when (and todo-state (not (equal state todo-state))) (setf match nil))
                 (when match (push obj results))))
             *memory*)
    results))

Active Projects (context-active-projects)

Returns headlines tagged as project that are not yet DONE. Used by the global awareness function to build the task overview.

(defun context-active-projects ()
  "Returns headlines tagged as 'project' that are not yet marked DONE."
  (remove-if (lambda (obj) (equal (getf (org-object-attributes obj) :TODO-STATE) "DONE"))
             (context-query :tag "project" :type :HEADLINE)))

Completed Tasks (context-recent-tasks)

Retrieves recently finished tasks from the store. Used by the Scribe and Gardener for journal summarization.

(defun context-recent-tasks () 
  "Retrieves recently finished tasks from the store."
  (context-query :todo-state "DONE" :type :HEADLINE))

Capability Discovery (context-skill-list)

Provides a sorted overview of currently loaded system capabilities. Each entry includes the skill name, priority, and dependencies.

(defun context-skill-list ()
  "Provides a sorted overview of currently loaded system capabilities."
  (let ((results nil))
    (maphash (lambda (name skill)
               (declare (ignore name))
               (push (list :name (skill-name skill) :priority (skill-priority skill) :dependencies (skill-dependencies skill)) results))
             *skills-registry*)
    (sort results #'> :key (lambda (x) (getf x :priority)))))

Skill Source Inspection (context-skill-source)

Reads the raw literate source of a specific skill for inspection. Used when the agent needs to understand or modify its own code.

(defun context-skill-source (skill-name)
  "Reads the raw literate source of a specific skill for inspection."
  (let* ((filename (format nil "~a.org" skill-name))
         (data-dir (uiop:ensure-directory-pathname (or (uiop:getenv "PASSEPARTOUT_DATA_DIR") (namestring (merge-pathnames ".local/share/passepartout/" (user-homedir-pathname))))))
         (org-dir (merge-pathnames "org/" data-dir))
         (full-path (merge-pathnames filename org-dir)))
    (if (uiop:file-exists-p full-path) (uiop:read-file-string full-path) nil)))

Harness Logs (context-logs)

Retrieves the most recent lines from the harness's internal log buffer. The log limit is configurable via CONTEXT_LOG_LIMIT env var (default 20).

(defun context-logs (&optional limit)
  "Retrieves the most recent lines from the harness's internal log."
  (let ((log-limit (or limit (ignore-errors (parse-integer (uiop:getenv "CONTEXT_LOG_LIMIT"))) 20)))
    (bt:with-lock-held (*logs-lock*)
      (let ((count (min log-limit (length *system-logs*)))) 
        (subseq *system-logs* 0 count)))))

AST to Org Rendering (context-object-render)

Recursively renders an org-object and its children to an Org-mode string, applying the Foveal-Peripheral model:

  • Objects within depth 2 are always included (outline)
  • The foveal object (the one the user is looking at) is always included with full content
  • Objects with semantic similarity above the threshold are included with full content
  • All other objects are omitted silently

This function is the heart of the context assembly. Its performance directly affects the agent's response time.

(defun context-object-render (obj &key (depth 1) (foveal-id nil) semantic-threshold (foveal-vector nil))
  "Recursively renders an org-object and its children to an Org string using a Foveal-Peripheral Hybrid model."
  (let* ((id (org-object-id obj))
         (is-foveal (equal id foveal-id))
         (title (or (getf (org-object-attributes obj) :TITLE) "Untitled"))
         (content (org-object-content obj))
         (children (org-object-children obj))
         (stars (make-string depth :initial-element #\*))
         (obj-vector (org-object-vector obj))
         (threshold (or semantic-threshold (ignore-errors (read-from-string (uiop:getenv "CONTEXT_SEMANTIC_THRESHOLD"))) 0.75))
         (similarity (if (and foveal-vector obj-vector (not is-foveal))
                         (cosine-similarity foveal-vector obj-vector)
                         0.0))
         (is-semantically-relevant (>= similarity threshold))
         (should-render (or (<= depth 2) is-foveal is-semantically-relevant))
         (output ""))
    
    (when should-render
      (setf output (format nil "~a ~a~%:PROPERTIES:~%:ID: ~a~%" stars title id))
      (when is-semantically-relevant
        (setf output (concatenate 'string output (format nil ":SEMANTIC_SCORE: ~,2f~%" similarity))))
      (setf output (concatenate 'string output (format nil ":END:~%")))
      
      (when (and content (or is-foveal is-semantically-relevant))
        (setf output (concatenate 'string output content (string #\Newline))))
      
      (dolist (child-id children)
        (let ((child-obj (lookup-object child-id)))
          (when child-obj
            (let ((next-foveal (if is-foveal child-id foveal-id)))
              (setf output (concatenate 'string output 
                                        (context-object-render child-obj 
                                                               :depth (1+ depth) 
                                                               :foveal-id next-foveal
                                                               :semantic-threshold threshold
                                                               :foveal-vector foveal-vector))))))))
    output))

Path Resolution (context-path-resolve)

Expands environment variables in a path string and strips quotes. Used to resolve configurable paths from .env.

(defun context-path-resolve (path-string)
  "Expands environment variables and strips literal quotes from a path string."
  (let ((path (if (stringp path-string) 
                  (string-trim '(#\" #\' #\Space) path-string)
                  path-string)))
    (if (and (stringp path) (search "$" path))
        (let ((result path))
          (ppcre:do-register-groups (var-name) ("\\$([A-Za-z0-9_]+)" path)
            (let ((var-val (uiop:getenv var-name)))
              (when var-val
                (setf result (ppcre:regex-replace (format nil "\\$~a" var-name) result var-val)))))
          result)
        path)))

Privacy Filter for Context Assembly

Checks if an org-object has tags matching the Bouncer's bouncer-privacy-tags. Objects with matching tags are excluded from the LLM's context window. This prevents private content tagged with @personal (or any user-configured privacy tag) from being included in prompts sent to external LLM providers.

(defun context-privacy-filtered-p (obj)
  "Returns T if an org-object's :TAGS attribute matches bouncer-privacy-tags."
  (let* ((attrs (org-object-attributes obj))
         (tags (getf attrs :TAGS))
         (privacy-tags (and (find-package :passepartout.security-dispatcher)
                            (symbol-value
                             (find-symbol "BOUNCER-PRIVACY-TAGS"
                                          :passepartout.security-dispatcher)))))
    (when (and tags privacy-tags)
      (let ((tag-list (if (listp tags) tags (list tags))))
        (some (lambda (tag)
                (some (lambda (private)
                        (string-equal (string-trim '(#\:) tag)
                                      (string-trim '(#\:) private)))
                      privacy-tags))
              tag-list)))))

Global Awareness (context-awareness-assemble)

Produces the high-level skeletal outline of the current Memory that is included in every LLM call. This is the "peripheral vision" of the agent — it knows what projects exist, their titles and IDs, but not their full content.

Privacy-filtered projects (those with tags matching bouncer-privacy-tags) are excluded from the output.

(defun context-awareness-assemble (&optional signal)
  "Produces a high-level skeletal outline of the current Memory for the LLM.
Privacy-filtered objects (matching bouncer-privacy-tags) are excluded."
  (let* ((foveal-id (or (getf signal :foveal-focus) 
                        (ignore-errors (getf (getf signal :payload) :target-id))))
         (all-projects (context-active-projects))
         (projects (remove-if #'context-privacy-filtered-p all-projects))
         (output (format nil "GLOBAL MEMEX AWARENESS (Peripheral Vision):~%")))
    (if projects
        (dolist (project projects)
          (setf output (concatenate 'string output
                                    (context-object-render project :foveal-id foveal-id))))
        (setf output (concatenate 'string output "No active projects found.~%")))
    output))

Test Suite

Verifies that the Foveal-Peripheral rendering correctly distinguishes between foveal (detailed) and peripheral (outline) content, and that the awareness budget includes all active projects.

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload :fiveam :silent t))

(defpackage :passepartout-peripheral-vision-tests
  (:use :cl :fiveam :passepartout)
  (:export #:vision-suite))
(in-package :passepartout-peripheral-vision-tests)

(def-suite vision-suite :description "Verification of Foveal-Peripheral context model.")
(in-suite vision-suite)

(test test-foveal-rendering
  (clrhash passepartout::*memory*)
  (let* ((ast '(:type :HEADLINE :properties (:ID "proj-root" :TITLE "Project" :TAGS ("project"))
                :contents ((:type :HEADLINE :properties (:ID "node-foveal" :TITLE "Foveal Node")
                             :raw-content "FOVEAL CONTENT" :contents nil)
                            (:type :HEADLINE :properties (:ID "node-peripheral" :TITLE "Peripheral Node")
                             :raw-content "PERIPHERAL CONTENT" :contents nil)))))
    (ingest-ast ast)
    (let ((output (context-awareness-assemble (list :foveal-focus "node-foveal"))))
      (is (search "FOVEAL CONTENT" output))
      (is (search "* Peripheral Node" output))
      (is (not (search "PERIPHERAL CONTENT" output))))))

(test test-awareness-budget
  (clrhash passepartout::*memory*)
  (ingest-ast '(:type :HEADLINE :properties (:ID "p1" :TITLE "Project 1" :TAGS ("project")) :contents nil))
  (ingest-ast '(:type :HEADLINE :properties (:ID "p2" :TITLE "Project 2" :TAGS ("project")) :contents nil))
  (let ((output (context-awareness-assemble)))
    (is (search "Project 1" output))
    (is (search "Project 2" output))))