Skip to content

feat(mecatui): add workspace snapshot inspector - #2676

Merged
jbeda merged 3 commits into
mainfrom
impl/workspace-status
Nov 3, 2026
Merged

jbeda merged 3 commits into
mainfrom
impl/workspace-status

Conversation

@jbeda

@jbeda jbeda commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add a read-only workspace snapshot inspector, opened by F10 or /workspace while idle or running. Capture workspace identity and observed Edit/Write intentions once per open; keep the view readable across ordinary run completion.
  • Show Git branch/HEAD, upstream/local-ref divergence, counts and per-file states only for an explicitly embedded server with a verified, trusted local execution root. Remote, nonlocal and file-less sessions retain known identity and observations without inspecting client cwd.
  • Replace F9's unbounded file enumeration with a bounded, remap-aware inspector invitation while preserving the header count, appendix provenance, reading anchors and selection/copy behavior.
  • Register the Workspace key action through help, settings/CLI overrides and effective-default collision validation; update the owning user guides.

Safety and limitations

Git runs with fixed arguments, a restricted scrubbed environment, bounded output and a 30-second overall deadline. It does not fetch or inspect submodule working trees. Effective clean/process filters make Git inspection explicitly unavailable rather than allowing helpers or disabling filters and misreporting status. This uses existing repository trust, not a new trust system or OS sandbox; trusted concurrent malicious configuration replacement is outside the confinement guarantee.

Snapshots are non-atomic and static; close and reopen to resample. Observed paths are announced tool intentions, not proof of successful changes or ownership of the current diff. Shell/delegated writes and resumed-history reconstruction remain outside the observation accumulator.

Validation

  • go test ./cmd/mecatui/... -count=1
  • task test
  • task lint — final all-module run passed with zero issues
  • task test:race — final complete offline suite passed
  • task docs
  • task site:build
  • go run ./cmd/mecademo — tool call/result, permission ask/approval and final result verified
  • Focused regressions cover production startup/F10 composition, exact session roots and trust, same-size-edit helper activation controls, parser failures/output limits/cancellation, frozen/reopened snapshots, approval/teardown/completion behavior, remapped entry points and bounded F9 projections. Removing the production collaborator assignment made its composition regression fail; the assignment was restored.

The aggregate non-race suite passed before lint-only factoring; affected non-race tests and the final complete race suite passed afterward. Manual interactive terminal verification has not been performed.

Review

Panel review against implementation start 1c085bbafc9b99e1aac760a7d2004e5c742a3e7d, with targeted incremental rechecks after fixes: Spec, Standards and Test adequacy clear; Security, Architecture and Duplication clear. One low-priority advisory remains: optionally inline the single-caller Git count helper; the coherent helper is retained. No blocking or important findings remain.

API compatibility

Client-only UI and host-side observation using the existing embedded local-context RPC unchanged. No engine/server semantics, wire fields, feature identifiers, API major or stored-data format changes. Existing remote clients need no upgrade. The UI keybinding action Workspace is additive, defaults to f10, and uses the documented collision checks.

Implementation plan

The full approved implementation plan, including the repository-trust/filter amendment and planned verification, is archived in a regular PR conversation comment. Its published contents were read back and SHA-256 verified. Actual validation results are listed above; the plan's verification section records intended checks rather than test receipts.

Open a frozen workspace inspector with F10 or /workspace while idle or
running. Keep observed Edit/Write paths distinct from sampled Git changes,
and replace the expanded file appendix with a bounded inspector invitation.

Inspect Git only for trusted, verified local execution roots on the embedded
transport. Bound and harden subprocesses, reject clean/process filters, and
keep failures distinct from confirmed non-repositories.

Cover production composition, snapshot lifetime, modal priority, keymaps,
Git parsing and helper suppression; document the limits and trust behavior.

Co-authored-by: Mecatl <noreply@mecatl.dev>
@jbeda

