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.
Wrap read-from-string/read with (let ((*read-eval* nil)) ...) at three
untrusted-input code paths:
1. think() in core-loop-reason — LLM output parsing. LLM output is
untrusted by definition; #.(shell ...) in a response must not execute.
2. action-system-execute in core-loop-act — :system :eval path processes
untrusted payload code from the signal pipeline.
3. load-memory-from-disk in core-memory — memory.snap file could be
corrupted or planted in ~/, must not execute #. reader macros.
Adds test-read-eval-rce-blocked to pipeline-reason-suite: mocks a
backend returning malicious output containing #.(setf ...), verifies
no side effects occur and safe fallback is returned.
RED proof recorded: *read-eval* T + #.(setf ...) → :PWNED (RCE active)
GREEN proof: *read-eval* NIL → reader-error caught (RCE blocked)
Test: reason 12/0, full suite 88/0
Croatoan child windows don't inherit the screen's :input-blocking nil.
Without explicit (setf (input-blocking iw) nil), get-char blocks the
main loop indefinitely, preventing redraw from running. New agent
messages queued by the reader-loop thread were never rendered until
the user pressed a key.
Now the loop runs at 30fps and responses appear immediately.
- connect-daemon: retry up to 3 times with 3s backoff instead of
single 10s attempt. Shows 'Connecting...' while retrying.
- Failed attempts show attempt count and error detail.
- After all retries exhausted: shows TIP to start daemon first.
- Connection status bar already shows Connected/Disconnected.
- passepartout tui already auto-starts daemon if port 9105 is closed.
TUI integration: 7/7 pass.
- recv-daemon: catch all errors silently (not just usocket:timeout-error
which doesn't match SBCL's SB-INT:IO-TIMEOUT). Prevents log-message
from bleeding through to the Croatoan screen.
- reader-loop: add (sleep 0.5) when recv-daemon returns nil, preventing
tight spin on repeated I/O timeouts during idle periods.
- Backspace: get-char returns raw ncurses integers (263=KEY_BACKSPACE),
not key structs. Use code-key + key-name to normalize codes >255
to keywords, so (eq ch :backspace) actually matches.
- TUI blank screen: add initial redraw+refresh before the main loop.
get-char blocks, so the first frame was never drawn on startup.
- connect-daemon: remove :element-type character (daemon listens in
binary mode, mismatch caused hang). Add :timeout 10.
- Tests: use actual ncurses codes (343=KEY_ENTER, 263=KEY_BACKSPACE,
9=TAB) instead of make-key or raw ascii codes.
TUI: 45/45 pass.
Croatoan returns key structs (make-key :name :backspace) for special
keys. The on-key handler was comparing these structs to keywords like
:backspace with eq, which always failed. Keys like Enter (returned as
13) worked, but Backspace/Tab/arrows didn't. Actually, the user couldn't
delete typed characters.
Fix: normalize at the top of on-key — if the input is a key struct,
extract the :name keyword. This allows the existing keyword-based
cond dispatches to work for all keys.
Updated all tests to use (make-key :name :enter/backspace/tab) instead
of raw integer codes, matching what Croatoan actually sends.
TUI: 43/43 pass.
- passepartout setup: add pre-compile step for :passepartout + :passepartout/tui.
So first daemon/TUI start is fast (~10s instead of ~120s).
- TUI test: remove pre-warm (now in setup). Add 3 rendering diagnostics:
* add-msg-render: /eval injects agent msg, verify text on screen
(isolates TUI rendering from daemon)
* daemon-msg-roundtrip: wait for LLM, check via /eval that :agent
entry exists in :messages list (isolates daemon\xe2\x86\x92TUI comm)
* agent-response-renders: full E2E \xe2\x80\x94 LLM response text on screen
(confirms complete TUI\xe2\x86\x92daemon\xe2\x86\x92LLM\xe2\x86\x92TUI pipeline)
- Fix missing #+end_src in shell block (was preventing tangle)
- Update Contract section with new Phase 3 diagnostic items
- Test: 7/7 pass (was 5/5)
tmux capture-pane strips the ⬇ (U+2B07) character; grep on it always
returns empty. Switch to case-insensitive text matching on actual
LLM response content (hello, hi, greeting, hey).
Also: reorder tests (cascade-parsing, eval-command, status-bar first
to warm the TUI; agent-responds last), increase handshake timeout
to 60s, increase agent poll timeout to 90s.
TUI integration: 5/5 pass (was 4/5 with false-negative on agent-responds)
- TUI agent-responds: hardened to detect and FAIL on cascade/exhausted
responses (previously a separate WARN-only test that let real
cascade failures slip through)
- New TUI cascade-parsing test: /eval *provider-cascade* on screen,
checks for clean keywords (no cl-dotenv quote artifacts)
- Pre-warm step: sbcl --eval '(ql:quickload :passepartout/tui)'
before launching tmux, cuts TUI startup from ~120s to ~10s
- Removed test_agent_not_cascade_failure (absorbed into agent-responds)
- New integration test: test-provider-cascade-parsing verifies
PROVIDER_CASCADE entries are keywords without quotes, matching
registered backends — catches the exact cl-dotenv quote bug
- Fixed stop-daemon ghost symbol (removed export) and paren bug
- Contract section updated with numbered Phase 2/3 items
cl-dotenv preserves surrounding quotes in .env values (unlike bash).
PROVIDER_CASCADE="deepseek,..." resulted in keywords like :"DEEPSEEK
instead of :DEEPSEEK, causing all cascade lookups to fail silently.
Fixes:
- .env.example: remove quotes from PROVIDER_CASCADE
- provider-cascade-initialize: add #" and #' to string-trim chars
- system-model-router: same fix for LOCAL_BACKENDS parsing
- repl: test-repl-list-vars used wrong keyword (REPL-SENSOR→PASSEPARTOUT),
test-repl-inspect-found expected nonexistent 'function' substring
- literate: test-extract-lisp-blocks had ~% as literal chars (→format nil),
test-block-balance-check-valid had broken path merging
- diagnostics: test-diagnostics-env-fail used fragile (setf uiop:getenv),
test-diagnostics-dependency-success included missing 'sbcl' binary
- llm-gateway: test-provider-rejects-bad-keyword made real HTTP request
- reason: test-backend-cascade-no-backends lacked isolation from backends,
test-loop-gate-reason-sets-status called real LLM
- context: delete-file cleanup error now ignore-errors'd
- messaging: *gateway-registry* unbound in jailed package; use symbol-value
4 remaining failures are test-registration issues from jailed packages
(FiveAM suite state conflicts across skill package boundaries).
84% reduction in failures (16→4).
- skill-package-forms-strip: only strip (in-package :passepartout),
preserving test-package declarations. This allows embedded test code
to evaluate in the correct package, fixing 7 previously-unreachable
test suites (vault, perms, policy, validator, lisp, org, archivist).
- Remove security-dispatcher from skill-topological-sort exclusion:
dispatcher was never loaded (neither via ASDF nor skill system).
Test package was previously NIL; now loads properly.
Test results: 146 pass, 16 fail (was 80P 1F).
Remaining failures are pre-existing test code bugs (variable access
across jailed packages, cleanup errors) now exposed by the fix.
Eliminates COMMON-LISP-USER::DEFSKILL and other package-related
STYLE-WARNINGs during compilation. Files affected:
- gateway-messaging, programming-repl, programming-standards,
system-memory, system-archivist
Remaining warnings are cross-skill references (vault functions)
and minor same-file forward refs — category 2 per ROADMAP.
Root cause: config inner cond had )))) (4 closes) but needed ))) (3).
The 4th ) prematurely closed the outer cond config clause, making
(t (cond ...)) a bare function call to T instead of the cond default.
Also fixed chat-render coordinate bug (:y 1 :x y -> :y y :x 1)
Added backtrace diag (handler-bind all errors, sb-debug to stderr)
Added asdf central-registry push + :force t for stale-cache prevention
- Added (push PASSEPARTOUT_DATA_DIR asdf:*central-registry*) before quickload
so TUI loads from deployed code, not stale Quicklisp cache
- Added :force t to ql:quickload :passepartout/tui to ensure recompilation
- Added handler-bind for undefined-function around tui-main call:
prints function name + full backtrace, then exits cleanly
- Added sb-debug:print-backtrace to *debugger-hook* for all unhandled errors
- TUI now starts without crash in tmux with TERM=screen-256color
- handler-case had (log-message ...) and t as malformed clauses instead of return value
- This caused the error clause to fall outside handler-case, making c undefined
- Wrapped success path in progn: write-file + log-message + return t
- Error clause (error (c) ...) now properly inside handler-case
- Both bugs fixed by same structural change
- Separated test code from programming-lisp.org and programming-org.org into tests/ directory (was tangled to main lisp/, causing LOADER ERROR because export hadn't run yet)
- Added eval-when to load fiveam before test defpackage
- Renamed t→tag in lambda parameters in system-archivist, programming-org
- Renamed t→obj-type in let binding in system-memory
- Fixed missing lambda close paren in org-privacy-tag-p (SOME called with 1 arg)