From 7fca4189b9de2e7046a83bb952ee08b29b986d88 Mon Sep 17 00:00:00 2001 From: Amr Gharbeia Date: Fri, 8 May 2026 19:54:07 -0400 Subject: [PATCH] =?UTF-8?q?v0.7.2:=20release=20=E2=80=94=20TDD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All 14 v0.7.2 items wired, tested, and documented. Release checklist: - ROADMAP: all 14 items marked DONE - README: version badge v0.7.1 → v0.7.2 - CHANGELOG: v0.7.2 entry with feature summaries - core-transport: make-hello-message 0.7.1 → 0.7.2 - .env.example: TAG_CATEGORIES, SELF_BUILD_MODE - /help list: all 16 commands documented Phase 1 (wire deferred): - call-with-tool-timeout in action-tool-execute - dispatcher-privacy-severity in dispatcher-check - Ctrl+G gate-trace toggle, Ctrl+F search placeholder Phase 2 (finish features): - /audit verify, /resume , /help Core: 88/88 TUI Main: 85/86 (1 pre-existing flake) --- .env.example | 10 +++++++ CHANGELOG.org | 52 +++++++++++++++++++++++++++++++++++ README.org | 6 ++-- docs/ROADMAP.org | 28 +++++++++---------- lisp/channel-tui-main.lisp | 28 ++++++++++++++++++- lisp/core-transport.lisp | 2 +- org/channel-tui-main.org | 56 +++++++++++++++++++++++++++++--------- org/core-transport.org | 2 +- 8 files changed, 151 insertions(+), 33 deletions(-) diff --git a/.env.example b/.env.example index 7d3175f..9d80c65 100644 --- a/.env.example +++ b/.env.example @@ -110,3 +110,13 @@ CONTEXT_MAX_TOKENS=16384 # Soft daily cost cap in USD. Warning injected into system prompt when # approaching budget. COST_BUDGET_DAILY=1.00 + +# v0.7.2: Privacy tag severity tiers. Format: @tag:block,@tag:warn,@tag:log +# :block = filter content, :warn = log+allow, :log = silently record +# Default: empty (no tags configured) +#TAG_CATEGORIES=@personal:block,@financial:block,@draft:warn + +# v0.7.2: Self-build core file protection mode +# When true, writes to core-*.org and core-*.lisp require HITL approval. +# Default: false (unrestricted — use during development) +SELF_BUILD_MODE=false diff --git a/CHANGELOG.org b/CHANGELOG.org index 29c2049..d38b1e0 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -5,6 +5,58 @@ All notable changes to Passepartout, extracted from [[file:docs/ROADMAP.org][ROADMAP.org]] DONE items with LOGBOOK timestamps. +:LOGBOOK: +- State "RELEASED" from "DONE" [2026-05-08 Fri 23:00] +:END: + +* v0.7.2 — Gate Trace, HITL, Identity, Search + Maturation +** Gate Trace Visualization +Gate-trace-lines wired into view-chat. Renders colored entries +below agent messages (green passed, red blocked, yellow approval). +Ctrl+G toggles collapse per message. Default: visible. +** HITL Inline Panels +/approve and /deny parsed as structured events in on-key. +on-daemon-msg detects :approval-required and renders styled system +messages with :panel flag and :hitl theme color. +** Identity File +load-identity-file reads ~/memex/IDENTITY.org on startup. +agent-identity injects into think() IDENTITY section. +/identity opens in $EDITOR, auto-reloads. +** Safe-Tool Allowlist +read-only-p slot on cognitive-tool struct. tool-read-only-p +registry lookup. Read-only tools auto-pass dispatcher-check. +7 tools marked read-only (search, find, read, list, eval, tests, org-find). +** Message Search +/search performs case-insensitive substring search across +message history. Shows match count and context previews. +** Context Visibility +/context shows message count, focus, token estimate, and last 5 +message summaries. /context why shows memory object info. +** Session Rewind +/rewind restores memory to snapshot n-1 via rollback-memory. +/sessions lists last 10 snapshots with timestamps and object counts. +Auto-snapshot at turn boundaries in think(). +** Undo/Redo +Operation-level memory undo/redo. undo-snapshot before destructive +tool execution. /undo and /redo TUI commands restore memory state. +** Tool Hardening +Per-tool timeouts (shell=300s, search-files=30s, eval-form=10s). +call-with-tool-timeout wraps tool execution with sb-ext:with-timeout. +verify-write re-reads after write-file and compares content. +** Tag Stack +TAG_CATEGORIES env var parses severity tiers (@tag:block, :warn, :log). +dispatcher-privacy-severity wired into dispatcher-check vector 5. +/tags TUI command lists configured categories. +** Merkle Audit +audit-node exposes memory object lineage (type, hash, scope, version). +/audit TUI command. /audit verify counts objects and snapshots. +** Self-Help +/why shows most recent gate trace from message history. +** Agent Identity Injection +assemble-config-section builds live CONFIG from *provider-cascade*, +tokenizer-context-limit, gate count, and *hitl-pending*. +Injected into all three system-prompt assembly paths via fboundp guard. + * v0.7.1 — Streaming + Markdown Rendering :LOGBOOK: - Released [2026-05-08 Fri] diff --git a/README.org b/README.org index 9a63a09..1082f3f 100644 --- a/README.org +++ b/README.org @@ -3,7 +3,7 @@ #+FILETAGS: :passepartout:ai:assistant: #+HTML:
-#+HTML: +#+HTML: #+HTML: #+HTML: #+HTML: @@ -121,8 +121,8 @@ Features marked =Stable= ship in the current release. Features marked =Planned= | TUI Unicode width | Stable | v0.7.0 | char-width: ASCII/CJK/emoji/combining marks, pure Lisp | | TUI scroll notification | Stable | v0.7.0 | :scroll-notify flag, new-message alert when scrolled up | | TUI deeper autocomplete | Stable | v0.7.0 | @ file paths, /theme subcommand, /focus directories | -| Streaming responses | Stable | v0.7.1 | SSE streaming, live output in TUI, interrupt-and-redirect | -| TUI markdown rendering | Stable | v0.7.1 | Bold/italic/inline code styled via Croatoan attributes | +| Streaming responses | Stable | v0.7.2 | SSE streaming, live output in TUI, interrupt-and-redirect | +| TUI markdown rendering | Stable | v0.7.2 | Bold/italic/inline code styled via Croatoan attributes | | Priority-queue signal processing | Planned | v0.7.2 | Preempts background for user interactions | | Markdown rendering (full) | Planned | v0.7.2 | Code blocks, tables, blockquotes, hyperlinks | | MCP-native tool ecosystem | Planned | v0.7.0 | 50+ tools from the MCP ecosystem | diff --git a/docs/ROADMAP.org b/docs/ROADMAP.org index a44e7fb..5e67c86 100644 --- a/docs/ROADMAP.org +++ b/docs/ROADMAP.org @@ -1246,7 +1246,7 @@ Implementation: a ~render-styled~ wrapper that takes a list of ~(text . plist-of Gate trace data is already stored per-message (~:gate-trace~ field in ~add-msg~) but never rendered. HITL approval requires typing raw text that happens to match ~/approve~ — no TUI-internal command handling. Context visibility and session control close the audit trail: the user can inspect what the LLM sees and undo what went wrong. These are Passepartout's architectural differentiators that remain invisible to users. -*** TODO Gate trace visualization +*** DONE Gate trace visualization :PROPERTIES: :ID: id-v062-gate-trace :CREATED: [2026-05-08 Fri] @@ -1261,7 +1261,7 @@ Render gate trace lines below each agent message in dim: Gate trace data format (already in messages): ~(:gate-trace ((:gate "dispatcher-path" :result :passed) (:gate "dispatcher-shell" :result :blocked :reason "rm -rf pattern") (:gate "dispatcher-network" :result :approval)))~. ~50 lines. -*** TODO HITL inline command handling +*** DONE HITL inline command handling :PROPERTIES: :ID: id-v062-hitl-inline :CREATED: [2026-05-08 Fri] @@ -1276,7 +1276,7 @@ Gate trace data format (already in messages): ~(:gate-trace ((:gate "dispatcher- - Clarifying-question escalation: when the same action has been blocked twice and retried (2 rejections in the 3-retry loop), the third attempt injects a /clarify prompt with targeted discriminating options instead of a generic rejection. Inspired by constrained conformal evaluation (Barnaby et al., arXiv:2508.15750v1): "This command touches ~/memex/ and /etc/. Is the /etc/ path intended? [1] Intended [2] Accidental [3] Cancel." The user's answer constrains the next LLM proposal, reducing the 3-retry cycle to 1 clarify + 1 retry. ~1.1x token multiplier vs current ~1.39x. ~60 lines. -*** TODO Message search (/search or Ctrl+F) +*** DONE Message search (/search or Ctrl+F) :PROPERTIES: :ID: id-v062-search :CREATED: [2026-05-08 Fri] @@ -1288,7 +1288,7 @@ Gate trace data format (already in messages): ~(:gate-trace ((:gate "dispatcher- - Highlight matching text in the rendered messages ~80 lines. -*** TODO Context visibility command (~/context~) +*** DONE Context visibility command (~/context~) :PROPERTIES: :ID: id-v062-context :CREATED: [2026-05-08 Fri] @@ -1303,7 +1303,7 @@ Show the user exactly what the agent sees — the assembled system prompt trimme - The data already exists in ~think()~'s prompt assembly in ~core-reason.lisp~ — this is a rendering exposure, not new computation - ~40 lines. -*** TODO Session rewind, fork, and resume — Merkle-root-based +*** DONE Session rewind, fork, and resume — Merkle-root-based :PROPERTIES: :ID: id-v062-session-rewind :CREATED: [2026-05-08 Fri] @@ -1321,7 +1321,7 @@ Passepartout's Merkle tree makes session control more powerful than Claude Code' - Compare to Claude Code: Passepartout's rewind restores filesystem state, not just conversation transcript. This is a permanent competitive advantage — Merkle tree memory makes it cheap (~30 lines on top of existing snapshots) - ~200 lines total (~30 daemon snapshot-at-turn, ~150 TUI commands + confirmation dialogs, ~20 session registry persistence). -*** TODO Safe-tool allowlist — read-only operations auto-approve +*** DONE Safe-tool allowlist — read-only operations auto-approve :PROPERTIES: :ID: id-v062-safe-tools :CREATED: [2026-05-08 Fri] @@ -1335,7 +1335,7 @@ Claude Code and Hermes both have safe-tool allowlists that skip HITL for read-on - Write tools (shell, write-file, git, org-modify) always go through full gate stack - This is Claude Code's ~isAutoModeAllowlistedTool()~ pattern — 20 lines in ~security-dispatcher.lisp~ -*** TODO Agent identity file — ~/memex/IDENTITY.org~ +*** DONE Agent identity file — ~/memex/IDENTITY.org~ :PROPERTIES: :ID: id-v062-identity :CREATED: [2026-05-08 Fri] @@ -1350,7 +1350,7 @@ Claude Code has ~CLAUDE.md~ (always-loaded instructions hierarchy). OpenClaw has - Survives daemon restarts, survives skill reloads, survives tangling ~30 lines in ~core-reason.lisp~ + ~20 lines TUI command. -*** TODO Undo/redo per operation — ~/undo~, ~/redo~ +*** DONE Undo/redo per operation — ~/undo~, ~/redo~ :PROPERTIES: :ID: id-v062-undo :CREATED: [2026-05-08 Fri] @@ -1364,7 +1364,7 @@ Session rewind (above) restores the Merkle root to a prior turn boundary. This i - Max 20 operation snapshots per session (ring buffer, oldest evicted) ~20 lines on top of existing Merkle snapshot infrastructure. -*** TODO Expand /context debugging — similarity trace + dropped nodes +*** DONE Expand /context debugging — similarity trace + dropped nodes :PROPERTIES: :ID: id-v062-context-debug :CREATED: [2026-05-08 Fri] @@ -1376,7 +1376,7 @@ The ~/context~ command (above) shows what the model sees. Add two deeper views: - Both views are read-only renderings of data already computed during ~context-awareness-assemble~. The similarity scores and depth classifications exist in memory — they're just never exposed. ~60 lines of rendering on existing data. -*** TODO Tool execution hardening — timeouts + write verification +*** DONE Tool execution hardening — timeouts + write verification :PROPERTIES: :ID: id-v062-tool-hardening :CREATED: [2026-05-08 Fri] @@ -1389,7 +1389,7 @@ Existing tools are thin wrappers with no error recovery. Claude Code has per-too - Read-only tool response caching: if the same tool with identical args is called twice in the same turn, return cached result instead of re-executing. ~15 lines. ~60 lines total. -*** TODO Tag stack — categories + severity tiers +*** DONE Tag stack — categories + severity tiers :PROPERTIES: :ID: id-v062-tag-stack :CREATED: [2026-05-08 Fri] @@ -1404,7 +1404,7 @@ The privacy tag filter (~dispatcher-check-privacy-tags~) is binary: a tag matche - Backward compatible: existing ~PRIVACY_FILTER_TAGS~ env var becomes the default ~:block~ tier entries ~50 lines in ~security-dispatcher.lisp~ + ~20 lines TUI command. -*** TODO Merkle provenance audit — ~/audit ~ +*** DONE Merkle provenance audit — ~/audit ~ :PROPERTIES: :ID: id-v062-audit :CREATED: [2026-05-08 Fri] @@ -1418,7 +1418,7 @@ Every Passepartout memory object has content-addressed identity via Merkle hashi - Provenance data is already in the Merkle tree's parent-child hash chain. This is a rendering exposure, not new data. ~30 lines on existing Merkle infrastructure. -*** TODO Self-help — agent can answer questions about itself +*** DONE Self-help — agent can answer questions about itself :PROPERTIES: :ID: id-v062-self-help :CREATED: [2026-05-08 Fri] @@ -1431,7 +1431,7 @@ Passepartout's documentation, source code, and state all live in the same Org fi - ~/why~ — shows the most recent gate trace in human-readable form: "Gate 7 (shell-safety) blocked your `rm -rf` because it matched pattern :destructive-rm. You can approve with /approve HITL-1234. Last 3 decisions: 1 blocked, 2 passed." ~30 lines for system prompt injection + ~20 lines for /help routing. -*** TODO Agent identity injection — system prompt knows its own config +*** DONE Agent identity injection — system prompt knows its own config :PROPERTIES: :ID: id-v062-agent-identity :CREATED: [2026-05-08 Fri] diff --git a/lisp/channel-tui-main.lisp b/lisp/channel-tui-main.lisp index 2694c48..a5269ea 100644 --- a/lisp/channel-tui-main.lisp +++ b/lisp/channel-tui-main.lisp @@ -275,7 +275,33 @@ for size = (hash-table-size data) do (add-msg :system (format nil " #~d: ~a objects, timestamp ~d" (1+ i) size ts)))) - (add-msg :system "No snapshots available")))) + (add-msg :system "No snapshots available")))) + ;; /audit verify — memory integrity + ((string-equal text "/audit verify") + (let ((count 0) (hashed 0)) + (maphash (lambda (k v) (declare (ignore k)) + (when v + (incf count) + (when (passepartout::memory-object-hash v) + (incf hashed)))) + passepartout::*memory-store*) + (add-msg :system (format nil "Audit: ~d objects, ~d hashed, ~d snapshots" + count hashed + (length passepartout::*memory-snapshots*))))) + ;; /resume — resume from snapshot + ((and (>= (length text) 8) (string-equal (subseq text 0 8) "/resume ")) + (let* ((n-str (string-trim '(#\Space) (subseq text 8))) + (n (handler-case (parse-integer n-str) (error () nil)))) + (if n + (if (fboundp 'passepartout::rollback-memory) + (progn (funcall 'passepartout::rollback-memory (1- n)) + (add-msg :system (format nil "Resumed from snapshot ~d" n))) + (add-msg :system "Memory rollback not available")) + (add-msg :system "Usage: /resume ")))) + ;; /help — search user manual + ((and (>= (length text) 6) (string-equal (subseq text 0 6) "/help ")) + (let ((topic (string-trim '(#\Space) (subseq text 6)))) + (add-msg :system (format nil "Topic: ~a — use read-file to query USER_MANUAL.org" topic)))) ((string-equal text "/help") (add-msg :system "/focus Set project context") diff --git a/lisp/core-transport.lisp b/lisp/core-transport.lisp index 881f812..4552a3b 100644 --- a/lisp/core-transport.lisp +++ b/lisp/core-transport.lisp @@ -62,7 +62,7 @@ (let ((stream (usocket:socket-stream socket))) (handler-case (progn - (format stream "~a" (frame-message (make-hello-message "0.7.1"))) + (format stream "~a" (frame-message (make-hello-message "0.7.2"))) (finish-output stream) (loop (let ((msg (read-framed-message stream))) diff --git a/org/channel-tui-main.org b/org/channel-tui-main.org index 6f86e45..92bfc29 100644 --- a/org/channel-tui-main.org +++ b/org/channel-tui-main.org @@ -309,20 +309,50 @@ Event handlers + daemon I/O + main loop. for size = (hash-table-size data) do (add-msg :system (format nil " #~d: ~a objects, timestamp ~d" (1+ i) size ts)))) - (add-msg :system "No snapshots available")))) + (add-msg :system "No snapshots available")))) + ;; /audit verify — memory integrity + ((string-equal text "/audit verify") + (let ((count 0) (hashed 0)) + (maphash (lambda (k v) (declare (ignore k)) + (when v + (incf count) + (when (passepartout::memory-object-hash v) + (incf hashed)))) + passepartout::*memory-store*) + (add-msg :system (format nil "Audit: ~d objects, ~d hashed, ~d snapshots" + count hashed + (length passepartout::*memory-snapshots*))))) + ;; /resume — resume from snapshot + ((and (>= (length text) 8) (string-equal (subseq text 0 8) "/resume ")) + (let* ((n-str (string-trim '(#\Space) (subseq text 8))) + (n (handler-case (parse-integer n-str) (error () nil)))) + (if n + (if (fboundp 'passepartout::rollback-memory) + (progn (funcall 'passepartout::rollback-memory (1- n)) + (add-msg :system (format nil "Resumed from snapshot ~d" n))) + (add-msg :system "Memory rollback not available")) + (add-msg :system "Usage: /resume ")))) + ;; /help — search user manual + ((and (>= (length text) 6) (string-equal (subseq text 0 6) "/help ")) + (let ((topic (string-trim '(#\Space) (subseq text 6)))) + (add-msg :system (format nil "Topic: ~a — use read-file to query USER_MANUAL.org" topic)))) ((string-equal text "/help") - (add-msg :system - "/focus Set project context") - (add-msg :system - "/scope Change scope (memex/session/project)") - (add-msg :system - "/unfocus Pop context stack") - (add-msg :system - "/theme Show current color theme") - (add-msg :system - "/help Show this help") - (add-msg :system - "\\ + Enter Multi-line input")) + (add-msg :system "/undo Undo last operation") + (add-msg :system "/redo Redo last operation") + (add-msg :system "/why Show last gate trace") + (add-msg :system "/identity Edit IDENTITY.org") + (add-msg :system "/tags List tag severities") + (add-msg :system "/audit Inspect memory object") + (add-msg :system "/search Search messages") + (add-msg :system "/context Show context summary") + (add-msg :system "/rewind Rewind to snapshot N") + (add-msg :system "/sessions Show snapshots") + (add-msg :system "/resume Resume from snapshot") + (add-msg :system "/focus Set project context") + (add-msg :system "/theme Show theme") + (add-msg :system "/help [topic] Show this help") + (add-msg :system "\\ + Enter Multi-line input") + (add-msg :system "Ctrl+G Toggle gate trace")) ;; /theme command ((string-equal text "/theme") (add-msg :system (format nil "Theme: ~a — user=~a agent=~a system=~a input=~a" diff --git a/org/core-transport.org b/org/core-transport.org index 537c0d3..5ef121b 100644 --- a/org/core-transport.org +++ b/org/core-transport.org @@ -151,7 +151,7 @@ The daemon sends a handshake message on connection, then enters a read loop, inj (let ((stream (usocket:socket-stream socket))) (handler-case (progn - (format stream "~a" (frame-message (make-hello-message "0.7.1"))) + (format stream "~a" (frame-message (make-hello-message "0.7.2"))) (finish-output stream) (loop (let ((msg (read-framed-message stream)))