jbeda commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Approved implementation plan: workspace-status

  • Source: .scratch/workspace-status-plan.md (local provenance, not a remote link)
  • Branch: impl/workspace-status; base: main
  • Implementation start: 1c085bbafc9b99e1aac760a7d2004e5c742a3e7d
  • Verified PR head: 4fc006f874b0c895581e2554911a2f195fb588d7
  • Updated: 2026-11-03T22:31:36Z
  • Published plan portion SHA-256: 230824d04644ea5c36c87a6f7fd36e44159c206b804d41e0081ef39f9c178206

This is the full approved plan, including the repository-trust/filter amendment and the operator's workspace-discovery, presentation, and toggle refinements. Its verification section describes intended checks; actual test and review results are recorded in the PR description and subsequent delivery notes. No plan content was omitted or redacted. Paths below describe repository files or local planning provenance.

Refinement delivery review: incremental Spec, Standards, Test adequacy, Security, Architecture, Reuse, and Duplication checks have no outstanding findings after follow-up fixes. Final full lint and race gates pass, along with focused regressions; documentation/site checks and the offline tool/approval/result demo also pass. The source plan below records the agreed behavior, not a claim of live/manual terminal verification.

Workspace status snapshot

Slug: workspace-status
Implementation branch: impl/workspace-status
Base branch: main
Status: Implemented and verified locally on impl/workspace-status; approved trust/filter amendment included below.

Goal and agreed scope

Replace F9's unbounded changed-file appendix with a bounded invitation to a dedicated read-only workspace inspector. F10 and /workspace open the same surface while idle or during an active run. The view presents existing workspace identity, captured observed Edit/Write paths, and a Git snapshot only when mecatui explicitly owns an embedded server and can resolve an accessible local execution root.

  • One snapshot per open. Copy observed paths/identity at open; resolve the local directory and inspect Git asynchronously once. No polling, live synchronization, manual refresh, or Git work triggered by F9/rendering. Close and reopen to resample.
  • Git commands never contact the network. Ahead/behind uses only existing local refs. Allow a 30-second overall inspection deadline, cancellation on close, and bounded output.
  • Confirmed non-repository: omit Git entirely. Missing Git, failed root resolution, permissions, timeout, malformed/truncated output, and other inspection failures must not masquerade as a clean repository or confirmed non-repository.
  • Remote/nonlocal/file-less sessions still get known identity and captured observations; they never use the client's directory as a proxy for session files. Do not add engine, server, protobuf, SDK, or wire behavior.
  • No Git mutations, diffs, line statistics, fetch, PR integration, commit browser, recursive submodule inspection, new filesystem authority, new activity tracking, or directory/brace compression.

Current seams and minimum approach

  • cmd/mecatui/main.go composes ui.Deps, including explicit Embedded and a LocalSessionContext client. The latter is currently injected for all transports: its presence alone is not authority to run local Git.
  • client.LocalSessionContextGetter in cmd/mecatui/client/local_session_context.go resolves a root for an exact session. Existing embedded-only service (cmd/mecatui/embed/embed.go; internal/adapter/server/local_session_context.go, read-only references) verifies local persisted placement and exact reattachment. Reuse unchanged; never fall back to launch cwd, Deps.Workspace, placement labels, or loopback detection. This supports resumed/selected worktrees without an engine change. Nonlocal execution gets no local root even inside an embedded deployment.
  • client.Placement is display metadata, not a path. Use current activePlacement kind/label/branch/revision and existing connection metadata for identity. Show the verified local effective directory, and repository root if different; show only identifiers already exposed by the client for remote workspaces. No invented paths, remote filesystem probing, raw remote URLs, or new environment identifiers/APIs.
  • scrollback.Conversation.AppendixSnapshot() in cmd/mecatui/ui/internal/scrollback/appendix.go already returns detached, deduplicated, first-seen paths. Reuse it, not a second accumulator. Accumulation occurs on announced tool calls, not successful results (ui/update.go, ui/render.go:mutatedPath). It omits Shell and delegated-child changes and is not a complete attribution of a Git diff.
  • Reuse the surface lifecycle, cancellation/owner-correlated async results, theme styles, terminaltext sanitization/tab expansion, and bounded scrolling from docs/tui.md, ui/surface.go, and ui/worktrees.go. Keep host/exec code outside ui.
  • /workspace belongs in the unconditional client builtin registry, not the existing wiredCollaborators.Workspace enrollment flag. Typed and palette dispatch already support local builtins during runs before steering/queue delivery.

