pmacs/docs/repository-audit-2026-07-03.md

18 KiB

Repository Audit - 2026-07-03

Scope: repository-wide audit of the current pmacs worktree at /home/jeans/Repos/active/pmacs.

This audit covers Rust crates, Lua built-ins and fixtures, package-manager code, GPU frontend code, documentation, repository hygiene, and verification commands. It audits the files currently on disk.

Worktree Context

Excluding this newly added report, git status --short reported these untracked worktree files at the end of the audit:

?? #run.sh#
?? docs/pmacs-gpu-editing-perf-handoff.md
?? docs/semantic-frontend-protocol.md.local-bak
?? docs/session-5-stale-styling-handover.md
?? pmacs
?? python_experiment.md

The audit did not revert or normalize this state.

Verification Summary

Commands run:

Command Result Notes
cargo fmt --all --check Pass Formatting is clean.
cargo clippy --all-targets -- -D warnings Pass This only checked the root default package from this workspace location.
cargo clippy --workspace --all-targets -- -D warnings Fail pmacs-gpu has 3 denied clippy warnings. See F-001.
cargo test -p pmacs-gpu --bin pmacs-gpu Pass 39 tests passed on the current worktree.
cargo test -p pmacs-protocol Pass 12 tests passed; 0 doctests.
cargo test -p pmacs --no-default-features --features lua54 --lib Pass 1431 passed, 0 failed, 3 ignored.
cargo test -p pmacs --features crdt --lib Pass 1601 passed, 0 failed, 3 ignored.
cargo test --workspace --all-targets --all-features Fail mlua-sys rejects enabling both luajit and lua54. See F-002.
cargo run --quiet --bin pmacs-audit -- --pretty builtin tests/fixtures Pass with warnings 0 errors, 3 warnings, 10 infos.

Sandbox caveat: an initial cargo test --workspace --all-targets run failed in three attach/socket tests with PermissionDenied: Operation not permitted. Rerunning the attach lib-test group with normal socket permissions passed (44 passed). I did not treat that sandbox failure as a product defect.

Findings

F-001 - High - Workspace clippy fails, while the documented clippy command misses workspace members

Evidence:

  • README.md:37-43 documents:
    • cargo test
    • cargo fmt --check
    • cargo clippy --all-targets -- -D warnings
  • cargo metadata --no-deps --format-version 1 reports workspace_default_members as only the root pmacs package.
  • cargo clippy --all-targets -- -D warnings passed, but cargo clippy --workspace --all-targets -- -D warnings failed.
  • The workspace clippy failures are in pmacs-gpu/src/main.rs:
    • pmacs-gpu/src/main.rs:2712: two clippy::cast_possible_wrap errors from centered as i64 - self.scroll_top as i64.
    • pmacs-gpu/src/main.rs:2948: clippy::format_push_string from readout.push_str(&format!(...)).

Impact:

The documented release/developer clippy command can give a false green while pmacs-gpu is not clippy-clean under the workspace's lint policy. This makes CI and local release validation vulnerable to missing frontend regressions.

Recommendation:

Fix the three pmacs-gpu lints, then update README and CI to use workspace-aware commands, e.g. cargo clippy --workspace --all-targets -- -D warnings and cargo test --workspace --all-targets. Alternatively set explicit workspace default-members if the intent is for plain cargo test and cargo clippy to cover all first-party crates.

F-002 - Medium - --all-features is not buildable because Lua features are mutually exclusive

Evidence:

  • Cargo.toml:63-75 defines default = ["luajit"], luajit, and lua54.
  • cargo test --workspace --all-targets --all-features fails in mlua-sys with:
You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau

Impact:

Generic CI, packaging automation, cargo hack, and distro tooling often starts with --all-features. For this workspace, that produces a hard build failure even though both Lua flavors pass independently.

Recommendation:

Document the supported feature matrix explicitly and avoid --all-features in CI. Prefer a matrix such as default LuaJIT, --no-default-features --features lua54, and --features crdt. If possible, add a clearer crate-local compile error for simultaneously enabled Lua flavors so users see a pmacs-specific message before the mlua-sys failure.

