pmacs/src
Levi Neuwirth 40027340df
feat(git): Stage 1 --- *git-status* and *git-diff*, no wire change
Implements docs/git-integration-framing.md revision 5 (approved
2026-08-09). COHERENCE.md section 15's largest named gap gets something
to attach to: a user can now answer "what have I changed?" without
leaving the editor.

  *git-status*  a `pmacs.listview` panel over
                `git --no-optional-locks -C <root> status
                 --porcelain=v2 --branch -z`. RET visits the file, `d`
                shows its diff, `g` refreshes.
  *git-diff*    the file-level diff, in a generated buffer rendered as
                plain text --- there is no bundled `diff` grammar and no
                hunk model anywhere in the tree.

NO WIRE CHANGE: no pmacs-protocol edit, no PROTOCOL_VERSION bump, no
DecorationKind variant. That is load-bearing for scheduling, not a
coincidence --- gutter markers (Stage 2) need all three and must be
scheduled alone, while this lane could run beside another.

One additive `listview` change, and the framing was wrong to say there
would be none: an optional `keys` table on the open spec. `d` cannot be
bound from outside the primitive safely, because a name collision
disambiguates to `<2>` and the name a consumer passed is not necessarily
the buffer it got. Keys are INSTALLED ONCE with the panel's buffer and
COMPARED on reopen: `Keymap::bind` refuses duplicates, and the async
completion model re-opens on every refresh, so a naive implementation
would have errored on every successful refresh.

One config-registry setting, `git.enabled`, through `pmacs.config.define`.

Facts measured against real git rather than reasoned about, each pinned
by a test:

* `ProjectKind::Git` means a BARE repository, and a language marker
  beside `.git` wins --- so pmacs reports `kind = "rust"` for its own
  repository. This module never asks pmacs whether something is a repo;
  it runs `rev-parse --show-toplevel` and lets a non-zero exit answer.
* `git diff --no-index` implies `--exit-code`: exit 1 means it
  SUCCESSFULLY found differences. The untracked predicate is exit in
  {0,1}; only >= 2 is failure. Under the naive predicate every untracked
  diff --- the case `--no-index` exists for --- would render a failure.
* An unborn HEAD makes `git diff HEAD` exit 128. Detected from
  `# branch.oid (initial)` in output already being parsed, never from a
  second `rev-parse`. `AM`/`AD` carry both states and get two labelled
  patches; rename/copy is asserted UNREACHABLE, because `git mv` on a
  staged-but-uncommitted file yields `1 A.`, not a `2` record.
* Under `-z` a rename's origin is the NEXT NUL-terminated field, not a
  tab-joined suffix, so the record tokenizer is new rather than ported
  from `tests/fixtures/pmacs-magit/`. What ports is that fixture's
  SEPARATION --- pure `parse_*` over a string --- and its case coverage.
  The fixture is untouched: it exists to prove the package system can
  host this, and bundled code becoming its dependency would make
  `m8_6_acceptance` test less than it claims.

Coherence impact, stated per CLAUDE.md:

* Section 14: `*git-status*` is the FIFTH `listview` call site and the
  first outside `lsp.lua` --- the evidence P5 asked for that the
  primitive generalizes past its first consumer.
* Section 6: no new interaction island. `d` is an ordinary buffer-local
  binding through the primitive's own path, so `describe-key` reports
  the truth and `init.lua` can rebind it. The count stays at six.
* Section 9: NEGATIVE, and named as such. A spawned process does not
  appear in `*workers*` --- that view is `async.lua`'s job list. This
  adds a fifth background thing with no single place to see it. Every
  spawn is labelled, which is better than anonymous, but a label is not
  attribution. Accepted only because these are short-lived reads.
* Journey: no step added. Git is not a journey step and this does not
  make it one.

Section 15's "no Git integration at all ... anywhere in the tree" is
narrowed here. It was literally false when written ---
`tests/fixtures/pmacs-magit/` is a tracked, installable package that
spawns git and parses porcelain v2 --- and the product gap it described
is what this closes.

Five things found by biting the suite rather than by reading, recorded
in docs/active-work.md: `listview.open`'s `seat_cursor` walks DOWN from
wherever the cursor is (so a re-opened panel lands one row low, and the
completion handler seats unconditionally from line 0); a selection test
that inserts ONE row above the selection is vacuous against exactly that
off-by-one; `{:?}` on a Rust string cannot build a `-z` fixture, because
Lua's decimal escape swallows the digit after `\0` --- which made one
test pass while parsing nothing; a path may contain a newline, so rows
escape it; and untracked rows sort after every tracked row.

