Commit Graph

40 Commits

Author SHA1 Message Date
Clover
400398ca7d v0.9.6: help canonical + prefix-free short commands (nc-find→nfind guard) + HCISITEDIR auto-init
Hands-on ergonomics for Bryan's Gundersen testing. All three changes are
backward-compatible — every old name still works.

1. `help` is now the canonical reference command (live, never-drifts table
   from bin/ + each tool's help block). `cheat` kept as a thin alias.

2. Prefix-free short commands: nc-table→table, nc-parse→parse, nc-msgs→msgs,
   nc-status→status, nc-engine→engine, nc-xlate→xlate, nc-inbound→inbound, plus
   the write tools (create-thread, set-field, insert-protocol, make-jump,
   provision-jumps, tclgen, document, revisions, diff-interface, smat-diff,
   regression). COLLISION GUARD: nc-find→`nfind` (NOT `find` — would shadow the
   system find on PATH); nc-paths keeps `paths`. Every nc-* name retained as a
   backward-compat alias. Tab-completion + the help/cheat table updated.

3. HCISITEDIR auto-init in the shared preflight (bin/_nc_common.sh): HCIROOT +
   HCISITE still required, but $HCIROOT/$HCISITE is created if missing rather
   than erroring. Conservative + idempotent; respects an operator-set HCISITEDIR.

VERSION→0.9.6, MANIFEST regenerated (--check clean), bash -n clean.

Co-Authored-By: Clover (Claude Opus 4.8) <noreply@anthropic.com>
2026-06-08 20:21:32 -07:00
bj
ba224477e3 v0.9.5: cheat on-PATH command (live, never-drifts reference) + verified no-uninstall config-preserving update
- bin/cheat: one-screen reference for ALL short commands, generated live from
  the bin/ wrapper set + each tool's help block (description from the wrapper
  header / backing lib line 1; ONE real example from the wrapper's example
  block / lib Usage form). cheat <filter> + cheat -h. LC_ALL=C em-dash slicing.
- Wired into install-larry.sh symlink loop + MANIFEST (auto-synced on update).
- Proved the update path: simulated v0.9.0 install with populated config/auth/
  site-data -> re-ran installer against v0.9.5 origin -> reached v0.9.5 with
  bin/cheat on PATH + completion wired + fixtures present, ALL 8 state files
  byte-identical (sha256), tbn adt + completion working after. Idempotent re-run
  (no duplicate rc line). NO uninstall needed for an update.
- VERSION + LARRY_VERSION -> 0.9.5; MANIFEST regenerated (--check clean, 95
  entries); bash -n clean. Deliverables: cheatsheet + update-procedure (myPKA).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 15:24:02 -07:00
Clover
df1810cba7 v0.9.4: short on-PATH commands + live 3-level tab-completion + durable fixture
Make the toolkit usable BY HAND without the `larry tools <name>` prefix.

- bin/ of thin wrappers (tbn/tbp/tbh/tbpr/where/paths/route_test + a full-name
  passthrough per operator tool). Installer symlinks them into LARRY_BIN_DIR so
  `tbn adt` runs directly. Each resolves lib/ via bin/_nc_common.sh
  (LARRY_LIB_DIR -> ../lib -> $LARRY_HOME/lib) and execs the matching tool.
- -h/--help on every wrapper.
- bin/nc-completion.bash: dynamic bash completion, 3 levels (command / SITE /
  THREAD) enumerated LIVE from the NetConfig tree under $HCIROOT via the same
  lib/nc-parse.sh the tools use; cached per (HCIROOT, newest-NetConfig-mtime).
  Installer appends a guarded source line to the user's bash rc.
- fixtures/integrator: durable 3-site demo (epic->ancout->codamx) with cross-
  site fan-out + fan-in and a multi-route inbound. RESOLVES the v0.9.3 fixture
  conflict: cross-site destination blocks are XS_*-prefixed so they never
  collide with a local protocol name (a collision makes nc-paths
  _xsite_down_targets suppress the cross-site hop, lib/nc-paths.sh:378).
- DEFERRED: fetch-token.sh broker wiring (broker contract still finalizing).

VERSION+LARRY_VERSION -> 0.9.4; MANIFEST regenerated (--check clean); bash -n
clean; verified live on .135 (short commands off PATH + all 3 completion levels).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 11:58:25 -07:00
bj
2b578f5058 v0.9.1: on upgrade to broker-mode, WIPE the now-obsolete local credentials
An install switching TO broker-mode (the v0.9.0 default) carried long-lived
Anthropic/OAuth credentials from the pre-broker era. Broker-mode authenticates
via short-lived broker tokens and never uses them — they are a pure security
liability on the box, acutely so on a PHI box. On the next self-update the agent
now cleans them up automatically:

- Secure-deletes $LARRY_HOME/.api-key and .oauth.json (reuses the
  uninstall-larry.sh shred -u -z -n3 -> overwrite -> rm logic).
- Strips the ANTHROPIC_API_KEY / CLAUDE_CODE_OAUTH_TOKEN LINES from
  $LARRY_HOME/.env and from ~/.bashrc, ~/.bash_profile, ~/.profile (backup
  first); every other line is kept.
- Idempotent (.broker-cred-wiped marker, written only after a run that removed
  something); silent no-op when clean.
- Hard-guarded on LARRY_AUTH_MODE=broker: does NOT fire under the apikey escape
  hatch (which legitimately still needs the key). Only the two Anthropic/OAuth
  vars are touched (LARRY_* / GITEA_TOKEN are still needed in broker mode).
- Prints a reminder to ALSO revoke at the source (local deletion != server
  revocation), per the decommission / kill-switch docs.