F-003 - Medium - pmacs-gpu can appear hung when attached to a non-CRDT daemon

Evidence:

  • pmacs-gpu/src/attach.rs:119-128 documents the failure mode directly: without daemon crdt, negotiation succeeds, no BufferSnapshot arrives, and the GPU window sits on (connecting...) forever.
  • pmacs-gpu/src/attach.rs:129-148 sends an AttachRequest requesting multi_frontend, crdt_replica, and semantic_render, but there is no explicit capability rejection path in the GPU client after the request.

Impact:

This is a confusing operational failure. A user can start the daemon without the right feature and get an inert GPU window instead of a clear compatibility error.

Recommendation:

Make capability negotiation explicit. The daemon should reject required capability mismatches with a structured Goodbye or handshake error, and the GPU frontend should render a fatal status explaining that the daemon must be started with --features crdt.

F-004 - Medium - Current GPU command-chord forwarding can break AltGr/international text input

Evidence:

  • pmacs-gpu/src/main.rs:4674-4688 reduces winit modifiers to Shift, Ctrl, Alt, and Super/Meta. There is no AltGraph/AltGr distinction in the protocol modifier set (pmacs-protocol/src/message.rs:92-115).
  • pmacs-gpu/src/main.rs:4775-4779 treats any Char, Enter, or Tab with Ctrl or Alt held as a command chord.
  • The current tests assert that this generalized command-chord path forwards Char plus Ctrl/Alt combinations (pmacs-gpu/src/main.rs:5822-5853).

Impact:

On many keyboard layouts, printable characters are entered through AltGr, which toolkits may expose as Ctrl+Alt or as an Alt-related modifier. Those characters can be misclassified as command chords and forwarded to the daemon keymap instead of inserted as text. This affects non-US layouts and users typing characters such as @, [], {}, \, |, or currency symbols depending on layout.

Recommendation:

Use winit's text/IME path or an explicit AltGraph signal to distinguish printable text input from command chords. Add tests for AltGr-style printable input. If the wire protocol needs it, add an AltGraph modifier bit rather than folding it into Ctrl/Alt.

F-005 - Medium - Package install paths collide for namespaced packages with the same basename

Evidence:

  • src/packages/installer.rs:44-49 states that install directories are named by package basename and accepts collisions for v0.1.
  • src/packages/installer.rs:1040-1051 strips a namespace prefix with package_basename.
  • src/packages/loader.rs:43-48 and src/packages/loader.rs:164-176 route require lookup by basename, with most-recent install winning on collisions.

Impact:

Two distinct packages such as owner/magit and other/magit can target the same install directory and require namespace. Depending on order, one can shadow or block the other. A resolver can reason about canonical names while the installer and loader collapse them to the same basename.

Recommendation:

Reject duplicate basenames during resolution/install until a namespace-aware install layout exists, or install under a namespace-preserving path. If basename requires remain a compatibility goal, add an explicit aliasing rule rather than silently collapsing distinct package names.

F-006 - Medium - Atomic save has file-metadata and crash-durability gaps

Evidence:

  • src/file_io.rs:139-148 creates a new sibling temp file, writes bytes, sync_alls the temp, then renames it over the target.
  • The save path does not copy the existing file mode/permissions onto the temp before rename.
  • The save path does not fsync the parent directory after rename.
  • src/file_io.rs:156-174 uses PID plus subsecond nanoseconds for the temp name and relies on create_new failing if a collision occurs; it does not retry.

Impact:

Saving an existing executable or otherwise specially-permissioned file can replace it with default temp-file permissions. On POSIX filesystems, a crash after rename but before the parent directory entry is synced can lose the rename despite the file bytes having been synced. The temp-name collision path is unlikely, but a same-process collision would surface as a spurious save failure.

Recommendation:

Before writing, read existing metadata and apply the target's mode to the temp file where supported. After a successful rename, fsync the parent directory on Unix. Add a bounded retry loop for temp-name collisions.

F-007 - Medium - Minibuffer dropdown height is unbounded and can render off-screen

