●SHEETS — Fill with Gemini expands to 28 more languages, so you can build spreadsheets in your own language with natural prompts●ALPHAEVOLVE — Gemini Enterprise makes AlphaEvolve generally available, autonomously discovering optimized code solutions●SPARK — Gemini Spark, a personal AI agent for macOS, organizes folders and runs workflows across Workspace●OMNI — Gemini Omni blends text, photos, and video for high-quality creation, plus custom AI avatars of you●GEMINI35 — Gemini 3.5 Pro slips to July 17 for a full rebuild, introducing a 2M-token context and Deep Think●DEEPTHINK — The 3.5 Pro rebuild targets better math reasoning, SVG scene generation, and image quality●SHEETS — Fill with Gemini expands to 28 more languages, so you can build spreadsheets in your own language with natural prompts●ALPHAEVOLVE — Gemini Enterprise makes AlphaEvolve generally available, autonomously discovering optimized code solutions●SPARK — Gemini Spark, a personal AI agent for macOS, organizes folders and runs workflows across Workspace●OMNI — Gemini Omni blends text, photos, and video for high-quality creation, plus custom AI avatars of you●GEMINI35 — Gemini 3.5 Pro slips to July 17 for a full rebuild, introducing a 2M-token context and Deep Think●DEEPTHINK — The 3.5 Pro rebuild targets better math reasoning, SVG scene generation, and image quality
What I Decided Before Letting an Agent Touch My Files: Folder Boundaries and Guardrails for Gemini Spark
A practical guardrail design for handing local folder cleanup to an autonomous agent: a content-hash manifest, an allowlist policy, and a post-run diff audit that catches irreversible operations before they slip past you.
When Gemini Spark arrived on macOS, the first thing I wanted to hand it was folder cleanup. Working solo as an indie developer, I accumulate image assets, half-finished drafts, and deliverables exported for the App Store and Google Play until a single workspace holds hundreds of files. Being able to say "reorganize this into something meaningful" in one sentence would genuinely help.
But I stopped before actually running it. Cleanup, taken to its core, is a sequence of moves and deletes. And moves and deletes usually cannot be undone. I wanted to delegate, yet the moment I delegated, things might become unrecoverable. I spent a while thinking about how to close that gap.
This article walks through the guardrails I actually built before letting a local file agent like Spark handle organization. The idea is not to doubt the agent's intelligence, but to insert human verification only around the irreversible operations.
Putting the fear into words
Stopping at a vague "this is scary" does not produce a design. So I first broke down exactly what I was afraid of.
What I feared
Concrete accident
How recoverable
Unintended deletion
A draft judged "probably unneeded" gets removed
Low (depends on backups)
Out-of-scope move
A published deliverable drifts into a working folder
Medium
Silent overwrite
A same-named file is replaced with different content
Low
Untraceable change
Hundreds of files processed at once, diff impossible to follow
Low
Laid out this way, the real shape of the fear was not "the operations themselves" but "not being able to confirm their results." If that is the case, the countermeasure is clear. Before restricting what the agent may do, build a state where you can always confirm afterward what the agent did.
Start by separating what to delegate from what to keep
Before writing any guardrail code, I set a principle for where to draw the line. I split work like this.
Reversible or proposal-only work — classification, reordering, naming suggestions — I delegate freely. Irreversible work — deletion, moving files out of the workspace, overwriting existing files — I never let the agent execute directly; it always routes through human approval.
Turning that principle into machinery produced three guardrails. Record the workspace state before the run, declare the range that may be touched, and reconcile the declaration against the actual diff after the run. Let's take them in order.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦A content-hash manifest taken before the run, so you can mechanically verify exactly what the agent touched afterward
✦An allowlist that declares what may be moved and what must never be deleted, with automatic rollback on violations
✦Measured results of the misoperation checks on my own workspace, plus a clear line between what to delegate and what to keep
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Guardrail 1: Take a content-hash manifest before the run
First, freeze the pre-run workspace state as a table mapping file paths to content hashes. File names alone cannot catch an overwrite where the contents were swapped, so hashing the content itself is the key.
import hashlibimport jsonfrom pathlib import Pathdef build_manifest(root: Path) -> dict[str, str]: """Record every file under the workspace as {relative path: content hash}.""" manifest: dict[str, str] = {} for path in sorted(root.rglob("*")): if not path.is_file(): continue # Skip temp files and hidden folders if any(part.startswith(".") for part in path.relative_to(root).parts): continue digest = hashlib.sha256(path.read_bytes()).hexdigest() manifest[str(path.relative_to(root))] = digest return manifestroot = Path.home() / "Workspace"before = build_manifest(root)Path("manifest_before.json").write_text(json.dumps(before, ensure_ascii=False, indent=2))print(f"Files recorded: {len(before)}")
The reason for hashing content here is to distinguish, later, whether a file was moved, overwritten, or deleted. If the same hash appears at a new path, it was moved; if a hash simply disappears, it was deleted; if the path stays but the hash changes, it was overwritten. Watching name matches alone cannot tell these apart.
Guardrail 2: Declare the operating range with an allowlist
Next, explicitly declare the range the agent may touch. Rather than implicitly granting broad permissions on the assumption that "it will probably be fine," write it in the direction of narrowing the allowed operations.
from dataclasses import dataclass, field@dataclassclass Policy: # Folders where reordering is allowed (moves permitted only under these) movable_roots: list[str] = field(default_factory=lambda: ["_inbox", "_sorting"]) # Folders that must never be deleted or moved protected_roots: list[str] = field(default_factory=lambda: ["_published", "_master"]) def allows_move(self, rel_path: str) -> bool: top = rel_path.split("/", 1)[0] return top in self.movable_roots and top not in self.protected_roots def is_protected(self, rel_path: str) -> bool: top = rel_path.split("/", 1)[0] return top in self.protected_roots
In my workspace, cleanup targets are limited to the intake folder _inbox and the in-progress _sorting, while the published _published and the original _master are protected. I tell the agent to "only reorder inside these two folders," and the verification side enforces that same declaration. Doubling up the request in the prompt with a check in code is essential. A promise made only in words gives you no way to notice when it is broken.
Guardrail 3: Audit the post-run diff and roll back violations
After the agent runs, take the manifest again and reconcile before against after. This is the heart of the mechanism. Mechanically confirm that nothing protected changed, that moves stayed within the allowed range, and that there were no unintended deletions; if a violation appears, route it to human approval.
def audit(before: dict[str, str], after: dict[str, str], policy: Policy) -> list[str]: violations: list[str] = [] before_hashes = set(before.values()) # Vanished files: if a hash is nowhere in 'after', it was deleted for path, digest in before.items(): if digest not in after.values(): if policy.is_protected(path): violations.append(f"Deletion of protected file: {path}") elif path not in after: violations.append(f"Content lost (deletion, not a move): {path}") # Newly appearing paths: detect moves/creation outside the allowed range for path, digest in after.items(): if path in before: if before[path] != digest: violations.append(f"Overwrite of existing file: {path}") continue if digest in before_hashes: # Existing content appeared at a different path = a move if not policy.allows_move(path): violations.append(f"Move outside allowed range: {path}") else: if policy.is_protected(path): violations.append(f"New creation inside protected folder: {path}") return violationsafter = build_manifest(root)issues = audit(before, after, Policy())if issues: print("Policy violations detected. Routing to approval.") for line in issues: print(" -", line)else: print("All changes were within the allowed range.")
Before inserting this check, I would hand off cleanup, open the folders myself, glance around, and settle on "probably fine." Eyeballing works for a few dozen files, but breaks down at a few hundred. Once I handed the verification to the machine, only the violating lines remained in front of me, and the set of things I had to confirm shrank to a handful.
Placing Before and After side by side makes the change in confirmation quality clear.
Aspect
Before (eyeballing only)
After (with diff audit)
What to confirm
Every folder that seemed changed
Only the few flagged as violations
Detecting overwrites
Sometimes missed
Caught reliably by hash difference
Mental load
"Maybe I missed something" lingers
You can say "within range" with confidence
Results from running it on my own workspace
I actually ran cleanup through this mechanism over roughly 480 image assets and documents that had piled up in the intake folder. The rough measured figures were as follows.
Item
Measured
Files covered by the manifest
~480
Time to build the pre-run manifest
about 3 seconds
Policy violations found after cleanup
2 (proposed moves next to a protected folder)
Items needing manual review
Only the 2 violations
The two detected cases were proposals to create a similarly named temporary folder right next to the published folder. Not bad operations, but off from my intent. Without the mechanism, those two would likely have been buried among hundreds of changes and slipped by. The value of the guardrail, I feel, lies less in stopping accidents outright and more in surfacing the "few items worth confirming."
The line between what to delegate and what to keep
Finally, here is the line I currently use, as a table. This is not a fixed correct answer but a starting point for judging by how recoverable something is.
Task
How I delegate
Why
Classification / tagging proposals
Delegate
Stays a proposal, reversible
Reordering within working folders
Delegate with verification
Range limited, confirmable by diff
Applying naming conventions
Delegate with verification
Safe if overwrites are detected
Deleting unneeded files
Keep for myself
Irreversible, needs context to judge
Moving published deliverables
Keep for myself
Hard to read the blast radius
The smarter the agent gets, the wider the range you can delegate. Even so, keep human approval around the irreversible operations alone. The tools for holding that one line were the content-hash manifest, the allowlist, and the diff audit.
I have only just begun to use Spark in earnest, but because I prepared the tools first, I could take that first step calmly. If you are looking to hand local cleanup to an agent too, I hope this helps with your own design. Thank you for reading.
Share
Thank You for Reading
Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.