Observable view and contracts

Identity and Git

Render a compact identity section immediately, followed by a scrollable/filterable file area in an inspector card. Show only known fields; do not claim that embedded transport necessarily means local execution. Once resolved, show the effective local working directory. For eligible Git workspaces show:

  • Repository root, branch or detached short HEAD, and upstream name when available.
  • Ahead/behind if Git can determine it from local refs; absent is not zero.
  • Staged, unstaged, untracked, and conflicted file counts, plus per-file index/worktree states. A path can be both staged and unstaged; do not imply counts are mutually exclusive.
  • Snapshot capture time and a short non-atomic/stale-by-design indication. Files may change while inspection runs; do not lock or coordinate with the agent.

Use one authoritative fixed status command: git --no-optional-locks status --porcelain=v2 -z --branch --ahead-behind --untracked-files=all --ignore-submodules=all, plus the minimum root query needed. Explicit flags override repository-local status.showUntrackedFiles, status.aheadBehind, and submodule settings. Preserve filenames containing spaces, newlines, tabs, rename source/destination pairs, and non-ASCII bytes until terminal-safe presentation. Enumerate actual untracked files rather than count a directory as one file. --ignore-submodules=all deliberately excludes submodule working-tree/HEAD inspection; staged gitlink changes reported by Git may remain visible, but totals are not recursive repository cleanliness. Show the concise Git scope note Submodule working trees not inspected; no separate discovery pass is needed. No nested Git commands or submodule helper processes. Parse errors and output bounds yield an incomplete/error state, never trusted clean totals.

Session observations

Label these as observed Edit/Write paths, not files successfully changed or current Git changes. Mark matching Git rows as observed and list unmatched observed paths separately (they may be clean, committed, removed, ignored, or outside the repo). Keep the section available without Git.

Join only using lexical normalization against the resolved local working directory/repository root; retain original display paths. Do not resolve symlinks, open files, or expand access to match rows. Do not apply local path semantics to opaque/remote observations. A marker is an observed tool-call fact, not ownership of the current diff; absence does not prove the session did not change the file.

Capture paths once before starting asynchronous inspection. Later tool calls update root conversation state but never mutate this surface's snapshot. Filter/navigation changes presentation only. No refresh action, timer, event subscription, or root live-sync hook.

Input and lifetime

  • F10 and typed/palette /workspace open the inspector in idle/running phases, independently of server command support or Git availability. Reject command arguments consistently with other no-argument builtins.
  • Preserve the prompt draft, queue/steer state, running phase, stream, and run cancellation semantics. Esc closes the inspector, not the run. Opening/closing must not submit the command to the model or server.
  • Respect existing modal priority: no opening over approval. An incoming approval takes priority and may replace/close the workspace inspector through existing lifecycle handling. Session replacement/connection restart closes it and cancels inspection.
  • Capture surface owner and exact session identity with the request. Ignore stale completions after close/reopen/rebind; pass unrelated stream messages to the root reducer. No global generation framework is needed.
  • Keep Workspace open across ordinary run completion without refreshing its captured snapshot or cancelling an in-flight inspection. Preserve modal input ownership and keep the prompt blurred until the inspector closes; still reduce run completion and queued-run transitions normally. Approval priority and session-change teardown remain unchanged.
  • Narrow/zero-height offers become safe close-only presentation. Keys, paste, pointer hits, and wheel cannot activate hidden controls or edit/scroll the underlying conversation.

F9 and keybindings

Keep the existing small header file count. Replace the expanded file enumeration with a short, width-bounded advert using the effective remapped Workspace key and /workspace fallback; no Git-derived counts or loading side effects. Preserve the existing appendix identity/provenance when observations exist rather than redesigning scrollback ownership.

Update both conversationContent and refreshView assembly paths together. Preserve frameWithAppendix, logical reading anchors, selection/copy equality, and conversationView restoration. Keep the no-observation case free of a fabricated changed-file appendix; F10 remains discoverable in key help and the command palette.

