Add portable record export across surfaces - #122
Open
tony wants to merge 71 commits into
Open
Conversation
tony
force-pushed
the
issue-80-deterministic-identities
branch
from
July 12, 2026 20:14
adcf5e0 to
cb6377d
Compare
tony
force-pushed
the
issue-80-deterministic-identities
branch
9 times, most recently
from
July 14, 2026 03:56
a68c683 to
5ffd718
Compare
tony
force-pushed
the
issue-80-deterministic-identities
branch
from
July 14, 2026 23:36
5ffd718 to
4e49bbd
Compare
tony
force-pushed
the
issue-80-deterministic-identities
branch
from
July 18, 2026 11:40
4e49bbd to
c755ba0
Compare
tony
force-pushed
the
issue-80-deterministic-identities
branch
from
July 19, 2026 01:06
c755ba0 to
a0c10d6
Compare
tony
force-pushed
the
issue-80-deterministic-identities
branch
from
July 19, 2026 01:42
a0c10d6 to
68db741
Compare
why: Directory completion delegated tilde expansion to process-global account lookup instead of the home already owned by the TUI session. what: - Pass the session home from the export dialog through the picker worker. - Resolve only current-user tilde syntax with the shared safe resolver. - Cover session-home expansion and other-user rejection without expanduser.
why: Canceling a Textual worker cannot stop a thread already blocked in scandir, so rapid edits could overlap filesystem enumerations. what: - Keep one active enumeration and coalesce newer input into one latest slot. - Launch the queued scan only after the blocked filesystem call returns. - Preserve debounce, generation, focus, and unmount invalidation contracts.
why: Opening an untrusted preference path with buffered I/O could block startup on a FIFO and accepted non-regular file types before parsing. what: - Open preference files nonblocking without following the final symlink. - Require a regular fstat result before one bounded read loop. - Cover FIFO latency, path-free fallback, and the exact 16 KiB boundary.
why: Unicode format controls can reorder or hide characters in a reviewed basename without appearing as ordinary control characters. what: - Reject category Cf in template literals and compiled filenames. - Cover zero-width and bidi override controls with focused regressions.
why: A clean first-use session remembered an app-owned export path that the review validator required to exist, so the default flow could not proceed. what: - Create only the computed private default through the descriptor-safe walker. - Keep arbitrary missing and symlinked directories validation-only and rejected. - Prove clean-home export, 0700 mode, and path safety in Pilot regressions.
why: Pressing y accepted the durable action while the disabled confirmation list still visibly highlighted No. what: - Move the review highlight to Save before disabling confirmation. - Pin the visible saving state in the existing single-write Pilot test.
why: The inline template error fell below a 30 by 10 tmux viewport, leaving a focused editor with no visible explanation. what: - Make edit and review stages vertically scrollable only when space requires it. - Scroll inline errors into view without adding pump-side work. - Cover feedback and stage reachability at 40x12, 30x10, and 60x16.
why: The TUI guide still described e as an immediate private export and blurred its remembered review state with one-shot slash commands. what: - Document the reviewed directory, template, exact filename, and No path. - Keep explicit and private-default slash-command behavior distinct. - Scope the documentation contract to the Export section.
why: A failed reviewed save restored its retained inline draft and also emitted a duplicate toast carrying the same error. what: - Let a mounted saving dialog consume its worker failure inline. - Preserve path-free error toasts for slash-command and no-dialog failures. - Cover retained drafts, cancel keys, and the no-dialog writer route.
why: Absolute export directories inside the active session home exposed a machine-specific prefix in the edit and review stages and in remembered preferences. what: - Compact session-home directories to tilde drafts without resolving paths. - Apply compaction to loaded and newly entered directory values. - Cover defaults, legacy preferences, outside paths, and persistence.
why: Ctrl-C dismissed the export modal immediately while editing, making a familiar terminal clear-field gesture unnecessarily destructive. what: - Clear a focused non-empty directory or template before dismissal. - Preserve focus after clearing and dismiss on a second Ctrl-C. - Retain immediate review cancellation and saving protection.
why: Restoring an asynchronous export failure at compact terminal sizes let deferred focus scrolling move the sole inline feedback outside the viewport. what: - Restore retained editor focus before applying asynchronous feedback. - Reveal the error through a named callback after refreshed layout. - Cover retained focus and visibility at 30 by 10.
why: A post-refresh error reveal could outlive its edit state and scroll a dialog after rapid dismissal or after its feedback had been cleared. what: - Scope deferred reveals to a generated non-empty error request. - Require the mounted active dialog to remain in its edit phase. - Invalidate pending reveals on updates, review, dismissal, and unmount. - Cover rapid Escape and cleared-error races deterministically.
why: Directory drafts could contain control, bidi-format, or surrogate code points that made reviewed destinations and completion rows ambiguous. what: - Share one reviewability rule across preference load, save, and submit. - Omit unsafe existing directory names from bounded completion. - Cover control, invisible, bidi, surrogate, config, and dialog cases.
why: Draft validation created the first-use export directory before the user accepted Save, so review, No, and cancel crossed an unexpected mutation boundary. what: - Validate a missing exact app default through a read-only no-symlink prefix walk. - Create that directory securely only in the accepted export worker. - Keep arbitrary missing and symlinked paths rejected. - Document artifact and private-preference mutation boundaries.
why: The widgets package re-exported completion rows, popup chrome, and export workflow values that are internal module details rather than reusable widgets. what: - Keep internal candidate, popup, draft, and intent types out of package exports. - Import the intent type directly from its defining module. - Pin the narrower package boundary without making an API claim.
why: The export changelog used an ambiguous pronoun and implied that every input state handled the shortcut identically. what: - State directly that e remains ordinary text when an input is focused. - Pin the exact focused-input wording in the export documentation contract.
why: An empty directory draft resolved to the process working directory, so review could display a blank value while Save targeted an implicit location. what: - Reject empty text in the shared directory validator while preserving literal whitespace-only Unix paths. - Cover resolution, preference load and save, dialog review, and completion boundaries.
why: Lexical directory compaction runs on the Textual pump, where an unbounded path with many components could stall input handling before validation was offloaded. what: - Apply a documented 4,096-character ceiling to the live editor and shared validation and persistence boundaries. - Reject oversized Enter snapshots and compaction inputs before path processing, with boundary regressions.
why: Export editing should follow the directional muscle memory of the rest of the TUI, while review should remain calm and legible in small terminals. what: - Add clamped Ctrl-H/J/K/L and completion-aware arrow traversal. - Pin edit and review hints without hiding validation feedback. - Shrink-wrap a No-first confirmation with a moving selection marker. - Cover navigation, layout, saving, and pump roles with Pilot tests.
why: Modal export obscured the active explorer and made the reviewed save flow hard to distinguish from the reader. A pane-owned state machine keeps context visible and gives shortcut and slash-command entry one behavior. what: - Replace the selected-record modal with a fresh detail-pane export flow. - Restore the exact search draft, selection, focus, zoom, and reader state. - Keep validation and durable output off the Textual message pump. - Document and test the reviewed /export contract and compact layout.
why: Leading-slash text that is not a registered command is a valid search. Treating every slash-prefixed value as transient restored an older query after the next successful command. what: - Remember the active input after dispatch classifies it as a literal query. - Cover exact value and selection restoration after a leading-slash search.
why: A second Tab could leave the export state for the live search input, allowing hidden searches whose results no longer matched the restored query. what: - Wrap Tab and Shift-Tab across export editors and the review choice. - Preserve Tab acceptance for an open directory completion. - Cover edit and compact-review focus containment with Pilot.
why: Textual mounts dynamically added children after the opening pump turn. A same-turn priority key could query export controls before compose completed. what: - Track when the pane's composed controls are ready for routed actions. - Ignore edit, cancel, and review actions during the deferred mount window. - Reproduce the race without an intervening Pilot pause.
why: The export action should replace the detail reader on wide terminals, not obscure the results that give the action its selection context. what: - Preserve the results column beside the export pane in wide layouts. - Keep the focused full-body export presentation for stacked terminals. - Cover responsive transitions without changing reader state.
why: Only the direct thread command uses an automatic canonical-ID name; the reviewed record command previews and confirms a templated filename. what: - Attribute automatic private naming specifically to /export-thread. - Pin the command distinction in the export documentation contract.
why: Reusing the FastMCP decorator mutates function metadata shared with the docs shim, making schema results depend on registration order. what: - Build dedicated Tool objects when registering docs schema functions. - Keep the live export docstring and documented schema text aligned.
why: Private Textual reactive hooks couple query-draft state to framework internals and lose cursor-only selections. what: - Observe value and selection through public watch registrations. - Guard the public pump callbacks and cursor-only restoration behavior.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add the initial portable export contract across CLI, TUI, and MCP using one deterministic, frontend-neutral renderer.
agentgrep exportfor stable NDJSON or Markdown, bodies on by default, standard output or explicit files, bounded selection, no-clobber output, and explicit--force./export [PATH]and/export-thread [PATH]for one selected record or its observed thread, with private collision-free defaults and all identity, rendering, and disk work off the Textual message pump.export_recordsMCP tool for one to 20 existing search refs, bodies opt-in, a 400 KiB inline artifact ceiling, bounded opaque refs, and no server-side filesystem authority.Relationship
This PR targets the branch for #111 because deterministic record identities are its prerequisite. It is independent of the bookmark PR and can be retargeted to
masterafter #111 lands.Closes #81
Tested
ty, 2,891 passed, 3 skipped, 3 expected failures, and Sphinx.Goal
Idempotent completion condition for
/goal. It asserts an end state, not an action: if the state already holds, the first evaluation passes and nothing is edited.git rev-parse --abbrev-ref HEAD && git -c rerere.enabled=false merge-tree --write-tree --name-only issue-80-deterministic-identities HEAD && grep -A3 '^## Status' docs/dev/adr/*portable-record-export.md && NO_COLOR=1 AGENTGREP_TUI_WATCHDOG=1 PATH=$PWD/.venv/bin:$PATH env -u VIRTUAL_ENV -u UV_NO_SYNC ./.venv/bin/python -m pytest -m '' --reruns 0 -q