refactor(tui): one palette, one settings save path, per-project prompt history - #314
Merged
Merged
Conversation
Max17190
force-pushed
the
trim-frontend-accretion
branch
from
September 7, 2026 15:37
7354d60 to
92864e7
Compare
| // surface in another project's composer. | ||
| let history_path = data_dir | ||
| .join("history") | ||
| .join(format!("{}.json", open_max_core::ledger::project_key(project_root))); |
There was a problem hiding this comment.
If two Unix project roots contain different invalid UTF-8 byte sequences, the project key converts both paths to the same lossy string before hashing. Those projects then use one prompt-history file, allowing prompts entered in one project to be recalled from the other. This must be fixed before merging.
How this was verified: Two directories differing only by invalid path bytes produced the same history path, and the second recalled a prompt written by the first.
Artifacts
- Authored Rust validation constructs two project roots with distinct invalid UTF-8 bytes and evaluates the exact history-path expression used by the TUI, demonstrating the collision condition.
- Executed control run shows distinct UTF-8 project directories have distinct keys and history paths, so the second project cannot read the first project's prompt.
- Executed non-UTF-8 run shows byte-distinct project paths produce equal keys and history paths, and the second project reads the first project's prompt, confirming the disclosure.
Ran code and verified through T-Rex
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/tui/src/input.rs
Line: 139
Comment:
**Preserve Unix Path Bytes**
If two Unix project roots contain different invalid UTF-8 byte sequences, the project key converts both paths to the same lossy string before hashing. Those projects then use one prompt-history file, allowing prompts entered in one project to be recalled from the other. This must be fixed before merging.
**How this was verified:** Two directories differing only by invalid path bytes produced the same history path, and the second recalled a prompt written by the first.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.…t history - `/theme` and its three color palettes are removed. The shipped default was monochrome, no theme was persisted (settings.json has no field for one), and the command was undocumented, so a chosen palette lived exactly one process. Tokens are now constants: no palette store, no lock, no init call. - Settings had two save paths: the core's `save_settings`, which fingerprints the exact bytes it writes under its lock, and a TUI-side `config::save` followed by `adopt_saved_settings`, which fingerprinted a re-serialization. The model picker now uses the core's path like `/provider` does, the adoption shim and `config::save` are deleted so no future caller can write settings.json past the fingerprint, and the receipt test asserts the same drift detection through the single path. - Prompt history was one global file, so one project's prompts surfaced in another project's composer through Up and Ctrl+R. It is now keyed by the canonical project root like every other per-project store, through a shared `ledger::project_key`. - The empty-session "READY" widget was a module with two tests for one dim word; it is one function beside its only caller. - `/compact` and `/export` were shipped but absent from the command table; they are documented. - Three self-descriptions said things the code does not do. The `--run-examples` help described the pre-sandbox refusal and omitted that auto runs every valid tool on the host. The ledger module doc claimed hash chaining makes tampering through bash detectable; it detects a torn write or a naive edit, not a rewrite of log and pin together. Recall's doc listed session titles as a searched source when `collect_chunks` deliberately does not search them. Each now states the mechanism precisely.
Max17190
force-pushed
the
trim-frontend-accretion
branch
from
September 7, 2026 15:50
92864e7 to
9188396
Compare
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.
Why
The shipped shape carried frontend accretion that a user had to understand without benefiting from it, two mechanisms for one job, a cross-project leak, and three self-descriptions that no longer matched the code. Each item below was checked against the code before it was changed.
Summary
/themeand its three color palettes are removed. The shipped default was monochrome, no theme was persisted (settings.json has no field for one), and the command was undocumented, so a chosen palette lived exactly one process. Tokens are now constants: no palette store, no lock, no init call.save_settingsfingerprints the exact bytes it writes under its lock; the model picker used a TUI-sideconfig::savefollowed byadopt_saved_settings, which fingerprinted a re-serialization. The picker now uses the core's path like/providerdoes, the adoption shim is deleted, andconfig::saveis gone, so no future caller can write settings.json past the fingerprint. The receipt test asserts the same drift detection through the single path.ledger::project_key. That key now hashes the path's bytes rather than a lossy UTF-8 rendering, so two roots differing only in invalid bytes no longer share a key (a pre-existing collision the ledger directory had too); valid UTF-8 paths keep their existing keys, and a regression test pins the distinction./compactand/exportwere shipped but absent from the command table; they are documented.--run-exampleshelp still described the pre-sandbox refusal and omitted that auto runs a valid tool on the host without content approval, under the same permission rules and pre_tool_use hooks as a turn. The ledger module doc claimed hash chaining makes tampering through bash detectable; it detects a torn write or a naive edit, not a rewrite of log and pin together. Recall's doc listed session titles as a searched source whencollect_chunksdeliberately does not search them.Test Plan
cargo test --workspace --locked: 940 passed, 0 failed, 11 ignored (the four removed tests were the palette-distinctness test, the two "READY" size tests, and a failure-path test for a helper that no longer performs I/O).cargo +1.97.0 clippy --workspace --all-targets --locked -- -D warnings: clean.git grepfor every removed identifier (ThemeId,set_tokens,detect_color_level,adopt_saved_settings,save_model_selection,invalidate_styles, thereadymodule) returns nothing.Greptile Summary
This PR simplifies the TUI palette and empty-session rendering, consolidates settings persistence through the core, scopes prompt history by canonical project root, and corrects user-facing documentation.
The project-specific prompt-history key now preserves raw Unix path bytes, preventing collisions between roots with distinct invalid UTF-8 sequences. The
--run-exampleshelp text now accurately explains that permission rules and pre-tool-use hooks apply.Confidence Score: 5/5
Safe to merge.
The previously reported prompt-history collision is fully fixed because the current project-key implementation hashes canonical Unix path bytes. The help-text thread was resolved by greptile-apps[bot] without explanation; the current help text describes the applicable permission rules and pre-tool-use hooks.
Files Needing Attention: None.
Reviews (2): Last reviewed commit: "refactor(tui): one palette, one settings..." | Re-trigger Greptile