Register public Workspace action with default f10 in ui/keys.go, help/key markings, override setters, keymap/validator.go action/default/global-open collision checks, and the production settings/CLI path. Validate collisions in both directions against effective defaults. Route after modal ownership with one opener that permits idle/running.

Local inspection safety and implementation boundary

Prefer a small host adapter in new cmd/mecatui/workspace_status.go (package main) over a new package/framework. Inject a narrow snapshot collaborator/data shape into ui.Deps; define only data the inspector needs. Wire the implementation solely under cfg.transportMode == modeLocal. Remote still opens the inspector with no local collaborator. Leave unrelated status-line local-context wiring unchanged.

Each open resolves the captured session using the existing local-context getter, then invokes Git via stdlib os/exec with fixed argv and Cmd.Dir set to that verified root. No shell interpolation, arbitrary user-selected Git commands, client-cwd fallback, or dependencies on microVM lifecycle Git packages. Construct the child environment from an explicit allowlist (executable lookup PATH and platform-required process variables only), never os.Environ() wholesale; set LC_ALL=C. Apply internal/adapter/gitenv.Scrub to that restricted base, then append GIT_TERMINAL_PROMPT=0, GIT_OPTIONAL_LOCKS=0, GIT_NO_LAZY_FETCH=1, and GIT_ALLOW_PROTOCOL= (deny all transports). The fixed status flags above suppress submodule inspection; root queries must not add external helpers. This reuses existing neutralization without copying it or forwarding ambient credential/proxy variables. Keep global/system extensions, fsmonitor, hooks, pager, external diff/textconv, and inherited GIT_* overrides from turning inspection into execution or networking. Test the actual command's environment and argv, not merely redacted output. Do not print environment contents or raw credential-bearing remote configuration.

Bound stdout/stderr and ensure cancellation/deadline releases the subprocess and pipes. Do not alter the shared gitenv policy for this feature. Missing binary/root resolution errors are nonfatal view states. Tests must distinguish confirmed non-repository from other Git failures. Keep Git operations read-only, including avoiding optional index refresh writes.

Approved implementation amendment: repository trust and filters

The operator approved using existing repository trust, not a new trust mechanism or a Git sandbox. Gate local Git on trust covering the actual resolved execution root; launch-directory trust must not silently authorize an unrelated selected root. Unknown/untrusted roots retain identity/observations but no Git inspection. Reuse existing trust resolution/confirmed server facts without engine/server/wire changes. Permit additional bounded read-only Git configuration inspection to detect clean/process filters, returning explicit Git-unavailable when helper-free status cannot be established. Keep lightweight environment/argv hardening and the no-helper/no-network inspector contract; do not silently disable filters and report potentially incorrect clean totals. Treat trusted concurrent malicious configuration replacement as outside this feature's confinement guarantees; the child is not an OS sandbox. The operator noted that prompt Git status commonly has this same exposure and asked for proportionate handling, not an elaborate new sandbox.

Implementation tasks

  1. Local one-shot snapshot adapter and contract. Add the minimal UI-facing result/collaborator and host implementation, using existing local session-context resolution and conditional embedded composition. Include root/Git outcome distinctions, parsing, environment hardening, 30-second deadline, output bounds, and focused adapter tests. Completion: fixtures prove exact worktree root use, local-only invocation, correct status, cancellation, and no network/helper execution; no engine/wire files changed.
  2. Workspace inspector. Add ui/workspace.go and focused tests using surface, captured detached observations/identity, one asynchronous load, bounded filter/navigation, and strict owner/session result correlation. Integrate chosen run-completion behavior narrowly. Completion: snapshot stays fixed while the root stream continues; close/reopen resamples and late results cannot affect another lifetime/session.
  3. Entry points and de-emphasis. Add builtin/palette/F10/help/override registration and running dispatch; replace F9 enumeration at both content paths with remap-aware bounded advert. Keep existing accumulator/header and appendix provenance. Completion: real typed/palette/key routes work idle/running without steering/queueing/cancelling; F9 height does not grow with path count; anchors and selected/copied text remain correct.
  4. Integration and user docs. Exercise real composition and root/surface event boundaries; update user-docs/mecatui/using-the-tui.md as canonical treatment plus keybindings.md reference and stale F9 help claims/comments. Use tech-writer skill, read user-docs/AGENTS.md and _STYLE.md before edits. Document local-only Git, no network, static/non-atomic snapshots, observation limitations, chosen history behavior, and reopening to resample. No architecture edit expected: reuse existing boundaries.