Gates: scripts/gate --acceptance git_status_stage1_acceptance
--acceptance listview_acceptance --acceptance config_registry_acceptance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
2026-08-09 14:43:21 +02:00
..
audit V0.2-prerequisite pull-forward + M10.11 clean audit round 2026-05-18 10:31:31 -04:00
bin fix(lsp): close review round 1 on the resource-op delete guard 2026-07-29 11:45:43 -04:00
lua_bindings test: the report itself, at a real PTY — and §1.1 was wrong 2026-08-07 19:37:46 +02:00
packages feat(persistence): autosave + crash recovery (Arc 3 phase 3) 2026-07-09 10:24:08 -04:00
terminal feat(terminal): copy mode over retained scrollback 2026-07-26 10:10:27 -04:00
ansi.rs fix(vterm): parse VT index operations 2026-07-21 15:31:15 -04:00
async_runtime.rs test(stage2a): unit pins for the URI teardown and the bus order 2026-07-29 18:23:41 -04:00
attach.rs feat(release): binaries on tag — Distribution Stage 1 2026-08-01 14:40:47 -04:00
attach_dispatch.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
attach_reconnect.rs Pin toolchain to 1.95.0 + mechanical clippy/rustc fixes 2026-05-18 11:38:57 -04:00
autosave.rs fix(persistence): adopt clears the old owner's skip cache; failing sweeps are loud 2026-07-09 12:06:49 -04:00
bootstrap.rs feat(bootstrap): make the ambient storage roots a parameter 2026-07-31 18:48:07 -04:00
buffer.rs wip(stage2a): name provenance plus the View rename hook 2026-07-29 17:38:21 -04:00
buffer_mirror.rs M10.10: complete optimistic-apply keystroke path + Day-5 corrections 2026-05-15 16:40:46 -04:00
buffer_registry.rs T M4.5 L1: cross-file nav foundations — per-buffer file_path, jump ring, x-file go-to-def 2026-05-19 09:53:29 -04:00
builtin_packages.rs M7 tail: package system, audit lint, lockfile, resolver 2026-05-07 16:50:37 -04:00
cell.rs session 1 commit 2/4: cell wire types moved to pmacs-protocol 2026-05-20 09:20:24 -04:00
code_action.rs T M4.5 L4: WorkspaceEdit resource ops (create/rename/delete file) 2026-05-19 12:31:35 -04:00
command.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
completion.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
completion_framework.rs fix(completion): address TUI-validation findings (LSP query gaps, scoping, prefix keys, window scope) 2026-07-07 16:54:14 -04:00
config.rs Collapse if-let nests into let-chains (MSRV-1.95 collapsible_if sweep) 2026-05-18 14:29:36 -04:00
config_registry.rs test: the report itself, at a real PTY — and §1.1 was wrong 2026-08-07 19:37:46 +02:00
crdt.rs fix(buffer): fan out generated writes, and clear the history that exists 2026-07-26 13:43:22 -04:00
daemon.rs feat: ui.line-wrap, resolved once and told to both frontends 2026-08-07 18:02:21 +02:00
daemon_attach.rs CI fixes v2 2026-05-18 13:12:10 -04:00
definition.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
desktop.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
diag.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
display_width.rs feat(render): unify tab-width projection 2026-07-22 15:03:30 -04:00
document_highlight.rs T M4.5: documentSymbol / workspace symbol / documentHighlight 2026-05-18 22:15:03 -04:00
editor.rs feat(git): Stage 1 --- *git-status* and *git-diff*, no wire change 2026-08-09 14:43:21 +02:00
editor_core.rs feat(view): wrap-aware coordinates, breaking in and additive out 2026-08-07 13:07:54 +02:00
file_io.rs feat(persistence): autosave + crash recovery (Arc 3 phase 3) 2026-07-09 10:24:08 -04:00
fold.rs fix(fold): address PR #142 review round 1 2026-07-23 13:42:20 -04:00
fold_view.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
font_pref.rs feat(font): FontFacts wire + daemon half (v17, FontPref, producer) 2026-07-18 11:45:51 +01:00
formatting.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
frontend.rs feat(protocol): LineWrapFacts at v22, and the GPU stops inheriting a default 2026-08-07 16:54:13 +02:00
fs.rs fix(dired): address PR #165 review round 1 2026-07-25 15:21:26 -04:00
hash.rs feat(persistence): autosave + crash recovery (Arc 3 phase 3) 2026-07-09 10:24:08 -04:00
help.rs feat: wire major modes through key dispatch 2026-07-21 20:25:48 -04:00
highlight.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
hook.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
hover.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
inlay_hint.rs Close pmacs-gpu phase A audit 2026-05-28 12:49:23 -04:00
instance_buffer.rs Collapse if-let nests into let-chains (MSRV-1.95 collapsible_if sweep) 2026-05-18 14:29:36 -04:00
instance_render.rs test(vterm): prove Stage 2 TUI integration 2026-07-21 20:16:23 -04:00
key.rs Fix PTY final-output drain race 2026-05-04 09:44:30 -04:00
keymap_stack.rs feat: wire major modes through key dispatch 2026-07-21 20:25:48 -04:00
keymap_tree.rs Collapse if-let nests into let-chains (MSRV-1.95 collapsible_if sweep) 2026-05-18 14:29:36 -04:00
lib.rs feat(bootstrap): make the ambient storage roots a parameter 2026-07-31 18:48:07 -04:00
locations.rs T M4.5: nav requests — references/declaration/typeDefinition/implementation 2026-05-18 21:39:02 -04:00
lockfile.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
lsp.rs fix(stage2a): close review round 1 — four unreported failures 2026-07-29 21:28:16 -04:00
lsp_status.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
lua.rs feat(completion): Lua driver, popup bindings, LSP scoping + flush seams 2026-07-07 12:09:25 -04:00
lua_isolation.rs M7 tail: package system, audit lint, lockfile, resolver 2026-05-07 16:50:37 -04:00
main.rs docs: absorb the v1.1.0 release, and correct what it made stale 2026-08-01 18:09:59 -04:00
mcp.rs Collapse if-let nests into let-chains (MSRV-1.95 collapsible_if sweep) 2026-05-18 14:29:36 -04:00
menu.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
message_bus.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
minibuffer.rs feat(persistence): state foundation + saveplace + recentf (Arc 3 phase 1) 2026-07-08 17:57:39 -04:00
optimistic.rs feat(edit): auto-pairing (Arc 2) 2026-07-11 17:11:56 +01:00
overlay.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
overlay_color.rs M10.10 ship gate 2026-05-13 16:28:46 -04:00
overlay_paint.rs feat(view): wrap-aware coordinates, breaking in and additive out 2026-08-07 13:07:54 +02:00
prepare_rename.rs T M4.5: textDocument/prepareRename (backlog item 3) 2026-05-19 14:23:57 -04:00
presence.rs M11.1: semantic-frontend protocol scaffolding (wire + capability) 2026-05-18 19:39:07 -04:00
process.rs fix(test): retire R2 and R4 — two readiness predicates weaker than their assertions 2026-08-05 13:41:05 +02:00
project.rs Collapse if-let nests into let-chains (MSRV-1.95 collapsible_if sweep) 2026-05-18 14:29:36 -04:00
project_index.rs T M4.5 L1: cross-file nav foundations — per-buffer file_path, jump ring, x-file go-to-def 2026-05-19 09:53:29 -04:00
protocol.rs feat(protocol): LineWrapFacts at v22, and the GPU stops inheriting a default 2026-08-07 16:54:13 +02:00
rename.rs T M4.5 L4: WorkspaceEdit resource ops (create/rename/delete file) 2026-05-19 12:31:35 -04:00
rope.rs session 1 commit 3/4: CrdtOp moved to pmacs-protocol 2026-05-20 09:32:09 -04:00
search.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
semantic_client.rs T M4.5: documentSymbol / workspace symbol / documentHighlight 2026-05-18 22:15:03 -04:00
semantic_render.rs feat: ui.line-wrap, resolved once and told to both frontends 2026-08-07 18:02:21 +02:00
semantic_tokens.rs Fix stale TUI styling after edits 2026-05-26 11:15:23 -04:00
signature.rs feat(fold): Stage 2 — grid (daemon-rendered) collapse 2026-07-23 19:24:07 -04:00
socket_path.rs Collapse if-let nests into let-chains (MSRV-1.95 collapsible_if sweep) 2026-05-18 14:29:36 -04:00
state.rs fix(persistence): never clobber unclaimed crash data; buffer-keyed cleanup 2026-07-09 10:52:10 -04:00
statusline.rs fix(panel): close Stage 2A review round 2 (2 P1, 1 P2) 2026-07-26 10:38:21 -04:00
symbol.rs T M4.5: documentSymbol / workspace symbol / documentHighlight 2026-05-18 22:15:03 -04:00
syntax.rs feat(lean4): editing surface + Stage 1 acceptance (Q#LN5, LN6, LN17) 2026-07-25 09:59:48 -04:00
text_view.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
transport.rs session 3 commit 1/N: move transport codec to pmacs-protocol 2026-05-20 12:07:32 -04:00
view.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
window.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
worker.rs fix(worker): deterministic pool teardown; EditorState::drop signals workers 2026-07-07 15:20:53 -04:00
workers_buffer.rs fix(dired): address PR #165 review round 1 2026-07-25 15:21:26 -04:00