Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Added

- **Queue several commands while one runs** ([#473](https://github.com/raiseCatError/notMyShell/issues/473), [#426](https://github.com/raiseCatError/notMyShell/issues/426)). **Ctrl+Q on an empty line** while a command runs opens the queue composer for that command: every line you enter in it is queued to run after it, shown as typed, once and in order, with no approval step — what Ctrl+Q on a line already did, without pressing it for each line. The composer says so while it is open; Esc, a second Ctrl+Q on the empty line, or the end of that command closes it, so it never carries over to the next command. Plain Enter keeps the input-ownership rules: a program NMSh sees waiting gets it, and a line NMSh cannot place waits for your choice. Hidden input closes the composer before anything typed is drawn: the first key is looked at as usual, and a password prompt reported while it is open and empty sends your typing to the program. Once a running command is seen asking for input, every line queued while it ran waits for your approval (its text hidden if the input is hidden); the session service decides this before the next queued command could start. **Limit:** NMSh cannot see every reader (on macOS nothing reports that a program reads with no prompt, and a timed read can give up before it is seen), so a line you type into the queue composer for such a program runs as a command after it; the composer exists for commands, and Ctrl+S sends to the program.

- **Console for the browser preview** ([#466](https://github.com/raiseCatError/notMyShell/issues/466); [design](docs/design/document-workspace.md#console-runtime-diagnostics-of-the-browser-preview)). In the Chromium preview of a Markdown or HTML document, **`C`** lists what the page itself reported for the document now loaded: script exceptions, `console` errors, warnings and messages, failed loads and HTTP errors, Content Security Policy refusals and requests NMSh refused. It works like Problems — newest first, Tab filters by severity, typing searches, Shift+↑↓ marks, Del clears, Ctrl+C shows exactly what would be copied before copying — and Enter goes to the line in the file when that is certain (an HTML document unchanged since the page loaded); otherwise it says why. The Console only reads: it never turns scripts or the network on (with scripts off, only loading and policy messages appear), accepts no expressions, and ignores `/browser` pages and the Mermaid renderer. Entries stay in memory for the current load, bounded, with the preview's address, query values, tokens and likely secrets redacted before they are kept. The status line counts errors and warnings.
- **`/doctor` reports document rendering** ([#462](https://github.com/raiseCatError/notMyShell/issues/462)). A Documents section names the Chrome or Chromium the previews would use (its version once a browser surface has checked it this session, and whether it is running), the Mermaid it found (path, version, a warning for a major version other than the tested 12), the Mermaid setting and, when diagrams would not be drawn, why. Nothing is launched and the Mermaid bundle is not read, so it says what was found, not that drawing works.
- **Mermaid diagrams in Markdown** ([#462](https://github.com/raiseCatError/notMyShell/issues/462); [design](docs/design/diagrams.md)). A ```` ```mermaid ```` fence is drawn as a real diagram — flowcharts, sequence, class, state, ER, Gantt, pie, journey, git graph, mind map, timeline, quadrant, XY, requirement, Sankey, block and C4 diagrams — in Preview (NMSh's renderer and the Chromium preview), Split's preview pane, the viewer and Hybrid's inactive blocks, with the image backend your terminal supports (Kitty graphics, iTerm2, Sixel, or character art); `i` opens it in the image viewer to zoom and pan. NMSh uses the Mermaid you installed (`npm install -g mermaid`, or `NMSH_MERMAID`) inside the real browser it already drives, and downloads nothing. The page that draws runs no document script, loads nothing from the network or disk and returns only pixels; Mermaid's security settings are locked against directives. Settings → General → **Mermaid diagrams**: *On request* (default: press `D`, or F1 › Diagrams while editing — opening a document never starts a browser), *Automatic*, or *Off*. The fence stays the source: in Hybrid the block under the caret is its exact text, edits are drawn after a pause, and anything not drawn (not asked yet, no browser or Mermaid, a syntax error, too large, a timeout) shows the fence as written with `(diagram not drawn: reason)`. Drawings are cached by content and theme, bounded in number, bytes and time, and the renderer page closes when no document shows a diagram.
Expand All @@ -25,6 +27,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- **Markdown editing helpers in `/edit`** ([#463](https://github.com/raiseCatError/notMyShell/issues/463), first slice; Markdown files only). **Enter** continues a bullet, numbered or task list (same marker and indentation, next number, unchecked box) and splits an item at the caret; on an empty item it ends the list (a nested one steps out a level). **Tab / Shift+Tab** on list items indent or outdent the item with its children (selected items together); elsewhere Tab is a tab as before. **F1** opens the Markdown actions: toggle a task checkbox, renumber the list at the caret, insert a link or image around the selection, wrap the selection in bold, italic, code or strikethrough (again to unwrap). Every action changes only the lines it names and is one undo step; text is never reflowed or regenerated.
- **Crash recovery for `/edit` and the document workspace** ([#469](https://github.com/raiseCatError/notMyShell/issues/469)). Unsaved changes are written, a moment after you stop typing, to a private draft in NMSh's data folder (`recovery/`, mode 0700; files 0600). After a crash, a kill or a closed window, opening the file offers **Restore draft**, **Compare with file**, **Discard draft** or **Keep for later**; nothing is applied until you choose, a draft made from an older version of the file says so, and the file itself is only ever written by the editor's normal checked save (which also retires the draft). Two windows on one file keep separate drafts, and a draft of a window that is still running is never offered. Drafts are bounded (2 MiB each, 50 in all, 30 days) and are never sent anywhere.

### Fixed

- **A command line the shell could not finish no longer leaves NMSh waiting forever.** NMSh draws no continuation prompt, so a line the shell read as unfinished — an unclosed quote, bracket or heredoc, or in zsh `echo "Done!"`, where `!"` (history expansion) removes the closing quote — showed as running with nothing happening, Ctrl+C did not bring the prompt back (zsh acts on it only when its next line arrives), and typing was held as "Can't tell whether echo is reading your typing". The shell's continuation prompt is now an invisible marker for zsh and Bash: when the shell asks for more of a line NMSh submitted whole, NMSh cancels it at once (nothing of it ran), says why, and puts the line back in the composer to fix. A queued line that cannot be finished pauses the queue before anything after it, whatever the failure policy. Multi-line blocks and heredocs are unaffected; Fish keeps unfinished lines in its own reader.
- **Held typing and held queue entries name the program accurately.** For a line of several commands (`a; b`, `a && b`, a pipeline, a multi-line block) NMSh says "the command" instead of the first word, which had often finished long ago ("Can't tell whether echo is reading your typing" while `sleep` ran).

## [0.23.0] - 2026-10-21 — Vespyr Arcade

The Arcade grows from five games to 33, and it can now be played while a command keeps running.
Expand Down
35 changes: 29 additions & 6 deletions docs/design/input-awareness.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,17 @@ first key of a draft and an Enter that would queue, and asks the session to look
The hold ends when the look answers or evidence arrives. Until then nothing typed is drawn, sent or queued: the keys
may be a hidden reply, and an Enter may answer the command or queue the next one (#426). The look says what it saw:
`hidden` (echo off for line input), `visible` (the terminal echoes), `unknown` (no sample said how it echoes: a probe
failed or timed out), `idle` (nothing runs) or `unavailable` (no way to look on this platform). `visible` routes the
keys as before (a terminal would show them too); `hidden` comes with a hidden request, so they go straight to the
failed or timed out), `idle` (nothing runs) or `unavailable` (no way to look on this platform). `visible` permits draft editing (a terminal would show it too), but says nothing about a reader. If the held keys include Enter and no reader was established, the entire draft moves into a confirmation hold, never the command-approval queue; `hidden` comes with a hidden request, so they go straight to the
program. Echo off with line input is a hidden request whatever else is known (confirmed when the group is idle,
likely when it computes or the kernel says it is blocked elsewhere), so NMSh never draws what the terminal would not
echo. An `unknown` answer gets one more look; if that is blind too, or no answer comes within 5 s, the keys stay held
and hidden: the activity rows say `Can't tell whether <program> is reading your typing` and Ctrl+S sends it to the
program, Tab shows it in the composer (for review; a final Enter is not replayed), Esc discards it. Later evidence
decides it the same way. A look still running after 800 ms says `Checking whether … · your typing is kept`.
program, Tab shows it in the composer (for review; a final Enter is not replayed), Esc discards it. Later reliable reader evidence can resolve an ordinary lookup hold. A held Enter with only later likely evidence requires confirmation; once Enter is in confirmation quarantine, no later result sends it automatically. Ctrl+S explicitly sends all captured bytes in order to the same still-running command. Tab or Ctrl+Q restores the editable text for queue review, without replaying the final Enter; a fresh Enter explicitly queues it. Esc and Ctrl+C discard the entire capture. Completion/done, a different command, terminal handover and shutdown invalidate confirmation holds; stale decisions cannot recover them. After completion/done, confirmation typing stays hidden in the existing prompt hold for Tab/Esc review, with Ctrl+S disabled; it is never sent to a later command. Existing approval-queue entries are never sent automatically. A look still running after 800 ms says `Checking whether … · your typing is kept`.

A command that ends while its typing was held past 800 ms never runs it: NMSh never learned whether it was a reply
An ordinary lookup hold (not confirmation quarantine) that ends while its typing was held past 800 ms never runs it: NMSh never learned whether it was a reply
(a timed `read -s` that gave up), so it is discarded with a note. A quick end (type-ahead after a short command) runs
it as before. Ctrl+C discards held typing, as a terminal flushes its input queue on an interrupt, and is itself
never held. A program that took the terminal meanwhile (Fish's reader) gets the keys as raw keys. An older service
never held. An ordinary lookup hold can follow authenticated native-reader ownership as raw keys. Confirmation quarantine is cancelled at terminal handover, before newer keys reach the new owner. An older service
does not say what its look saw; its answers are routed as before. Typing right after Ctrl+C or Ctrl+Z stays
type-ahead for the prompt. Both ends negotiate the feature; an older service is never asked and behaves as before.

Expand Down Expand Up @@ -171,6 +169,31 @@ typed is drawn, not even as dots.
- NMSh gestures and pointer events are never sent.
- Nothing typed is shown, journaled, put in history or written to the key-debug log.
- **There are no answer buttons.** NMSh never submits an answer on your behalf.
- **No question in sight** (nothing waits that NMSh can see): Enter holds a line for approval and shows it by where it
came from, because a prompt-less read is indistinguishable from a command NMSh cannot see (#426). Ctrl+Q queues it
as typed. **The queue composer** (#473): Ctrl+Q on an empty line while a command runs opens it for that command
only; each line entered in it is queued as typed, without approval, until Esc, a second Ctrl+Q on the empty line,
or the end of that command. It is the person's explicit choice, made after seeing what runs, and it never carries
over to the next command, so a reflexive Enter there gets the rules above. Plain Enter is never made to queue by a
standing mode: on macOS nothing proves that a program does not read, so no setting can make Enter safe for every
command (#514's boundary stays).
- Hidden input closes it before anything is drawn: its first key is looked at like the composer's, and a hidden
look closes it and holds the typing unseen; a request reported while it is open and empty closes it, so the
keys go where ownership sends them. A line already begun in it stays a queued command, then it closes.
- A reader seen late (an echoing terminal is no evidence, and a loaded machine delays the follow-up sample) is
caught by the owner: when the running command is first reported asking for input, every entry queued since it
was submitted waits for approval (hidden input also hides their text), decided by the session service beside the
prompt, so the next dispatch cannot overtake it.
- Unavoidable limit: a reader NMSh never sees before it ends (a prompt-less external reader on macOS, or a timed
read that gives up within about a second) — a line typed for it into the queue composer runs after it as a
command, exactly as a Ctrl+Q line always has. A secret typed with echo on into the composer is shown and queued
like any queued text. Tests: `queueComposer.test.ts`, `queueComposerRaceLive.test.ts`, `queueReplyHold.test.ts`.
- **Unfinished command lines.** NMSh draws no continuation prompt, so zsh and Bash print PS2 as an invisible marker.
The session counts the continuation prompts a submission's own line breaks explain; one more, before the command
starts, means the shell is waiting for the rest of a line NMSh sent whole (an unclosed quote, bracket or heredoc;
zsh's `!"` history expansion). The session cancels it there (zsh needs Ctrl+C then Enter: it acts on the interrupt
only when its read returns; Bash needs Ctrl+C), nothing of it has run, and the next prompt carries `incomplete` so
every window explains it and the queue pauses before what follows. Fish keeps unfinished lines in its own reader.

## Other sessions

Expand Down
23 changes: 23 additions & 0 deletions docs/performance/2026-10-28-resource-followup-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Deferred resource investigation

Execute separately after the input-ownership correction has completed review and validation. This is an investigation plan, not authorization for a broad memory optimization or a claim that the input gate has passed.

## Evidence to preserve

The actual original be3f6abe versus merged c690de05 benchmark raw data remains at `/Volumes/T7Tmp/ai-dev/nmsh-stabilization-followup-20261027/`, with provenance, samples, harnesses and measurements.json. The ownership checkpoint records their hashes; nothing is pooled or overwritten.

Warm median output/history results are substantial: 10k Unicode output 42503.28 to 269.84 ms, archive restore 684.59 to 251.93 ms, PageUp 872.55 to 40.11 ms, history typing 448.71 to 31.47 ms. Warm first-command output improves 1510.81 to 753.13 ms and durable completion 1643.01 to 858.59 ms. These are warm observations, not true-cold startup gains.

Costs remain visible: idle CPU 3.35% to 3.75% of one core; idle frontend RSS 129.28 to 152.78 MiB; idle service RSS 49.86 to 60.53 MiB; output-finish service RSS 20.38 to 70.62 MiB. Point-in-time RSS is not retained heap, peak memory or leak evidence. The output-finish snapshots differ in elapsed process lifetime: baseline 38–49 s, merged 0.27–0.30 s. Frontend after short typing is a different observation (155.17 to 153.95 MiB), not a reason to erase the idle increase.

Runtime/idle n=3 warm sessions; history n=4; long editing n=7 with correlated key samples. Small-n nearest-rank p95 is the observed maximum for runtime/history. Five-second CPU windows cover frontend/service/shell, omit other descendants, and are quantized by ps. No OS-cache purge, physical pixel latency, energy or sustained retention claim. Prior +6.7% history-typing p95 and +2.3% deletion p95 remain separate experiments without causal attribution.

## Investigation sequence

1. Reconstruct the matched benchmark environment with locked dependencies, built CLI, private HOME/service, fixed geometry/provider/shell and independently recorded exact heads. Keep original/raw results immutable. Alternate run order in a predefined bounded matrix, record all samples and external host activity, and separate source-loader costs from built runtime.
2. Idle CPU/memory: record all owned roles and descendants at common elapsed times and command lifecycle points over longer predefined windows. Collect CPU deltas, RSS, heap/GC/allocation evidence and timer/probe/render counts separately. Account for service backlog limits and sampler overhead. Do not attribute RSS differences to a leak without retained-object evidence.
3. Startup loading: measure authenticated shell readiness, first composer frame, first output and durable completion separately; record import/initialization paths and time spent starting shell/service/providers. Keep first-observed and warm samples separate. Define a reproducible cold-cache protocol before claiming any cold result; do not purge user caches or alter existing sessions.
4. Long sessions: use a bounded repeated output/history/edit workload with exact record/order checks, fixed idle checkpoints, detach/reattach and session closure. Measure retained heap and RSS across cycles, journals/backlogs, listener/process counts and reclamation after owned sessions close. Separate bounded caches from monotonic retention.
5. Form one evidence-backed hypothesis and a focused follow-up issue per cost. Agree acceptance budgets from the current milestone before code changes; validate any improvement against input safety and output/history performance. No broad cleanup or dependency churn.

All scratch and artifacts use verified T7 storage. Physical Ghostty/Zed QA remains a separate manual gate.
Loading
Loading