Dependencies: task 1's narrow data contract precedes task 2; tasks 2 and 3 integrate together; task 4 verifies the complete candidate. Do not stage/commit/push/create a PR unless separately requested. If an implementation PR is authorized, preserve the latest publishable plan in one verified PR conversation comment per plan-implement, keeping local plan/run metadata ignored.

Verification

Focused tests at production seams (not source-text assertions):

  • Host adapter: test-owned repositories including clean/non-Git, unborn and detached HEAD, local-only upstream divergence, linked worktree and nested working directory, staged-plus-unstaged same path, untracked files/directories, rename/delete/conflict, submodule scope, hostile names/config/ambient GIT_*, missing Git, inaccessible root, malformed/bounded output, timeout/cancel. Include local status.showUntrackedFiles=no and status.aheadBehind=false negative controls with positive file/divergence fixtures; the explicit argv must win. Submodule fixtures assert no nested command/helper, staged-gitlink treatment, and the visible non-recursive scope note. Inject a deterministic command seam for failure/ordering controls; real Git fixtures verify parser/invocation integration. No network fixtures. Verify prohibited helper hooks cannot run; assert actual child argv/environment include the required controls and exclude injected credential/proxy sentinels.
  • Composition: external/connect (including loopback/Unix-looking endpoint), embedded nonlocal execution, no-FS/debug, and local-context failure cannot inspect client cwd. Active-session root is resolved on each open, not cached from launch/status source. Explicit temporary UserModelDir and isolated helpers in any composition fixture; no ambient HOME/XDG reliance.
  • UI: F10 plus typed and palette /workspace idle/running; builtin wins server-name collision; bad arguments; server commands absent; draft/queue/steer unchanged. Snapshot A then live Edit/Write B leaves view A and root A+B; reopening shows A+B. Failed calls remain labeled observations. Empty/no-Git/remote states, filter matching, status marker joins, no automatic/manual refresh, close cancellation, late results across close/reopen/session changes, permission arrival, and selected run-completion behavior.
  • Input/layout: narrow/zero geometry, resize before input, tabs/control-sequence sanitization, non-color status cues, modal wheel/paste/key isolation, restored prompt focus and off-tail conversation reading while streaming.
  • Keymaps/F9: effective-default collisions in both directions, safe rebinding, settings/CLI precedence and remapped help/advert. Large path count produces bounded appendix; conversationContent and displayed projection agree; appendix anchor fallback and selection/copy remain correct. Existing anchors include changedfiles_test.go, conversation_view_test.go, logical_selection_test.go, modal_lifecycle_test.go, and toolcalls boundary tests.

Integrator runs focused go test ./cmd/mecatui/... first, then task test once for integrated changes (1800-second outer timeout). Markdown/user-doc changes require task docs and task site:build (600 seconds each). Before PR-ready handoff: task lint (600 seconds), task test:race (1800 seconds), and go run ./cmd/mecademo showing tool call, approval, and result. Use panel-review against the implementation branch's recorded start commit (capture during implementation, not moving main); fixes get targeted incremental review. No tests/build gates were run during planning.

API compatibility

Client-only UI and host-side observation using the existing embedded local-context RPC unchanged. No engine/server semantics, wire fields, feature identifiers, API major, or stored data format change. Existing remote clients need no upgrade. UI keybinding schema gains an additive action; document its default and collision rules. API compatibility statement should accompany any eventual PR.

