GEMINI LABJP
3.8 FLASH — gemini-3.8-flash reached general availability on September 2, aimed at long-horizon software work, autonomous agents and complex enterprise workflowsLYRIA 3.5 — The lyria-3.5 music model is in public preview. It generates full-length songs in 44.1 kHz stereo and accepts both text and image inputSEPT 30 — Fifteen days until gemini-omni-flash-preview shuts down. Its successor, gemini-omni-1.1-flash, has been generally available since August 27CACHE — The documented minimum for implicit caching is 4,096 tokens, but developers report nothing firing until past 12k. Whether it is working is something you have to measure yourselfNEW — When BLOCK_NONE changes nothing. Telling apart the cases a lower threshold clears from the ones it never will403 — Listing models returns 200 while generateContent alone returns 403. The same question resurfaces weekly, including on projects that have just enabled billing3.8 FLASH — gemini-3.8-flash reached general availability on September 2, aimed at long-horizon software work, autonomous agents and complex enterprise workflowsLYRIA 3.5 — The lyria-3.5 music model is in public preview. It generates full-length songs in 44.1 kHz stereo and accepts both text and image inputSEPT 30 — Fifteen days until gemini-omni-flash-preview shuts down. Its successor, gemini-omni-1.1-flash, has been generally available since August 27CACHE — The documented minimum for implicit caching is 4,096 tokens, but developers report nothing firing until past 12k. Whether it is working is something you have to measure yourselfNEW — When BLOCK_NONE changes nothing. Telling apart the cases a lower threshold clears from the ones it never will403 — Listing models returns 200 while generateContent alone returns 403. The same question resurfaces weekly, including on projects that have just enabled billing
Articles/Dev Tools
Dev Tools/2026-09-15Advanced

The approval rules I had written never matched once — auditing my Gemini CLI policies

A deny rule I trusted for half a year had never matched. The pattern was tested against a JSON string, the folder I kept my rules in is not read, and ask_user turns into deny the moment nobody is watching. Here is the audit, and the script I wrote to keep doing it.

gemini-cli5policy-engineapproval-modemcp3automation54python106

Premium Article

The asset conversion I had left running overnight had not moved a single line by morning.

As an indie developer I keep a set of wallpaper apps, and the dull part of shipping new images — resizing to fixed dimensions, renaming to match a convention — is something I hand to a coding agent. The work itself is simple enough that I only wanted to write down where the line sits, then leave it alone.

I opened the log expecting to find why it had stalled. What I found instead was less comfortable. The rule that was supposed to stop the dangerous command had never matched anything at all. Something else had stalled the run, and the line I had trusted for half a year was swinging at air.

I spent that day reading my policy files from the top. Here is what came out.

The short version: I recounted where matching actually happens

A policy rule is not a string compared against your command. The thing it is compared to, the folder it is loaded from, and the mode you are running in all change the answer. The four problems I hit were not about writing the rule wrong. In every case what I wrote never reached the point of being evaluated.

SymptomWhat was really happeningHow it shows up
A deny rule does nothingThe regex anchored to the start of a JSON stringYou never once see the deny message
Project-level rules are ignoredThat tier is not loaded right nowMoving the file to the user tier fixes it instantly
Unattended runs refuse instead of waitingask_user is treated as denyThe same run passes interactively
No confirmation on redirectionThe downgrade is skipped in permissive modesDefault mode does prompt

Let me take them one at a time.

A ^ in commandRegex anchors to the JSON, not to the command

This was the line I had written first, meaning to stop destructive deletes.

[[rule]]
toolName = "run_shell_command"
commandRegex = "^rm -rf"
decision = "deny"
priority = 900

There is nothing wrong with it as a regular expression. But the reference says commandRegex is tested against a stable JSON representation of the arguments. So the subject is not the command — it is the single string {"command":"rm -rf ./build"}.

I checked on my own machine.

import json
import re
 
args = {"command": "rm -rf ./build"}
subject = json.dumps(args, separators=(",", ":"))
print("subject:", subject)
 
for pattern in ("^rm -rf", "rm -rf", '"command":"rm -rf'):
    print(f"  {pattern!r:<22} -> {bool(re.search(pattern, subject))}")
subject: {"command":"rm -rf ./build"}
  '^rm -rf'              -> False
  'rm -rf'               -> True
  '"command":"rm -rf'    -> True

The moment you add the anchor, the rule stops matching. Worse, nothing tells you. A deny rule that swings at air prints exactly the same thing as one that never had to fire — nothing. A deny rule you have never watched actually deny something is a rule with no evidence behind it.

If you want anchoring, commandPrefix is the safer tool: it means what it looks like it means. Keep commandRegex for conditions that genuinely need a regex, and leave the anchors out. That alone cleared up my swings.

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
You will be able to find, mechanically and from your own files, the approval rules that have never matched a single call
You will know which tier actually gets read, so you stop writing careful rules into a folder nobody loads
You will be able to decide where ask_user belongs and where it has to become an explicit allow or deny, before an unattended run quietly refuses itself
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.

or
Unlock all articles with Membership →
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.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

Related Articles

Dev Tools2026-04-26
Build an Auto-Documentation Pipeline with Gemini API and GitHub Actions
Tired of outdated docstrings and READMEs? This guide shows you how to build a CI pipeline that uses Gemini API and GitHub Actions to automatically suggest documentation updates on every Pull Request.
Dev Tools2026-04-08
Automating Accessibility Audits with the Gemini API — A Design That Survives False Positives
Build a WCAG 2.2 audit pipeline with the Gemini API. Separate what can be measured from what needs judgment, then use structured output, baseline diffing, and tiered CI gates to keep false positives from killing the practice.
Dev Tools2026-07-02
url_context Still Answers When the Fetch Fails — Gating on Retrieval Status Before You Trust It
The url_context tool returns a confident answer even when it failed to fetch the target page. This walks through reading url_retrieval_status from url_context_metadata to build a verification gate, plus a fallback that only finalizes an answer when the source URL was truly read.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links