Fires at the broker-resolution block (after self_update synced a fresh
lib/broker.sh, before the fail-closed preflight). New functions in
lib/broker.sh: _broker_wipe_obsolete_credentials,
_broker_strip_cred_lines_from_env, _broker_strip_cred_lines_from_rc.
VERSION + MANIFEST regenerated. Tested: 31/31 assertions pass across the
upgrade-wipe, apikey-non-wipe, clean-no-op, idempotency, dangerous-path-guard,
and selective-line-strip paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:42:11 -07:00
bj
ea9f4c2399 v0.9.0: broker mode is the DEFAULT — wire the remote kill-switch into every Cloverleaf-Larry
Phase 3 of the Larry remote kill-switch (Pax design; Mack's broker on .135 LAN
8181 / Tailscale 100.86.16.114:8181). Deployed Larry no longer holds a long-lived
sk-ant-… key: it holds a per-deployment enrollment secret, mints a short-lived
token from the broker, and routes every LLM call THROUGH the broker /v1/messages
(real key injected server-side). set-authorized <id> false => the deployment 401s
and dies, no box access required.

- LARRY_AUTH_MODE=broker is the DEFAULT (was apikey). Self-update flips existing
  installs to broker-mode too, so upgrading Gundersen delivers the kill-switch.
  Escape hatch (documented, not default): LARRY_AUTH_MODE=apikey (no kill-switch,
  never for PHI boxes).
- New lib/broker.sh: enroll+mint, fail-closed heartbeat, best-effort PHI wipe
  (reuses uninstall-larry.sh's shred/overwrite secure-delete + LARRY_HOME guard).
- Fail-closed preflight at launch + in-REPL heartbeat (default 60s, 3-miss budget):
  disabled => refuse to run (+ PHI wipe for profile:phi); unreachable past budget
  => refuse to run (NO wipe on a network blip — only an explicit disable wipes).
- call_api / call_api_stream broker branch: Bearer short-lived token, no x-api-key,
  token never on disk.
- install-larry.sh enrollment provisioning: LARRY_DEPLOYMENT_ID + LARRY_ENROLL_SECRET
  (+ LARRY_PROFILE/LARRY_BROKER_URL) baked 0600 + into the shim; box shows up in the
  dashboard ready to toggle.
- /auth reports broker state.

Reachability (flagged for Bryan): the broker is LAN + Tailscale only (no public
route). Egress-restricted boxes reach it over Tailscale (default URL = tailnet).
A box that can reach neither fail-closes = won't run (correct kill, useless work
state) — such a box MUST run Tailscale, or Bryan must stand up a hardened public
broker ingress.

Bug fixed in test: _broker_json_field jq `// empty` rendered literal false as
empty, mis-classifying a DISABLED deployment as an unreachable MISS (delaying
fail-close + skipping the PHI wipe). Fixed to `if has($k) then .[$k] else "" end`.
Verified end-to-end against the live broker: enroll -> mint -> proxied call ->
disable -> instant 401 + heartbeat fail-close + 5 PHI files shredded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:10:09 -07:00
bj
6b45543652 v0.8.34: harden uninstall-larry.sh into a first-class PHI-grade decommission
One-run `larry uninstall` / uninstall-larry.sh that:
- stops detached larry.sh REPL + phi-presidio-sidecar + larry-tunnel
  (pgrep+kill by pattern, never kills itself/parent/uninstall-larry)
