Skip to content

Add keyboard workflows and data safety hardening - #1

Merged
exula merged 2 commits into
mainfrom
feat/qol-and-hardening
Aug 31, 2026
Merged

Add keyboard workflows and data safety hardening#1
exula merged 2 commits into
mainfrom
feat/qol-and-hardening

Conversation

@exula

@exula exula commented Aug 31, 2026

Copy link
Copy Markdown
Owner

What Packrat is

Packrat is a deliberately small, portable backpacking gear planner:

  • gear_core.py is the reusable domain/persistence layer: the flat-file schema, weight calculations, review heuristics, and Markdown rendering.
  • gear_tui.py is a Textual adapter over that model: inventory CRUD, trip composition, dashboards, and exports.
  • gear_data.json is both the source of truth and the portability boundary. The design optimizes for “copy or sync one folder and run it anywhere,” not multi-user collaboration.

That separation is a good fit for the project. The largest risks were not architectural complexity; they were concentrated state mutation, an optimistic JSON loader, an inaccurate claim that every action had a keyboard equivalent, and no executable regression suite around Textual or persistence.

What this changes

Keyboard-first workflows

  • Adds 1 / 2 / 3 tab navigation and / to focus the active search.
  • Adds contextual Add, Edit, Delete/Remove, Review, Open, and Export shortcuts.
  • Adds Ctrl+S to forms/pickers and Enter to confirmation dialogs.
  • Adds Esc-to-clear-search behavior and preserves table selection across refreshes.
  • Adds a discoverable ? shortcut overlay and exposes useful bindings in the footer.
  • Keeps letter shortcuts scoped to their pane so typing in an Input remains normal.

Data and filesystem hardening

  • Validates the entire JSON schema on load and before every save.
  • Rejects negative/non-finite weights and costs, invalid quantities/usefulness, duplicate IDs/assignments, unsupported categories/types, and newer unsupported schema versions.
  • Turns malformed JSON into a concise location-aware startup error.
  • Keeps atomic writes, but now flushes them to disk and cleans temporary files on failure.
  • Retains the previous successful file as gear_data.json.bak on every save.
  • Detects external file changes and refuses to clobber a newer sync/process update; failed edits roll back in memory.
  • Adds Ctrl+B for an on-demand backup.
  • Makes exports atomic and locates them beside the selected --data file instead of beside the source code.
  • Reports save, backup, export, and startup failures in the UI/CLI instead of crashing or pretending success.
  • Rejects negative form values before they reach persistence.
  • Makes modal widths adapt better to smaller terminals.

Regression safety

  • Adds 8 standard-library/headless Textual tests for schema validation, malformed data, atomic saves, backups, external-change conflicts, custom export paths, missing gear references, and keyboard workflows.
  • Adds GitHub Actions coverage for Python 3.9 (the declared minimum) and 3.12.
  • Replaces the placeholder package description and documents shortcuts, recovery behavior, and test commands.

Verification

  • uv run --python 3.9 python -m unittest discover -s tests -v
  • uv run --python 3.12 python -m unittest discover -s tests -v
  • uvx ruff check --select E4,E7,E9,F,I gear_core.py gear_tui.py tests
  • git diff --check
  • Loaded, validated, summarized, and rendered the committed 20-item / 1-trip data file.

Deliberate limits

This does not attempt to merge simultaneous edits. It detects and blocks an overwrite, keeps a backup, and tells the user to reload. True multi-writer support would require locking plus sync-aware conflict resolution or a different storage model, which would work against Packrat’s single portable JSON-file goal.

@exula
exula merged commit 9e71415 into main Aug 31, 2026
2 checks passed
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