Evidence:

  • pmacs-gpu/src/main.rs:3160-3164 computes dropdown top_y as band_top - n as f32 * MB_DROP_ROW_HEIGHT.
  • pmacs-gpu/src/main.rs:3176-3193 draws a background rect and selected row for all candidates without clipping/windowing the candidate list.

Impact:

Large completion lists can extend above the top of the window. The selected row can be outside the visible area, and hit testing/rendering can become inconsistent with what the user can see.

Recommendation:

Cap visible rows based on available height, keep a scroll/window offset around the selected candidate, clamp top_y, and make hit testing use the same visible window.

F-008 - Medium - GPU attach outbound queue is unbounded

Evidence:

  • pmacs-gpu/src/attach.rs:156 uses std::sync::mpsc::channel.
  • pmacs-gpu/src/attach.rs:186-199 writes queued events on a separate writer thread to avoid blocking the UI thread.
  • pmacs-gpu/src/attach.rs:326-333 sends every FrontendEvent into that unbounded queue.

Impact:

If the daemon or socket stalls, the UI thread can continue enqueueing key, pointer, paste, viewport, and CRDT events without backpressure. This can grow memory and can also deliver stale pointer/viewport traffic after the daemon recovers.

Recommendation:

Use a bounded channel with a clear policy. Coalesce superseded events such as viewport and pointer motion. Consider failing fast or entering a degraded state when the writer cannot keep up.

F-009 - Low - Package fetch cache key uses non-cryptographic 64-bit FNV-1a

Evidence:

  • src/packages/fetcher.rs:161-165 derives the bare-repo cache path from a hash of the normalized URL.
  • src/packages/fetcher.rs:510-518 implements 64-bit FNV-1a and describes it as "collision-resistant enough for a cache key."

Impact:

The package cache is keyed by potentially attacker-controlled repository URLs. A deliberate or accidental hash collision could make two URLs share one bare mirror path and lock file, mixing refs or producing confusing installs. This is not a content-integrity bypass by itself, but it is avoidable risk in security-adjacent package infrastructure.

Recommendation:

Use SHA-256 or another cryptographic digest of the normalized URL for cache directory names. Store the normalized URL in a sidecar file for diagnostics.

F-010 - Low - Timed-out git subprocesses return before stdout/stderr drain threads are joined

Evidence:

  • src/packages/fetcher.rs:599-604 spawns stdout/stderr drain threads.
  • src/packages/fetcher.rs:611-617 kills and waits for the child on timeout, then returns FetchError::Timeout immediately.
  • src/packages/fetcher.rs:630-631 only joins the drain threads on the normal completion path.

Impact:

Repeated fetch timeouts can leave short-lived detached reader threads behind until their pipe reads finish. It is unlikely to leak permanently after the child is killed, but it makes timeout behavior less deterministic and harder to test.

Recommendation:

After killing and waiting for the child, join the drain threads before returning the timeout error, or restructure process execution around a timeout-aware wait_with_output helper.

F-011 - Low - ResolvedPackage::commit is documented as a SHA but can hold a tag string

Evidence:

  • src/packages/resolver.rs:143-144 documents ResolvedPackage.commit as a "Full 40-character commit hash."
  • src/packages/resolver.rs:1036-1044 returns the tag string from TagCandidate::commit_for_tag.
  • src/packages/lockfile.rs:573-610 compensates by resolving the commit-ish to a SHA before writing the lockfile.

Impact:

The lockfile path is protected, but the internal type contract is misleading. Downstream code that trusts ResolvedPackage.commit as a SHA can accidentally re-resolve a moving commit-ish or use the field in error messages and markers as if it were immutable.

Recommendation:

Either resolve tags to SHA inside the resolver before constructing ResolvedPackage, or rename the field to revision/commitish and update docs and call sites to reflect the actual contract.

F-012 - Low - Resolver topological sort contains dead/shadowed indegree work

Evidence:

  • src/packages/resolver.rs:705-715 builds and mutates an indegree map.
  • src/packages/resolver.rs:735-738 immediately shadows it with the actual indegree map used by the algorithm.

Impact:

This is a small inefficiency and a readability hazard in dependency-ordering code. The surrounding comments already show the algorithm was corrected in place, but the abandoned block remains.

