feat(tui): wire Engram data-directory screens into model, router, and app#489
Closed
tonyblu331 wants to merge 16 commits into
Closed
feat(tui): wire Engram data-directory screens into model, router, and app#489tonyblu331 wants to merge 16 commits into
tonyblu331 wants to merge 16 commits into
Conversation
Adds internal/storage package with: - FormatBytes: human-readable sizes using 1024-based units (B/KiB/MiB/GiB) - AvailableBytes: platform-gated disk space check (syscall.Statfs on Unix, GetDiskFreeSpaceExW on Windows) No Engram coupling. Used by the upcoming data-directory domain layer.
Adds the engram domain layer needed for first-class Engram data-directory management (issue Gentleman-Programming#346). Introduces DataDirRef for future-proof persistence, CopyDB with OS-native streaming via io.Copy, snapshot-guarded DataDirService, SuggestLocations with cross-platform volume discovery, presenter helpers for TUI display, and InjectWithOptions for optional ENGRAM_DATA_DIR injection.
…e, and TUI model Adds EngramDataDirOp string enum (keep/copy/move/delete/fresh) and EngramDataDir string field to Selection for install-time data dir choice. Persists EngramDataDir in state.json (engram_data_dir) and pre-loads it into the TUI Model before the first render so management screens have it available without a separate state read.
- Add ScreenEngramDataDir, ScreenEngramDataDirConfirm, ScreenEngramDataDirResult, ScreenEngramDataDirInstall screen constants to model.go - New EngramDataDirDoneMsg async result message dispatched from goroutine - Model fields: HomeDir, EngramDetected, EngramDataDirFn (injected), engramDir* state - Helper methods: hasEngram(), resolvedEngramDir(), engramDBSize() - Welcome enter handler: inserts "Manage Engram directory" item between OpenCode Profiles and Manage Backups using the next-counter pattern - confirmSelection() handlers for all 4 new screens; Cancel uses m.PreviousScreen for correct back-nav from both management and install flows - Fix: cursor on ScreenEngramDataDirConfirm uses m.Cursor (not removed engramDirConfirmCursor field which was never updated by j/k nav) - Fix: EngramDataDirDoneMsg handler updates m.EngramDataDir per-op (Move → dstDir, Delete/Fresh → "", Copy/Keep → unchanged) - Fix: ScreenEngramDataDirInstall Keep case now navigates to ScreenDependencyTree - router.go: add routes for all 4 new screens (esc key handling) - screens/engram_datadir.go: 4 render funcs using renderOptions() and style primitives - screens/welcome.go: hasEngram bool param added to WelcomeOptions + RenderWelcome - app.go: wire HomeDir, EngramDetected, engramIsPresent(), buildEngramDataDirFn() which executes op via DataDirService and persists new dir to state.json
…m dir is set SuggestLocations was using: add(DefaultDir, currentDir== || currentDir!=DefaultDir) which sets IsCurrent=true on the default entry even when a custom path is active. The dedup map only prevents the duplicate when currentDir==DefaultDir, not when it is a different custom path. Fix: add(DefaultDir, currentDir=="") — only the explicit-empty case means the default is the current directory. Added regression test: TestSuggestLocations_DefaultNotCurrentWhenCustomDirSet
This was referenced May 9, 2026
Contributor
Author
|
👋 Maintainer request: could you please add the `type:feature` label? Also a note on the size check: this PR is part of a 7-PR chain (
Each PR's check will turn green as soon as its predecessor merges. All seven are independently within the 400-line budget. |
ef8263b to
4c0d759
Compare
Resolve inject.go: injectCore + InjectWithOptions + InjectWithPromptDir.
Adds internal/storage package with: - FormatBytes: human-readable sizes using 1024-based units (B/KiB/MiB/GiB) - AvailableBytes: platform-gated disk space check (syscall.Statfs on Unix, GetDiskFreeSpaceExW on Windows) No Engram coupling. Used by the upcoming data-directory domain layer.
…r helpers Introduces the foundational types and location-discovery layer for Engram data-directory management (issue Gentleman-Programming#346, PR B1 of B1+B2+B3): - DataDirRef: future-proof path type with Resolve(homeDir), DefaultDir, DBPath helpers - SuggestLocations: ordered candidate list with deduplication and free-space labels - Platform-specific volume discovery (Statfs on Unix, GetDiskFreeSpaceEx on Windows) - FormatDirLine / FormatSpaceWarning presenter helpers (no Bubbletea dep) 10 unit tests cover all location and label behaviours. Depends on PR A (feat/storage-utils).
Adds the copy primitive for Engram data-directory operations (issue Gentleman-Programming#346, PR B2 of B1+B2+B3): - CopyDB(src, dst string) error: writes to a .tmp sibling then os.Rename for atomicity; uses io.Copy which activates sendfile/splice on Linux and macOS automatically — no manual buffer needed - Size verification before rename catches truncated writes - Deferred os.Remove on the tmp file is cancelled only on successful rename - 5 unit tests cover happy path, idempotency, missing source, and dir creation Depends on PR B1 (feat/engram-domain-types).
…ions Completes the engram domain layer for data-directory management (issue Gentleman-Programming#346, PR B3 of B1+B2+B3): - DataDirService.CopyTo / MoveTo / Delete: snapshot-guarded operations that call backup.Snapshotter.Create before touching data; return snapshot ID - DiskSpaceOK: pre-flight check before CopyTo/MoveTo - InjectWithOptions: backward-compatible extension of Inject that accepts InjectOptions{DataDir} to include ENGRAM_DATA_DIR in MCP env config; existing Inject() delegates to InjectWithOptions with zero options unchanged - 6 service unit tests + inject backward-compat coverage Depends on PR B2 (feat/engram-domain-copy).
…e, and TUI model Adds EngramDataDirOp string enum (keep/copy/move/delete/fresh) and EngramDataDir string field to Selection for install-time data dir choice. Persists EngramDataDir in state.json (engram_data_dir) and pre-loads it into the TUI Model before the first render so management screens have it available without a separate state read.
Add four headless render functions for the Engram data-directory feature: - RenderEngramDataDir: location picker (management flow from Welcome) - RenderEngramDataDirInstall: install-time picker (Keep/Move/Fresh) - RenderEngramDataDirConfirm: destructive-op confirmation with snapshot reminder - RenderEngramDataDirResult: success/error result screen Includes 10 unit tests covering all render paths (no Bubbletea dependency). Depends on PR C (feat/engram-data-dir-state).
… app - model.go: add ScreenEngramDataDir* constants, engram state fields, helper methods (hasEngram, resolvedEngramDir, engramDBSize), and Update handlers for all four Engram screens (management, confirm, result, install) - router.go: add linearRoutes entries for ScreenEngramDataDir, ScreenEngramDataDirConfirm, ScreenEngramDataDirResult (all back to Welcome) and ScreenEngramDataDirInstall (forward to DependencyTree, back to Preset) - app.go: pre-load HomeDir, EngramDetected (via engramIsPresent), and EngramDataDirFn closure (via buildEngramDataDirFn) before tui.NewModel() - welcome.go/welcome_test.go: add hasEngram bool param to WelcomeOptions and RenderWelcome; insert 'Manage Engram directory' before 'Manage backups' Depends on PR D1 (feat/engram-data-dir-screens).
c6cc63d to
ccba562
Compare
Make Engram install-flow transitions and disk preflight deterministic while normalizing Windows path and line-ending edge cases that were causing flaky tests.
Isolate HOME/profile discovery side effects and normalize path-sensitive assertions so CLI integration tests behave consistently on Windows and Unix.
Contributor
Author
|
Superseded by the ≤400 LOC chain (no \size:exception).
This draft exceeded the 400-line review budget vs \main. |
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 Wires the Engram data-directory screens (D1 / #488) into the TUI model, router, welcome menu, and app startup (issue #346, PR D2 of D1+D2): -
model.go— 4ScreenEngramDataDir*iota constants; engram state fields; helper methodshasEngram(),resolvedEngramDir(),engramDBSize(); fullUpdatehandlers for all four Engram screens -router.go— routes forScreenEngramDataDir(← Welcome),ScreenEngramDataDirConfirm(← DataDir),ScreenEngramDataDirResult(← DataDir),ScreenEngramDataDirInstall(Preset ↔ DependencyTree) -app.go— pre-loadHomeDir,EngramDetected(viaengramIsPresent), andEngramDataDirFnclosure (viabuildEngramDataDirFn) beforetui.NewModel()-welcome.go— newhasEngram boolparam; inserts "Manage Engram directory" before "Manage backups" when true ## Changes | File | Type | Lines | |------|------|-------| |internal/tui/model.go| Modified | +184 / −1 | |internal/tui/screens/welcome.go| Modified | +9 / −8 | |internal/tui/screens/welcome_test.go| Modified | +49 / −14 | |internal/app/app.go| Modified | +66 | |internal/tui/router.go| Modified | +6 | |internal/tui/model_test.go| Modified | +2 / −2 | | Total | | +316 / −24 = 340 / budget: 400 ✅ | ## Test plan - [x]go test ./internal/tui/screens/...— all tests pass including 14 new/updated Engram + Welcome tests - [x]go build ./...— clean build - [x] Full repogo test ./...— ✅ Pass — CI run - [x] E2Ecd e2e && ./docker-test.sh(ubuntu / arch / fedora) — ✅ Pass — same workflow - [x] Pre-existing failures (TestStartUninstall_*,TestPresetSelectionNextScreenFlowMatrix,TestCustomPresetPostComponentFlowMatrix) confirmed present on upstreammainbefore any of our changes ## PR Chain — Closes #346 | # | PR | Lines | |---|----|-------| | A | #465 | 152 ✅ | | B1 | #484 | 340 ✅ | | B2 | #485 | 171 ✅ | | B3 | #486 | 370 ✅ | | C | #487 | 33 ✅ | | D1 | #488 | 282 ✅ | | D2 | this PR | 340 ✅ | Supersedes #480 (closed — original monolithic PR, 642 lines over budget). --- ## CI status (GitHub Actions) — latest (ci: trigger unit and e2e workflows) Checks: https://github.com/Gentleman-Programming/gentle-ai/pull/489/checks Unit + E2E workflow: https://github.com/Gentleman-Programming/gentle-ai/actions/runs/25600339510 | Gate | Status | |------|--------| | Unit Tests (go test ./...) | ✅ Pass | | E2E (ubuntu) | ✅ Pass | | E2E (arch) | ✅ Pass | | E2E (fedora) | ✅ Pass | | Issue reference (Closes #346) +status:approved| ✅ Pass | | PR cognitive load vsmain| ⚠️ Fails while stack is open (full diff vsmain); this slice ≤400 lines ✅ | | Exactly onetype:*label | ⚠️ Maintainer applies |bash go test ./... cd e2e && ./docker-test.sh🤖 Generated with Claude CodeChain Context (SDD - stacked PRs targeting main)
The Summary / Changes tables above describe the scoped review unit for this numbered PR. GitHub diff vs main is cumulative while every PR still targets main, so totals grow down-chain even though each PR narrative stays slice-focused.
Merge-order diagram
main -> PR #465 -> PR #484 -> PR #485 -> PR #486 -> PR #487 -> PR #488 -> [THIS] PR #489Autonomy