call-with-tool-timeout wraps tool execution with sb-ext:with-timeout
using per-tool timeout from *tool-timeouts*. On timeout returns
(:status :error :message "Timed out after Ns"). Wired into
action-tool-execute before the funcall. Timeout result detected and
propagated as :tool-error.
- core-act: call-with-tool-timeout fn, wired into action-tool-execute
- Act tests: +3 (timeout enforcement test)
- Core: 88/88
/search <query>: case-insensitive substring search across message
history. Reports match count, previews with context around matches.
/context: shows message count, focus, token estimate, last 5 messages.
- channel-tui-main: /search and /context handlers, 1 test each
- TUI Main: 85/86 (1 pre-existing core flake)
Gate trace lines rendered below each agent message in dim color.
Collapsed-gates state field for Tab toggle (default: visible).
Uses passepartout::gate-trace-lines for colored entries.
- channel-tui-view: view-chat renders gate-trace after message content
- channel-tui-state: :collapsed-gates field in init-state
- View tests: 29/29 (1 new state-field test)
- CONFIG section in system prompt: providers, context window, gate count,
rules learned, docs path
- /why TUI command: shows most recent gate trace from message history
- assemble-config-section reads live state at each think() call
- Core: 75/76 TUI Main: 77/78 (1 pre-existing RCE test flake)
Live config section injected into system prompt between time and
IDENTITY. assemble-config-section reads *provider-cascade*,
tokenizer-context-limit, gate count, and *hitl-pending* at each
think() call. fboundp-guarded. Tested.
- core-reason: assemble-config-section fn, config-section binding,
injected into all 3 prompt assembly paths
- Reason tests: +4 checks (Passepartout, version, gates)
char-width: contract 5, 4 tests (6 assertions), 100% pass
ASCII=1, CJK/Hangul/Kana/halfwidth=2, combining marks=0, tab=8
Pure Lisp, ~25 lines, no deps. Used by word-wrap for unicode.
status bar: contract 6, timestamp right-aligned at (- w 12)
Fixes overlap where focus map and timestamp both drew at :y 2 :x 1
RED: embedding-backend-native does not exist. No CFFI llama binding.
GREEN (REPL progress):
- cffi:define-foreign-library libllama → loaded
- defcstruct with correct sizes (verified via C sizeof program):
llama-mparams (72 bytes), llama-cparams (136 bytes), llama-batch (56)
- Field offsets verified via C offsetof program
- llama_backend_init discovered as required prerequisite
- llama-model-default-params correctly fills 72-byte struct (verified)
- llama-embedding CLI verified: 768-dim vectors, 22ms/4tokens
BLOCKED: llama_model_load_from_file segfaults via CFFI. Suspect struct-by-value
vs pointer ABI mismatch on x86-64. Needs interactive SBCL REPL to debug the
calling convention (structs >16 bytes passed by hidden reference on SysV).
CFFI bindings preserved in org/system-model-embedding-native.org for
continued REPL work. Includes: model load, context create, tokenize,
encode, embeddings-ith, batch init/free.
Model: nomic-embed-text-v1.5.Q4_K_M.gguf (80MB, 768-dim, nomic-bert)
at ~/.local/share/passepartout/models/
Adds Discord gateway: REST API POST /channels/{id}/messages for
sending, HTTP GET for polling messages. Maps Discord mentions to
:user-input signals. HITL commands intercepted before injection.
Adds Slack gateway: Web API chat.postMessage for sending,
conversations.history for polling. Uses SLACK_TOKEN from vault.
Each gateway registered in *gateway-registry* following the same
jail-loaded skill pattern as Telegram and Signal.
Registry now has 4 platforms: telegram, signal, discord, slack.
RED: Messaging suite had only 1 test (5 checks). No Telegram or Signal
integration tests existed.
GREEN: 4 new tests, 12 new checks (5 → 17):
test-telegram-send-format: verifies URL/body construction for
telegram-send — URL contains sendMessage + token, body encodes
chat_id + text as JSON.
test-telegram-poll-hits-interception: verifies HITL commands
(/approve, /deny, /approve <token>) are intercepted before
signal injection. Non-HITL messages pass through.
test-signal-send-format: verifies signal-send constructs correct
CLI args for signal-cli (account, send, -m, text, chat-id).
test-signal-poll-json-parse: verifies signal-cli JSON output is
parsed correctly — extracts envelope source and dataMessage text.
Test: 123/0 across 13 suites (messaging 17/0).
RED proofs (pre-v0.4.0):
- dispatcher-check-secret-path 'core-loop-reason.org' → NIL (unprotected)
- dispatcher-check-core-path function does not exist
- Write to core file passes through gate unchanged
- test-self-build-core-protection does not exist
- Dispatcher suite: 19/0
GREEN proofs (v0.4.0):
- dispatcher-check-core-path: T for core-*.org/lisp, NIL for others
- SELF_BUILD_MODE=true: core write → :approval-required Flight Plan
- SELF_BUILD_MODE=false (default): writes pass through
- Dispatcher suite: 24/0 (new test-self-build-core-protection)
Prose:
- New 'Self-Build Safety Boundary' section: explains thin harness/fat
skills corollary, regex-based core-* detection, Flight Plan vs LOG
blocking, SELF_BUILD_MODE env var semantics.
Gate trace: cognitive-verify accumulates (:gate name :result status) for
each deterministic gate. Trace prepended to action plist via list*.
TUI on-daemon-msg extracts :gate-trace and stores on message object.
add-msg accepts &key gate-trace for future rendering (collapsible Tab).
Rule counter: TUI actuator enriches response payload with :rule-count
=(hash-table-count *hitl-pending*). TUI status bar shows 'Rules:N'.
Focus map: TUI actuator adds :foveal-id from signal context. TUI stores
in state and renders second status line '[Focus: id]'.
Status bar: now two lines — line 1 (connection, mode, msgs, scroll,
rules, thinking spinner), line 2 (focus map, timestamp).
Test: 112/0 across 14 suites (reason 15/0 including gate-trace assertions)
Adds dispatcher-check-core-path: regex-based detection of core-*.org and
core-*.lisp files (Perceive-Reason-Act loop, Merkle-tree memory, skill
engine, Dispatcher gates).
Vector 2b in dispatcher-check: when SELF_BUILD_MODE=true and a core file
write is detected, produces :approval-required (Flight Plan HITL) instead
of allowing the write through. When SELF_BUILD_MODE=false (default),
writes pass through — development mode.
Core file protection is separate from secret-path protection
(*dispatcher-protected-paths*) which blocks credentials/keys/tokens.
Test test-self-build-core-protection:
- core-loop-reason.org, core-memory.lisp → protected
- gateway-tui-view.org → not protected
- SELF_BUILD_MODE=true → writes blocked as :approval-required
- SELF_BUILD_MODE=false → writes pass through
Test: 102/0 (dispatcher 24/0)
SIGWINCH: handle KEY_RESIZE (410) in main loop — re-measure screen,
re-create status/chat/input windows at new dimensions, force redraw.
Scroll clamp: PageUp clamped to (max 0 (- total 1)), prevents scrolling
past message list end. Status bar shows 'msgs:N scroll:0'.
/quit: saves :input-history to ~/.cache/passepartout/history (one line
per entry, most recent first), sends goodbye handshake, sets :running nil.
/reconnect: closes stale socket via disconnect-daemon, re-runs
connect-daemon with retry backoff. Connection-loss detection: reader-loop
counts consecutive nils; after 10, queues :disconnected event. Handler
clears :connected/:busy, shows red system message.
Load-history: reads ~/.cache/passepartout/history on startup, populates
:input-history for up-arrow recall.
Message vector: :messages init as adjustable vector with fill pointer.
add-msg uses vector-push-extend (O(1) append). view-chat uses aref
(O(1) access) instead of nth (O(n) for lists).
Adds word-wrap(text width) — splits strings into lines at word
boundaries respecting terminal width. Rewrites view-chat to:
- Wrap each message with word-wrap before rendering
- Render each wrapped line as a separate add-string call
- Account for wrapped line count in visible-message calculation
RED proof: tmux capture shows messages split mid-word at terminal edge.
GREEN proof: tmux capture shows clean word-boundary wrapping:
The quick brown fox jumps over the lazy dog while the cat naps
peacefully in the sunny garden
1. Shell actuator: remove double bash -c wrapping (format ~s produces
S-expression-safe strings, not shell-safe). Now passes cmd directly
to (timeout N bash -c cmd) via run-program arg list.
2. Dispatcher: extend high-impact approval gate to :system :eval.
Previously only :shell, :tool "shell", and :emacs :eval triggered
HITL. Now :system :eval also requires Flight Plan approval.
3. Skill sandbox: before promoting a skill from its jailed package to
:passepartout, scan for restricted symbol references (uiop:run-program,
uiop:shell, uiop:run-shell-command). Block promotion on violation.
New skill-entry status :sandbox-blocked for blocked skills.
Test: 91 pass, 0 fail across 13 suites.