Recommendation:

Delete the first indegree construction block and keep only the final outgoing dependency-count implementation.

F-013 - Low - GPU documentation and crate metadata are stale relative to implementation

Evidence:

  • pmacs-gpu/Cargo.toml:6 still describes the crate as "session 2: hello-world."
  • docs/pmacs-gpu-design.md:3-5 says the GPU work is pre-implementation with sessions queued.
  • The current GPU code has attach, CRDT, rendering, minimap, diagnostics, minibuffer/menu, mouse, and editing code.

Impact:

New contributors and future audit passes start from incorrect project state. This also makes it harder to decide which TODOs are live versus historical.

Recommendation:

Update pmacs-gpu metadata and the top of the design note to point at the current status and the active per-phase framing/audit docs.

F-014 - Low - GPU visual behavior lacks a committed screenshot/pixel regression harness

Evidence:

  • pmacs-gpu/src/main.rs:5670-6750 contains 39 unit tests focused on helper logic and vertex construction.
  • The design note explicitly deferred acceptance-test shape to golden-frame or screenshot comparison (docs/pmacs-gpu-design.md:356-360).
  • I did not find an automated headless GPU screenshot/pixel test in tests/ or pmacs-gpu/src.

Impact:

Layout and rendering regressions can pass the current unit suite. This matters because the frontend has many pixel-local responsibilities: hit testing, dropdown layout, minimap projection, diagnostic underlines, selection/current-line backgrounds, and status/minibuffer composition.

Recommendation:

Add at least one deterministic headless render smoke test or recorded-frame pixel/golden harness. Start with narrow cases: nonblank frame, text visible, dropdown visible and clipped, minimap visible, diagnostic underline visible, and resize behavior.

F-015 - Low - Repository contains backup/build artifacts and untriaged untracked files

Evidence:

  • find located backup/editor artifacts:
    • #run.sh#
    • docs/semantic-frontend-protocol.md.local-bak
  • git status --short also showed an untracked root-level pmacs file, which appears likely to be a local build/run artifact.
  • Several untracked docs are present and may be intentional, but they need explicit triage before commit.

Impact:

Backup and binary artifacts increase review noise and can be committed by accident. Untracked docs make it unclear which design notes are canonical.

Recommendation:

Remove or .gitignore editor backups and local binaries. Triage untracked docs as either real project documents or local handoff notes.

F-016 - Low - Very large source files are maintenance hotspots

Evidence from line counts:

15202 src/lua_bindings.rs
 7093 src/editor.rs
 6761 pmacs-gpu/src/main.rs
 3729 src/lsp.rs
 3495 src/semantic_render.rs
 3178 src/buffer.rs
 2988 src/editor_core.rs

Impact:

These files concentrate unrelated behavior, make review difficult, and increase the chance of hidden coupling. src/lua_bindings.rs in particular combines many Lua API domains, package management glue, LSP stores, theme bindings, attachment bindings, and tests in one file.

Recommendation:

Split only along stable boundaries, not as a drive-by refactor. Good candidates: Lua package APIs, Lua LSP APIs, Lua theme APIs, GPU input handling, GPU render pipelines, GPU layout/dropdown logic, and editor command groups.

Lua Audit Notes

The Lua audit command reported no errors. Warnings were:

  • builtin/packages/repl/init.lua:274: pmacs.process.spawn(spec).
  • tests/fixtures/pmacs-magit/gestures.lua:357: io.open(tmpfile, "w").
  • tests/fixtures/pmacs-magit/status.lua:55: pmacs.process.spawn { ... }.

These are not automatically defects. The REPL package needs process access, and fixture warnings are expected if those packages deliberately exercise filesystem or process APIs. They should still be checked against package manifests whenever fixtures graduate into real packages.

Suggested Priority Order

  1. Fix F-001 and update workspace verification commands.
  2. Fix F-003 and F-004 before treating pmacs-gpu as user-ready.
  3. Fix F-006 before relying on pmacs for editing permission-sensitive files.
  4. Decide the package basename-collision policy in F-005 before wider package ecosystem growth.
  5. Add the GPU visual regression harness in F-014 before large layout changes.