| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
| |
The bundle hook now prints a short test summary to the terminal on a red test (the run tally and the failing test names) and keeps the full ERT backtrace in the agent's context. Synced the project copy to match.
|
| |
|
|
|
|
| |
diff-refine-changed rendered the default foreground (#f0fef0) on the bright-gold yellow-1 (#ffd700) at WCAG contrast 1.35, unreadable wherever the face is a plain background, not inside diff-mode's own foreground overlay. diff-changed was weak too at 2.78.
Both now use the default foreground on the dark amber yellow-2 (#875f00), already in dupre's palette, for a contrast of 5.49. diff-refine-changed keeps its bold weight so it stays stronger than diff-changed. A WCAG-contrast test guards both faces from dropping below 4.5.
|
| |
|
|
| |
dupre's diff-refine-changed is bright gold under near-white text (WCAG ~1.35), unreadable as a plain background. Filed with the side-by-side comparison render from the pearl session that surfaced it.
|
| |
|
|
|
|
| |
The pearl package moved from a single Linear workspace to multi-account, so the config follows. pearl-accounts now declares two workspaces, one for work and one personal, each resolving its own API key from authinfo and rendering to its own Org file. pearl-default-account picks which one opens, and pearl-switch-account swaps at runtime.
This replaces the old single-account setup (pearl-org-file-path plus one pearl-api-key lookup). The module file moves from linear-config.el to pearl-config.el to match the package name. init.el, the module-headers allowlist, and the module-inventory row follow the rename.
|
| |
|
|
| |
Reorder the Emacs Open Work section: Calibre pulled to the top, the message-client and localrepo tasks regrouped. Stamp 7 task-review tasks with today's date and regrade the dashboard-keybinding task to B. Mark the M-F9 close-removes-split bug DONE now that the fix shipped in 1a097b7e.
|
| |
|
|
| |
M-F9 close deleted the agent's window, collapsing the layout. Close now swaps that window to the working buffer and kills the agent buffer, so the split stays put. F9 hide still collapses the split. Close no longer does.
|
| |
|
|
| |
Created a Calibre parent grouping the bookmark-title, keybindings, and metadata-embed children. Relocated the project-aware capture and Calibre-keybindings items out of the global capture inbox. Added tasks for project-aware capture, the "? = curated help menu" convention survey, and embedding Calibre's DB metadata into the book files. Flipped the in-progress tasks to DOING and added manual-verify checklist items for the shipped features.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Three things in the calibredb search buffer.
A curated transient on ? exposes just my frequent workflows (switch library, filter by format or author, sort by author/title/pubdate/format, open, describe) instead of leaving me to remember calibredb's top-level single keys. calibredb's own full dispatch moves to H. which-key can't help here: these are mode-map single keys, not a prefix. So a curated menu on ? is the discoverable entry point. The transient is defined in :config rather than top-level: its macro expands against the elpa transient, but a batch Emacs loads the older built-in transient and breaks the load.
The book-detail view (d, or v) now docks to the bottom 30% and q dismisses it. calibredb-show-entry-switch goes to pop-to-buffer so a display-buffer-alist rule applies. The default switch-to-buffer-other-window ignores it. It's the same bottom-dock pattern as the signal chat buffer.
Sorting kept dropping the active filter. calibredb's macro-generated sort commands all end in calibredb-search-refresh-and-clear-filter, which nulls every filter flag. I override each to refresh via calibredb-search-refresh-or-resume, which re-applies the filter, so a sort over a filtered list keeps it. I used named advice, so reloading the module doesn't stack it.
Tests cover the describe command and the sort helper. The transient, bindings, dock, and advice wiring need the elpa transient and a live calibredb, so they're verified in the running daemon.
|
| |
|
|
|
|
|
|
|
|
| |
In a nov buffer m is bound to bookmark-set. nov's nov-bookmark-make-record names the record after the buffer name, the raw EPUB filename. So bookmarks read like "Frege_ A Guide for the Perplexed - Edward Kanterian.epub".
I advise nov-bookmark-make-record to rebuild the name from the record's filename: split on the last " - " into title and author per Calibre's "<Title> - <Author>.epub" naming, restore the colon Calibre sanitized to "_ ", and reorder to "Author, Title". That book becomes "Edward Kanterian, Frege: A Guide for the Perplexed".
I pulled the name from the filename rather than the embedded EPUB metadata on purpose. In real books the embedded metadata is the worse copy (truncated titles, authors in "Last, First" sort form, lost punctuation), while the filenames come from Calibre's curated database. A separate task tracks embedding the good metadata back into the files.
cj/--nov-clean-title and cj/--nov-bookmark-name-from-file are pure and carry the logic. The advice is a thin wrapper. 10 ERT tests cover colon restoration, the last-separator split, and the no-separator fallback.
|
| |
|
|
|
|
|
|
| |
C-c c t (Task) and a new C-c c b (Bug) now file into the current projectile project's todo.org under its "... Open Work" heading instead of always landing in the global inbox. Bug stamps the entry [#C]. Task stays a plain TODO.
Both share one function target, cj/--org-capture-project-location. It matches an existing top-level "... Open Work" heading when there is one, so a directory like .emacs.d resolves to the existing "Emacs Open Work" rather than a name derived from the basename. It only creates "<Project> Open Work" when the file has none. Outside a project, or in a project whose root has no todo.org, it falls back to the global inbox under "Inbox". In the no-todo.org case it also warns, naming the project. It never creates a project's todo.org.
I split the logic into pure helpers (project name, target decision, find-or-create heading) so they test directly, with the impure buffer-positioning left thin. 15 ERT tests cover the helpers and the wiring. I confirmed a real capture lands the entry under Open Work at the right level in the running daemon.
|
| | |
|
| |
|
|
|
|
|
|
| |
Entering copy-mode from C-; x c left the cursor at the live column, far right after a prompt, so scrolling up ran the cursor up the right edge instead of the left.
In the tmux branch I append C-a after C-b [, which tmux's emacs copy-mode reads as start-of-line. It has to go after C-b [: before copy-mode is active, C-a would hit the shell's readline instead. In the non-tmux ghostel-copy-mode branch I call beginning-of-line after entering the view, for the same column-0 result. Both branches now run the cursor up the left edge.
The non-tmux test asserts ghostel-copy-mode runs before beginning-of-line, since the move only repositions inside an active copy view. Its tracker variable is dwim-order, not calls, to avoid clashing with the variable the tmux mock macro binds.
|
| |
|
|
|
|
|
|
| |
The C-; M Signal prefix didn't take effect on a fresh Emacs launch. signal-config.el was the only feature module that bound into cj/custom-keymap directly, wrapped in (with-eval-after-load 'keybindings (when (boundp 'cj/custom-keymap) ...)). The boundp guard turned a load-order miss into a silent no-op, so the binding never landed at startup. A later live-reload always papered over it because keybindings was loaded by then.
I switched to the documented cj/register-prefix-map helper and added (require 'keybindings) at the top, matching every other prefix map. The require guarantees keybindings loads before registration, so the guard is gone.
I verified at a full emacs --batch init.el launch, the actual failing scenario, that C-; M resolves to the signel prefix. I added a contract test asserting the registration, since the boundp guard was robust under unit timings and only failed at full launch.
|
| |
|
|
| |
The Duet design work graduated into a standalone package, so its task moved out of this todo. modules/duet-config.el is a use-package stub for it, inert until wired into init.el. Daily task-review also synced three ghostel follow-up priorities to their parent.
|
| |
|
|
| |
The shipped no-dim behavior works fine, which is the measured decision the task asked for, so the revisit is unneeded. Rewrote it to a dated cancellation log entry per the sub-task close rule.
|
| |
|
|
| |
Inside a ghostel terminal or agent buffer, semi-char mode forwarded F10 and C-F10 to the pty, so the music-playlist toggle and the server-shutdown command never ran. Both are global bindings with no ghostel-mode-map entry, so I added them to ghostel-keymap-exceptions and rebuilt the semi-char map. The lookup then falls through to the global map. Same shape as the earlier F9, F12, and window-nav fixes.
|
| |
|
|
| |
Synced into .claude/rules/ by the startup language-bundle drift check. Points at the rulesets pattern catalog for interaction-design decisions.
|
| |
|
|
| |
Daily task-review pass over the seven oldest-unreviewed tasks: all kept as-is and re-stamped to 2026-06-05. Tagged the TTY C-; keymap task :solo:quick: and the Slack popup task :quick:.
|
| | |
|
| |
|
|
|
|
|
|
|
|
| |
Two keystrokes weren't reaching Emacs inside a ghostel terminal, both because of how ghostel routes keys in semi-char mode.
C-SPC was the worse one. ghostel forwards the `C-@' event but not the distinct `C-SPC' event GUI Emacs produces, so C-Space fell through to the global `set-mark-command' and set an Emacs region in the terminal buffer. That region followed point as output streamed (a stuck "selection" Escape and C-g couldn't clear), and it meant tmux copy-mode's begin-selection never started, so M-w copied nothing. I bind C-SPC to cj/term-send-C-SPC, which forwards NUL like a terminal key.
The C-M-arrows (buffer-move, window swap) were being forwarded to the terminal program the same way the F9 family was. I added the windmove S-arrows and buffer-move C-M-arrows to ghostel-keymap-exceptions and rebuilt the semi-char map. The S-arrows already reached Emacs by keymap precedence, but listing them makes the window-nav contract explicit rather than accidental.
Regression tests cover all three: C-SPC bound to the forwarder, and the window-nav keys in the exceptions with the semi-char map no longer forwarding them.
|
| |
|
|
|
|
|
|
| |
F9 did nothing in an agent buffer: ghostel's semi-char mode forwards every key not in ghostel-keymap-exceptions to the pty, and ghostel-semi-char-mode-map outranks the major-mode map, so the F9-family and F12 bindings I'd put in ghostel-mode-map never fired. The keys went to Claude/the shell, which ignored them.
I added the F9 family (in ai-term) and F12 plus C-; (in term-config) to ghostel-keymap-exceptions and rebuilt the semi-char map with ghostel--rebuild-semi-char-keymap. add-to-list updates the list but not the already-built map, so the rebuild is what actually lets the keys through. C-; had the same latent bug for the same reason.
Two regression tests assert the keys are in the exceptions and that the rebuilt semi-char map no longer forwards them. I also corrected the spec note that claimed binding in ghostel-mode-map was enough (true for vterm, wrong for ghostel) and codified the gotcha.
|
| |
|
|
|
|
|
|
| |
I swapped the terminal engine from vterm to ghostel (libghostty-vt) everywhere. term-config replaces vterm-config (the F12 terminal, the C-; x menu, tmux history capture), and ai-term replaces ai-vterm (the F9 Claude-agent launcher). ghostel renders the agent TUI without vterm's flicker under heavy streaming, and one engine now covers every terminal workflow.
Two behavior changes fall out of the swap. F9 launches in a terminal frame now: ghostel renders in TTY frames, so the old GUI-only guard is gone. Terminal windows no longer dim when unfocused: ghostel resolves its palette into the native module per-terminal, so there's no per-window color hook to dim through the way vterm had.
auto-dim drops its vterm color-advice path, the dashboard Terminal button launches ghostel, and the vterm and vterm-toggle packages are removed. The tmux pane-history and copy-mode machinery carried over unchanged. It keys on the pty tty, which ghostel exposes.
|
| |
|
|
|
|
| |
I restored the heading "Color dashboard navigator independently of list items" above its PROPERTIES drawer. The 2026-06-02 archive cleanup dropped the heading, which orphaned the drawer and body into the preceding DONE task and tripped org-lint's obsolete-properties-drawer check. org-lint is clean again.
I also filed "TTY-accessible personal C-; keymap" [#B]: C-; is GUI-only, so the whole custom prefix family is unreachable in a terminal. The task records the single-point fix in keybindings.el and the candidate TTY-safe mirror prefixes.
|
| |
|
|
|
|
|
|
| |
I bound pearl-prefix-map globally under C-; L with :bind-keymap, so the full command surface is reachable from any buffer instead of only inside a pearl-rendered one or through M-x. The first press autoloads pearl. It's the same map pearl-mode binds in-buffer, so behavior is identical everywhere.
I also set pearl-assignee-tag-short, so the assignee @-tag renders as the first name only (@first instead of @first_last), trading disambiguation for a tighter tag line.
Both layer onto the prior vanilla setup after dogfooding the out-of-box experience. I updated the commentary to match.
|
| | |
|
| |
|
|
| |
Move the f9 toggle, completing-read prompts, and GPG pinentry subtrees into Resolved, and file the pearl vanilla-config dogfooding follow-ups surfaced by the cross-project handoff.
|
| | |
|
| |
|
|
|
|
|
|
| |
The config carried a full custom command surface: a hand-built C-; L keymap, which-key labels, a lazy API-key loader wired in as advice, and a pinned DeepSat team id. That's worth keeping only if the out-of-box pearl experience isn't good enough on its own, and the point now is to find out.
Stripped it back to exactly what pearl's README documents for a first install: pearl owns its own keymap (pearl-mode binds the command surface under pearl-keymap-prefix, default C-; L, in any Linear buffer), no global binding, no advice. The API key comes from authinfo.gpg via auth-source-pick-first-password, and the synced file moves to gtd/linear.org under org-directory. The only deviation from the README is loading from the local checkout at ~/code/pearl instead of an archive.
Deleted tests/test-linear-config.el — it covered the custom helpers (the key-loader advice, the keymap wiring) that no longer exist. What's left is declarative use-package config with nothing of its own to test.
|
| |
|
|
|
|
|
|
| |
A picker prompt is the last thing shown before a command commits, so a bare noun leaves a mis-keyed command ambiguous. Hitting C-f8 (project agenda) instead of C-f9 (AI-vterm picker) gave the same "Project:" prompt with no signal which one was about to run.
Reworded 17 prompts across 8 modules so each names the operation rather than just the thing being chosen: "Project:" becomes "Show agenda for project:", "F6:" becomes "Run tests:", the dwim-shell sub-prompts gain their context (checksum algorithm, PDF compression quality, text-to-speech voice, run dwim-shell command), the two contact pickers split into "Find contact:" and "Insert contact email:", and the dirvish ediff, org finalize, and custom-comments length/box-style prompts get the same treatment.
I audited all ~124 completing-read / read-* call sites; the rest already named their operation and were left alone. These are prompt-string changes only, no logic touched.
|
| |
|
|
|
|
|
|
|
|
| |
Audit reconciliation:
- EAT consolidation: moved DOING back to TODO — the eval matrix has no recorded run since the 2026-05-26 direction note, so it isn't active work.
- Finish terminal GPG pinentry: CANCELLED as a duplicate. The "Terminal GPG pinentry Completion" task is canonical; its audit already found the terminal-pinentry branch gone, so the work restarts from main and is tracked there.
- M-F9 close: added a note that it's still a bug, distinct from the F9 toggle collapse — close should leave the surrounding layout intact, and cj/--ai-vterm-close-buffer still deletes the window.
- De-linked the coverage-v1 cross-reference, which pointed at a session file that was never archived.
Review: re-stamped LAST_REVIEWED on the seven oldest-unreviewed tasks (debug-profiling, org-noter, API workspace, Company-to-Corfu, F2 preview, TRAMP/dirvish, F-key Completion), all kept as-is.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
F9 toggle-off used quit-restore-window to dismiss the agent. With several agents alive sharing one slot, switching among them (C-F9) reuses the window via set-window-buffer, which leaves the window's quit-restore parameter pointing at the first agent shown. Once stale, quit-restore-window falls back to switch-to-prev-buffer and surfaces a different agent instead of removing the window, so F9 appeared to "show another agent" rather than hide the split.
Toggle-off now collapses the split with delete-window, which is independent of the slot's buffer history, so the working buffer reclaims the frame. Geometry is captured first so the next toggle-on re-splits at the same width.
Toggle-on reopens the exact agent that was hidden (new cj/--ai-vterm-last-hidden-buffer), falling back to the most-recent agent only when that buffer has been killed. Hide-then-show is now a faithful round trip, not a jump to whichever agent is most-recent in buffer-list.
Sole-window toggle-off returns to the most-recent non-agent buffer instead of other-buffer, which could land on another agent.
I updated the two reuse-edge-window tests that asserted the old restore-displaced-into-a-kept-slot behavior to match the new always-collapse behavior.
|
| |
|
|
| |
Updating authinfo.gpg in a running daemon does nothing until auth-source-forget-all-cached clears its two-hour result cache. I added the symptom, cause, and fix to the running-Emacs caveats so the next session reaches for the cache clear instead of re-diagnosing a "key not set" error.
|
| |
|
|
| |
I stamped LAST_REVIEWED on the seven oldest-unreviewed tasks and tagged the gptel-magit velox bug and the Signal dashboard task as quick.
|
| |
|
|
| |
The daily-prep workflow now keeps its stable symlink at the project root as daily-prep.org instead of inbox/today-prep.org. I repointed cj/open-project-daily-prep (C-c p d) to match, updating its docstring, not-found message, and test for the new path.
|
| |
|
|
|
|
| |
AI-vterm launches a graphical vterm side window, so F9 / C-F9 / M-F9 now decline with a message in a terminal frame instead of opening a vterm. The guard checks the current frame at command time rather than at load. That matters under the daemon, which serves GUI and terminal frames both with display-graphic-p nil at load, so a load-time gate would have disabled the launcher in its GUI frames too.
Routed the three window-behavior tests through a GUI-frame stub, since a batch run is itself a terminal frame.
|
| |
|
|
|
| |
velox's gptel is below the 0.9.8 that gptel-magit requires, so the package
fails to activate at startup. Captures the repro and the version-check fix.
|
| | |
|
| | |
|
| | |
|
| |
|
|
| |
I folded the "Chat buffer placement + exit keys" task into a dated event-log entry under the DOING Signal parent. The work landed in dotemacs 998e9c7a (bottom-30 docking via display-buffer-alist plus tests) and signel-fork df02d79 (pop-to-buffer + signel--cancel-input + C-c C-k binding).
|
| |
|
|
|
|
|
|
| |
I added a display-buffer-alist entry matching "*Signel:" chat buffers and routing them through display-buffer-at-bottom with window-height 0.3. The signel fork's signel-chat now uses pop-to-buffer instead of switch-to-buffer, which is what makes the rule apply. Without that switch the buffer replaces the current window and skips display-buffer entirely.
Two new tests in test-signal-config.el lock the entry shape and the regex's buffer-name match set. A new test-signel-cancel-input.el covers the fork's C-c C-k handler. It clears the editable region between signel--input-marker and point-max, then quit-windows so the buffer survives the dismiss.
Closes the "Chat buffer placement + exit keys" task filed during the 2026-05-28 manual-verify walk.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Walked the open-task backlog twice tonight. The first pass was a content audit (is each task still factually accurate?). The second was a relevance/priority review. Together they surfaced enough drift to be worth landing as one batch rather than dribbling into the next session.
The audit filed three new completion-task parents, each with an audit-finding body and child-task recommendations: F-key Completion (roughly 75% shipped per evidence), Terminal GPG pinentry Completion (no trace of the prior branch on this machine, treat as fresh), and Localrepo Documentation (build is shipped, docs land in three artifacts, four gap-fix tasks spin out as siblings). Headline-indicators-wrap and Buttercup closed DONE, Rework-dev-F-keys cancelled as superseded.
Manual Testing and Validation became its own top-level task with the 10 misfiled verify children moved in. Walk started tonight (tests 1 and 2 verified, two signel bugs surfaced and fixed in the same session), deferred to 2026-05-29 for the message-sending tests.
The Buttercup eval doc captures the rubric I came to during the brainstorm: adopt the moment a project crosses the "test reader is no longer the test author at write-time" threshold. ERT stays the right default until then. None of my projects have crossed yet.
Lint pass resolved all 21 org-lint follow-ups inline rather than letting them accumulate in a hidden inbox: 5 wrong-prefix design-doc links (../docs/design/X.org should have been docs/design/X.org), 4 file:line bare references wrapped in code formatting, 1 timestamp moved out of org-link brackets, 1 nested src block converted to begin_example, the wttrin diagnostic's stale link replaced with a note about where the surviving record lives, 8 markdown-bold patterns converted to org italics, 2 verbatim ** TODO references trimmed so the linter stops misreading them as headings.
|
| |
|
|
|
|
|
|
|
|
| |
cj/signel--ensure-started in modules/signal-config.el was reading signel--process-name in the first branch of its cond before the use-package autoload of signel had fired. The forward-declared (defvar signel--process-name) at L137 silences the byte-compile warning but doesn't actually bind the variable. Its value comes from signel.el's defconst, which doesn't run until signel is loaded.
The first call to cj/signel-connect (C-; M SPC) after Emacs launch produced "Symbol's value as variable is void: signel--process-name" instead of starting the daemon. Surfaced tonight during the manual verify walk of the initiate-message workflow.
I added (require 'signel) at the top of cj/signel--ensure-started so signel loads before any of its variables get read. The require is idempotent, so callers that hit the function after signel is already loaded pay nothing.
The new ERT test test-signal-config-ensure-started-requires-signel-first asserts ordering: require must be the first call inside the function, not just called somewhere. A future refactor that moves the require below the cond would fail this test instead of passing silently.
|
| |
|
|
| |
The wrap-up's sync-child-priority pass bumped two intentionally-deprioritized children of the Signal parent: the [#C] handle-error leak and the [#D] groups vNext. Both are deliberate. The leak is bounded by the stop/start clrhash and the groups task is explicitly post-1:1-stability, so I tagged them :no-sync: to stick at their chosen priorities across future wrap-ups.
|
| |
|
|
|
|
| |
The earlier checklist was one sub-header with all the steps in a single bullet list, which means a partial walk-through can't be marked done piece by piece. Per the manual-testing convention, one sub-header per scenario is the format that lets a failing step promote cleanly to its own bug.
I split the original entry into 9 sub-headers, one per scenario, each with a "What we're verifying" line, the exact key chords or commands, and a single Expected outcome. The set covers connect, picker opens with names, send-a-message, Note-to-Self via the direct command (the spec's D3 critical check), Note-to-Self via the pinned picker entry, the clobber-fix survival test, dashboard, stop, and refresh.
|
| |
|
|
|
|
|
|
| |
initiate-message workflow
I shipped the picker, daemon guard, cache, keymap, and clobber fix as unit-tested code (commits 4daf2328 here, 4740d97 and 5ec56c0 in the signel fork). The unit tests cover branches and contracts but can't exercise a real signal-cli daemon against a linked phone. This checklist captures the live verification I'll walk through on my next restart of Emacs.
Each step lists the exact command or key, the expected observable outcome, and (implicitly) the failure signal: any deviation gets logged under the failing step and promoted to a TODO bug, per the verification convention.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I built pieces 2-7 of the initiate-message workflow from docs/design/signal-client.org and added tests covering the fork's clobber-fix (commit 5ec56c0 over there). The picker is the user-facing change: a single key opens a name-based completing-read for any contact, with "Note to Self" pinned first.
The picker stack from the bottom up:
cj/signel--ensure-started is the daemon guard. With a live process it's a no-op. With signel-account set but no process it calls signel-start and pre-warms the contact cache. With signel-account nil it user-errors naming the remedy. Pre-warming on start means the picker feels instant on first use.
cj/signel--fetch-contacts issues a listContacts RPC through the new request-callback contract (signel--send-rpc with a success-callback). The callback runs the result through the verified cj/signal--parse-contacts and stores the (LABEL . RECIPIENT) alist in cj/signel--contact-cache, a cj-owned variable kept separate from signel's receive-time contact-map. An empty result populates the cache as nil, distinct from an RPC failure (which never invokes the callback so the prior cache survives). cj/signel-refresh-contacts is the user-facing command that clears and refetches.
cj/signel-message is the picker. Warm cache opens completing-read immediately. Cold cache kicks off a fetch and accept-process-outputs up to cj/signel-fetch-timeout seconds (3s default), then user-errors if the daemon hasn't responded so a wedged process can't hang Emacs. The candidate list pins "Note to Self" first (resolves to signel-account) with a display-sort metadata function that preserves the given order rather than alphabetizing.
cj/signel-message-self skips the picker and goes straight to signel-account. cj/signel-connect is the friendly verb on the prefix key.
cj/signel-prefix-map binds m / s / d / q / SPC and attaches under C-; M via with-eval-after-load keybindings so the binding survives load-order. l stays unbound for the future link command.
15 new ERT tests cover the ensure-started branches, the fetch + cache contract (issued, populated, empty), refresh-contacts, the picker's four scenarios (warm-cache contact, warm-cache Note to Self, cold-cache resolves in time, cold-cache timeout), message-self, and the keymap bindings. Plus 4 new tests in tests/test-signel-input-preservation.el for the fork's clobber fix: pending-input captures typed text and returns nil when empty; both signel--insert-msg and signel--insert-system-msg redraw the prompt without clobbering "halfwritten".
todo.org closes three tasks as dated event-log entries: the contact picker, the input clobber, and the use-package wiring.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The fork commit (4740d97 in the signel fork) added a request-callback table, extended signel--send-rpc with an optional success-callback, and routed result responses through signel--dispatch. These tests lock that contract from the consuming project so a future fork change can't silently break the picker that will read listContacts through it.
Five tests, Normal / Boundary / Error categories plus a reconnect-invalidation case:
- result-invokes-callback (Normal): a result response with a registered id fires the callback with the value and removes the handler.
- send-rpc-registers-success-callback (Normal): passing a success callback stores it under the returned id.
- unknown-id-is-noop (Boundary): a result with no registered id is silent — no receive or error handler fires, map stays empty.
- error-cleans-up-handler (Error): an error response removes the handler without firing the callback, so a retry starts clean.
- stop-clears-handler-map: signel-stop empties the map, so a restart can't replay stale callbacks waiting on responses that will never arrive.
The dispatch tests synthesize JSON alists directly. No live process is needed. The send-rpc test stubs get-process and process-send-string so it doesn't need a running signal-cli.
Refactor audit on signel.el surfaced one unrelated pre-existing smell I'm not fixing here: signel--handle-error reads from signel--request-buffer-map but never remhashes, so error responses leak request-id → buffer-name entries. Filed as a separate [#C] follow-up under the Signal parent task; the maps clear on stop/start so the impact is bounded to a single live session.
todo.org: the dispatch task flips to DOING (the fork commit is in, the test contract is locked) and gets the leak follow-up appended.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I wrote an initiate-message workflow spec on top of the existing Signal client design doc, covering the keymap, name-based picker, message-to-self, and the whole flow. A follow-up review caught three blockers I'd hand-waved: signel had no JSON-RPC success-result dispatch path (so cj/signel--fetch-contacts couldn't actually receive listContacts results), D4's "auto-connect when linked" didn't define what "linked" meant or how process death surfaced, and the contact cache had no ownership or invalidation story.
I verified the central one against the fork. signel--dispatch handles only "receive" and error responses, so success results were dropped. Then I folded all three into an Architecture additions subsection: a request-callback table keyed by JSON-RPC id with cleanup on success/error/reconnect, a cj/signel--ensure-started contract with branches for live process / account-set / account-nil, and a cj-owned cj/signel--contact-cache separate from signel's receive-time map.
A second review pass caught the remaining sync/async boundary. completing-read is synchronous and the fetch is async. I resolved it with pre-warm on connect plus a bounded accept-process-output fallback for cold caches, so the warm case feels instant and a dead daemon can't hang Emacs.
The follow-up re-review then converged to Ready-with-caveats and surfaced one concrete code finding I'd missed: the #2 input-clobber fix has to cover both signel--insert-msg AND signel--insert-system-msg, since both delete from the prompt line through point-max. The pieces-to-build entry and the prompt-preservation regression test now name both paths.
A few smaller tightenings landed in the same pass. The listContacts assumption is now a researched fact (verified on 94 contacts), the two open questions on account source and fork remote are marked decided (defcustom in the gitignored local config, local checkout with no remote for now), and a forward-flag in the scope summary names the four notification details to spec before that later slice starts.
docs/design/signal-client-review.org carries the review as the closure record. todo.org gets two tasks: a [#B] for the JSON-RPC success-result dispatch (the first build step), and a [#D] for groups in the picker as a vNext after the 1:1 flow is stable.
Spec is Ready. Implementation order is pinned to the Pieces-to-build list. RPC dispatch first, then the guard, then fetch/cache, then the picker and keymap, then the clobber fix.
|
| |
|
|
|
|
|
|
|
|
| |
The *AI-Assistant* side window always opened at a fixed 0.4 width, so resizing it by hand was lost the next time it opened. Now the F-key toggle captures the panel's width when it closes and reopens at that width for the rest of the session, the same way the music playlist remembers its height.
The panel has three entry points that all open the same buffer: the toggle, loading a saved conversation, and escalating a quick-ask. I gave them one shared remembered-width var (cj/--ai-assistant-width, owned by ai-config; the other two forward-declare it to avoid a circular require), so the panel comes back at one consistent width whichever door opens it. Capture lives only in the toggle's close path; the other two just replay.
One latent edge: ai-conversations replays with its configurable side, which defaults to right. If that's ever set to top or bottom, the remembered width fraction would land as a height. It can't happen at the default, so I left it as a known edge rather than complicating the call now.
The escalation test needed a top-level defvar for the shared var: a value-less defvar in the module is only file-local to the byte-compiler, so without it the function read the var dynamically and hit void when the test loaded ai-quick-ask without ai-config. ai-quick-ask 13/13 and ai-conversations 47/47 green.
|