The file tools are absent from the list unless file access is on, which is the
difference between a model that cannot call them and one that is told not to. Each
handler is thin: argument checks here, and the operation itself in files.ts.
move_path reads `from`, not the shared path helper, which is the bug the tests
caught: an empty source path would have been refused by the policy on every move.
The wording lives beside the dialog rather than beside the paths, which is what
keeps files.ts free of UI and lets every refusal path be tested without a
launcher. Vicinae's rememberUserChoice is declared in the API but not implemented
in the server, so the ledger is the only memory and it lasts one answer.
A dialog that cannot be shown is a refusal, not consent.
One module with no Vicinae import, so the whole boundary tests headlessly: the
roots, the credential denylist, resolution that follows symlinks before judging
containment, and the seven operations, each returning a result rather than
throwing, so a refusal and an I/O failure reach the model in the same shape.
The mutating operations take an approval callback rather than importing a dialog,
which is what keeps this file free of UI and lets every refusal path be tested.
52 tests, including a symlink out of a root, a sibling whose name merely starts
like a root, and a temporary home with its own symlinks resolved first, because
mkdtemp on macOS hands back a path under /var.
Ten tasks: the path policy and its operations, the approval dialog and its ledger,
seven tools, three preferences, what the model is told, the hook and setup screen
wiring, the README, and a verification task that mutation checks the policy rather
than trusting the suite.
Seven file tools behind a path policy and an approval step, with the platform
findings that shaped it: extensions are unsandboxed Node, confirmAlert exists,
rememberUserChoice does not, and environment.supportPath is the one directory an
extension is invited to write to.
Records the decisions taken with the user (read, write, edit and destructive
tools; one approval per path per answer; home minus a credential denylist; extra
roots in preferences) and the assumptions that follow from them, including the
two that go beyond the questions asked: delete and move asking every time, and
file access being off until switched on.