Repository navigation
Release v0.21.0: native viewing and editing, Semantic Output and /track - #429
raiseCatError wants to merge 72 commits into
Conversation
…w redraws #403: an interactive Fish on a fresh home starts its man-page completion generator detached; it outlived test shells and raced home removal. Test homes now mark that generation as done (Fish 3 and 4 locations), and the runner no longer passes ambient XDG data, cache and state directories to tests. A regression asserts no generator starts. #408: newer shell knowledge resolved in-flight classifications as unknown, so a stored queued command could keep the unclassified color. A superseded lookup is now asked again against the new knowledge (bounded); stale answers are still never cached. The highlight tests wait for the shell's startup knowledge and for the lookups they started instead of one event-loop turn. #388: the output parser ignored row movement, so every frame of a multi-row progress display stayed in the transcript. Programs can now redraw their own recent rows (cursor up/down, save/restore, partial erases), never above NMSh's lines or more than a screen height; absolute positioning keeps the previous behavior.
/view <path> reads a regular file relative to the shell's directory, /view N completed output N (numbered as /copy), /view alone the latest output; block Actions gain View output. Markdown files render (m shows the source), source files get syntax roles and line numbers, diffs read as diffs. Find, go to line, wrap or horizontal scroll, copy all. Files are opened non-blocking and only regular files are read: at most 8 MiB and 200,000 lines, binary and UTF-16 refused, invalid UTF-8 shown with a note. Drawing is bounded to what is visible, and every control or format character is drawn as a visible mark. Viewing never writes to the file or transcript.
/edit <path> (or e in /view) opens one small text file in a full-height panel: typing, multi-line, caret by character/word/line/page, Shift selection, Ctrl+Z/Ctrl+Y undo and redo (bounded, coalesced), Ctrl+F find, Ctrl+C/Ctrl+X copy and cut, Ctrl+S save, Esc closes and asks about unsaved changes. Ctrl+Y now decodes as redo (it was dropped before). Only files that can be saved back exactly are opened: regular UTF-8 text up to 1 MiB, owned by this user, one hard link, writable file and folder, one kind of line ending (BOM and final newline kept); a missing path in a writable folder opens empty. Saving writes beside the original (exclusive create, its mode and group), syncs, re-checks that the original is still the file opened (device, inode, size, time, content hash) and renames over its real path; a symlink stays a symlink. A changed, replaced or deleted original is a conflict: Keep editing, Overwrite, or Reload. A new file never replaces one that appeared meanwhile.
…#386) A completed block whose output a recognizer is sure of gets one overview row with the counts the tool printed and, when it listed failures or diagnostics, a details row that opens them (verbatim lines, at most 20 plus a count). Recognizers: Node's test runner (spec, TAP), Jest, Vitest, pytest, cargo test, go test, TypeScript, Rust and GCC/Clang diagnostics (diagnostics only from build commands or failed ones). Short passing output gets nothing. Readings are derived per record, never stored: the transcript lines, raw folding, find, selection and plain /copy are unchanged, and derived rows are skipped by find and selection. Details start open when the raw output is folded; a click on the details row or Actions -> Show or hide details toggles them. /copy --semantic and --both copy the reading (raw once for an output without one, said in the note). Settings -> Transcript -> Semantic Output.
A security review found patterns that could backtrack quadratically on crafted output (trailing-whitespace stripping, the Jest summary, TAP directives, GCC/TypeScript location prefixes), and recognition runs when a block is first drawn. Lines over 2,048 characters are no longer read, trailing whitespace is removed with trimEnd, and those formats are parsed with string operations and end-anchored bounded patterns. A test runs eight adversarial outputs (up to 2 MB each) and bounds each one's time; behavior is unchanged.
/track <command> (or --every 5s -- <command>) writes the command and a note once, then draws a status row and the latest completed result under them, replaced on every refresh with a stable height. Live rows are derived: never stored, found, selected or copied, so nothing accumulates per refresh. /track copy, refresh, pause, resume and stop; stopping leaves the last result in the transcript once. /watch keeps its manager and behavior. The shared scheduler takes draft PR #394's hardening as written (strict cadence parsing; refusing substitutions, background jobs and heredocs; UTF-8-safe bounded capture with a truncation flag; visible timeouts and spawn errors; completion on stdio close; a stop grace period; at most 8 trackers), and its tests. Samples now use the live shell's PATH. The derived-row flag from Semantic Output is generalized for both.
…mings (#364, #365) Bun's requested version comes from .bun-version or package.json (packageManager, engines.bun) and the active one from its install location; the Nix shell module reads IN_NIX_SHELL; the container module names Docker, Podman, a dev container or Codespaces from marker files and host variables. Nothing is run. IN_NIX_SHELL, REMOTE_CONTAINERS and CODESPACES join the shell's allowlisted environment report. A Context Pack may now require 24 capabilities (as many as its modules), since the project pack needed a 17th. /modules explain and /modules timings write plain-text reports from the engine's snapshots: each configured module's surface, visibility reason and sources' state, age and timing; or the slowest last collections. No value is shown and nothing is collected to produce them.
Twelve variants with colors copied exactly from each project's MIT-licensed source at a recorded revision (sources, revisions, licenses and role mapping in docs/design/theme-families.md). Every role fill keeps 4.5:1 module text contrast; light variants keep NMSh's text tiers. Existing ids and saved choices are unchanged; Lavender stays the default. Oxocarbon (grays computed at runtime in HSLuv), GitHub Primer (values from a built primitives release), Material/Palenight and Monokai (licensing not verified) are deferred with reasons. Theme Studio's Built-in list gains / to filter by name, family or dark/light; Esc clears the filter before closing.
…release readiness Refs #410
…on its own target Review of #416/#420 found commands that /track (and /watch) started at once and repeated every interval without asking: - `echo x > ~/.zshrc 2>/dev/null`: the shared classifier excused every redirect in a command once `>/dev/null` appeared anywhere; `>|` was split as a pipe. Paste review labelled the same text read-only. - `env rm -rf …`: `env` is read-only on its own but runs its operand. - Anything classified network: `npm publish`, `npm login`, `npm audit fix`, `npm x`/`pnpm dlx`/`bun x`, `ssh host rm …`, `scp`, `rsync`, `curl -X POST`, `curl -o file`, plain `wget`. - `git -c core.fsmonitor=… status`, `GIT_EXTERNAL_DIFF=… git diff`, `--ext-diff` (run arbitrary programs) and `git log --output=…` (writes files). pasteGuard now judges each redirection's target (descriptor duplication and the null/standard-stream devices are not files) and treats `>|` as a redirection. repeatSafety.ts adds the repetition gate on top of the classifier: network commands start at once only as read-only probes; env prefixes, wrappers and git program options ask first; file-writing options, --fix/--write/snapshot updates, --watch modes, scp/rsync and power control are refused. `2>&1` is no longer mistaken for a background job. Network targets are remote unless they are this computer. Refs #393, #416, #420
… after wiring - When a sample's shell exits, the close handler cancelled the KILL fallback even if other members of its process group were still running. A child that ignored TERM and closed its output survived `/track stop` (verified with a pid probe; the new test fails without the fix), and a stray process left by a finished sample piled up on every refresh. On close, anything still in the group now gets TERM, then KILL after the grace period; that timer keeps a closing window alive for at most the grace period so nothing is left behind. - #420's refresh-start event now fires after the sample's handlers are attached, so a listener that throws cannot leave a child without them. - A tracker whose directory disappeared said `spawn /bin/sh ENOENT`; it now names the directory and keeps trying at its interval. Refs #393, #416, #420
…esult rows - Above the composer each active tracker had its own row, repeating its block's status; eight trackers took eight rows. More than two now share one summary row (`4 trackers · 1 failing · /track opens the manager`). - The state marks (◉ ■ ‖ ◌) ignored Safe glyphs; they now use ASCII there, in the block and the manager. - Live rows split and sanitized the whole 200 kB result on every frame (1–3 ms per frame with eight large trackers); the first rows and line count are now computed once per result (~0.5 ms). - The confirmation for an unknown /track command says "Track …?", not "Watch …?". - Live PTY tests: four trackers at once (slow, failing, missing command, large output) with `refreshing…`, a resize and `/track stop all` ending every process; Bash and Fish trackers finding a PATH change made after startup. Refs #393, #416, #420
Native /edit: a small, safe editor for one text file (#402)
Semantic Output, first phase: test and build readings in their blocks (#386)
/track: one live result of a finite command, refreshed in place (#393)
Theme families: Ayu, Kanagawa, Everforest, Flexoki, Night Owl (#401)
v0.21 checkpoint: ledger, integration verification and release readiness
…ening fix(v0.21): harden /track refresh and finalization lifecycle
/track review: repetition safety gate, process cleanup, bounded presentation
Flaky CI at the cause: task exit verdict, race-free private-root removal, stalled-file teardown
v0.21 ledger: stabilization findings, verification, ordered integration and release gates
…onflict A path that did not exist yet kept its unresolved path as the file's real path. After the first save, every later save compares realpath() of the path as given with that real path, so in a folder reached through a symlink (macOS /tmp and /var, or any linked project folder) the second save reported "was replaced on disk since you opened it" and offered Overwrite or Reload. The folder is now resolved when the file is opened, so the real path is the one the file has once it exists. A file replaced after the first save is still a conflict; the regression covers both, and fails on the old code.
…ompletes The waiting-read test typed its follow-up command as soon as the answer's output was on screen, and its "nothing was queued" check ran before that follow-up had run. On a busy host the shell's prompt for the answered command can arrive after the program's output; an Enter in that window queues, as designed, so the check failed on the test's own follow-up (seen on PR CI, Ubuntu, and locally under load). A diagnostic run recorded the queued entry as `echo BACK-…` with queued: true and the queue empty once it ran. The test now waits for the answered command's journal record (zsh, Bash) and for the shell to be back at its prompt (every shell), then waits for the follow-up to complete before checking that the answer never ran as a command and nothing is left queued. An answer that is itself queued still fails the test, at the wait for the program's output (#426).
…nd runbook The ledger records the merged state (#411–#424 on dev at 433af8f, each merge commit), CI on that exact commit, the local gates, benchmarks, the code review's findings (#425, #426, #427), the issue and PR disposition (#403 and #394 closed) and the remaining release gates. The physical QA checklist gains 3.9 (#425) and a walkthrough: three sittings over the twelve passes, with what is unavailable on the QA Mac and the coverage that exists instead. scripts/qa/v021-fixtures.sh builds every fixture in one new folder, with a launcher that gives the QA build its own configuration and session service. The runbook follows the v0.20 release: release branch, merge into master, tag on the merge commit, GitHub release, a manual tap PR, website sync and the approved manual publish, then the post-release smoke and rollback.
The v0.20.0 release updated the other release pointers (README, ROADMAP, AGENTS, SECURITY, CONTRIBUTING) but not this one.
The changelog's Unreleased work becomes a 0.21.0 section marked as an unpublished release candidate, with an overview, compatibility (session protocol unchanged; the background-task fix needs a new service), upgrade notes and known limitations, and the two user-visible fixes from #422 that had no entry. package.json and package-lock.json are 0.21.0. README, ROADMAP and AGENTS describe the candidate and its surfaces; the latest published release pointers stay at v0.20.0 until it is published.
|
Homebrew formula rehearsal on the candidate (
The formula needs only its |
|
Pre-validation of the final candidate tree (local preview, never pushed). This branch with #427, #425 and #428 merged on top as regular merge commits gives commit
Physical QA build: |
Input ownership test: type the follow-up after the answered command completes
/edit: a new file in a symlinked folder saves again without a false conflict
v0.21 release audit: integrated state, verification, QA walkthrough and runbook
chore(v0.21): sync audited dev fixes and QA runbook into release candidate
|
Release state after the 2026-10-19 crash recovery: automated gates done on the exact head; physical QA not run. Head:
Gate 2, done
Known limitations to add. These came out of the v0.22 work. Each was checked on the release code, and each is also in v0.20.0, so none is a v0.21 regression.
Gate 3, physical QA: not run. The fixtures are ready: Gate 4, final release commit: drafted locally, not pushed. It holds the limitations above, the status pointers and the ledger evidence. It waits for the QA results and the publication date. |
|
Superseded by #444 (v0.22.0). The maintainer decided to ship the latest stable version instead of every planned one. v0.21.0 will not be tagged or published.
This PR stays open and unmerged until v0.22.0 is released. It is then closed as superseded, never merged for bookkeeping. The |
|
v0.22.0 is published: https://github.com/raiseCatError/notMyShell/releases/tag/v0.22.0 (merged through #444 as |
Draft: do not merge. This is the release PR for v0.21.0, following the v0.20.0 flow (#392): one release branch, merged into
masterwith a regular merge commit, and the tag created on that merge commit. It merges only after the gates below are met and the maintainer authorizes the release.What it carries
dev433af8fddocs/development/v021-ledger.md)d9e083930.21.0, README/ROADMAP/AGENTS status/editfalse conflict), #427 (test race), #428 (ledger, QA walkthrough, runbook,llms.txt)master(6af5cbab) is an ancestor ofdev, so this PR has no conflicts. Once #425, #427 and #428 are merged intodev,devis merged into this branch with a regular merge commit (no force push).Gates (in order)
dev, then merged into this branch.npm run verify:releaseon that head.docs/testing/v021-physical-qa.mdrecorded on the candidate build (walkthrough in v0.21 release audit: integrated state, verification, QA walkthrough and runbook #428). Not run yet.llms.txtat v0.21.0, and dispatch CI on that exact head.docs/development/v021-release-runbook.md).Verification so far
dev433af8fd: push run 39135227830, all 13 jobs green.433af8fd, clean T7 worktree (macOS arm64, Node 26.10):npm ci: 0 vulnerabilities.typecheck:bench,bench:smoke(every budget) andgit diff --check: pass.node dist/index.js --versionreadsnotMyShell 0.21.0, and the build-identity and update tests pass (25/25).Package version:
0.21.0. No npm publication (private); the release has no uploaded assets (the source archive is GitHub's tag archive, which the Homebrew formula uses).