- SECURELY deletes cleartext PHI (auto-phi.log, lookup.tsv, sessions/*.log.md)
  via shred -u -z -n 3, with overwrite-then-rm fallback on Windows/MobaXterm
  where shred is absent, honest per-platform "secure achieved?" reporting,
  and a find-less bash-glob fallback for session files
- strips ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN|LARRY_*|GITEA_TOKEN from
  shell rc with a timestamped backup (default), or prints them under --keep-rc
- removes ~/larry, ~/.local/bin/larry, ~/bin/larry, ~/larry-anywhere (our shims
  only; foreign `larry` preserved), then self-removes a standalone checkout
- prints a FINISH-AT-THE-SOURCE reminder: revoke API key + OAuth grant + PAT,
  plus a BAA/PHI-disclosure note
- hard rm-rf-/ guards (empty/unset/root/$HOME/non-larry LARRY_HOME refused),
  scoped strictly to the built target list; DRY-RUN default; new --keep-rc and
  --no-shred flags

Tested: full real run, dry-run scope, all rm-rf guards, --keep-data,
no-shred(Windows) fallback, idempotency, standalone-checkout self-uninstall.
MANIFEST regenerated so the self-update ships it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:52:24 -07:00
7606a535c9 v0.8.33: uninstall command + --no-api deterministic-only mode
Two operator-requested features:

1. `larry uninstall` / uninstall-larry.sh — there was no uninstaller before.
   Reverses install-larry.sh exactly: removes $LARRY_HOME (bundle + bin/jq +
   optional phi-venv + all runtime artifacts incl. log/headers.log, sessions,
   journal, lessons, creds) and the `larry` PATH shim. DRY-RUN by default;
   --yes to delete, --keep-data to preserve user data. Removes ONLY what the
   installer created (shim removed only if it carries our auto-gen header;
   shell rc / Cloverleaf sites / $HCIROOT never touched). Stops running PHI
   sidecar / tunnel via their own pidfiles. Shipped by the installer +
   manifest-synced; dispatched early like `larry tools` so it works offline.

2. --no-api (env LARRY_NO_API=1) — deterministic-only mode making ZERO LLM API
   calls (zero cost). REPL + all local/deterministic commands still work; a
   free-text prompt is routed to the matching `larry tools <name>` instead of
   the model. No API key required (first-run auth prompt skipped). call_api /
   call_api_stream hard-refuse as defense in depth.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 09:43:51 -07:00
39f0e00c01 v0.8.32: nc_provision_jumps — capstone inter-server jump-thread provisioner
Point at a site and provision server_jump thread sets for ALL inbound root
threads (route the existing-env inbound feed to a new env). Pure composition
of validated tools (nc_find_inbound, nc-parse, nc_make_jump, nc_insert_protocol,
nc_add_route) under ONE journal session — whole batch rolls back in one command.
ALL-OR-NOTHING: steps gated on prior success, first failure auto-rolls-back the
session (exit 6); pre-flight collision check aborts (exit 5) before any write if
a jump-port or thread-name already exists. --dry-run previews the full plan.
Output hands `roots: <csv>` to nc_regression for bulk env-A-vs-B testing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 19:38:07 -07:00
7a715c802a v0.8.31: nc_set_field — change a thread's PORT/HOST/PROCESSNAME/ENCODING (journaled)
New mutating tool, built on the proven journal/rollback foundation. Curated
safe field set only (rejects anything else; never creates a missing field).
Edits are line-number-anchored to the target thread's protocol block via
nc-parse (a shared port/host value in another thread is never touched),
brace-balance-checked before an atomic write, journaled for byte-identical
rollback. Flags: --dry-run (no write), --confirm yes, --site, --netconfig.
Copy-tested: PORT + HOST applied surgically, rollback byte-identical.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 18:43:27 -07:00
5bc3195f98 v0.8.30: write/mutate tool validation pass — 2 fixes; rollback proven reliable
Tested all mutating tools (nc_table/nc_add_route/nc_insert_protocol/
nc_create_thread/nc_make_jump/nc_tclgen) on a throwaway copy: every change is
journaled and rolls back byte-identical across --session/--entry/--target/
--last granularities. Fixed nc-create-thread --host brace-collision (emitted
invalid TCL { HOST x} }; now balanced { HOST x }, and { HOST {} } when omitted)
and lessons.sh:142 printf option-injection. Read fixture verified untouched.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 18:28:21 -07:00
67cf5fed89 v0.8.29: read/inspect tool validation pass — 7 portability/correctness fixes
Ran every read/analysis tool against the real 24-site integrator (lib + wired
dispatch). Fixed: nc-find --name (GNU sed \+ → POSIX; 0 rows on BSD/macOS),
nc-find tsv/jsonl exit-1-on-success, nc-parse tclproc-refs dropping
digit-leading procs (3M_check_ack), nc-xlate diff missing --site,
nc-diff-interface + nc-smat-diff printf '-'-leading option-injection dropping
output, nc-status not-up crashing on --format, and nc-status not-up's gawk-only
\<up\> word-boundary → portable form (BSD/macOS). Test matrix in Deliverables.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 18:11:22 -07:00
d58e4e0ec8 v0.8.28: expose 5 lib-only tools + fix nc-engine arg-parsing crash
Wires nc_status, nc_engine, nc_xlate, nc_smat_diff, nc_tclgen as first-class
LLM tools (all 4 surfaces). nc_engine unlocks TPS testing (hcitps) + the
route-test driver. Fixes a real nc-engine.sh bug surfaced by the exposure:
the dispatcher treated every --flag as taking a value (--dry-run ate the next
token) and a set -u leak from journal.sh crashed start/stop/bounce on bash 3.2;
fixed with set +u + a multi-case parser (no over-shift on bare trailing flags).
Corrects stale CHANGELOG + nc_engine schema text that misstated the bug as live.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 17:18:23 -07:00
5214d87a04 v0.8.27: nc-revisions — NetConfig change-history / revision diff
New tool: show how a thread/system/site changed over time by diffing
Cloverleaf NetConfig revision snapshots, annotated with who saved each and
when. Handles the non-zero-padded NetConfig<TS> revision dirs by parsing the
prologue date into a sortable key; scopes diffs to the requested thread/system
via nc-parse. Flags: <thread>[.<site>], --system, --site, --format
timeline|diff, --limit, --since. Wired as nc_revisions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 16:53:10 -07:00
111be2c744 v0.8.26: harden control-byte sanitize across the tool suite + ssh-helper traps
Shared _sanitize_ctl (unconditional, nc-document) and _sanitize_ctl_tty
(strips only when stdout is a terminal) now live in cygwin-safe.sh. nc-msgs,
nc-parse, and the hl7-* tools route stdout through the tty-gated variant, so a
terminal is protected from raw HL7/NetConfig control bytes while pipes and
redirects stay byte-exact (the 0x1c framing route_test needs is preserved).
Exit codes propagate via PIPESTATUS. ssh-helper _read_hidden installs its
restore trap before stty -echo on every path and saves/restores the prior trap.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 16:35:06 -07:00
9289352454 v0.8.25: fix terminal corruption from larry tools (control-byte + tty leaks)
nc-document.sh now sanitizes C0 control bytes (except tab/LF/CR) at the single
output sink, so raw ESC sequences embedded in NetConfig/.tcl content can no
longer flip the terminal's mode and break line editing when output isn't
redirected. ssh-helper.sh password prompts save/restore termios via stty -g +
trap so a ^C mid-prompt no longer leaves echo off. UTF-8 preserved; portable.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 14:38:31 -07:00
88fc104c54 v0.8.24: doc tool plain-text output for OneNote (no markdown)
Default render is now plain text (no #/**/pipe-tables/fences) so it pastes
cleanly into OneNote. Tabular sections default to label:value hop blocks;
--onenote-table emits tab-separated rows for paste -> Insert>Table. Raw TCL
moved behind opt-in --raw-tcl (readable UPOC bits stay inline). Removed the
verbose "Filter / translation logic (surfaced deterministically...)" label.
Fixes a markdown leak in the proc-not-found fallback (Vera gate). Folds in
two prior deferred minors (dead counters; local _dest_hit/_d).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:09:11 -07:00
f5f56439d0 v0.8.23: regression chain-walk route-test capture (nc-regression --chain-walk)
Resolves the downstream route chain via nc-paths, grabs N recent messages
from the START inbound's SMAT, walks each ENTRY node (START + post-==> remote
inbounds) running hciroutetest -a -d -f nl, chaining each step's selected
.out.<DEST> across cross-site hops. Generates per-chain commands.sh for the
engine box; --dry-run stubs the engine. Command syntax mined verbatim from
the v1/v2 route_test wrappers. Fixes --help sed range (header ends at 94).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 12:44:38 -07:00
3c8b5d6f49 v0.8.22: document tool follow-on — xlate-internal filtering (If/Suppress) + fan-out (Continue/Send) surfaced in the doc; configurable inbound-systems lookup (curated feed->identity, falls back to honest generic); list-form { DEST {a b c} } capture + nc_paths-penultimate fallback for cmd_sources flakiness; --strict-delivery gate; --help leak fix; printf footer fix; removed auto-gen signature lines (no doc-signing). Verified on real 24-site integrator.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 12:25:15 -07:00
474a0710a4 v0.8.21: interface document tool — <thread>/<system> document. Legacy ADT-Messages template (flow via nc_paths, Platform|Action|Description|From|To, per-delivery breakdown); deterministic API-free UPOC-bits extraction (comments/HL7 fields/event matches/table/disposition) + raw-TCL appendix; LLM polishes to prose only when API present. Verified on the real 24-site integrator (ADTto_CodaMetrix, codametrix system, PeriWatch UPOC proof).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 11:51:28 -07:00
9364c7edeb v0.8.20: nc_paths route-chain tracer — parse-once in-memory engine (84s→0.7s single, ~5.5s full-tree), authoritative destination-block cross-site resolution, v1-fidelity output (site/thread nodes, --> intra-route / ==> cross-site) as default + --format table/nodes, pipe-first (site/thread in, awk field-1 = root). Verified EXACT vs v1 on the real 24-site integrator.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 11:26:31 -07:00
12989b2ced v0.8.19: nc_paths deterministic route-chain tracer — DFS path enumerator (SITE/THREAD/HOPS/PATH), cross-site, DEST-routing; wires the previously-dark walker into the LLM schema + /paths + manual tool, consolidates the BFS walker, cheatsheet steers to it. Kills brute-force route-tracing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 10:25:57 -07:00
65807308d8 v0.8.18: readable terminal output (vertical entity lists + verbatim-fenced aligned tables) + cmd_push direct-mode branch + _direct_ssh_opts dedup
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 09:57:36 -07:00
d55e222341 v0.8.17: per-alias DIRECT (no-multiplex) SSH mode for servers that reject ControlMaster session multiplexing — /ssh-set-direct + per-command sshpass (forced password auth), banner/sudo stderr filter; zero traffic-bypass primitives
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 09:42:37 -07:00
0e6495223a v0.8.16: hotfix set -u unbound-variable abort in /ssh-set-hciroot + /ssh REPL slash handlers
Single-line `local a=… b="…$a…"` referenced the 1st var before it was bound
within the SAME `local` statement, aborting under set -u on Cygwin/MobaXterm
(and modern bash). Split larry.sh:6903 (/ssh-set-hciroot) and the same latent
pattern at larry.sh:6925 (/ssh) into set-u-safe declare-then-assign form.
Codebase-wide bug-class audit (larry.sh + all lib/*.sh + scripts): zero
remaining instances. Closed the v0.8.15 gate gap by driving the ACTUAL REPL
slash-dispatch handler bodies under set -u + BASH_COMPAT=3.2 (not just the
ssh-helper subcommand): /ssh-set-hciroot normal + empty-path-clear, /ssh, and
usage paths all pass; old code aborts under the same harness. No-traffic-bypass
line unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 09:13:26 -07:00
fc667e2451 v0.8.15: legacy/qa remote-enumeration fix — per-alias HCIROOT pin (sudo-gated profile bypass), hcisitelist-free NetConfig walk, ControlMaster banner+rotating-pw hardening; zero traffic-bypass primitives
MAJOR-1: regenerate MANIFEST (larry.sh, lib/ssh-helper.sh, VERSION,
CHANGELOG.md hashes now authoritative for the v0.8.15 bytes).
MINOR-1: print_help /sites line documents the --hciroot <path> pin
convenience and the pinned-vs-login resolution distinction.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 08:58:49 -07:00
6703ee154e v0.8.14: manual-tools dispatcher (larry tools) + honest blocked-API detection (_diagnose_api_block) — zero bypass primitives
larry tools list / <name> [args] makes all 24 lib/ Cloverleaf+HL7 tools
discoverable and runnable by hand with no API/LLM; dispatches before
bootstrap/self-update/network. _diagnose_api_block recognizes a blocked
API (curl rc/stderr/body/headers, incl. Cisco Umbrella fingerprints) and
guides the operator to manual-tools mode + IT allowlisting instead of a
raw error dump. Graceful degradation + honest guidance only — NO traffic
masking/proxy-hiding/circumvention on a PHI box.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 08:31:26 -07:00
fe2f67a1aa v0.8.13: $HCIROOT login-shell fix + both-mode detection + list_sites/sites + per-delta jq-fork slowness fix
Root-cause fix for the live-session friction where "how many sites are on
qa?" stalled on repeated `export $HCIROOT` nags despite a working `qa` SSH
alias:

1. $HCIROOT login-shell fix: ssh-helper.sh `exec` now wraps remote commands in
   `bash -lc` so the Cloverleaf login profile sources and $HCIROOT/$HCISITE/PATH
   populate as for an interactive operator login. Escape hatch: NOLOGIN prefix
   or LARRY_SSH_NO_LOGIN=1. pull-smat find/sample use the same wrapper.
2. Both-mode detection: startup surfaces a MODE= line (LOCAL / REMOTE / UNKNOWN)
   and leads with what it found instead of asking for paths.
3. First-class list_sites tool + /sites [alias]: enumerates sites in both modes
   (hcisitelist fast-path, NetConfig-walk fallback) via new ssh-helper discover.
4. System-prompt de-nagging: agents/larry.md + env-diff/regression prompts no
   longer tell Larry to ask Bryan to export $HCIROOT for a reachable host.
5. Streaming slowness (dominant residual): new pure-bash _json_str_decode
   un-escapes the common escape-free delta with zero forks, halving per-turn
   jq forks on top of v0.8.12. Round-trip verified.
6. pull-smat path capture hardened (Vera Minor #1): resolved path now emitted
   behind a SMATDB_PATH: sentinel and selected by pattern not position, so a
   login-shell MOTD/banner on stdout can't be mistaken for the path; falls back
   to prior tail -1 when no sentinel present. Selection logic unit-verified.

Vera gate: PASS-WITH-NOTES (v0.8.13). bash -n clean on larry.sh + ssh-helper.sh;
MANIFEST regenerated (48 entries) and --check clean.

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-28 07:40:53 -07:00
3208d50337 v0.8.12: post-response arithmetic crash fix + streaming speedup + prompt-caching + PHI notice default-silent
Crash/slowness/cost pass on the API-key rail (Vera-QA-passed). Full diagnosis:
Deliverables/2026-05-27-cloverleaf-larry-v0812-crash-slowness-cost.md.

- Crash fix: CR-coerce at 3 response-path accounting sites (non-streaming cost,
  streaming cost, _record_ctx_used) via coerce_int promoted to lib/cygwin-safe.sh
  — guards CRLF-tainted jq usage counts on Cygwin/MobaXterm (v0.7.5 Anomaly-#4
  recurrence on the response path).
- Slowness: collapsed per-delta jq forks in the SSE hot path (3 -> 2 forks/event
  on text, 1 on ignored deltas) — dominant fix for the laggy per-turn render on
  Windows fork emulation. Plus per-session PHI sidecar /health probe caching.
- Cost: prompt caching wired in — system sent as block array + last tool marked
  cache_control: ephemeral, billing the ~12.7K static prefix at the cache-read
  rate after turn 1 (~90% prefix cut, lower TTFB).
- PHI tier-5 notice now default-silent (LARRY_PHI_NOTICE=1 to re-enable).

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-28 00:12:49 -07:00
a12f2416c4 v0.8.11: API-key default rail (OAuth-impersonation off, secure per-client /set-api-key) + manifest-hashing auto-update speedup
Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 22:40:18 -07:00
b80f2fb29d v0.8.9: manifest-sync live progress indicator — silent ~3-min relaunch no longer looks frozen
Root cause: sync_from_manifest fully downloads all 48 manifest entries
sequentially (authenticated HTTPS via proxy + Cloudflare), then cmp-compares
locally to find the few that changed — 48 silent round-trips, ~3 min, no output.

Add _sync_progress/_sync_progress_done: live in-place "checking N/48 <file>"
(switching to "downloading N/48 <file>" on real changes) via \r\033[K only —
MobaXterm-safe (no scroll-region/cursor-save/abs-pos). Gates on [ -t 2 ];
non-TTY emits a plain heartbeat every 10 files (no \r). Current filename shown
so a hang is visible by name; per-file curl --max-time bounds each stall.

Hash-skip speedup deferred: MANIFEST is paths-only (no hashes), so local
skip-unchanged needs a manifest-format + release-tooling change — filed for
v0.9.x. Sync correctness unchanged.

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 22:07:36 -07:00
5ed82db770 v0.8.8: force unconditional 429 header capture so headers.log always generates
Bryan's MobaXterm work-box 429s never wrote headers.log because the v0.8.5
gate only fired on (OAuth + unified-*) OR retry-after — and his bare burst
429s carry neither. Detect 429 from the HTTP status line in the -D dump and
ALWAYS write the full raw header block, exempt from the OAuth 50-call cap
(own STATUS_429_HEADER_LOG_LIMIT budget), with a live phi/rl> stderr pointer.
Non-stream path already reached the parser (call_api -D dump); the bug was
the write-gate, not the call. Streaming path shares the same function.

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 21:51:49 -07:00
4a992d9668 v0.8.7: status line renders on MobaXterm — gate on turn count not data presence
Root cause: render_status_line suppressed the OAuth line whenever ctx_used,
5h_util, and 7d_util were ALL empty. On a rate-limited session ctx is never
recorded (the error path returns before _record_ctx_used) and pre-v0.8.5 the
unified-* headers weren't captured on errors — so all three stayed empty turn
after turn and the line never appeared on Bryan's work-box. NOT a positioning
bug: the line is a plain printf'd dim line (no scroll-region/cursor escapes)
and is not coupled to streaming or mouse mode.

Fix: suppress only before the first turn (_LARRY_TURNS==0); thereafter always
render — empty fields show "—" placeholders, reset date fills in once headers
populate. /status now renders on demand even pre-first-turn. CR-taint sweep:
coerce_int the reset-epoch arithmetic comparisons + strip_cr the oauth-status
color case (MobaXterm CRLF would otherwise crash/blank the line).

Verify: bash -n clean; 7/7 unit tests (turn-0 suppressed, turn>=1 placeholders,
reset date when populated, renders with LARRY_NO_STREAM=1 + mouse off, survives
CR-tainted epoch, LARRY_NO_STATUS=1 still disables).

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 21:41:03 -07:00
578cefcc35 v0.8.6: work-box → Mac headers.log sync (tsk-2026-05-27-023)
Closes the last gap in the rate-limit-diagnosis pipeline: anthropic-ratelimit-*
headers captured on the MobaXterm work-box now flow to the Mac memory daemon
(Tier 4 Hindsight + Tier 7 mem0) automatically.

- lib/headers-sync.sh: incremental, offset-tracked, idempotent push of
  headers.log to ~/.cloverleaf/headers-<hostname>.jsonl on the Mac, riding the
  existing authenticated SSH ControlMaster. No new auth; password never in
  argv/env. No-op when nothing new; re-seed on local rotation/shrink. Fully
  graceful (no target / closed master / transport error → warn + continue;
  never crashes the session).
- /headers-sync on|off|status|target <alias>|now slash command + TAB-completion
  + /help. Config persisted to $LARRY_HOME/.env. Auto-sync fires on REPL exit.
- Security: headers.log carries only anthropic-* headers + status lines — NO
  PHI per Vera audit V7; transport reused unchanged (not weakened).

Layered cleanly on top of Clover #8's v0.8.5 (4f1ea86) — edits isolated to new
lib + help/array/trap/dispatch hunks; no overlap with the streaming parser,
retry/backoff, error-display, or phi-notice regions.

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 21:01:54 -07:00
4f1ea86051 v0.8.5: rate-limit backoff + actionable message, streaming single-send, ErrorPI CR fix, phi once-notice
Diagnose-don't-assume rate-limit cluster (Clover #8). The rate_limit_error on a
work-box with 90% of the 5h Max quota free was a short-window BURST rail, not 5h
exhaustion — tripped by a stream->non-stream double-send per turn with no backoff.

- Rate-limit backoff honoring retry-after (else exp 2/4/8 cap 30) + actionable
  header-parsed message naming the tripped rail; headers.log now captures every
  429 (was OAuth+unified-* only), tagged with retry-after + rail.
- parse_stream_to_response detects a non-SSE JSON error body (429/overload) and
  returns a distinct code so agent_turn surfaces it WITH backoff instead of
  re-sending the whole prompt (single-send invariant). Auto LARRY_NO_STREAM=1 on
  MobaXterm/Cygwin/MSYS; explicit LARRY_NO_STREAM=0 still forces streaming on.
- ErrorPI fix: strip_cr on err_type/err_msg in _humanize_api_error (a trailing
  CR broke the case match AND carriage-overprinted "API error"); err/warn/log
  now strip embedded CRs defensively. (v0.7.5 sweep missed the error-display path.)
- phi tier-5 notice once-per-session via $LARRY_HOME/.phi-notice-shown SESSION_ID
  flag (old export flag died in the $(...) subshell -> per-turn nag). Same-pattern
  sweep fixed the identical subshell-flag bug in _auto_phi_b64_roundtrip.

Deliverable: Deliverables/2026-05-27-cloverleaf-larry-v085-ratelimit-streaming-fixes.md

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 20:54:10 -07:00
31ffae6f36 v0.8.4: installer/updater detects HTML-sign-in-page responses and fails loud
Hardens the installer + auto-updater against the Gitea private-repo trap
(Clover #5 diagnosis): an unauthenticated raw-file read of a sign-in-gated
Gitea returns the HTML Sign-In page at HTTP 200, which `curl -fsSL` treats as
success — so the old code parsed HTML as VERSION/MANIFEST/larry.sh content and
silently aborted (or overwrote real files with HTML). This stranded a work-box
at v0.7.3 until the REQUIRE_SIGNIN_VIEW=false flip.

- New lib/fetch-safe.sh: fetch_validate URL DEST KIND [MAX_TIME]. Detects the
  HTML-login trap (DOCTYPE/<html/"Sign In - Gitea"/<title>Sign In markers, or
  text/html Content-Type) and validates content shape per file type (semver
  VERSION, path-list MANIFEST, shebang larry.sh, non-HTML .sh). On failure:
  actionable error + non-zero, target file left untouched.
- install-larry.sh (curl|bash bootstrap) and larry.sh self_update() each carry
  a byte-identical inline copy (both run before lib/ can be sourced).
- Every remote-content fetch routed through the validator: install fetch();
  agent fetch; sync_from_manifest MANIFEST + per-file; _fetch_with_fallback.
- Optional LARRY_GITEA_TOKEN / GITEA_TOKEN env var adds Authorization: token
  <PAT> for authenticated fetch against private repos. Never hardcoded/logged.
  Documented in --help + MANUAL.md.

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 20:28:58 -07:00
d4c382dc6d v0.8.3: tab-completion trailing-space no longer breaks command dispatch
The slash-command completer (__larry_complete_slash) intentionally appends
a trailing space after a unique match for arg-command ergonomics, but the
main_loop dispatcher matched exact `case` globs — so a completed `/quit `
missed the `/quit)` arm and fell through to "unknown command". Latent since
v0.6.6 (tab completion). Fixed by rtrimming the dispatch key once at the
`case "$input"` boundary, which also transitively protects the sub-command
dispatchers (/origin, /phi-auto, /phi-sidecar, /mouse) that consume the
same $input via _slash_args. Interior `/load FILE` spacing is preserved.
Added a shared rtrim() helper to lib/cygwin-safe.sh next to strip_cr.

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 20:11:19 -07:00
60b8f0e1c8 v0.8.2: Presidio sidecar for free-text NER (tier-5) — closes V1
The only path that closes V1 (free-text PHI gap — the dominant real-world
failure mode per Vera). Opt-in install; larry runs in v0.8.1 mode on hosts
without Presidio (MobaXterm/Cygwin per Bryan's accepted tradeoff).

New files:
- lib/phi-presidio-sidecar.py — FastAPI service on 127.0.0.1:$LARRY_PHI_PORT
  (default 41189). Presidio AnalyzerEngine + AnonymizerEngine over spaCy
  en_core_web_sm + 3 HL7-specific custom recognizers (HL7_MRN, HL7_CARET_NAME,
  HL7_PHONE_BARE). POST /redact and GET /health.
- lib/phi-sidecar.sh — lifecycle (start/stop/status/health/ensure). ensure
  is idempotent; called backgrounded from main_loop so it never blocks the
  first prompt. Honors LARRY_PHI_VENV.
- lib/phi-client.sh — bash client (phi_client_available / phi_redact_text /
  phi_redact_entities). CR-safe; 5s timeout bounds tier-5 stall.

larry.sh:
- auto_detect_phi gains tier-5: after tiers 1-4, before status summary,
  source phi-client.sh, run Presidio on a token-masked copy of the input,
  tokenize each entity through hl7-sanitize.sh tokenize-value (category
  presidio_<TYPE>) so token IDs stay stable. Honors confirm + strict modes.
  Removed the v0.7.3 early-return that skipped past tier-5 when tiers 1-4
  found nothing — pure prose now always reaches tier-5.
- Token-safe substitution: existing [[...]] tokens are pulled to sentinels,
  tier-5 value is replaced, sentinels restored — prevents the token-within-
  token corruption that naive literal-replace caused on already-tokenized
  text. Acronym guard drops HL7/clinical jargon (SSN/MRN/DOB/ADT) Presidio
  over-tags as ORGANIZATION.
- Graceful degradation: sidecar unreachable → tier-5 no-ops with a one-time
  stderr warning. /phi-sidecar slash command + completion table.

install-larry.sh:
- Probes python3 3.9+; offers to create $LARRY_HOME/phi-venv and install
  presidio + fastapi + uvicorn + en_core_web_sm. Skips silently (with a
  v0.8.1-mode note) on Cygwin/MobaXterm without python3, and on
  non-interactive pipe installs. Sets LARRY_PHI_VENV in the larry shim.

MANIFEST: three new lib files added for auto-sync.

Prototype validation (Bryan's Mac, Apple Silicon, Python 3.14):
  cold start (en_core_web_sm): ~9s   (vs ~82s if Presidio auto-grabs _lg;
                                       we pin _sm for the REPL budget)
  warm analyzer latency:       P50 20.6ms / P95 22.7ms
  end-to-end HTTP round-trip:  ~57ms warm; ~150ms first-post-startup
All comfortably under the 200ms-per-turn budget.

MobaXterm verdict: v0.8.2 is Mac/Linux-only. MobaXterm stays on v0.8.1 +
nudges, per Bryan's explicit acceptance. install-larry.sh enforces this
by platform detection; larry.sh tier-5 silently no-ops when the sidecar
is absent (which IS the MobaXterm path — no code is platform-gated).

Verification: bash -n clean on larry.sh + all 3 new lib scripts; python3
ast.parse clean on the sidecar; end-to-end tier-5 tested live against the
sidecar (pure prose, rule-pack+tier-5 combined with no token corruption,
!nophi bypass); strict-mode fail-closed abort tested; CR-taint, path-block,
and base64 round-trip batteries re-run green.

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 20:00:23 -07:00
9fc38e743d v0.8.1: tool-result content-shape gating + base64 round-trip + review gate
Three changes expanding PHI safety envelope on the tool-result surface.
Closes V2 + V12 + V2-sub from Vera's audit. No behavior change for users
not interacting with HL7-shaped data.

- Tool-name allow-list dropped. The v0.7.3 tool-result auto-PHI gate ran
  only on read_file (.hl7|.txt), nc_msgs, hl7_field, hl7_diff. v0.8.1
  runs _auto_phi_looks_like_hl7 on EVERY tool result. On hit → route
  through lib/hl7-sanitize.sh. On miss → pass through unchanged.
  Closes V2: bash_exec / ssh_exec / grep_files / read_file of any
  extension all get scanned when their output is HL7-shaped. False-
  positive cost is negligible (extra regex pass on non-HL7 has zero
  behavioral impact).

- Base64-wrapped HL7 round-trip. New _auto_phi_b64_roundtrip helper.
  Detects candidate base64 runs (length >= 200, [A-Za-z0-9+/=] only,
  length divisible by 4 — NOT entropy-based per Pax §V2-sub: HL7's
  repetitive prefixes survive base64 with LOW entropy, so entropy is
  the wrong signal). Speculatively decodes each candidate; if decoded
  bytes look like HL7, routes through hl7-sanitize.sh and re-encodes
  back into the result. Catches ssh_pull_smat sampled mode's TSV
  format. Requires python3 (installed everywhere larry runs); skipped
  with a one-time stderr warning when unavailable. Server-side TSV
  encoding kept (binary-safe transport); client-side unwrap handles
  the safety concern, no remote refactor needed.

- Operator review gate for bash_exec/ssh_exec/ssh_pull/ssh_pull_smat
  results. When the tool produced HL7-shaped output OR the result
  exceeds LARRY_TOOL_RESULT_REVIEW_THRESHOLD bytes (default 8192),
  Larry prompts [Y/n/i] before passing the result back to the model.
  'i' opens the full output in $PAGER then re-prompts. Default Y
  (zero friction). N substitutes a refusal JSON so the model surfaces
  that something was withheld. Skipped when LARRY_AUTO_PHI=off (opt-out
  consistency) OR no TTY (headless scripts unaffected). Override with
  LARRY_TOOL_RESULT_REVIEW=always for paranoid mode. Closes V12.

Proactive same-pattern sweep. Searched for other call sites where tool
output bypasses content-shape gating: only the one in agent_turn. The
v0.8.0-c strict-mode tool-result branch was updated in lockstep so it
now triggers on the broader (content-only) eligibility.

Verification: bash -n clean; b64 round-trip unit-tested with three
cases (real-world HL7 base64 → decoded contains tokenized PHI not
clear-text PHI; plain text → passthrough; non-HL7 b64 → passthrough,
no false positive).

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 19:45:23 -07:00
7434e6e8b8 v0.8.0: PHI safety quick-wins — path-block + /load HL7 routing + strict mode
Three independent zero-risk patches closing V3/V4/V5/V6/V11 gaps from
Vera's static PHI-leak audit. Implemented per Pax's mitigation
recommendations. No new deps, no behavior change for users not handling PHI.

- tool_read_file / tool_grep_files / tool_glob_files / tool_list_dir now
  refuse paths under $LARRY_HOME/{log,sanitize,sessions} and
  $LARRY_HOME/{.oauth.json,.env} with a structured JSON error the model
  must surface. Block-list evaluates at call time; comparison runs against
  both the literal and realpath-canonicalized form of both PATH and
  $LARRY_HOME. Closes V4 + V6 + V11 (de-sanitization key, OAuth tokens,
  PHI clear-text audit log). The proactive same-pattern sweep extended
  the block from read_file alone to grep_files/glob_files/list_dir.

- /load <file> pre-routes HL7-shaped content through lib/hl7-sanitize.sh
  (segment-aware tokenizer) BEFORE the user_input auto-PHI pass. Closes
  V3 — smat dumps loaded via /load no longer rely on the lighter per-word
  classifier.

- LARRY_AUTO_PHI=strict (fourth value alongside off/on/confirm) is the
  fail-closed mode. Aborts the turn when sanitizer is missing or returns
  empty on HL7-shaped content, or when tokenize-value fails. On the
  tool-result surface (can't kill an in-flight tool_use), substitutes
  the result with a refusal sentinel so raw HL7 NEVER reaches the model.
  Existing off/on/confirm semantics unchanged. /phi-auto strict toggle,
  /help text, and tests updated. Closes V5.

Refs:
  Deliverables/2026-05-27-cloverleaf-larry-phi-leak-audit.md (Vera)
  Deliverables/2026-05-27-cloverleaf-larry-phi-mitigation-research.md (Pax)

Verification: bash -n clean; path-block unit-tested with 13 cases including
symlink resolution (file and dir), ../ traversal, nonexistent paths, and
the empty-LARRY_HOME edge case — all pass.

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 19:38:42 -07:00
9dd5821436 v0.7.5: OAuth CR-taint fix + mouse opt-in + CR-safety sweep
- Fix bash arithmetic crash on MobaXterm/Cygwin: $(date +%s) was
  returning CR-tainted values landing in $(( )) operands
- Mouse mode off by default; opt in via LARRY_MOUSE=1 or /mouse on
- Comprehensive CR-safety sweep across lib/*.sh and larry.sh — every
  command-substitution result, file read, and user input that feeds
  an arithmetic context, case dispatcher, or path/header is now
  CR-stripped at the source

New shared helper lib/cygwin-safe.sh defines three primitives:
  coerce_int VAL [DEFAULT]   — for arithmetic / integer-test operands
  strip_cr VAL               — for case patterns, regex tests, paths, headers
  read_clean VAR [PROMPT]    — read -r wrapper that strips CR pre-assign

Hardened call sites (14 files, 60+ patch points):
  - larry.sh:  status-line date/tput, 3 y/N approvals, auth menu, API key
  - lib/oauth.sh:  cmd_login + cmd_refresh date+%s captures
  - lib/nc-engine.sh:  5 y/N action prompts + find|wc arithmetic
  - lib/nc-msgs.sh:  parse_time_ms (4 date sites) + meta-TSV time + MSG_COUNT
  - lib/nc-regression.sh:  tr|wc count + hl7-diff ?-fallback arithmetic
  - lib/nc-smat-diff.sh:  A_COUNT/B_COUNT/DIFFS_TOTAL
  - lib/nc-insert-protocol.sh:  every awk-emitted line number → head/tail math
  - lib/journal.sh:  _next_seq wc -l arithmetic
  - lib/lessons.sh:  _next_id/_count + 2 y/N prompts
  - lib/hl7-sanitize.sh:  cmd_count + clear-table y/N
  - lib/ssh-helper.sh:  4 local+remote wc -c integer compares
  - lib/nc-find.sh, lib/nc-table.sh, lib/nc-document.sh, larry-rollback.sh

Reproduces the exact error Bryan hit:
  bash: ...: arithmetic syntax error: invalid arithmetic operator (error token is "")

lib/cygwin-safe.sh added to MANIFEST so it auto-syncs on next launch.

Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
2026-05-27 19:17:48 -07:00