Confirmed behavior decisions

  1. Run completion: keep Workspace open across ordinary completion so the snapshot remains readable; it stays frozen and Esc still closes it. Preserve approval priority and session-change teardown. The current endRun closes all modals (ui/update.go), so make a narrow exception without redesigning other surfaces, refocusing the underlying prompt, or blocking normal terminal/queue reduction.
  2. Resumed history: retain the existing observed-path accumulator scope in this first slice. conversationFromTranscript reconstructs tool cards but does not populate that accumulator. Label/document observations as those retained by the current client view, not a complete session history. Do not backfill old events or add persistence in this change. Git still reports its independently sampled current working-tree state.

The operator confirmed both recommendations. No material behavior decisions remain open. Do not reopen remote Git, refresh, network, key choice, or engine expansion during implementation.

Approved review refinements: workspace discovery and presentation

The operator requested a normal-view F10 prompt when the session is in Git or has observed Edit/Write paths, richer formatting matching existing data surfaces, clearly separated state sections, and a concise shared standard in docs/tui.md. Use the effective remapped Workspace key. Do not require F9 or a prior inspector open for a clean Git repository to be discoverable. A one-shot, session-correlated repository-membership check at binding is allowed for this hint; reuse the same exact-session local root, actual-root trust, deadline, bounded output and hardened environment, and run only the minimal root query (not status/filter inspection). No render/F9 Git work or polling. Display-only confirmed Git branch metadata can also support the hint without granting local inspection authority. Session changes cancel/discard old discovery; the inspector itself still captures one fresh, frozen snapshot per open.

Organize the inspector by provenance: session identity/placement, sampled local Git state (including its own unavailable/loading/nonrepo state and time), and retained observed Edit/Write intentions (including limitations/unmatched paths). Use existing theme-owned heading/field/value/status styles, spacing and labels with non-color section cues; keep sanitization, tab expansion, wrapping, bounds and close-only input behavior. Add the reusable inspector/data-section presentation convention under the existing lists/detail-view contributor guidance; do not invent a styling framework or new palette.

The operator also requested readable F9-state hints, stronger filenames distinct from headings, and a symmetric Workspace toggle. In expanded conversation mode, advertise the effective F9 action as hiding details; label the workspace invitation with its action. Use bold normal theme text for Git/observed filenames (including rename paths), accented headings/labels, normal field values, and quiet file status/annotations. The effective Workspace binding and all configured aliases close an open workspace inspector, including loading/compact/nonempty-filter states; Escape still closes it. Closing cancels only the captured inspection, never the active run, and retains draft/queue/steer state. Preserve confirmed complete placement across successor bindings while invalidating stale binding metadata and discovery results.

Design review

Independent fresh-context plan critique completed. It identified one blocker (Git defaults could hide untracked files/divergence) and two important corrections (explicit submodule scope and a restricted child environment). The draft now pins the status argv, non-recursive scope note, allowlisted environment and no-network controls, with negative-control fixtures. The same reviewer rechecked the exact replacement sections and found all three issues addressed. Review used inlined draft text because ignored scratch files are unavailable in the review checkout; it did not verify implementation or execute tests. The operator subsequently confirmed both reserved behavior recommendations unchanged. No review findings or behavior decisions remain unresolved. Implementation was subsequently authorized and completed locally on impl/workspace-status, including the approved trust/filter amendment above. Execution, gate, and review receipts are in .scratch/workspace-status/run.md; the planning review itself did not verify implementation.

Advertise the workspace inspector for confirmed Git sessions or retained
Edit/Write intentions, using one trusted local membership probe per binding.
Group snapshot details by provenance, emphasize filenames in bold normal text,
and clarify F9 hints and the remap-aware F10 open/close toggle.

Preserve confirmed successor placement across clear and worktree handoffs,
and strengthen asynchronous, transport-authority, alias, and style regressions.

Co-authored-by: Mecatl <noreply@mecatl.dev>
Co-authored-by: Mecatl <noreply@mecatl.dev>
@jbeda
jbeda enabled auto-merge (squash) November 3, 2026 23:13
@jbeda
jbeda merged commit 6d76b8b into main Nov 3, 2026
41 checks passed
@jbeda
jbeda deleted the impl/workspace-status branch November 3, 2026 23:18

This branch was successfully deployed

1 active deployment
Preview — 7adf8ef3 Deployed Nov 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant