Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ Release notes for the GitHub Release: [`docs/release-notes/v0.7.0.md`](docs/rele
folders, and loose files, hidden space and the rest as unnamed sums; no file names or lists) in `~/Library/Application Support/mac-dir-stat/history.json`, six per
folder and twenty folders at most. It never leaves the Mac.

- **Plans can't trash your own folders.** A plan proposed over the web page, REST or MCP that
would trash Documents, Desktop, Pictures, Movies, Music, Mail, Messages, a Photos library,
anything inside one (also through a link to another disk), or a folder that holds one is refused
with an error naming the folder, and can't be approved. Before, only your approval stopped it.
A project's build output inside them (`node_modules`, `target/`, `.next/`, …) may still go.

### Changed
- **New look: the survey register.** Book typography on paper: Source Serif 4 (bundled, SIL Open
Font License) for all text with a real italic for notes, ink on paper, hairline rules, and one
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,12 @@ file is, so the token stays out of log files. The page works on a phone in light
disks and free space, the largest folders and files, cleanup groups, and a find box (`node_modules`,
`.venv`, `*.dmg`, …). Tick items, choose **Move to Trash** or **Move to <disk>**, and create a
**plan**. The plan lists every item with its size, destination, errors and warnings. Nothing runs
until you press **Approve**.
until you press **Approve**. A plan that would trash one of your own folders (Documents, Desktop,
Pictures, Movies, Music, Mail, Messages, a Photos library), anything inside one (also when the
folder was moved to another disk and left as a link), or a folder that holds one is refused: the
error names the folder, and the plan can't be approved. A project's build output inside one, such
as `node_modules` under Documents, may still go. This is the same
list as the Cleanup window's *Only you have these* column.

It listens on `127.0.0.1` only, so a phone cannot reach it until you pass `--bind`. Over
Tailscale, bind to the Mac's Tailscale IP (`--bind 100.x.y.z`; `tailscale ip -4` shows it) and
Expand Down
1 change: 1 addition & 0 deletions docs/SIGNATURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ scale bar, names set in the parcel.
| 2026-10-30 | 3. The disk over time | 3B small multiples of past scans | Pavol | Store a small summary per scan (totals and top folders, no file lists), show at most 6, and keep scans on the Mac: nothing leaves the machine. |
| 2026-10-30 | Visual signature | Survey register (serif, paper, oxblood accent) | Pavol | Replaces the violet, dark-first theme. Keep a dark variant that follows the system setting. |
| 2026-10-30 | All products | A move approved for one product may be reused by another | Pavol | "Dont just blindly copy the pattern though." |
| 2026-11-03 | Safety: plan checks | Refuse such plans in validation | Pavol | Chosen on the decision page (card mac-dir-stat.o0, "Should the MCP server refuse cleanup plans that include one of your own folders?", 11:52 UTC), no note; relayed by the orchestrating session. Plans that would trash an only-yours folder, anything in it (also through a link) or a folder holding one are refused, naming the folder. Reversible call on top (not Pavol's): a project's build output inside them, such as `node_modules` under Documents, stays allowed, as the inventory lists it on the rebuildable side. |

---

Expand Down
139 changes: 121 additions & 18 deletions docs/review/2026-10-30.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,109 +35,168 @@
"decisions": [
{
"question": "How to run tests in a Linux container for a macOS-only crate?",
"options": ["cfg shims for the 4 macOS-only calls", "skip macOS-only tests off a Mac", "don't test locally"],
"options": [
"cfg shims for the 4 macOS-only calls",
"skip macOS-only tests off a Mac",
"don't test locally"
],
"default_taken": "cfg shims; no tests skipped",
"why": "Lets 100+ tests run here without touching macOS behaviour; the 6 that need macOS still run in CI."
},
{
"question": "Screenshots without a Mac or GPU?",
"options": ["CPU rasteriser over egui's meshes (examples/screenshots.rs)", "update the mock-ups instead"],
"options": [
"CPU rasteriser over egui's meshes (examples/screenshots.rs)",
"update the mock-ups instead"
],
"default_taken": "CPU rasteriser; the real App::update over a made-up home folder",
"why": "Shows what was built, not a sketch. Font hinting differs a little from macOS; check on a Mac before release."
},
{
"question": "Which Source Serif 4 cut?",
"options": ["SmText (small-text optical size)", "Text", "Display"],
"options": [
"SmText (small-text optical size)",
"Text",
"Display"
],
"default_taken": "SmText Regular + Italic, bundled (SIL OFL)",
"why": "The UI is set at 10–14 pt, where SmText reads best."
},
{
"question": "Tabular figures for sizes?",
"options": ["accept the face's default figures, right-aligned", "bundle a tabular-figures font for numbers"],
"options": [
"accept the face's default figures, right-aligned",
"bundle a tabular-figures font for numbers"
],
"default_taken": "default figures, right-aligned",
"why": "egui can't switch OpenType features; columns still align on their right edge."
},
{
"question": "Colour of destructive buttons?",
"options": ["the oxblood accent", "a separate red"],
"options": [
"the oxblood accent",
"a separate red"
],
"default_taken": "oxblood",
"why": "Move to Trash is the one action of its window; SIGNATURE.md allows one accent per window."
},
{
"question": "In-app light/dark toggle?",
"options": ["follow the system only", "add a toggle"],
"options": [
"follow the system only",
"add a toggle"
],
"default_taken": "follow the system only",
"why": "Pavol asked for a dark variant that follows the system setting."
},
{
"question": "How to combine 1A and 1B?",
"options": ["1B's line under each left-column row", "a separate 1B table"],
"options": [
"1B's line under each left-column row",
"a separate 1B table"
],
"default_taken": "one line under each row: 'To get it back: …. Last changed ….'",
"why": "The proposal's recommendation; keeps the two-column look."
},
{
"question": "Time estimates in minutes (mock-up: '~10 min')?",
"options": ["plain words per rule", "minute estimates"],
"options": [
"plain words per rule",
"minute estimates"
],
"default_taken": "plain words ('about a minute each' only for npm)",
"why": "No claim the app can't back (Article VIII)."
},
{
"question": "Label for trashed bytes no rule covers in the closing account?",
"options": ["'of which no tool can rebuild'", "'of which only you had' (mock-up)"],
"options": [
"'of which no tool can rebuild'",
"'of which only you had' (mock-up)"
],
"default_taken": "'of which no tool can rebuild'",
"why": "An app bundle trashed from the map isn't something only you had; the mock-up wording would misstate it."
},
{
"question": "When does the closing account show?",
"options": ["after trashes made while Cleanup is open, until Close", "after every trash"],
"options": [
"after trashes made while Cleanup is open, until Close",
"after every trash"
],
"default_taken": "while Cleanup is open; closing the window clears it",
"why": "No pop-up after a single trash from the map."
},
{
"question": "Ledger row layout?",
"options": ["one line with a bar column", "bar under each name (mock-up)"],
"options": [
"one line with a bar column",
"bar under each name (mock-up)"
],
"default_taken": "one line; bar scaled to the largest row of its level",
"why": "Keeps the tree compact and still compares by length."
},
{
"question": "Where does the folder list start when the map is zoomed?",
"options": ["at the zoomed folder", "always at the scan root"],
"options": [
"at the zoomed folder",
"always at the scan root"
],
"default_taken": "at the zoomed folder; Locate outside it zooms back out",
"why": "List and map show the same level, as 2A describes."
},
{
"question": "What does a history summary keep?",
"options": ["8 largest top-level folders by name; loose files, hidden and the rest as unnamed sums", "more folders", "deeper levels"],
"options": [
"8 largest top-level folders by name; loose files, hidden and the rest as unnamed sums",
"more folders",
"deeper levels"
],
"default_taken": "8 named top-level folders + unnamed sums; 6 per folder, 20 folders",
"why": "Pavol: totals and top folders, no file lists. File names are never kept."
},
{
"question": "Do partial refreshes (⇧⌘R) record history?",
"options": ["full scans only", "every refresh"],
"options": [
"full scans only",
"every refresh"
],
"default_taken": "full scans only",
"why": "A partial refresh isn't a new look at the whole folder."
},
{
"question": "Version",
"options": ["bump Cargo.toml to 0.7.0 now", "leave at 0.6.0 until release"],
"options": [
"bump Cargo.toml to 0.7.0 now",
"leave at 0.6.0 until release"
],
"default_taken": "bumped to 0.7.0; CHANGELOG heading says 'not yet released'",
"why": "The tag must equal Cargo.toml's version; with the bump done, releasing is one tag push when Pavol decides."
}
],
"proposals": [
{
"name": "Use the inventory in the server's plan checks, so a plan proposed over MCP can't include an only-yours folder",
"options": ["refuse such plans in validation", "warn in the plan", "leave the server as it is"],
"options": [
"refuse such plans in validation",
"warn in the plan",
"leave the server as it is"
],
"recommendation": "Refuse in validation. Today an AI can propose trashing ~/Documents and only Pavol's approval stops it; the inventory already knows these folders."
},
{
"name": "Bring the landing site (site/) and its screenshot into the survey register",
"options": ["update with the 0.7.0 release", "update now", "keep the old look"],
"options": [
"update with the 0.7.0 release",
"update now",
"keep the old look"
],
"recommendation": "Update with the release: it's outward-facing, so it waits for Pavol's sign-off."
},
{
"name": "A one-line 'since last scan' note (3A) drawn from the stored history",
"options": ["add it under the toolbar after a rescan", "leave history to the History window"],
"options": [
"add it under the toolbar after a rescan",
"leave history to the History window"
],
"recommendation": "Wait for return-scan data: 3A wasn't picked, and History already shows growth."
}
],
Expand All @@ -162,5 +221,49 @@
"docs/screenshots/main-light.png",
"docs/screenshots/main-dark.png",
"docs/screenshots/inventory-dark.png"
],
"followups": [
{
"date": "2026-11-03",
"title": "Plans refuse to trash your own folders (proposal 1)",
"pr": "https://github.com/Chartres/mac-dir-stat/pull/20",
"merged": true,
"decided_by": "Pavol, 2026-11-03 11:52 UTC on decision card mac-dir-stat.o0: \"Refuse such plans in validation\" (relayed by the orchestrating session; recorded in SIGNATURE.md's decision log)",
"what": "Plan validation refuses any trash op that is, is inside, or holds Documents, Desktop, Pictures, Movies, Music, Mail or Messages under home, or a Photos library, naming the folder; the plan can't be approved. Checked by the path as asked and as resolved, and at each folder's real location if it is a link.",
"checked": "Tests first (unit, inventory, and an MCP journey proposing ~/Documents and a subfolder, refused, approve 409). macOS CI green on head. Independent review found two blockers (symlink bypass; folder holding a Photos library), both fixed and confirmed closed by a second review.",
"decisions": [
{
"question": "Moves to an external disk of an own folder?",
"options": [
"allowed (copy verified first)",
"refused too"
],
"default_taken": "allowed",
"why": "Offloading Movies or Pictures is what Move to disk is for; only trash is cleanup."
},
{
"question": "Build output inside an own folder (node_modules under ~/Documents/project)?",
"options": [
"allowed for project build rules only",
"refused"
],
"default_taken": "allowed for node_modules, target/, __pycache__, .pytest_cache, .next, .turbo, loose DerivedData; caches, logs, .gradle, .m2 stay refused",
"why": "The inventory lists them as rebuildable; refusing would contradict the Cleanup suggestions."
},
{
"question": "Refuse at propose (4xx) or store an invalid plan?",
"options": [
"invalid plan with the reason",
"HTTP error"
],
"default_taken": "invalid plan",
"why": "Same as protected system paths today; the model sees why."
}
],
"open": [
"A subfolder moved to disk with a link left behind (e.g. ~/Documents/Proj -> ext/Proj) is protected only through the link, not by its real path. Fixing it means remembering what the app moved; not done.",
"The Photos-library search is bounded (20k folders, 4 levels); a larger tree is allowed rather than refused. Folding it into relocate::measure would remove the bound."
]
}
]
}
Loading
Loading